Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
972e7a9971 | ||
|
|
b23282d426 | ||
|
|
603aa1dfc8 | ||
|
|
b31a74e9af | ||
|
|
c33657e921 | ||
|
|
367ef208cb | ||
|
|
c6ab28a3ce | ||
|
|
b49d78e266 | ||
|
|
8c4ea103c0 | ||
|
|
bb4fde6374 | ||
|
|
74a4b28313 | ||
|
|
d9d06dd61d | ||
|
|
b9a25ecfac | ||
|
|
8bb587155e | ||
|
|
b9b6aef4c8 | ||
|
|
d3ca3e25d7 | ||
|
|
833f19a5cd | ||
|
|
174ffd0967 | ||
|
|
c4f0229373 | ||
|
|
a54bb91bba | ||
|
|
f78a8f2b55 | ||
|
|
be4b2a0504 | ||
|
|
c74934836f | ||
|
|
ec35c544b7 | ||
|
|
2bd48bc45c | ||
|
|
67ecee8322 | ||
|
|
a7b8611c70 | ||
|
|
dacaea7443 | ||
|
|
a7f7e00a14 | ||
|
|
5db3dcf8da | ||
|
|
66e011c71b | ||
|
|
d2ea240ed4 | ||
|
|
61bc6d3b7a | ||
|
|
f414fe447d | ||
|
|
98e3b0ce77 | ||
|
|
84ba42f42e | ||
|
|
7cc1ffcab2 | ||
|
|
669cd76018 | ||
|
|
8108158573 | ||
|
|
268f9de23c | ||
|
|
9fc7d0e825 | ||
|
|
ea1cdf52dd | ||
|
|
7cf6850cdf | ||
|
|
231fee52d2 | ||
|
|
1da198f3bc | ||
|
|
fff144a827 | ||
|
|
85ca2b1363 | ||
|
|
4d4399e95f | ||
|
|
05b153f9cc | ||
|
|
4aba290358 | ||
|
|
6a190589f5 | ||
|
|
6218da4c53 | ||
|
|
bdeb941682 | ||
|
|
2c042e7f11 | ||
|
|
8f899fca72 | ||
|
|
5f56ba4fb9 | ||
|
|
b50dd68030 | ||
|
|
95698911f9 | ||
|
|
156c8aa75b | ||
|
|
7df6f305c5 | ||
|
|
f34197b8d2 | ||
|
|
2483b8dbe3 | ||
|
|
0c8f13e1d9 | ||
|
|
85084e1808 | ||
|
|
d87920f556 | ||
|
|
4b5e6aa893 | ||
|
|
a5c1395c46 | ||
|
|
c48d5a73ca | ||
|
|
a68c928896 | ||
|
|
2eae236c5a | ||
|
|
0db5c1b8fa | ||
|
|
3632f4c8ab | ||
|
|
62bfe1da21 | ||
|
|
0291bcde17 | ||
|
|
e3e504310a | ||
|
|
ff23c54fd5 | ||
|
|
4cc2f7411c | ||
|
|
41e5478cb3 | ||
|
|
14d7e4d20c | ||
|
|
e24925c27f | ||
|
|
8ec98ef00c | ||
|
|
be44fb8aea | ||
|
|
f06098f3dd | ||
|
|
b30dc8d4f2 | ||
|
|
57f7af5f90 | ||
|
|
510a20133c | ||
|
|
3bd0f6f911 | ||
|
|
66b45ed35c | ||
|
|
34bd1e9c49 | ||
|
|
007c4fc003 | ||
|
|
ec80acf36b | ||
|
|
26ebe6d63a | ||
|
|
132bf89677 | ||
|
|
6d43e32f8c | ||
|
|
9066bd1a0f | ||
|
|
73da3e122e | ||
|
|
fbbcd47c4c | ||
|
|
21e0e68a02 | ||
|
|
21323b3755 | ||
|
|
6f2eb00c43 | ||
|
|
797ea29113 | ||
|
|
3bf8af4840 | ||
|
|
350807bc2d | ||
|
|
0fb313aec7 | ||
|
|
fe42eb1d01 | ||
|
|
1e9a481451 | ||
|
|
7f2f407e2a | ||
|
|
75c84e3b5d | ||
|
|
74a4926e83 | ||
|
|
41950ba4de | ||
|
|
583f32e5c5 | ||
|
|
2cedbcb9b3 | ||
|
|
075f1b5df4 | ||
|
|
2b1ba0b8f8 | ||
|
|
22319e8dfe | ||
|
|
72604d5d73 | ||
|
|
37ee7091f7 | ||
|
|
3c938252a7 | ||
|
|
09d0fb095f | ||
|
|
878b43ab31 | ||
|
|
b81193c41f | ||
|
|
3ad6e554dc | ||
|
|
c9037f55bc | ||
|
|
2c6b51857f | ||
|
|
398ae568a0 | ||
|
|
e17d41a15e | ||
|
|
8bf33a3fce | ||
|
|
e9f6473a00 | ||
|
|
c0d44a9e69 | ||
|
|
41cffc2418 | ||
|
|
d7c09df7d2 | ||
|
|
507f84df4c | ||
|
|
b39bbfc97a | ||
|
|
f85080ebd3 | ||
|
|
d14a96027a | ||
|
|
5fd86d29ba | ||
|
|
2461836ae8 | ||
|
|
15dbe227e8 | ||
|
|
f2a0ae0a2c | ||
|
|
46970ae424 | ||
|
|
f7bfa5c112 | ||
|
|
cad8ed89cc | ||
|
|
bae8180385 | ||
|
|
ea97d9dd02 | ||
|
|
27f73e692f | ||
|
|
e4d1e0d4cb | ||
|
|
6f90a8d027 | ||
|
|
7a03e0ef12 | ||
|
|
aa7ad516ee | ||
|
|
d05c4ea9ef | ||
|
|
72921f04bb | ||
|
|
9b4322c8c7 | ||
|
|
5db7988066 | ||
|
|
1634a14246 | ||
|
|
b7dfd17cbb | ||
|
|
7f16c55ff4 | ||
|
|
15e4d89d20 | ||
|
|
a276fe13a6 | ||
|
|
5042a1f853 | ||
|
|
2003d90467 | ||
|
|
12795c5995 | ||
|
|
5084cddf37 | ||
|
|
064d3dd5f7 | ||
|
|
a3201f4d28 | ||
|
|
bbefc67b54 | ||
|
|
bf24cf48fc | ||
|
|
2ff27674d0 | ||
|
|
c80cd9d0b0 | ||
|
|
c019a68fc5 | ||
|
|
12051a5164 | ||
|
|
5ca09cdf09 | ||
|
|
2c44f83453 | ||
|
|
a545f8c1a2 | ||
|
|
561a24c481 | ||
|
|
1ea6552cd1 | ||
|
|
96065d11a5 | ||
|
|
d82c72f1c1 | ||
|
|
202cc5a797 | ||
|
|
289e78dbd2 | ||
|
|
6dd63db16c | ||
|
|
fa00564883 | ||
|
|
c65ef9bb31 | ||
|
|
a33cad0d13 | ||
|
|
979a439078 | ||
|
|
bdd958b24b | ||
|
|
34b2168d0d | ||
|
|
2c90e7c585 | ||
|
|
2105f4d805 | ||
|
|
6a05f4fb4e | ||
|
|
0d10282dbe | ||
|
|
8c23d12487 | ||
|
|
31e801cc39 | ||
|
|
a2587f94da | ||
|
|
40b782788a | ||
|
|
45163e2d2d | ||
|
|
b711cbae94 | ||
|
|
b84dc20ded | ||
|
|
100288b3e1 | ||
|
|
3eb4d2c341 | ||
|
|
a0a482aa8e | ||
|
|
3d63b6006b | ||
|
|
7d86af2b00 | ||
|
|
c60e80d25b | ||
|
|
fb728baf0c | ||
|
|
436db99a5b | ||
|
|
ac9b2f7c8f | ||
|
|
f0f0634e83 | ||
|
|
d6d4629b5a | ||
|
|
5283ba9ebb | ||
|
|
0c352ab143 | ||
|
|
7c89336ea9 | ||
|
|
bca1a2f95b | ||
|
|
6a2f120d00 | ||
|
|
e8ccfb6743 | ||
|
|
69333ea64f | ||
|
|
8f5e2c4af3 | ||
|
|
e5c0d0ea18 | ||
|
|
a35712c708 | ||
|
|
47e64c269c | ||
|
|
cd69b03ac1 | ||
|
|
5afaa5bce6 | ||
|
|
60d62a6ae3 | ||
|
|
4f46adc389 | ||
|
|
f2bc6f773b | ||
|
|
d2dfcf36d9 | ||
|
|
353768b4b5 | ||
|
|
11ad8c7b30 | ||
|
|
4f3e83b363 | ||
|
|
263ec64453 | ||
|
|
140dc6db25 | ||
|
|
47a7097044 | ||
|
|
b8e774107a | ||
|
|
3d3c78260b | ||
|
|
e8f4c4d8b8 | ||
|
|
2156b28445 | ||
|
|
a9fc74a9b4 | ||
|
|
e0a1651784 | ||
|
|
02de9164f0 | ||
|
|
140a2d7915 | ||
|
|
17c4c96c29 | ||
|
|
2e1162ed73 | ||
|
|
56e5585730 | ||
|
|
51a0dd8861 | ||
|
|
8bfd94474a | ||
|
|
6789e8564a | ||
|
|
8b6856fc89 | ||
|
|
b48c6a5b9c | ||
|
|
b39b9f5463 | ||
|
|
e72f186a36 | ||
|
|
c3d495e7c6 | ||
|
|
70e5b71bdd | ||
|
|
a84c8f518b | ||
|
|
98c6ea467a | ||
|
|
6e73abde50 | ||
|
|
7ff6f24a90 | ||
|
|
6790c22794 | ||
|
|
60a9b4b934 | ||
|
|
8368461b57 | ||
|
|
7dc8a2d659 | ||
|
|
bddbaa7bc2 | ||
|
|
434fa3226a | ||
|
|
f9abfe5244 | ||
|
|
97d57b05ad | ||
|
|
4334fbbb9d | ||
|
|
e689feddcb | ||
|
|
595c657b57 | ||
|
|
375fda0eff | ||
|
|
ed04769947 | ||
|
|
44dbd462b1 | ||
|
|
291797b238 | ||
|
|
1178a48e68 | ||
|
|
34b7bfd19c | ||
|
|
02c418f4bb | ||
|
|
ae3b061d4c | ||
|
|
eb49bb5e10 | ||
|
|
38a238df80 | ||
|
|
79745d1892 | ||
|
|
8b31c0768c | ||
|
|
25e294fd92 | ||
|
|
017d2deeee | ||
|
|
f154c03385 | ||
|
|
e1e20275a6 | ||
|
|
3c4f8f6796 | ||
|
|
406a9492fd | ||
|
|
c859cd2278 | ||
|
|
cd70d9d09e | ||
|
|
86e7e1c600 | ||
|
|
e540148d09 | ||
|
|
30b108ab36 | ||
|
|
ec535aaa9c | ||
|
|
bb108b2144 | ||
|
|
f3c76ac60b | ||
|
|
23ccb3b991 | ||
|
|
992ecfefa2 | ||
|
|
fd390d2560 | ||
|
|
3f1ac6b5db | ||
|
|
d8f112278b | ||
|
|
0704cfb8b6 | ||
|
|
5e806c3879 | ||
|
|
27b0fd35b4 | ||
|
|
5c1818fc9a | ||
|
|
a7fb9662e3 | ||
|
|
cb00cfc1b7 | ||
|
|
d7bc8d100d | ||
|
|
d067b3976c | ||
|
|
892ee60527 | ||
|
|
526e10865f | ||
|
|
d83cb75f95 | ||
|
|
697aaa895d | ||
|
|
8add28d328 | ||
|
|
634df8f6f2 | ||
|
|
d194cc7a5a | ||
|
|
e38988126a | ||
|
|
0834f6ed1e | ||
|
|
9d5e288bca | ||
|
|
0fe40ad9ec | ||
|
|
4903f3a814 | ||
|
|
0765278195 | ||
|
|
f782b9c298 | ||
|
|
e611685e50 | ||
|
|
3edef95c4e | ||
|
|
9b902e1ca0 | ||
|
|
fc835815d8 | ||
|
|
da083c2e20 | ||
|
|
6ee17ab080 | ||
|
|
1e34585548 | ||
|
|
76cd5a2844 | ||
|
|
ffd1b93abf | ||
|
|
1eea102f26 | ||
|
|
94c1367684 | ||
|
|
9bf19f81a4 | ||
|
|
21de0ec396 | ||
|
|
ab4fec0f3c | ||
|
|
663384e62f | ||
|
|
b983479476 | ||
|
|
f6e5e514ef | ||
|
|
ddd828083e | ||
|
|
e246099794 | ||
|
|
e079b4df17 | ||
|
|
4e5ff17e18 | ||
|
|
86e1a9cd5d | ||
|
|
8a0efffdf8 | ||
|
|
c21f710a70 | ||
|
|
f3382abe3b | ||
|
|
0b3343674b | ||
|
|
d6c86f3cba | ||
|
|
3a6516f0cb | ||
|
|
b795115042 | ||
|
|
1d33701af0 | ||
|
|
52fe845d24 | ||
|
|
b2b6bb2d39 | ||
|
|
db40ca25ac | ||
|
|
df69ade422 | ||
|
|
3eef42eee0 | ||
|
|
38d0ad74a9 | ||
|
|
3bbc805ee4 | ||
|
|
c37a454bd5 | ||
|
|
6f98be5608 | ||
|
|
ac0f13ad18 | ||
|
|
ab4dab2ade | ||
|
|
88ffddfeba | ||
|
|
36f148a7cb | ||
|
|
fd1af07e43 | ||
|
|
fca87b53f0 | ||
|
|
ce0473480b | ||
|
|
828f6ea321 | ||
|
|
34b1654077 | ||
|
|
5dcd1b630e | ||
|
|
7ff4067e88 | ||
|
|
491d27638a | ||
|
|
e1b016cf3d | ||
|
|
c850669777 | ||
|
|
04c7372a4e | ||
|
|
9fe4aedec1 | ||
|
|
64ee60a008 | ||
|
|
b6d7c71cc8 | ||
|
|
778cd22e72 | ||
|
|
434ed895ed | ||
|
|
daf314b037 | ||
|
|
1bdc1017d6 | ||
|
|
6218d8c7ed | ||
|
|
c250554177 | ||
|
|
de772a8c3b | ||
|
|
23f1cf5f49 | ||
|
|
723fa11a74 | ||
|
|
94b602e167 | ||
|
|
b17359ec6f | ||
|
|
cdef8a33e4 | ||
|
|
97ff2bd2fa | ||
|
|
11a91bfc78 | ||
|
|
4282906833 | ||
|
|
aeb3dd0220 | ||
|
|
9647a33209 | ||
|
|
2d966863e3 | ||
|
|
a66632f767 | ||
|
|
1c2ee631f4 | ||
|
|
259ff3436f | ||
|
|
a750ecaafd | ||
|
|
8b3601248d | ||
|
|
b1cfd8229a | ||
|
|
202f4235d7 | ||
|
|
6420020309 | ||
|
|
65d86a42d7 | ||
|
|
4eaffdc6a4 | ||
|
|
454e8a3962 | ||
|
|
11e8a9d5c4 | ||
|
|
8137f2692f | ||
|
|
6933424fca | ||
|
|
1fff4e0eed | ||
|
|
9d2227bffb | ||
|
|
ff8a10c231 | ||
|
|
6a94500170 | ||
|
|
d9e361cf1b | ||
|
|
41eb47bc15 | ||
|
|
211f772447 | ||
|
|
6d6e3fd127 | ||
|
|
14ef0ab6d1 | ||
|
|
070de69ebe | ||
|
|
a72d730373 | ||
|
|
a1efe50659 | ||
|
|
30e3b448fd | ||
|
|
29d895742c | ||
|
|
c23047a59f | ||
|
|
4aa26e292e | ||
|
|
0a20795ea4 | ||
|
|
c3a27f6d17 | ||
|
|
4c9a90a461 | ||
|
|
eb58d335c0 | ||
|
|
8434069539 | ||
|
|
c7621f251f | ||
|
|
2f2c4723a6 | ||
|
|
02819ca169 | ||
|
|
a5122cc076 | ||
|
|
5993d6862b | ||
|
|
348b612f98 | ||
|
|
f714b59c88 | ||
|
|
b627499acd | ||
|
|
43beccc2eb | ||
|
|
b8275b3404 | ||
|
|
6e4da08164 | ||
|
|
f2ac52bcdf | ||
|
|
46e976513b | ||
|
|
816ccf6509 | ||
|
|
73e265d8fd | ||
|
|
6117097a93 | ||
|
|
fee732dba1 | ||
|
|
1138e8062f | ||
|
|
123cf0d7b8 | ||
|
|
bd14ee3b1f | ||
|
|
a1ae4d7ef0 | ||
|
|
e63eee0c68 | ||
|
|
070feb4b66 | ||
|
|
308417852c | ||
|
|
9c3a976250 | ||
|
|
688973bae5 | ||
|
|
d93b0be0d4 | ||
|
|
773abbae13 | ||
|
|
0f3579fd7b | ||
|
|
db7b7158fa | ||
|
|
b5a61f2dd8 | ||
|
|
8347f15de2 | ||
|
|
26309fad10 | ||
|
|
ba4d8b0b35 | ||
|
|
dc11e0c79c | ||
|
|
a13d931cfd | ||
|
|
fea2ad3d7d | ||
|
|
53d42a17ff | ||
|
|
cc21da6266 | ||
|
|
ab05af79ec | ||
|
|
c46c5e9c8f | ||
|
|
9c50b81c70 | ||
|
|
144771bebf | ||
|
|
422aabd562 | ||
|
|
b8c10def8e | ||
|
|
80afc28719 | ||
|
|
561cd77e68 | ||
|
|
d8554ec67e | ||
|
|
076dda8bd3 | ||
|
|
a43237d4f9 | ||
|
|
6af7d782d4 | ||
|
|
6d2b778ac5 | ||
|
|
eada773048 | ||
|
|
9bff4e061e | ||
|
|
f2f13450b5 | ||
|
|
753ce8e119 | ||
|
|
4be6719db4 | ||
|
|
a6e1dc4269 | ||
|
|
4356aaff23 | ||
|
|
ad241b9f84 | ||
|
|
53e76288cb | ||
|
|
0f15b6d198 | ||
|
|
143e93f483 | ||
|
|
93a5bb2baa | ||
|
|
ebfc8e5a1b | ||
|
|
972b9b6e91 | ||
|
|
56cccc6781 | ||
|
|
b680bb5372 | ||
|
|
217b3ae488 | ||
|
|
959392f815 | ||
|
|
d1e13e48fe | ||
|
|
1c04502963 | ||
|
|
34da23783e | ||
|
|
2b603cfbb5 | ||
|
|
a1fe22c08c | ||
|
|
bc04ae24a7 | ||
|
|
48216e1653 | ||
|
|
8aca82a593 | ||
|
|
68d8cbe68a | ||
|
|
4bc3acb14d | ||
|
|
a974ec9dee | ||
|
|
e91c43466d | ||
|
|
dfc4ab6dd8 | ||
|
|
0b9afcb443 | ||
|
|
9f5ffde2dd | ||
|
|
678bef2360 | ||
|
|
3b12cdc179 | ||
|
|
261754f18b | ||
|
|
980db9cb0c | ||
|
|
a80cd455de | ||
|
|
289e3d6286 | ||
|
|
4b181620d8 | ||
|
|
088ec7fd94 | ||
|
|
289f1fe134 | ||
|
|
6b9ac52af6 | ||
|
|
0e422eb70d | ||
|
|
df32545b07 | ||
|
|
bab340a849 | ||
|
|
5e7e9e8c0d | ||
|
|
5841a3abea | ||
|
|
494d44f8bb | ||
|
|
ad1a81e8a0 | ||
|
|
b0d507c2dd | ||
|
|
619e5e944b | ||
|
|
e2d0faa539 | ||
|
|
cbb6c82113 | ||
|
|
00376b43f8 | ||
|
|
fb163a2493 | ||
|
|
d753fc06c9 | ||
|
|
a85d652db4 | ||
|
|
a9e6bf62bd | ||
|
|
14c493fc63 | ||
|
|
625682a4da | ||
|
|
0674ce19fe | ||
|
|
3fc251c1f6 | ||
|
|
67c3f663a1 | ||
|
|
eec8f45de5 | ||
|
|
0855e3e32f | ||
|
|
fab3ce46b8 | ||
|
|
08c8ebc100 | ||
|
|
2050796a33 | ||
|
|
7c4c467e46 | ||
|
|
0039fe09be | ||
|
|
4f4f3e4593 | ||
|
|
97beed6fd1 | ||
|
|
994314cfa3 | ||
|
|
5160d59d79 | ||
|
|
db6dbe34c3 | ||
|
|
fa7ef361e2 | ||
|
|
ab4774a59a | ||
|
|
b0d1ccc311 | ||
|
|
8c2ebde3d5 | ||
|
|
508e8c87be | ||
|
|
365e8455e6 | ||
|
|
393684fb19 | ||
|
|
909a23e529 | ||
|
|
b68a2e2b41 | ||
|
|
33f09f0887 | ||
|
|
aee05a549f | ||
|
|
0155c7d8ce | ||
|
|
64fca5893e | ||
|
|
221831e4e5 | ||
|
|
cb755af2ee | ||
|
|
f71f3126db | ||
|
|
42a2767211 | ||
|
|
e410205886 | ||
|
|
07433b8005 | ||
|
|
58575706fe | ||
|
|
ebba71077e | ||
|
|
6c0df108c7 | ||
|
|
5f3ffcb0eb | ||
|
|
265d57b9f4 | ||
|
|
42e85f693c | ||
|
|
30dacb0369 | ||
|
|
304d5e6256 | ||
|
|
597de31116 | ||
|
|
425b3d98b3 | ||
|
|
c2fb8b5be8 | ||
|
|
1ba299585c | ||
|
|
174ad1a987 | ||
|
|
199246ea6c | ||
|
|
eeb8d4b3de | ||
|
|
82a728df71 | ||
|
|
3f067c793c | ||
|
|
2489cc5b91 | ||
|
|
1c832a24b7 | ||
|
|
e40198cd26 | ||
|
|
c81556b64c | ||
|
|
60546d2ce3 | ||
|
|
fa0b4d7edd | ||
|
|
d5593e059b | ||
|
|
477d71e0b9 | ||
|
|
93ae4e504e | ||
|
|
fcac5d1033 | ||
|
|
f69392566a | ||
|
|
8e3689a11d | ||
|
|
5804a204fe | ||
|
|
09682111d6 | ||
|
|
3a845d13f8 | ||
|
|
2cb4df9e0b | ||
|
|
daa602ed9e | ||
|
|
eabf24d750 | ||
|
|
e2475ad73f | ||
|
|
4f79e84399 | ||
|
|
e4c0638f72 | ||
|
|
e774f71205 | ||
|
|
b2986ca289 | ||
|
|
271853e155 | ||
|
|
a2a3d316d5 | ||
|
|
33b75f2f23 | ||
|
|
929ca9cbd9 | ||
|
|
ea3bcf4320 | ||
|
|
0b018bfad5 | ||
|
|
c360afb341 | ||
|
|
2dba189830 | ||
|
|
d40d5652ba | ||
|
|
d40ad94384 | ||
|
|
8e2f44bbc0 | ||
|
|
87df11434b | ||
|
|
95c178cee3 | ||
|
|
46a468b06d | ||
|
|
8d4eef6649 | ||
|
|
9421c0ba05 | ||
|
|
1c827f9830 | ||
|
|
4114d08961 | ||
|
|
98917523fc | ||
|
|
e9a3601d2e | ||
|
|
3c53b3de15 | ||
|
|
8becd9aad3 | ||
|
|
5eadc0c850 | ||
|
|
89d0da5eee | ||
|
|
03fbc1cd70 | ||
|
|
738e07c424 | ||
|
|
4fb81bd686 | ||
|
|
aff8b562d6 | ||
|
|
236c99b39f | ||
|
|
5488b68b9b | ||
|
|
6d18fb689f | ||
|
|
9d7e9e9869 | ||
|
|
6d15fd29ee | ||
|
|
d78e067246 | ||
|
|
5b227b2b4f | ||
|
|
16fe2be04d | ||
|
|
61c0292089 | ||
|
|
c8173fd9f9 | ||
|
|
72f8baf6b3 | ||
|
|
e135af8c37 | ||
|
|
61eb0e7acd | ||
|
|
0b841f2e05 | ||
|
|
9662267628 | ||
|
|
34f2e4466e | ||
|
|
73f288cd9b | ||
|
|
622f1360bb | ||
|
|
4ecdf7558f | ||
|
|
25fac166cf | ||
|
|
66d1546081 | ||
|
|
181b564bf3 | ||
|
|
872e503ff8 | ||
|
|
38012903b8 | ||
|
|
6ab699d650 | ||
|
|
af6513ccc7 | ||
|
|
30fe263bfe | ||
|
|
51cf57025d | ||
|
|
28884cb409 | ||
|
|
6e29152b76 | ||
|
|
0defff70af | ||
|
|
d93efd9e9b | ||
|
|
eab46523ee | ||
|
|
c4473f3515 | ||
|
|
8b3f8ac501 | ||
|
|
fb2e8b4a31 | ||
|
|
871e229fae | ||
|
|
e1b3f38315 | ||
|
|
b8b1512e26 | ||
|
|
3300c69a03 | ||
|
|
09a95dfef6 | ||
|
|
ea141f62d7 |
No files matched your search
@@ -8,3 +8,20 @@ end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{js,html}]
|
||||
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_brace_style = 1TBS
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 80
|
||||
quote_type = auto
|
||||
spaces_around_operators = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
|
||||
trim_trailing_whitespace = false
|
||||
@@ -0,0 +1 @@
|
||||
packages/*
|
||||
+34
-12
@@ -1,16 +1,19 @@
|
||||
{
|
||||
"extends": "eslint:recommended",
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:meteor/recommended",
|
||||
"prettier",
|
||||
"prettier/standard"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"browser": true
|
||||
"browser": true,
|
||||
"meteor": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true
|
||||
}
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"strict": 0,
|
||||
@@ -20,7 +23,7 @@
|
||||
"consistent-return": 2,
|
||||
"dot-notation": 2,
|
||||
"eqeqeq": 2,
|
||||
"indent": [2, 2],
|
||||
"indent": 0,
|
||||
"no-cond-assign": 2,
|
||||
"no-constant-condition": 2,
|
||||
"no-eval": 2,
|
||||
@@ -28,11 +31,12 @@
|
||||
"no-unneeded-ternary": 2,
|
||||
"radix": 2,
|
||||
"semi": [2, "always"],
|
||||
"camelcase": [2, {"properties": "never"}],
|
||||
"camelcase": [2, { "properties": "never" }],
|
||||
"comma-spacing": 2,
|
||||
"comma-style": 2,
|
||||
"eol-last": 2,
|
||||
"linebreak-style": [2, "unix"],
|
||||
"meteor/audit-argument-checks": 0,
|
||||
"new-parens": 2,
|
||||
"no-lonely-if": 2,
|
||||
"no-multiple-empty-lines": 2,
|
||||
@@ -43,7 +47,6 @@
|
||||
"quotes": [2, "single"],
|
||||
"semi-spacing": 2,
|
||||
"space-unary-ops": 2,
|
||||
"arrow-parens": 2,
|
||||
"arrow-spacing": 2,
|
||||
"no-class-assign": 2,
|
||||
"no-dupe-class-members": 2,
|
||||
@@ -52,8 +55,27 @@
|
||||
"prefer-const": 2,
|
||||
"prefer-spread": 2,
|
||||
"prefer-template": 2,
|
||||
"no-unused-vars" : "warn"
|
||||
"no-unused-vars": "warn",
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
],
|
||||
"meteor/no-session": 0
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"meteor": {
|
||||
"extensions": [".js", ".jsx"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": ["prettier", "meteor"],
|
||||
"globals": {
|
||||
"Meteor": false,
|
||||
"Session": false,
|
||||
@@ -100,7 +122,7 @@
|
||||
"Attachments": true,
|
||||
"Boards": true,
|
||||
"CardComments": true,
|
||||
"DatePicker" : true,
|
||||
"DatePicker": true,
|
||||
"Cards": true,
|
||||
"CustomFields": true,
|
||||
"Lists": true,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
## Issue
|
||||
|
||||
Add these issues to elsewhere:
|
||||
- LDAP: https://github.com/wekan/wekan-ldap/issues
|
||||
- Snap: https://github.com/wekan/wekan-snap/issues
|
||||
|
||||
Other Wekan issues can be added here.
|
||||
|
||||
+14
-2
@@ -8,8 +8,6 @@ npm-debug.log
|
||||
.vscode/
|
||||
.idea/
|
||||
.build/*
|
||||
packages/
|
||||
package-lock.json
|
||||
**/parts/
|
||||
**/stage
|
||||
**/prime
|
||||
@@ -17,3 +15,17 @@ package-lock.json
|
||||
snap/.snapcraft/
|
||||
.idea
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
.build*
|
||||
*.browserify.js.cached
|
||||
*.browserify.js.map
|
||||
.build*
|
||||
versions.json
|
||||
.versions
|
||||
.npm
|
||||
.build*
|
||||
._*
|
||||
.Trashes
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
.eslintcache
|
||||
@@ -0,0 +1,18 @@
|
||||
# This file contains information which helps Meteor properly upgrade your
|
||||
# app when you run 'meteor update'. You should check it into version control
|
||||
# with your project.
|
||||
|
||||
notices-for-0.9.0
|
||||
notices-for-0.9.1
|
||||
0.9.4-platform-file
|
||||
notices-for-facebook-graph-api-2
|
||||
1.2.0-standard-minifiers-package
|
||||
1.2.0-meteor-platform-split
|
||||
1.2.0-cordova-changes
|
||||
1.2.0-breaking-changes
|
||||
1.3.0-split-minifiers-package
|
||||
1.3.5-remove-old-dev-bundle-link
|
||||
1.4.0-remove-old-dev-bundle-link
|
||||
1.4.1-add-shell-server-package
|
||||
1.4.3-split-account-service-packages
|
||||
1.5-add-dynamic-import-package
|
||||
@@ -0,0 +1,2 @@
|
||||
dev_bundle
|
||||
local
|
||||
@@ -0,0 +1,7 @@
|
||||
# This file contains a token that is unique to your project.
|
||||
# Check it into your repository along with the rest of this directory.
|
||||
# It can be used for purposes such as:
|
||||
# - ensuring you don't accidentally deploy one app on top of another
|
||||
# - providing package authors with aggregated statistics
|
||||
|
||||
dvyihgykyzec6y1dpg
|
||||
Whitespace-only changes.
@@ -0,0 +1,93 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
meteor-base@1.2.0
|
||||
|
||||
# Build system
|
||||
ecmascript
|
||||
stylus@2.513.13
|
||||
standard-minifier-css@1.3.5
|
||||
standard-minifier-js@2.2.0
|
||||
mquandalle:jade
|
||||
|
||||
# Polyfills
|
||||
es5-shim@4.6.15
|
||||
|
||||
# Collections
|
||||
aldeed:collection2
|
||||
cfs:standard-packages
|
||||
cottz:publish-relations
|
||||
dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
matb33:collection-hooks
|
||||
matteodem:easy-search
|
||||
mongo@1.3.1
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
kenton:accounts-sandstorm
|
||||
service-configuration@1.0.11
|
||||
useraccounts:unstyled
|
||||
useraccounts:flow-routing
|
||||
wekan-ldap
|
||||
wekan-accounts-cas
|
||||
wekan-accounts-oidc
|
||||
|
||||
# Utilities
|
||||
check@1.2.5
|
||||
jquery@1.11.10
|
||||
random@1.0.10
|
||||
reactive-dict@1.2.0
|
||||
session@1.1.7
|
||||
tracker@1.1.3
|
||||
underscore@1.0.10
|
||||
3stack:presence
|
||||
alethes:pages
|
||||
arillo:flow-router-helpers
|
||||
audit-argument-checks@1.0.7
|
||||
kadira:blaze-layout
|
||||
kadira:dochead
|
||||
meteorhacks:picker
|
||||
meteorhacks:subs-manager
|
||||
mquandalle:autofocus
|
||||
ongoworks:speakingurl
|
||||
raix:handlebar-helpers
|
||||
tap:i18n
|
||||
http@1.3.0
|
||||
|
||||
# UI components
|
||||
blaze
|
||||
reactive-var@1.0.11
|
||||
fortawesome:fontawesome
|
||||
mousetrap:mousetrap
|
||||
mquandalle:jquery-textcomplete
|
||||
mquandalle:jquery-ui-drag-drop-sort
|
||||
mquandalle:mousetrap-bindglobal
|
||||
peerlibrary:blaze-components@=0.15.1
|
||||
templates:tabs
|
||||
verron:autosize
|
||||
simple:json-routes
|
||||
rajit:bootstrap3-datepicker
|
||||
shell-server@0.3.0
|
||||
simple:rest-accounts-password
|
||||
useraccounts:core
|
||||
email@1.2.3
|
||||
horka:swipebox
|
||||
dynamic-import@0.2.0
|
||||
staringatlights:fast-render
|
||||
|
||||
accounts-password@1.5.0
|
||||
cfs:gridfs
|
||||
rzymek:fullcalendar
|
||||
momentjs:moment@2.22.2
|
||||
browser-policy-framing
|
||||
mquandalle:moment
|
||||
msavin:usercache
|
||||
wekan-scrollbar
|
||||
mquandalle:perfect-scrollbar
|
||||
mdg:meteor-apm-agent
|
||||
meteorhacks:unblock
|
||||
lucasantoniassi:accounts-lockout
|
||||
wekan-markdown
|
||||
@@ -0,0 +1,2 @@
|
||||
server
|
||||
browser
|
||||
@@ -0,0 +1 @@
|
||||
METEOR@1.6.0.1
|
||||
@@ -0,0 +1,189 @@
|
||||
3stack:presence@1.1.2
|
||||
accounts-base@1.4.0
|
||||
accounts-oauth@1.1.15
|
||||
accounts-password@1.5.0
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
aldeed:schema-index@1.1.1
|
||||
aldeed:simple-schema@1.5.3
|
||||
alethes:pages@1.8.6
|
||||
allow-deny@1.1.0
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
audit-argument-checks@1.0.7
|
||||
autoupdate@1.3.12
|
||||
babel-compiler@6.24.7
|
||||
babel-runtime@1.1.1
|
||||
base64@1.0.10
|
||||
binary-heap@1.0.10
|
||||
blaze@2.3.2
|
||||
blaze-tools@1.0.10
|
||||
boilerplate-generator@1.3.1
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
caching-compiler@1.1.9
|
||||
caching-html-compiler@1.1.2
|
||||
callback-hook@1.0.10
|
||||
cfs:access-point@0.1.49
|
||||
cfs:base-package@0.0.30
|
||||
cfs:collection@0.5.5
|
||||
cfs:collection-filters@0.2.4
|
||||
cfs:data-man@0.0.6
|
||||
cfs:file@0.1.17
|
||||
cfs:gridfs@0.0.34
|
||||
cfs:http-methods@0.0.32
|
||||
cfs:http-publish@0.0.13
|
||||
cfs:power-queue@0.9.11
|
||||
cfs:reactive-list@0.0.9
|
||||
cfs:reactive-property@0.0.4
|
||||
cfs:standard-packages@0.5.9
|
||||
cfs:storage-adapter@0.2.3
|
||||
cfs:tempstore@0.1.5
|
||||
cfs:upload-http@0.0.20
|
||||
cfs:worker@0.1.4
|
||||
check@1.2.5
|
||||
chuangbo:cookie@1.1.0
|
||||
coffeescript@1.12.7_3
|
||||
coffeescript-compiler@1.12.7_3
|
||||
cottz:publish-relations@2.0.8
|
||||
dburles:collection-helpers@1.1.0
|
||||
ddp@1.4.0
|
||||
ddp-client@2.2.0
|
||||
ddp-common@1.3.0
|
||||
ddp-rate-limiter@1.0.7
|
||||
ddp-server@2.1.1
|
||||
deps@1.0.12
|
||||
diff-sequence@1.0.7
|
||||
dynamic-import@0.2.1
|
||||
ecmascript@0.9.0
|
||||
ecmascript-runtime@0.5.0
|
||||
ecmascript-runtime-client@0.5.0
|
||||
ecmascript-runtime-server@0.5.0
|
||||
ejson@1.1.0
|
||||
email@1.2.3
|
||||
es5-shim@4.6.15
|
||||
fastclick@1.0.13
|
||||
fortawesome:fontawesome@4.7.0
|
||||
geojson-utils@1.0.10
|
||||
horka:swipebox@1.0.2
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.0.11
|
||||
htmljs@1.0.11
|
||||
http@1.3.0
|
||||
id-map@1.0.9
|
||||
idmontie:migrations@1.0.3
|
||||
jquery@1.11.10
|
||||
kadira:blaze-layout@2.3.0
|
||||
kadira:dochead@1.5.0
|
||||
kadira:flow-router@2.12.1
|
||||
kenton:accounts-sandstorm@0.7.0
|
||||
lamhieu:meteorx@2.0.1
|
||||
launch-screen@1.1.1
|
||||
livedata@1.0.18
|
||||
localstorage@1.2.0
|
||||
logging@1.1.19
|
||||
lucasantoniassi:accounts-lockout@1.0.0
|
||||
matb33:collection-hooks@0.8.4
|
||||
matteodem:easy-search@1.6.4
|
||||
mdg:meteor-apm-agent@3.1.2
|
||||
mdg:validation-error@0.5.1
|
||||
meteor@1.8.0
|
||||
meteor-base@1.2.0
|
||||
meteor-platform@1.2.6
|
||||
meteorhacks:aggregate@1.3.0
|
||||
meteorhacks:collection-utils@1.2.0
|
||||
meteorhacks:meteorx@1.4.1
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
meteorhacks:unblock@1.1.0
|
||||
meteorspark:util@0.2.0
|
||||
minifier-css@1.2.16
|
||||
minifier-js@2.2.2
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.4.3
|
||||
mobile-status-bar@1.0.14
|
||||
modules@0.11.0
|
||||
modules-runtime@0.9.1
|
||||
momentjs:moment@2.22.2
|
||||
mongo@1.3.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.6
|
||||
mongo-livedata@1.0.12
|
||||
mousetrap:mousetrap@1.4.6_1
|
||||
mquandalle:autofocus@1.0.0
|
||||
mquandalle:collection-mutations@0.1.0
|
||||
mquandalle:jade@0.4.9
|
||||
mquandalle:jade-compiler@0.4.5
|
||||
mquandalle:jquery-textcomplete@0.8.0_1
|
||||
mquandalle:jquery-ui-drag-drop-sort@0.2.0
|
||||
mquandalle:moment@1.0.1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
mquandalle:perfect-scrollbar@0.6.5_2
|
||||
msavin:usercache@1.0.0
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@2.2.33
|
||||
oauth@1.2.1
|
||||
oauth2@1.2.0
|
||||
observe-sequence@1.0.16
|
||||
ongoworks:speakingurl@1.1.0
|
||||
ordered-dict@1.0.9
|
||||
peerlibrary:assert@0.2.5
|
||||
peerlibrary:base-component@0.16.0
|
||||
peerlibrary:blaze-components@0.15.1
|
||||
peerlibrary:computed-field@0.7.0
|
||||
peerlibrary:reactive-field@0.3.0
|
||||
percolate:synced-cron@1.1.1
|
||||
promise@0.10.0
|
||||
raix:eventemitter@0.1.3
|
||||
raix:handlebar-helpers@0.2.5
|
||||
rajit:bootstrap3-datepicker@1.7.1
|
||||
random@1.0.10
|
||||
rate-limit@1.0.8
|
||||
reactive-dict@1.2.0
|
||||
reactive-var@1.0.11
|
||||
reload@1.1.11
|
||||
retry@1.0.9
|
||||
routepolicy@1.0.12
|
||||
rzymek:fullcalendar@3.8.0
|
||||
service-configuration@1.0.11
|
||||
session@1.1.7
|
||||
sha@1.0.9
|
||||
shell-server@0.3.1
|
||||
simple:authenticate-user-by-token@1.0.1
|
||||
simple:json-routes@2.1.0
|
||||
simple:rest-accounts-password@1.1.2
|
||||
simple:rest-bearer-token-parser@1.0.1
|
||||
simple:rest-json-error-handler@1.0.1
|
||||
softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.0.15
|
||||
spacebars-compiler@1.1.3
|
||||
srp@1.0.10
|
||||
standard-minifier-css@1.3.5
|
||||
standard-minifier-js@2.2.3
|
||||
staringatlights:fast-render@2.16.5
|
||||
staringatlights:inject-data@2.0.5
|
||||
stylus@2.513.13
|
||||
tap:i18n@1.8.2
|
||||
templates:tabs@2.3.0
|
||||
templating@1.3.2
|
||||
templating-compiler@1.3.3
|
||||
templating-runtime@1.3.2
|
||||
templating-tools@1.1.2
|
||||
tracker@1.1.3
|
||||
ui@1.0.13
|
||||
underscore@1.0.10
|
||||
url@1.1.0
|
||||
useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.4.0
|
||||
webapp-hashing@1.0.9
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-oidc@1.0.10
|
||||
wekan-ldap@0.0.2
|
||||
wekan-markdown@1.0.7
|
||||
wekan-oidc@1.0.12
|
||||
wekan-scrollbar@3.1.3
|
||||
yasaricli:slugify@0.0.7
|
||||
zimme:active-route@2.3.2
|
||||
Generated
+4572
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "wekan",
|
||||
"version": "v3.06.0",
|
||||
"description": "Open-Source kanban",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "eslint --cache --ext .js --ignore-path .eslintignore .",
|
||||
"lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
|
||||
"lint:staged": "lint-staged",
|
||||
"prettify": "prettier --write '**/*.js' '**/*.jsx'",
|
||||
"test": "npm run lint"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"meteor npm run prettify",
|
||||
"meteor npm run lint:eslint:fix",
|
||||
"git add --force"
|
||||
],
|
||||
"*.jsx": [
|
||||
"meteor npm run prettify",
|
||||
"meteor npm run lint:eslint:fix",
|
||||
"git add --force"
|
||||
],
|
||||
"*.json": [
|
||||
"prettier --write",
|
||||
"git add --force"
|
||||
]
|
||||
},
|
||||
"pre-commit": "lint:staged",
|
||||
"eslintConfig": {
|
||||
"extends": "@meteorjs/eslint-config-meteor"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/wekan/wekan.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/wekan/wekan/issues"
|
||||
},
|
||||
"homepage": "https://wekan.github.io",
|
||||
"devDependencies": {
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-meteor": "0.0.9",
|
||||
"eslint-config-prettier": "^3.6.0",
|
||||
"eslint-import-resolver-meteor": "^0.4.0",
|
||||
"eslint-plugin-import": "^2.18.0",
|
||||
"eslint-plugin-meteor": "^5.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.0",
|
||||
"lint-staged": "^7.3.0",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^1.18.2",
|
||||
"prettier-eslint": "^8.8.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"bson-ext": "^2.0.0",
|
||||
"es6-promise": "^4.2.4",
|
||||
"hoek": "^5.0.4",
|
||||
"meteor-node-stubs": "^0.4.1",
|
||||
"os": "^0.1.1",
|
||||
"page": "^1.8.6",
|
||||
"qs": "^6.5.2",
|
||||
"source-map-support": "^0.5.9",
|
||||
"xss": "^1.0.6"
|
||||
}
|
||||
}
|
||||
Executable
+161
@@ -0,0 +1,161 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Note: If you use other locale than en_US.UTF-8 , you need to additionally install en_US.UTF-8"
|
||||
echo " with 'sudo dpkg-reconfigure locales' , so that MongoDB works correctly."
|
||||
echo " You can still use any other locale as your main locale."
|
||||
|
||||
#Below script installs newest node 8.x for Debian/Ubuntu/Mint.
|
||||
#NODE_VERSION=8.16.0
|
||||
#X64NODE="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
|
||||
function pause(){
|
||||
read -p "$*"
|
||||
}
|
||||
|
||||
function cprec(){
|
||||
if [[ -d "$1" ]]; then
|
||||
if [[ ! -d "$2" ]]; then
|
||||
sudo mkdir -p "$2"
|
||||
fi
|
||||
|
||||
for i in $(ls -A "$1"); do
|
||||
cprec "$1/$i" "$2/$i"
|
||||
done
|
||||
else
|
||||
sudo cp "$1" "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
# sudo npm doesn't work right, so this is a workaround
|
||||
function npm_call(){
|
||||
TMPDIR="/tmp/tmp_npm_prefix"
|
||||
if [[ -d "$TMPDIR" ]]; then
|
||||
rm -rf $TMPDIR
|
||||
fi
|
||||
mkdir $TMPDIR
|
||||
NPM_PREFIX="$(npm config get prefix)"
|
||||
npm config set prefix $TMPDIR
|
||||
npm "$@"
|
||||
npm config set prefix "$NPM_PREFIX"
|
||||
|
||||
echo "Moving files to $NPM_PREFIX"
|
||||
for i in $(ls -A $TMPDIR); do
|
||||
cprec "$TMPDIR/$i" "$NPM_PREFIX/$i"
|
||||
done
|
||||
rm -rf $TMPDIR
|
||||
}
|
||||
|
||||
#function wekan_repo_check(){
|
||||
## UNCOMMENTING, IT'S NOT REQUIRED THAT /HOME/USERNAME IS /HOME/WEKAN
|
||||
# git_remotes="$(git remote show 2>/dev/null)"
|
||||
# res=""
|
||||
# for i in $git_remotes; do
|
||||
# res="$(git remote get-url $i | sed 's/.*wekan\/wekan.*/wekan\/wekan/')"
|
||||
# if [[ "$res" == "wekan/wekan" ]]; then
|
||||
# break
|
||||
# fi
|
||||
# done
|
||||
#
|
||||
# if [[ "$res" != "wekan/wekan" ]]; then
|
||||
# echo "$PWD is not a wekan repository"
|
||||
# exit;
|
||||
# fi
|
||||
#}
|
||||
|
||||
echo
|
||||
PS3='Please enter your choice: '
|
||||
options=("Install Wekan dependencies" "Build Wekan" "Quit")
|
||||
select opt in "${options[@]}"
|
||||
do
|
||||
case $opt in
|
||||
"Install Wekan dependencies")
|
||||
|
||||
if [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
echo "Linux";
|
||||
# Debian, Ubuntu, Mint
|
||||
sudo apt-get install -y build-essential gcc g++ make git curl wget
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
echo "macOS";
|
||||
pause '1) Install XCode 2) Install Node 8.x from https://nodejs.org/en/ 3) Press [Enter] key to continue.'
|
||||
elif [[ "$OSTYPE" == "cygwin" ]]; then
|
||||
# POSIX compatibility layer and Linux environment emulation for Windows
|
||||
echo "TODO: Add Cygwin";
|
||||
exit;
|
||||
elif [[ "$OSTYPE" == "msys" ]]; then
|
||||
# Lightweight shell and GNU utilities compiled for Windows (part of MinGW)
|
||||
echo "TODO: Add msys on Windows";
|
||||
exit;
|
||||
elif [[ "$OSTYPE" == "win32" ]]; then
|
||||
# I'm not sure this can happen.
|
||||
echo "TODO: Add Windows";
|
||||
exit;
|
||||
elif [[ "$OSTYPE" == "freebsd"* ]]; then
|
||||
echo "TODO: Add FreeBSD";
|
||||
exit;
|
||||
else
|
||||
echo "Unknown"
|
||||
echo ${OSTYPE}
|
||||
exit;
|
||||
fi
|
||||
|
||||
## Latest npm with Meteor 1.6
|
||||
npm_call -g install npm
|
||||
npm_call -g install node-gyp
|
||||
# Latest fibers for Meteor 1.6
|
||||
npm_call -g install fibers@2.0.0
|
||||
# Install Meteor, if it's not yet installed
|
||||
curl https://install.meteor.com | bash
|
||||
break
|
||||
;;
|
||||
"Build Wekan")
|
||||
echo "Building Wekan."
|
||||
#wekan_repo_check
|
||||
# REPOS BELOW ARE INCLUDED TO WEKAN REPO
|
||||
#rm -rf packages/kadira-flow-router packages/meteor-useraccounts-core packages/meteor-accounts-cas packages/wekan-ldap packages/wekan-ldap packages/wekan-scrfollbar packages/meteor-accounts-oidc packages/markdown
|
||||
#mkdir packages
|
||||
#cd packages
|
||||
#git clone --depth 1 -b master https://github.com/wekan/flow-router.git kadira-flow-router
|
||||
#git clone --depth 1 -b master https://github.com/meteor-useraccounts/core.git meteor-useraccounts-core
|
||||
#git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-cas.git
|
||||
#git clone --depth 1 -b master https://github.com/wekan/wekan-ldap.git
|
||||
#git clone --depth 1 -b master https://github.com/wekan/wekan-scrollbar.git
|
||||
#git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-oidc.git
|
||||
#git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git
|
||||
#mv meteor-accounts-oidc/packages/switch_accounts-oidc wekan_accounts-oidc
|
||||
#mv meteor-accounts-oidc/packages/switch_oidc wekan_oidc
|
||||
#rm -rf meteor-accounts-oidc
|
||||
#if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# echo "sed at macOS";
|
||||
# sed -i '' 's/api\.versionsFrom/\/\/api.versionsFrom/' ~/repos/wekan/packages/meteor-useraccounts-core/package.js
|
||||
#else
|
||||
# echo "sed at ${OSTYPE}"
|
||||
# sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' ~/repos/wekan/packages/meteor-useraccounts-core/package.js
|
||||
#fi
|
||||
#cd ..
|
||||
rm -rf node_modules
|
||||
meteor npm install
|
||||
rm -rf .build
|
||||
meteor build .build --directory
|
||||
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
|
||||
#Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
|
||||
#https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
|
||||
#https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
|
||||
#cd ~/repos/wekan/.build/bundle/programs/server/npm/node_modules/meteor/npm-bcrypt
|
||||
#rm -rf node_modules/bcrypt
|
||||
#meteor npm install bcrypt
|
||||
cd .build/bundle/programs/server
|
||||
rm -rf node_modules
|
||||
meteor npm install
|
||||
#meteor npm install bcrypt
|
||||
cd ../../../..
|
||||
echo Done.
|
||||
break
|
||||
;;
|
||||
"Quit")
|
||||
break
|
||||
;;
|
||||
*) echo invalid option;;
|
||||
esac
|
||||
done
|
||||
@@ -16,3 +16,4 @@ notices-for-facebook-graph-api-2
|
||||
1.4.1-add-shell-server-package
|
||||
1.4.3-split-account-service-packages
|
||||
1.5-add-dynamic-import-package
|
||||
1.7-split-underscore-from-meteor-base
|
||||
+28
-25
@@ -3,17 +3,16 @@
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
meteor-base@1.2.0
|
||||
meteor-base@1.4.0
|
||||
|
||||
# Build system
|
||||
ecmascript
|
||||
stylus@2.513.13
|
||||
standard-minifier-css@1.3.5
|
||||
standard-minifier-js@2.2.0
|
||||
ecmascript@0.12.4
|
||||
standard-minifier-css@1.5.3
|
||||
standard-minifier-js@2.4.1
|
||||
mquandalle:jade
|
||||
|
||||
# Polyfills
|
||||
es5-shim@4.6.15
|
||||
es5-shim@4.8.0
|
||||
|
||||
# Collections
|
||||
aldeed:collection2
|
||||
@@ -23,7 +22,7 @@ dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
matb33:collection-hooks
|
||||
matteodem:easy-search
|
||||
mongo@1.3.1
|
||||
mongo@1.6.2
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
@@ -31,17 +30,17 @@ kenton:accounts-sandstorm
|
||||
service-configuration@1.0.11
|
||||
useraccounts:unstyled
|
||||
useraccounts:flow-routing
|
||||
wekan:wekan-ldap
|
||||
wekan:accounts-cas
|
||||
wekan-ldap
|
||||
wekan-accounts-cas
|
||||
wekan-accounts-oidc
|
||||
|
||||
# Utilities
|
||||
check@1.2.5
|
||||
check@1.3.1
|
||||
jquery@1.11.10
|
||||
random@1.0.10
|
||||
reactive-dict@1.2.0
|
||||
session@1.1.7
|
||||
tracker@1.1.3
|
||||
random@1.1.0
|
||||
reactive-dict@1.3.0
|
||||
session@1.2.0
|
||||
tracker@1.2.0
|
||||
underscore@1.0.10
|
||||
3stack:presence
|
||||
alethes:pages
|
||||
@@ -49,13 +48,11 @@ arillo:flow-router-helpers
|
||||
audit-argument-checks@1.0.7
|
||||
kadira:blaze-layout
|
||||
kadira:dochead
|
||||
meteorhacks:picker
|
||||
meteorhacks:subs-manager
|
||||
mquandalle:autofocus
|
||||
ongoworks:speakingurl
|
||||
raix:handlebar-helpers
|
||||
tap:i18n
|
||||
http@1.3.0
|
||||
http@1.4.2
|
||||
|
||||
# UI components
|
||||
blaze
|
||||
@@ -66,29 +63,35 @@ mquandalle:jquery-textcomplete
|
||||
mquandalle:jquery-ui-drag-drop-sort
|
||||
mquandalle:mousetrap-bindglobal
|
||||
peerlibrary:blaze-components@=0.15.1
|
||||
perak:markdown
|
||||
templates:tabs
|
||||
verron:autosize
|
||||
simple:json-routes
|
||||
rajit:bootstrap3-datepicker
|
||||
shell-server@0.3.0
|
||||
shell-server@0.4.0
|
||||
simple:rest-accounts-password
|
||||
useraccounts:core
|
||||
email@1.2.3
|
||||
horka:swipebox
|
||||
dynamic-import@0.2.0
|
||||
dynamic-import@0.5.1
|
||||
staringatlights:fast-render
|
||||
|
||||
mixmax:smart-disconnect
|
||||
accounts-password@1.5.0
|
||||
accounts-password@1.5.1
|
||||
cfs:gridfs
|
||||
rzymek:fullcalendar
|
||||
momentjs:moment@2.22.2
|
||||
browser-policy-framing
|
||||
browser-policy-framing@1.1.0
|
||||
mquandalle:moment
|
||||
msavin:usercache
|
||||
wekan-scrollbar
|
||||
mquandalle:perfect-scrollbar
|
||||
mdg:meteor-apm-agent
|
||||
meteorhacks:unblock
|
||||
mdg:meteor-apm-agent@3.2.0-rc.0!
|
||||
coagmano:stylus
|
||||
lucasantoniassi:accounts-lockout
|
||||
meteorhacks:subs-manager
|
||||
meteorhacks:picker
|
||||
lamhieu:unblock
|
||||
meteorhacks:aggregate@1.3.0
|
||||
wekan-markdown
|
||||
konecty:mongo-counter
|
||||
percolate:synced-cron
|
||||
easylogic:summernote
|
||||
+1
-1
@@ -1 +1 @@
|
||||
METEOR@1.6.0.1
|
||||
METEOR@1.8.1
|
||||
+86
-78
@@ -1,29 +1,29 @@
|
||||
3stack:presence@1.1.2
|
||||
accounts-base@1.4.0
|
||||
accounts-oauth@1.1.15
|
||||
accounts-password@1.5.0
|
||||
accounts-base@1.4.4
|
||||
accounts-oauth@1.1.16
|
||||
accounts-password@1.5.1
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
aldeed:schema-index@1.1.1
|
||||
aldeed:simple-schema@1.5.3
|
||||
aldeed:simple-schema@1.5.4
|
||||
alethes:pages@1.8.6
|
||||
allow-deny@1.1.0
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
audit-argument-checks@1.0.7
|
||||
autoupdate@1.3.12
|
||||
babel-compiler@6.24.7
|
||||
babel-runtime@1.1.1
|
||||
base64@1.0.10
|
||||
binary-heap@1.0.10
|
||||
blaze@2.3.2
|
||||
autoupdate@1.6.0
|
||||
babel-compiler@7.3.4
|
||||
babel-runtime@1.3.0
|
||||
base64@1.0.11
|
||||
binary-heap@1.0.11
|
||||
blaze@2.3.3
|
||||
blaze-tools@1.0.10
|
||||
boilerplate-generator@1.3.1
|
||||
boilerplate-generator@1.6.0
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
caching-compiler@1.1.9
|
||||
caching-html-compiler@1.1.2
|
||||
callback-hook@1.0.10
|
||||
caching-compiler@1.2.1
|
||||
caching-html-compiler@1.1.3
|
||||
callback-hook@1.1.0
|
||||
cfs:access-point@0.1.49
|
||||
cfs:base-package@0.0.30
|
||||
cfs:collection@0.5.5
|
||||
@@ -36,78 +36,83 @@ cfs:http-publish@0.0.13
|
||||
cfs:power-queue@0.9.11
|
||||
cfs:reactive-list@0.0.9
|
||||
cfs:reactive-property@0.0.4
|
||||
cfs:standard-packages@0.5.9
|
||||
cfs:storage-adapter@0.2.3
|
||||
cfs:tempstore@0.1.5
|
||||
cfs:standard-packages@0.5.10
|
||||
cfs:storage-adapter@0.2.4
|
||||
cfs:tempstore@0.1.6
|
||||
cfs:upload-http@0.0.20
|
||||
cfs:worker@0.1.4
|
||||
check@1.2.5
|
||||
cfs:worker@0.1.5
|
||||
check@1.3.1
|
||||
chuangbo:cookie@1.1.0
|
||||
coffeescript@1.12.7_3
|
||||
coffeescript-compiler@1.12.7_3
|
||||
coagmano:stylus@2.0.0
|
||||
coffeescript@1.0.17
|
||||
cottz:publish-relations@2.0.8
|
||||
dburles:collection-helpers@1.1.0
|
||||
ddp@1.4.0
|
||||
ddp-client@2.2.0
|
||||
ddp-common@1.3.0
|
||||
ddp-client@2.3.3
|
||||
ddp-common@1.4.0
|
||||
ddp-rate-limiter@1.0.7
|
||||
ddp-server@2.1.1
|
||||
ddp-server@2.3.0
|
||||
deps@1.0.12
|
||||
diff-sequence@1.0.7
|
||||
dynamic-import@0.2.1
|
||||
ecmascript@0.9.0
|
||||
ecmascript-runtime@0.5.0
|
||||
ecmascript-runtime-client@0.5.0
|
||||
ecmascript-runtime-server@0.5.0
|
||||
diff-sequence@1.1.1
|
||||
dynamic-import@0.5.1
|
||||
easylogic:summernote@0.8.8
|
||||
ecmascript@0.12.7
|
||||
ecmascript-runtime@0.7.0
|
||||
ecmascript-runtime-client@0.8.0
|
||||
ecmascript-runtime-server@0.7.1
|
||||
ejson@1.1.0
|
||||
email@1.2.3
|
||||
es5-shim@4.6.15
|
||||
es5-shim@4.8.0
|
||||
fastclick@1.0.13
|
||||
fetch@0.1.1
|
||||
fortawesome:fontawesome@4.7.0
|
||||
geojson-utils@1.0.10
|
||||
horka:swipebox@1.0.2
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.0.11
|
||||
htmljs@1.0.11
|
||||
http@1.3.0
|
||||
id-map@1.0.9
|
||||
http@1.4.2
|
||||
id-map@1.1.0
|
||||
idmontie:migrations@1.0.3
|
||||
jquery@1.11.10
|
||||
inter-process-messaging@0.1.0
|
||||
jquery@1.11.11
|
||||
kadira:blaze-layout@2.3.0
|
||||
kadira:dochead@1.5.0
|
||||
kadira:flow-router@2.12.1
|
||||
kenton:accounts-sandstorm@0.7.0
|
||||
konecty:mongo-counter@0.0.5_3
|
||||
lamhieu:meteorx@2.0.1
|
||||
lamhieu:unblock@1.0.0
|
||||
launch-screen@1.1.1
|
||||
livedata@1.0.18
|
||||
localstorage@1.2.0
|
||||
logging@1.1.19
|
||||
logging@1.1.20
|
||||
lucasantoniassi:accounts-lockout@1.0.0
|
||||
matb33:collection-hooks@0.8.4
|
||||
matteodem:easy-search@1.6.4
|
||||
mdg:meteor-apm-agent@3.1.2
|
||||
mdg:meteor-apm-agent@3.2.3
|
||||
mdg:validation-error@0.5.1
|
||||
meteor@1.8.2
|
||||
meteor-base@1.2.0
|
||||
meteor@1.9.3
|
||||
meteor-base@1.4.0
|
||||
meteor-platform@1.2.6
|
||||
meteorhacks:aggregate@1.3.0
|
||||
meteorhacks:collection-utils@1.2.0
|
||||
meteorhacks:meteorx@1.4.1
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
meteorhacks:unblock@1.1.0
|
||||
meteorspark:util@0.2.0
|
||||
minifier-css@1.2.16
|
||||
minifier-js@2.2.2
|
||||
minifier-css@1.4.2
|
||||
minifier-js@2.4.1
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.4.3
|
||||
mixmax:smart-disconnect@0.0.4
|
||||
minimongo@1.4.5
|
||||
mobile-status-bar@1.0.14
|
||||
modules@0.11.0
|
||||
modules-runtime@0.9.1
|
||||
momentjs:moment@2.22.2
|
||||
mongo@1.3.1
|
||||
modern-browsers@0.1.4
|
||||
modules@0.13.0
|
||||
modules-runtime@0.10.3
|
||||
momentjs:moment@2.24.0
|
||||
mongo@1.6.3
|
||||
mongo-decimal@0.1.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.6
|
||||
mongo-id@1.0.7
|
||||
mongo-livedata@1.0.12
|
||||
mousetrap:mousetrap@1.4.6_1
|
||||
mquandalle:autofocus@1.0.0
|
||||
@@ -119,70 +124,73 @@ mquandalle:jquery-ui-drag-drop-sort@0.2.0
|
||||
mquandalle:moment@1.0.1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
mquandalle:perfect-scrollbar@0.6.5_2
|
||||
msavin:usercache@1.0.0
|
||||
msavin:usercache@1.8.0
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@2.2.33
|
||||
oauth@1.2.1
|
||||
oauth2@1.2.0
|
||||
npm-mongo@3.1.2
|
||||
oauth@1.2.8
|
||||
oauth2@1.2.1
|
||||
observe-sequence@1.0.16
|
||||
ongoworks:speakingurl@1.1.0
|
||||
ordered-dict@1.0.9
|
||||
ordered-dict@1.1.0
|
||||
peerlibrary:assert@0.2.5
|
||||
peerlibrary:base-component@0.16.0
|
||||
peerlibrary:blaze-components@0.15.1
|
||||
peerlibrary:computed-field@0.7.0
|
||||
peerlibrary:reactive-field@0.3.0
|
||||
perak:markdown@1.0.5
|
||||
promise@0.10.0
|
||||
peerlibrary:computed-field@0.9.0
|
||||
peerlibrary:reactive-field@0.5.0
|
||||
percolate:synced-cron@1.3.2
|
||||
promise@0.11.2
|
||||
raix:eventemitter@0.1.3
|
||||
raix:handlebar-helpers@0.2.5
|
||||
rajit:bootstrap3-datepicker@1.7.1
|
||||
random@1.0.10
|
||||
rate-limit@1.0.8
|
||||
reactive-dict@1.2.0
|
||||
rajit:bootstrap3-datepicker@1.7.1_1
|
||||
random@1.1.0
|
||||
rate-limit@1.0.9
|
||||
reactive-dict@1.3.0
|
||||
reactive-var@1.0.11
|
||||
reload@1.1.11
|
||||
retry@1.0.9
|
||||
routepolicy@1.0.12
|
||||
reload@1.3.0
|
||||
retry@1.1.0
|
||||
routepolicy@1.1.0
|
||||
rzymek:fullcalendar@3.8.0
|
||||
server-render@0.3.1
|
||||
service-configuration@1.0.11
|
||||
session@1.1.7
|
||||
session@1.2.0
|
||||
sha@1.0.9
|
||||
shell-server@0.3.1
|
||||
shell-server@0.4.0
|
||||
simple:authenticate-user-by-token@1.0.1
|
||||
simple:json-routes@2.1.0
|
||||
simple:rest-accounts-password@1.1.2
|
||||
simple:rest-bearer-token-parser@1.0.1
|
||||
simple:rest-json-error-handler@1.0.1
|
||||
socket-stream-client@0.2.2
|
||||
softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.0.15
|
||||
spacebars-compiler@1.1.3
|
||||
srp@1.0.10
|
||||
standard-minifier-css@1.3.5
|
||||
standard-minifier-js@2.2.3
|
||||
staringatlights:fast-render@2.16.5
|
||||
staringatlights:inject-data@2.0.5
|
||||
stylus@2.513.13
|
||||
srp@1.0.12
|
||||
standard-minifier-css@1.5.3
|
||||
standard-minifier-js@2.4.1
|
||||
staringatlights:fast-render@3.2.0
|
||||
staringatlights:inject-data@2.3.0
|
||||
tap:i18n@1.8.2
|
||||
templates:tabs@2.3.0
|
||||
templating@1.3.2
|
||||
templating-compiler@1.3.3
|
||||
templating-runtime@1.3.2
|
||||
templating-tools@1.1.2
|
||||
tracker@1.1.3
|
||||
tracker@1.2.0
|
||||
twbs:bootstrap@3.3.6
|
||||
ui@1.0.13
|
||||
underscore@1.0.10
|
||||
url@1.1.0
|
||||
url@1.2.0
|
||||
useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.4.0
|
||||
webapp@1.7.4
|
||||
webapp-hashing@1.0.9
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-oidc@1.0.10
|
||||
wekan-ldap@0.0.2
|
||||
wekan-markdown@1.0.7
|
||||
wekan-oidc@1.0.12
|
||||
wekan-scrollbar@3.1.3
|
||||
wekan:accounts-cas@0.1.0
|
||||
wekan:wekan-ldap@0.0.2
|
||||
yasaricli:slugify@0.0.7
|
||||
zimme:active-route@2.3.2
|
||||
@@ -0,0 +1,13 @@
|
||||
dependencies:
|
||||
- libreadline-dev
|
||||
- libssl-dev
|
||||
- bsdtar
|
||||
targets:
|
||||
ubuntu-18.04:
|
||||
ubuntu-16.04:
|
||||
ubuntu-14.04:
|
||||
centos-6:
|
||||
centos-7:
|
||||
debian-10:
|
||||
sles-12:
|
||||
sles-11:
|
||||
@@ -0,0 +1,7 @@
|
||||
packages/
|
||||
node_modules/
|
||||
.build/
|
||||
.meteor/
|
||||
.vscode/
|
||||
.tx/
|
||||
.github/
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
+1
-1
@@ -39,7 +39,7 @@ host = https://www.transifex.com
|
||||
# tap:i18n requires us to use `-` separator in the language identifiers whereas
|
||||
# Transifex uses a `_` separator, without an option to customize it on one side
|
||||
# or the other, so we need to do a Manual mapping.
|
||||
lang_map = bg_BG:bg, en_GB:en-GB, es_AR:es-AR, el_GR:el, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, no:nb, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, zh_CN:zh-CN, zh_TW:zh-TW
|
||||
lang_map = bg_BG:bg, en_GB:en-GB, es_AR:es-AR, el_GR:el, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, no:nb, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
|
||||
|
||||
[wekan.application]
|
||||
file_filter = i18n/<lang>.i18n.json
|
||||
|
||||
+777
-26
@@ -1,3 +1,754 @@
|
||||
# v3.06 2019-08-07 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix board query](https://github.com/wekan/wekan/pull/2587).
|
||||
Thanks to justinr1234.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.05 2019-08-07 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fixed LDAP group filtering bug on Snap settings](https://github.com/wekan/wekan/pull/2584).
|
||||
Thanks to KuenzelIT.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.04 2019-08-06 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fixed Snap: Use Meteor 1.6.0.1 dependencies on Snap on master branch](https://github.com/wekan/wekan/commit/74a4b28313e9cfedcb927e4496c0dd3800b1e6f9).
|
||||
Thanks to xet7.
|
||||
- [Hide Admin Panel user delete button until someone has time to fix it](https://github.com/wekan/wekan/commit/b9a25ecfaca067d0392c83d97a0deb65e6e296dd).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.03 2019-08-04 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Add RICHER_CARD_COMMENT_EDITOR=true to docker-compose.yml](https://github.com/wekan/wekan/commit/268f9de23c8167dca9499587ee31fb74edb6b83e).
|
||||
Thanks to xet7.
|
||||
- [Add popup to confirm deleting one account](https://github.com/wekan/wekan/pull/2573).
|
||||
Thanks to Akuket.
|
||||
- [Add admin setting to prevent users to self deleting their account](https://github.com/wekan/wekan/pull/2573).
|
||||
Thanks to Akuket.
|
||||
- [Add Packager.io DEB/RPM Wekan packages for Debian/Ubuntu/CentOS/SLES](https://packager.io/gh/wekan/wekan).
|
||||
Does not work yet, [debugging in progress here](https://github.com/wekan/wekan/issues/2582).
|
||||
Thanks to xet7 and sfahrenholz.
|
||||
- [Add setting field LDAP_USER_AUTHENTICATION_FIELD=uid](https://github.com/wekan/wekan/pull/2581).
|
||||
Thanks to Trekky12.
|
||||
|
||||
and adds the following upgrades:
|
||||
|
||||
- [Upgrade MongoDB to 4.0.11](https://github.com/wekan/wekan/commit/ec35c544b780e563a973fd887c5190f429431bfb).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Remove mixmax:smart-disconnect, previously it did disconnect Wekan when
|
||||
browser tab was not active, but because now users are working on multiple
|
||||
boards at different browser tabs and switching all time time, there was
|
||||
constant loading, so now after removing all tabs keeps active. This can
|
||||
increase server CPU usage](https://github.com/wekan/wekan/commit/669cd76018cbbfbd3ee58610a35959fa8a84ea36).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.02 2019-07-26 Wekan release
|
||||
|
||||
This release adds the following updates:
|
||||
|
||||
- [Updated Wekan on OpenShift](https://github.com/wekan/wekan/commit/85ca2b1363ed0bad8639ba7ed65c55e445816947).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Set LDAP_BACKGROUND_SYNC_INTERVAL='' (empty string) so it works](https://github.com/wekan/wekan/commit/fff144a8279ac36ce83e6b975f17f6dbc35f39d6)
|
||||
and [does not crash](https://github.com/wekan/wekan/issues/2354#issuecomment-515305722).
|
||||
Also updated wekan-postgresql docker-compose.yml to use devel branch docker image, because ToroDB requires MongoDB 3.2,
|
||||
it's not tested yet could newest master branch docker image work with MongoDB 3.2.
|
||||
Thanks to benh57 and xet7.
|
||||
|
||||
and tries to fix following bugs:
|
||||
|
||||
- [Try to fix Snap](https://github.com/wekan/wekan/commit/7cf6850cdf77ef51808784e3d275c5be86ff6c92).
|
||||
This [will be tested soon, does this work](https://github.com/wekan/wekan/issues/2533#issuecomment-515329490).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.01 2019-07-26 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Rich text editor at card comments, based on
|
||||
Summernote](https://github.com/wekan/wekan/pull/2560).
|
||||
Thanks to whowillcare.
|
||||
- [Add setting RICHER_CARD_COMMENT_EDITOR=true to
|
||||
Source/Snap/Docker/Sandstorm](https://github.com/wekan/wekan/commit/4aba290358455433c0fc676e8c9cf1bd627eddde).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v3.00 2019-07-25 Wekan release
|
||||
|
||||
This release:
|
||||
|
||||
- works with source, and docker-compose.yml at master branch.
|
||||
- Docker release uses Meteor 1.8.1 and MongoDB 4.0.10, so you need to backup your old MongoDB database and
|
||||
restore with --noIndexRestore as described at https://github.com/wekan/wekan/wiki/Backup
|
||||
- Snap and Sandstorm builds are not fixed yet, see progress at https://github.com/wekan/wekan/issues/2533
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Added label text to labels on minicard](https://github.com/wekan/wekan/commit/c48d5a73cab04db1a1e113e4367dc88573110438).
|
||||
Thanks to xet7.
|
||||
- [Allow to shrink attached/pasted image](https://github.com/wekan/wekan/pull/2544).
|
||||
Thanks to whowillcare.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix invites](https://github.com/wekan/wekan/pull/2549).
|
||||
Thanks to justinr1234.
|
||||
- [Makes LDAP background sync work. If the sync interval is unspecified, falls back to a hourly default](https://github.com/wekan/wekan/pull/2555).
|
||||
Thanks to pshunter.
|
||||
- [Prevent isCommentOnly user adding attachments, editing list names, moving lists,
|
||||
and seeing board settings menu. Show non-editable Custom Fields to isCommentOnly user](https://github.com/wekan/wekan/commit/a68c928896a94c377134f29a7183aa0b5a423720).
|
||||
Thanks to xet7.
|
||||
- [Many](https://github.com/wekan/wekan/pull/2546) [Snap](https://github.com/wekan/wekan/pull/2552) [fixes](https://github.com/wekan/wekan/pull/2553).
|
||||
In Progress. Thanks to justrinr1234.
|
||||
- [Fixed Dockerfile](https://github.com/wekan/wekan/commit/7df6f305c5cf41ac213623aeffaa7e48c981e0b6) and
|
||||
[docker-compose.yml](https://github.com/wekan/wekan/commit/95698911f92ca728dbaab69406fd09bcbf81339d).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.99 2019-07-17 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Add Corteza theme. In progress](https://github.com/wekan/wekan/commit/289e78dbd29cca9d97d3b5787c3368583d43b40f).
|
||||
Thanks to xet7.
|
||||
- [Notify Due Days: System timelines will be showing any user modification for duat startat endat receivedat,
|
||||
also notification to the watchers and if any card is due, about due or past due](https://github.com/wekan/wekan/pull/2536).
|
||||
ENV variables: NOTIFY_DUE_DAYS_BEFORE_AND_AFTER (default is 2, if 0, will turn off notification for and dued cards),
|
||||
NOTIFY_DUE_AT_HOUR_OF_DAY (any number between 0 - 23, standing for at what hour of a day that the notification will
|
||||
be sent to the watchers). Also [bug fix for this, timeOldValue needs to set to "" in params when it's
|
||||
not set](https://github.com/wekan/wekan/pull/2541).
|
||||
Thanks to whowillcare.
|
||||
- [Notify Due Days: Add settings to Snap/Docker/Source. Rename env variables to NOTIFY_DUE_DAYS_BEFORE_AND_AFTER and
|
||||
NOTIFY_DUE_AT_HOUR_OF_DAY](https://github.com/wekan/wekan/commit/5084cddf37ba16ce0855f8575c39f5e62d1b7f67).
|
||||
Thanks to xet7.
|
||||
- [BIGEVENTS_PATTERN: When user being @ in comment, as long as it's not himself, a notification will be
|
||||
sent out no matter this user is watching the board or not](https://github.com/wekan/wekan/pull/2541).
|
||||
Introduced a system env var BIGEVENTS_PATTERN default as "due", so any activityType matches the pattern,
|
||||
system will send out notifications to all board members no matter they are watching or tracking
|
||||
the board or not. Owner of the Wekan server can disable the feature by setting this variable to "NONE"
|
||||
or change the pattern to any valid regex. i.e. '|' delimited activityType names.
|
||||
Thanks to whowillcare.
|
||||
- [Add BIGEVENTS_PATTERN to Source/Snap/Docker](https://github.com/wekan/wekan/commit/d7c09df7d2649bf2d2b61772c251f81793a6ed77).
|
||||
Thanks to xet7.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Update Meteor mongo package version](https://github.com/wekan/wekan/commit/96065d11a543852c1069cbab528bd08508b4a27c).
|
||||
Thanks to xet7.
|
||||
- [Update dependencies](https://github.com/wekan/wekan/commit/d82c72f1c1df908e92045e5034fa12b33fc7f70c).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- RELAX THEME: Use [only in this theme](https://github.com/wekan/wekan/commit/3ad6e554dceea822dee7390872260e872a792dcd)
|
||||
the aggressive [red color and big bold serif font style
|
||||
number](https://github.com/wekan/wekan/commit/bbc68309af0029f2bc4194db4c7e79689f888ea4#commitcomment-34216371) and
|
||||
[card details text emphasis](https://github.com/wekan/wekan/commit/48ebc5f11745b125ce01d08d60e2d8e3a9419a5f#commitcomment-34268095)
|
||||
Thanks to hever and xet7.
|
||||
- [Try to fix docker-compose.yml to use correct master branch that has
|
||||
meteor 1.8.1](https://github.com/wekan/wekan/commit/202cc5a797b6269ec422c6f2e532a49f09d4e30a).
|
||||
Thanks to xet7.
|
||||
- [Outgoing Webhooks setCustomField: Add board name, card name, custom field name to be
|
||||
visible](https://github.com/wekan/wekan/commit/2003d90467debeadf51b69630c80ee6040524f52).
|
||||
Still missing: custom field value, list name, swimlane name.
|
||||
Thanks to xet7.
|
||||
- [Don't remove boardoverlay when mouse leaves carddetails](https://github.com/wekan/wekan/pull/2540).
|
||||
This reduces Wekan board flashiness.
|
||||
Thanks to newhinton.
|
||||
- [Limit the board list to 2 or 1 for mobile clients](https://github.com/wekan/wekan/pull/2542).
|
||||
As a mobile user, the board size of in the home page too small, so the user is easily to
|
||||
click on archive or copy button by accident. Increase the board size to 50% for pixel
|
||||
greater than 360 and lesser than 800 and height to 8rem, 100% for any screen is even smaller.
|
||||
This will reduce the accident much more.
|
||||
Thanks to whowillcare.
|
||||
- [Add check for board member isActive](https://github.com/wekan/wekan/commit fe42eb1d014c06dfed8114a00b29eac9b08baec6).
|
||||
Thanks to xet7.
|
||||
|
||||
and has the following features in progress, not anything visible yet:
|
||||
|
||||
- [Teams/Organizations: Add beginnings of database structure](https://github.com/wekan/wekan/issues/802#issuecomment-505986270).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.98 2019-07-02 Wekan release
|
||||
|
||||
This release adds the following new features, thanks to xet7:
|
||||
|
||||
- [Add Wekan v2.95-v2.97 master branch features and fixes to meteor-1.8 branch](https://github.com/wekan/wekan/commit/34b2168d0dda253dedabbee47031873efa4ae446).
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.97 2019-07-01 Wekan release
|
||||
|
||||
This release [adds the following new features](https://github.com/wekan/wekan/commit/3e8cb8c6e1617ef03ebce045d3b93aeb2cf91228), thanks to xet7:
|
||||
|
||||
- Add background color names to background colors.
|
||||
- Add new background colors: moderatepink, strongcyan, limegreen.
|
||||
- Add new background colors with themes: dark, relax.
|
||||
|
||||
Note: Due Date etc on cards is visible on all background colors and themes. Hiding is not implemented yet.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.96 2019-07-01 Wekan release
|
||||
|
||||
This release removes the following features, that were added at Wekan v2.95:
|
||||
|
||||
- [Remove RELAX MODE and DARK MODE](https://github.com/wekan/wekan/commit/8477e94f3b8f531a4209f49758200009d274c1cf),
|
||||
because [they changed look of all existing boards](https://github.com/wekan/wekan/issues/1149#issuecomment-507255114).
|
||||
At some later Wekan release they will be added back as separate themes
|
||||
that can be selected, without changing existing boards.
|
||||
Thanks to chirrut2 and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.95 2019-07-01 Wekan release
|
||||
|
||||
This release adds the following new features, thanks to xet7:
|
||||
|
||||
- [Make list header add card + button more black, to make it more visible](https://github.com/wekan/wekan/commit/b260d05a8b2f87c29dd998d42103d1220b20cc08).
|
||||
- [On minicard, make comment icon and number of comments have red color
|
||||
on white rounded background, so it is more visible when there is comments on card](https://github.com/wekan/wekan/commit/bbc68309af0029f2bc4194db4c7e79689f888ea4).
|
||||
- [Make card description text more visible with black borders and more white text area](https://github.com/wekan/wekan/commit/48ebc5f11745b125ce01d08d60e2d8e3a9419a5f).
|
||||
|
||||
and adds the following [themes to board background colors](https://github.com/wekan/wekan/commit/c04292e98832e3aa7952e8a7858d47a853f40aad), thanks to xet7:
|
||||
|
||||
- RELAX MODE, so when green background selected, list background is light green.
|
||||
- [DARK MODE](https://github.com/wekan/wekan/issues/1149), when dark backgroud color selected. Please test and send color visibility fixes as pull requests.
|
||||
- In RELAX MODE and DARK MODE, [hide card fields: received, start, due, end, members, requested, assigned](https://github.com/wekan/wekan/commit/b42ecb7948ad194433dc4460305174965106a751).
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.94 2019-06-29 Wekan release
|
||||
|
||||
This release adds the following updates:
|
||||
|
||||
- [Prettier & eslint project style update](https://github.com/wekan/wekan/pull/2520).
|
||||
Thanks to justinr1234.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.93 2019-06-28 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [LDAP: Check if email attribute is an array, that has many email addresses](https://github.com/wekan/wekan/pull/2519).
|
||||
Thanks to tdemaret and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.92 2019-06-27 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix Outgoing Webhook messages for Checlists, Checklist Items, Card comments, Add/Remove CustomField to board](https://github.com/wekan/wekan/commit/5283ba9ebbedf11540ffef1d4d87891c5ce9efc7).
|
||||
Not yet fixed is Outgoing Webhook message about setting CustomField value.
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.91 2019-06-27 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix Attachment Outgoing Webhook missing list and swimlane name](https://github.com/wekan/wekan/commit/6a2f120d00b5ce9089ad2e12d01edb1ed9f94800).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.90 2019-06-21 Wekan release
|
||||
|
||||
This release reverts the following Sandstorm changes:
|
||||
|
||||
- [Revert v2.89 setting every Sandstorm Wekan user as admin](https://github.com/wekan/wekan/commit/e5c0d0ea18fe74a47afdfe101160280854e2c74f).
|
||||
Thanks to xet7. [Related #2405](https://github.com/wekan/wekan/issues/2405).
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.89 2019-06-21 Wekan release
|
||||
|
||||
This release adds the following Sandstorm features:
|
||||
|
||||
- [Sandstorm Wekan: Set everyone as Admin](https://github.com/wekan/wekan/commit/60d62a6ae3a79059e68b2cd1d554d67b7d50b6aa).
|
||||
Please test does this help with [Problem with the user management: can't add users or give wekan admin rights](https://github.com/wekan/wekan/issues/2405).
|
||||
Thanks to xet7.
|
||||
- [If board does not exist, redirect to All Boards page, at all Wekan platforms](https://github.com/wekan/wekan/commit/4f46adc389126597266d71110f9754841f86857c).
|
||||
So now at Sandstorm when loading Wekan grain, if first Sandstorm board is found,
|
||||
it is opened. If first Sandstorm board is not found (it's deleted or archived),
|
||||
then redirect automatically to All Boards page. [Closes #3132](https://github.com/wekan/wekan/issues/3132).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.88 2019-06-21 Wekan release
|
||||
|
||||
This release adds the following updates:
|
||||
|
||||
- Update translations.
|
||||
|
||||
Thanks to translators for their translations.
|
||||
|
||||
# v2.87 2019-06-21 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Rule cardAction - SetDate](https://github.com/wekan/wekan/pull/2506).
|
||||
Thanks to road42.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix Move card to top/bottom of list](https://github.com/wekan/wekan/pull/2508).
|
||||
Thanks to road42.
|
||||
- [Translation fixes](https://github.com/wekan/wekan/pull/2507).
|
||||
Thanks to road42.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.86 2019-06-19 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix Wekan unable to Select Text from Description edit box](https://github.com/wekan/wekan/issues/2451)
|
||||
by removing feature of card description submit on click outside. This is because when selecting text
|
||||
and dragging up did trigger submit of description, so description was closed and selecting text failed.
|
||||
This did affect all Chromium-based browsers: Chrome, Chromium, Chromium Edge.
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.85 2019-06-19 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fixed bug: rule doesn't move card to top/bottom](https://github.com/wekan/wekan/pull/2502).
|
||||
Thanks to road42.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.84 2019-06-18 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Buttons for adding rules to a board where missing for isBoardAdmin](https://github.com/wekan/wekan/pull/2500).
|
||||
Thanks to road42.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.83 2019-06-17 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix Bug: Unable to click board submenu on mobile](https://github.com/wekan/wekan/commit/7ff6f24a90374ae95edbb87b37e0c235e7aee434).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.82 2019-06-14 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix OIDC Docker login. Empty string results to empty array at wekan/server/authentication.js](https://github.com/wekan/wekan/commit/bddbaa7bc2f3cfe8553a2265e168231ab51876f3).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.81 2019-06-13 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Change OAuth2 whitelist default to empty string at snap, so it would be used as array on
|
||||
wekan/server/authentication.js](https://github.com/wekan/wekan/commit/4334fbbb9dacf45b0262019526a9697b015049a1).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.80 2019-06-13 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix OAuth2 typos in snap-src/bin/config](https://github.com/wekan/wekan/commit/44dbd462b19e613fcb47161d44e4046d5d91a319).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.79 2019-06-13 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix OAuth2 typos in Dockerfile and docker-compose.yml](https://github.com/wekan/wekan/pull/2488).
|
||||
Thanks to DominikPf.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.78 2019-06-12 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Try to fix OIDC login](https://github.com/wekan/wekan/commit/8b31c0768c34fc4557b54cec936a0b4288a8e722).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.77 2019-06-11 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix Snap build](https://github.com/wekan/wekan/commit/e1e20275a673d3065c6cf239db8d2f1a505baa69).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.76 2019-06-11 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Add support for CORS headers "Access-control-allow-headers" and
|
||||
"Access-control-expose-headers"](https://github.com/wekan/wekan/pull/2429).
|
||||
Thanks to risacher and xet7.
|
||||
- [Support scopes in OAuth2, so that Authentication via OAuth2 with Google is now possible](https://github.com/wekan/wekan/pull/2483).
|
||||
Thanks to moserben16.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix Scope parsing Issue for OAuth2 Login with simple String](https://github.com/wekan/wekan/pulls/2427).
|
||||
Thanks to DominikPf.
|
||||
- [Show attachment name in Outgoing Webhook when attachment is added to card](https://github.com/wekan/wekan/commit/992ecfefa2e46ee7321ec9b8bfc3400532e5645e).
|
||||
Thanks to xet7. Related [#2285](https://github.com/wekan/wekan/issues/2285).
|
||||
- [Show attachment name in Outgoing Webhook when attachment is removed from card](https://github.com/wekan/wekan/commit/23ccb3b991be6d7196e59f7d68df17b8949df049).
|
||||
Thanks to xet7. Related [#2285](https://github.com/wekan/wekan/issues/2285).
|
||||
- [Allow BoardAdmin to create board rules](https://github.com/wekan/wekan/pull/2433).
|
||||
Thanks to road42.
|
||||
- [Fix typo](https://github.com/wekan/wekan/pull/2442).
|
||||
Thanks to Jason-Cooke.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.75 2019-05-22 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [CAS allowed LDAP groups](https://github.com/wekan/meteor-accounts-cas/pull/4).
|
||||
Thanks to ppoulard. Please test. Related [#2356](https://github.com/wekan/wekan/issues/2356).
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix](https://github.com/wekan/wekan/commit/634df8f6f26a7a7a2df6f87a705d322d88638425):
|
||||
[OAuth2 Requested Scopes are wrong / cannot be configured](https://github.com/wekan/wekan/issues/2412).
|
||||
Thanks to DominikPf and xet7. Please test.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.74 2019-05-14 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- Add missing [wekan-ldap#40](https://github.com/wekan/wekan-ldap/pull/40) code about
|
||||
[LDAP_SYNC_ADMIN_STATUS](https://github.com/wekan/wekan/commit/0fe40ad9ec82ef2045578f4cc1e2ebb6cc80d47a).
|
||||
Thanks to JulianJacobi, n-st, chirrut2 and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.73 2019-05-14 Wekan release
|
||||
|
||||
This release fixes the following bugs with Apache I-CLA:
|
||||
|
||||
- [Card count placement and export API functionality back](https://github.com/wekan/wekan/pulls/2406).
|
||||
Thanks to bentiss.
|
||||
- [Few fixes for Dockerfile](https://github.com/wekan/wekan/pulls/2407).
|
||||
Thanks to bentiss.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- Fixed [#2338](https://github.com/wekan/wekan/issues/2338) -> [Slow opening of big boards with too many archived items](https://github.com/wekan/wekan/pull/2402).
|
||||
If some Wekan users see errors with this, please empty your browser cache.
|
||||
Thanks to nerminator.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.72 2019-05-13 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Added BIDI support to "Add Card"](https://github.com/wekan/wekan/pull/2401).
|
||||
Related [#884](https://github.com/wekan/wekan/issues/884).
|
||||
Thanks to guyzyl.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.71 2019-05-12 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Add partentId support on card web API](https://github.com/wekan/wekan/pulls/2400).
|
||||
Thanks to atilaromero.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.70 2019-05-11 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [View markdown on List names, Custom Fields (Text and Dropdown), Label names,
|
||||
All Boards view Board names and Board descriptions](https://github.com/wekan/wekan/commit/b795115042c2eb6bccbf029f21d78849a44128ca).
|
||||
Related [#2334](https://github.com/wekan/wekan/issues/2334).
|
||||
Thanks to shaygover and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.69 2019-05-11 Wekan release
|
||||
|
||||
This release fixes the following translation names:
|
||||
|
||||
- [Fix translation name in Wekan menu: oc to Occitan](https://github.com/wekan/wekan/commit/db40ca25ac5df17fcc8b7c93f12b7e2bffc349d2).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.68 2019-05-10 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Option to login to the LDAP server with the user's own username and password, instead of an administrator
|
||||
key](https://github.com/wekan/wekan/pull/2399). Default: false (use administrator key).
|
||||
With new setting: LDAP_USER_AUTHENTICATION=true.
|
||||
Thanks to thiagofernando.
|
||||
- [Added above new LDAP_USER_AUTHENTION=true option to Snap, Docker and Source settings](https://github.com/wekan/wekan/commit/3bbc805ee42e3c1638b50260d3fafc2b5f936923).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.67 2019-05-10 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Move board to Archive button at each board at All Boards page](https://github.com/wekan/wekan/commit/828f6ea321020eda77fea399df52889e2081dfac).
|
||||
Thanks to xet7. Related [#2389](https://github.com/wekan/wekan/issues/2389).
|
||||
- [If adding Subtasks does not work on old board, added wiki page how to make it work again](https://github.com/wekan/wekan/wiki/Subtasks).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.66 2019-05-09 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Delete user feature](https://github.com/wekan/wekan/pull/2384).
|
||||
Thanks to Akuket.
|
||||
- Change to Delete user feature: [When last board admin is removed, board is not deleted, other board users can
|
||||
still use it](https://github.com/wekan/wekan/commit/e1b016cf3d4ff93e9e0fe1feb96372e3e1625233).
|
||||
Thanks to xet7.
|
||||
|
||||
and adds the following new translations:
|
||||
|
||||
- Add Chinese (Hong Kong).
|
||||
Thanks to translators.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix OIDC login](https://github.com/wekan/wekan/pull/2385). Related [#2383](https://github.com/wekan/wekan/issues/2383).
|
||||
Thanks to faust64.
|
||||
- [Fix missing profile checks](https://github.com/wekan/wekan/pull/2396).
|
||||
Thanks to justinr1234.
|
||||
- [Fix RTL issue #884, part 1](https://github.com/wekan/wekan/pull/2395).
|
||||
Thanks to guyzyl.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.65 2019-04-24 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Now a loading animation is displayed when the authentication is performed. This allows users
|
||||
to know that it's in progress](https://github.com/wekan/wekan/pull/2379).
|
||||
Thanks to Akuket.
|
||||
|
||||
and removes the following UI duplicates:
|
||||
|
||||
- [Remove from card menu, because they also exist at card:
|
||||
members, labels, attachments, dates received/start/due/end](https://github.com/wekan/wekan/issues/2242).
|
||||
Thanks to sfahrenholz, jrsupplee and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.64 2019-04-23 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Board Archive: Restore All/Delete All of Cards/Lists/Swimlanes](https://github.com/wekan/wekan/pull/2376).
|
||||
Thanks to Akuket.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.63 2019-04-23 Wekan release
|
||||
|
||||
This release removes the following Caddy plugins:
|
||||
|
||||
- [Remove Caddy plugins http.filter, http.ipfilter and http.realip from Caddy](https://github.com/wekan/wekan/commot/6a94500170509d2d82bd9a0fdc94a7ce66215b3d)
|
||||
because they are currently broken, preventing download of Caddy during Wekan Snap build.
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.62 2019-04-23 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Mobile UI: Center cards in list view](https://github.com/wekan/wekan/issues/2371).
|
||||
Thanks to hupptechnologies.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.61 2019-04-20 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- Admin Panel/People: Can now search users by username, full name or email and using regex to find them.
|
||||
Display the number of users. All registered users by default else the number of users selected by the search.
|
||||
Thanks to Akuket.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Update to use newest GitHub flawored markdown](https://github.com/wekan/wekan/commit/fea2ad3d7d09b44c3de1dbcdd3f8750aaa6776d5),
|
||||
because [it was found old version was in use](https://github.com/wekan/wekan/issues/2334).
|
||||
Thanks to shaygover and xet7.
|
||||
- [Upgrade to Node 8.16.0](https://github.com/wekan/wekan/commit/6117097a93bfb11c8bd4c87a23c44a50e22ceb87).
|
||||
Thanks to Node developers and xet7.
|
||||
- [Upgrade Docker base image to ubuntu:disco](https://github.com/wekan/wekan/commit/bd14ee3b1f450ddc6dec26ccc8da702b839942e5).
|
||||
Thanks to Ubuntu developers and xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix Full width of lists and space before first list](https://github.com/wekan/wekan/pull/2343).
|
||||
Thanks to hupptechnologies.
|
||||
- Remove [extra](https://github.com/wekan/wekan/pull/2332) [quotes](https://github.com/wekan/wekan/pull/2333) from docker-compose.yml.
|
||||
Thanks to hibare.
|
||||
- Fix Docker builds by moving all separately cloned wekan/packages/* repos like ldap, oidc, etc code to wekan repo code,
|
||||
so that in build scripts it's not needed to clone those. Also archived those wekan repos and moved issues
|
||||
to https://github.com/wekan/wekan/issues because changes and development to those packages now happends on wekan/wekan repo.
|
||||
There was also fixes to repo urls etc. Thanks to xet7.
|
||||
- [Additional updates](https://github.com/wekan/wekan/pull/2347) to meteor-1.8 branch, that contains
|
||||
Meteor 1.8.1 version that works in Docker but not yet at Snap and Sandstorm. Thanks to justinr1234.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.60 2019-04-08 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix: Description of Board is out of visible after Feature "Duplicate Board"](https://github.com/wekan/wekan/issues/2324).
|
||||
Thanks to sfahrenholz and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.59 2019-04-06 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Add variables for activity notifications, Fixes #2285](https://github.com/wekan/wekan/pull/2320).
|
||||
Thanks to rinnaz.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.58 2019-04-06 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Duplicate Board](https://github.com/wekan/wekan/issues/2257). Related #2225.
|
||||
Thanks to Angtrim.
|
||||
- [Add Duplicate Board tooltip, and remove adding text "Copy" to duplicated board](https://github.com/wekan/wekan/commit/0f15b6d1982c383f76e8411cb501ff27e8febd42).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Add proper variables for unjoin card](https://github.com/wekan/wekan/pull/2313).
|
||||
Thanks to chotaire.
|
||||
- [Center reduce left margin in card view on mobile browser](https://github.com/wekan/wekan/pull/2314).
|
||||
Thanks to hupptechnologies.
|
||||
- [Remove not needed ARGS from Dockerfile to reduce amount of Docker layers](https://github.com/wekan/wekan/issues/2301).
|
||||
Thanks to folhabranca and xet7.
|
||||
- [Fix Swimlane Rules don't work](https://github.com/wekan/wekan/issues/2225).
|
||||
Thanks to Angtrim.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.57 2019-04-02 Wekan release
|
||||
|
||||
This release fixes the following bugs, thanks to justinr1234:
|
||||
|
||||
- [Add proper variables for join card](https://github.com/wekan/wekan/commit/289f1fe1340c85eb2af19825f4972e9057a86b7a),
|
||||
fixes [Incorrect variable replacement on email notifications](https://github.com/wekan/wekan/issues/2285).
|
||||
|
||||
and fixes the following bugs with Apache I-CLA, thanks to bentiss:
|
||||
|
||||
- [List: Do not use IntersectionObserver to reduce CPU usage](https://github.com/wekan/wekan/pull/2302).
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.56 2019-03-27 Wekan release
|
||||
|
||||
This release [fixes the following bugs](https://github.com/wekan/wekan/pull/2287), thanks to bentiss with Apache I-CLA:
|
||||
|
||||
- [#2250 -> the spinner could be shown on startup and never goes away](https://github.com/wekan/wekan/issues/2250).
|
||||
- The code will now only load extra cards that will be in the current viewport.
|
||||
- When 2 users were interacting on the same board, there was a situation where the spinner could show up on the other user, without being able to load the extra cards.
|
||||
- The code is now much simpler, thanks to the IntersectionObserver, and all of this for fewer lines of code :)
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.55 2019-03-25 Wekan release
|
||||
|
||||
This release fixes the following bugs, thanks to bentiss with Apache I-CLA:
|
||||
|
||||
- [Use older api2html@0.3.0](https://github.com/wekan/wekan/commit/625682a4dab43c525494af10121edbfd547786d7)
|
||||
to fix [broken snap and docker build](https://github.com/wekan/wekan/issues/2286),
|
||||
because newer api2html caused
|
||||
[breaking change](https://github.com/tobilg/api2html/commit/a9a41bca18db3f9ec61395d7262eff071a995783)
|
||||
at api2html/bin/api2html.js:23 has error about "php": "PHP".
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.54 2019-03-25 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- Fix typos.
|
||||
- [Fix Outgoing Webhook message about created new swimlane](https://github.com/wekan/wekan/issues/1969).
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.53 2019-03-23 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix filename and URLs](https://github.com/wekan/wekan/ccommit/994314cfa339e52a2ad124194af4e89f57ddd213).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v2.52 2019-03-22 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
@@ -17,7 +768,7 @@ and fixes the following bugs:
|
||||
|
||||
- [Fix IFTTT email sending](https://github.com/wekan/wekan/pull/2279).
|
||||
Thanks to justinr1234.
|
||||
|
||||
|
||||
Thanks to above GitHub users for their contributions.
|
||||
|
||||
# v2.51 2019-03-21 Wekan release
|
||||
@@ -138,7 +889,7 @@ and reverts the following change of v2.42, because they did not fix anything, th
|
||||
|
||||
- [Revert: Tried to fix snap mongodb-control not starting database](https://github.com/wekan/wekan/commit/4055f451fdadfbfdef9a10be29a0eb6aed91182c).
|
||||
|
||||
Thanks to above GitHub users for their contributions, and translators for their translations.
|
||||
Thanks to above GitHub users for their contributions, and translators for their translations.
|
||||
|
||||
# v2.42 2019-03-07 Wekan release
|
||||
|
||||
@@ -177,7 +928,7 @@ This release fixes the following bugs:
|
||||
- [Fix](https://github.com/wekan/wekan/commit/e845fe3e7130d111be4c3a73e2551738c980ff7b)
|
||||
[manifest](https://github.com/wekan/wekan/issues/2168) and
|
||||
[icon](https://github.com/wekan/wekan/issues/1692) paths. Thanks to xet7.
|
||||
|
||||
|
||||
Thanks to above GitHub users for their contributions, and translators for their translations.
|
||||
|
||||
# v2.38 2019-03-06 Wekan release
|
||||
@@ -229,13 +980,13 @@ and moved the following code around:
|
||||
xet7 can make changes directly and GitHub issues are enabled.
|
||||
|
||||
Thanks to GitHub user xet7 for contributions.
|
||||
|
||||
|
||||
# v2.35 2019-03-01 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Add Filter fix back](https://github.com/wekan/wekan/issues/2213),
|
||||
because there was no bug in filter fix.
|
||||
- [Add Filter fix back](https://github.com/wekan/wekan/issues/2213),
|
||||
because there was no bug in filter fix.
|
||||
|
||||
Thanks to GitHub user xet7 for contributions.
|
||||
|
||||
@@ -301,7 +1052,7 @@ This release adds the following new Sandstorm features and fixes:
|
||||
- New Sandstorm board first user is Admin and [has IFTTT Rules](https://github.com/wekan/wekan/issues/2125) and Standalone Wekan Admin Panel.
|
||||
Probably some Admin Panel features do not work yet. Please keep backup of your grains before testing Admin Panel.
|
||||
- Linked Cards and Linked Boards.
|
||||
- Some not needed options like Logout etc have been hidden from top bar right menu.
|
||||
- Some not needed options like Logout etc have been hidden from top bar right menu.
|
||||
- [Import board now works. "Board not found" is not problem anymore](https://github.com/wekan/wekan/issues/1430), because you can go to All Boards page to change to imported board.
|
||||
|
||||
and removes the following features:
|
||||
@@ -383,7 +1134,7 @@ and fixes the following bugs:
|
||||
mobile browser](https://github.com/wekan/wekan/issues/2183). Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions, and translators for their translations.
|
||||
|
||||
|
||||
# v2.21 2019-02-12 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
@@ -440,12 +1191,12 @@ and fixes the following bugs with Apache I-CLA, thanks to bentiss:
|
||||
header)
|
||||
- make sure the placeholder never get placed between the header and the
|
||||
list of cards in a swimlane
|
||||
- fix the finding of the next and previous list of cards.
|
||||
- fix the finding of the next and previous list of cards.
|
||||
For all of this to be successful, we need to resize the swimlanes to a
|
||||
known value. This can lead to some visual jumps with scrolling when you
|
||||
drag or drop the swimlanea. I tried to remedy that by computing the new
|
||||
scroll value. Still not ideal however, as there are still some jumps when
|
||||
dropping.
|
||||
dropping.
|
||||
Fixes [#2159](https://github.com/wekan/wekan/issues/2159).
|
||||
|
||||
Thanks to above GitHub users and translators for contributions.
|
||||
@@ -473,7 +1224,7 @@ Thanks to GitHub user ChronikEwok for contributions.
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fix: Not displaying card content of public board: Snap, Docker and Sandstorm Shared Wekan Board
|
||||
- [Fix: Not displaying card content of public board: Snap, Docker and Sandstorm Shared Wekan Board
|
||||
Link](https://github.com/wekan/wekan/issues/1623) with
|
||||
[code from ChronikEwok](https://github.com/ChronikEwok/wekan/commit/cad9b20451bb6149bfb527a99b5001873b06c3de).
|
||||
|
||||
@@ -505,7 +1256,7 @@ This release adds the following new features with Apache I-CLA, thanks to bentis
|
||||
- [When writing to minicard, press Shift-Enter on minicard to go to next line
|
||||
below](https://github.com/wekan/wekan/commit/7a35099fb9778d5f3656a57c74af426cfb20fba3),
|
||||
to continue writing on same minicard 2nd line.
|
||||
|
||||
|
||||
Thanks to GitHub user bentiss for contributions.
|
||||
|
||||
# v2.12 2019-01-31 Wekan release
|
||||
@@ -630,7 +1381,7 @@ and fixes the following bugs with Apache I-CLA, thanks to bentiss:
|
||||
- [Make sure Swimlanes and Lists have a populated sort field](https://github.com/wekan/wekan/commit/5c6a725712a443b4d03b4f86262033ddfb66bc3d).
|
||||
When moving around the swimlanes or the lists, if one element has a sort
|
||||
with a null value, the computation of the new sort value is aborted,
|
||||
meaning that there are glitches in the UI.
|
||||
meaning that there are glitches in the UI.
|
||||
This happens on the first swimlane created with the new board, or when
|
||||
a swimlane or a list gets added through the API;
|
||||
- UI: Lists: [Make sure all lists boxes are the same height](https://github.com/wekan/wekan/commit/97d95b4bcbcab86629e368ea41bb9f00450b21f6).
|
||||
@@ -700,7 +1451,7 @@ and fixes the following bugs:
|
||||
- When clicking Move Card, go to correct page position. Currently it's at empty page position, and there is
|
||||
need to scroll page up to see Move Card options. It should work similarly like Copy Card, that is visible.
|
||||
- Also check that other buttons go to visible page.
|
||||
|
||||
|
||||
Thanks to above GitHub users for their contributions.
|
||||
|
||||
# v1.97 2018-12-26 Wekan release
|
||||
@@ -709,7 +1460,7 @@ This release adds the following new features:
|
||||
|
||||
- Upgrade to Node 8.15.0 and MongoDB 3.2.22.
|
||||
- Stacksmith: back to Meteor 1.6.x based Wekan, because Meteor 1.8.x based is currently broken.
|
||||
|
||||
|
||||
Thanks to GitHub user xet7 for contributions.
|
||||
|
||||
# v1.96 2018-12-24 Wekan release
|
||||
@@ -871,7 +1622,7 @@ Thanks to GitHub user xet7 for contributions.
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- Remove extra commas `,` and add missing backslash `\`.
|
||||
- Remove extra commas `,` and add missing backslash `\`.
|
||||
Maybe after that login, logout and CORS works.
|
||||
|
||||
Thanks to GitHub user xet7 for contributions.
|
||||
@@ -1026,7 +1777,7 @@ and hides the following features at Sandstorm:
|
||||
|
||||
- Hide Linked Card and Linked Board on Sandstorm, because they are only
|
||||
useful when having multiple boards, and at Sandstorm
|
||||
there is only one board per grain. Thanks to ocdtrekkie and xet7. Closes #1982
|
||||
there is only one board per grain. Thanks to ocdtrekkie and xet7. Closes #1982
|
||||
|
||||
Thanks to above mentioned GitHub users for their contributions.
|
||||
|
||||
@@ -1188,7 +1939,7 @@ and tries to fix the following bugs:
|
||||
- Try to fix snap.
|
||||
|
||||
Thanks to GitHub users Akuket and xet7 for their contributions.
|
||||
|
||||
|
||||
# v1.53.5 2018-10-10 Wekan Edge relase
|
||||
|
||||
This release tries to fix the following bugs:
|
||||
@@ -1212,7 +1963,7 @@ This release adds the following new features:
|
||||
- [Upgrade](https://github.com/wekan/wekan/issues/1522) to [Meteor](https://blog.meteor.com/meteor-1-8-erases-the-debts-of-1-7-77af4c931fe3) [1.8.1-beta.0](https://github.com/meteor/meteor/issues/10216).
|
||||
with [these](https://github.com/wekan/wekan/commit/079e45eb52a0f62ddb6051bf2ea80fac8860d3d5)
|
||||
[commits](https://github.com/wekan/wekan/commit/dd47d46f4341a8c4ced05749633f783e88623e1b). So now it's possible to use MongoDB 2.6 - 4.0.
|
||||
|
||||
|
||||
Thanks to GitHub user xet7 for contributions.
|
||||
|
||||
# v1.53.2 2018-10-10 Wekan Edge release
|
||||
@@ -1240,7 +1991,7 @@ and fixes the following bugs:
|
||||
- [Feature rules: fixes and enhancements](https://github.com/wekan/wekan/pull/1936).
|
||||
|
||||
Thanks to GitHub users Akuket, Angtrim, dcmcand, lberk, maximest-pierre, InfoSec812, schulz and xet7 for their contributions.
|
||||
|
||||
|
||||
# v1.52.1 2018-10-02 Wekan Edge release
|
||||
|
||||
This release adds the following new features:
|
||||
@@ -1255,7 +2006,7 @@ and reverts previous change:
|
||||
Oidc already works with [doorkeeper](https://github.com/doorkeeper-gem/doorkeeper-provider-app).
|
||||
|
||||
Thanks to GitHub user xet7 for contributions.
|
||||
|
||||
|
||||
# v1.51.2 2018-09-30 Wekan Edge release
|
||||
|
||||
This release adds the following new features:
|
||||
@@ -1314,7 +2065,7 @@ Thanks to GitHub users Angtrim, maurice-schleussinger, suprovsky and xet7 for th
|
||||
This release adds the following new features:
|
||||
|
||||
- Change from Node v8.12.0 prerelease to use official Node v8.12.0.
|
||||
|
||||
|
||||
Thanks to GitHub user xet7 for contributions.
|
||||
|
||||
# v1.49 2018-09-17 Wekan release
|
||||
@@ -1472,10 +2223,10 @@ Thanks to GitHub users hever, salleman33, tlevine and xet7 for their contributio
|
||||
This release adds the following new features:
|
||||
|
||||
Add Caddy plugins:
|
||||
- [http.filter](https://caddyserver.com/docs/http.filter)
|
||||
- [http.filter](https://caddyserver.com/docs/http.filter)
|
||||
for changing Wekan UI on the fly, for example custom logo,
|
||||
or changing to all different CSS file to have custom theme;
|
||||
- [http.ipfilter](https://caddyserver.com/docs/http.ipfilter)
|
||||
- [http.ipfilter](https://caddyserver.com/docs/http.ipfilter)
|
||||
to block requests by ip address;
|
||||
- [http.realip](https://caddyserver.com/docs/http.realip)
|
||||
for showing real X-Forwarded-For IP to behind proxy;
|
||||
@@ -1771,7 +2522,7 @@ and fixes the following bugs:
|
||||
* To fix ["title is required"](https://github.com/wekan/wekan/issues/1576) fix only
|
||||
[add-checklist-items and revert all other migration changes](https://github.com/wekan/wekan/issues/1734);
|
||||
* [Restore invitation code logic](https://github.com/wekan/wekan/pull/1732). Please test and add comment
|
||||
to those invitation code issues that this fixes.
|
||||
to those invitation code issues that this fixes.
|
||||
|
||||
Thanks to GitHub users TNick and xet7 for their contributions.
|
||||
|
||||
@@ -1942,7 +2693,7 @@ This release adds the following new features:
|
||||
and fixes the following bugs:
|
||||
|
||||
* [Error: title is required](https://github.com/wekan/wekan/issues/1576).
|
||||
|
||||
|
||||
Thanks to GitHub users Shahar-Y, thiagofernando and ThisNeko for their contributions.
|
||||
|
||||
# v0.94 2018-05-03 Wekan release
|
||||
|
||||
+74
-144
@@ -1,111 +1,17 @@
|
||||
FROM ubuntu:cosmic
|
||||
FROM ubuntu:disco
|
||||
LABEL maintainer="wekan"
|
||||
|
||||
# Declare Arguments
|
||||
ARG DEBUG
|
||||
ARG NODE_VERSION
|
||||
ARG METEOR_RELEASE
|
||||
ARG METEOR_EDGE
|
||||
ARG USE_EDGE
|
||||
ARG NPM_VERSION
|
||||
ARG FIBERS_VERSION
|
||||
ARG ARCHITECTURE
|
||||
ARG SRC_PATH
|
||||
ARG WITH_API
|
||||
ARG ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE
|
||||
ARG ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD
|
||||
ARG ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW
|
||||
ARG ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE
|
||||
ARG ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD
|
||||
ARG ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW
|
||||
ARG EMAIL_NOTIFICATION_TIMEOUT
|
||||
ARG MATOMO_ADDRESS
|
||||
ARG MATOMO_SITE_ID
|
||||
ARG MATOMO_DO_NOT_TRACK
|
||||
ARG MATOMO_WITH_USERNAME
|
||||
ARG BROWSER_POLICY_ENABLED
|
||||
ARG TRUSTED_URL
|
||||
ARG WEBHOOKS_ATTRIBUTES
|
||||
ARG OAUTH2_ENABLED
|
||||
ARG OAUTH2_LOGIN_STYLE
|
||||
ARG OAUTH2_CLIENT_ID
|
||||
ARG OAUTH2_SECRET
|
||||
ARG OAUTH2_SERVER_URL
|
||||
ARG OAUTH2_AUTH_ENDPOINT
|
||||
ARG OAUTH2_USERINFO_ENDPOINT
|
||||
ARG OAUTH2_TOKEN_ENDPOINT
|
||||
ARG OAUTH2_ID_MAP
|
||||
ARG OAUTH2_USERNAME_MAP
|
||||
ARG OAUTH2_FULLNAME_MAP
|
||||
ARG OAUTH2_EMAIL_MAP
|
||||
ARG LDAP_ENABLE
|
||||
ARG LDAP_PORT
|
||||
ARG LDAP_HOST
|
||||
ARG LDAP_BASEDN
|
||||
ARG LDAP_LOGIN_FALLBACK
|
||||
ARG LDAP_RECONNECT
|
||||
ARG LDAP_TIMEOUT
|
||||
ARG LDAP_IDLE_TIMEOUT
|
||||
ARG LDAP_CONNECT_TIMEOUT
|
||||
ARG LDAP_AUTHENTIFICATION
|
||||
ARG LDAP_AUTHENTIFICATION_USERDN
|
||||
ARG LDAP_AUTHENTIFICATION_PASSWORD
|
||||
ARG LDAP_LOG_ENABLED
|
||||
ARG LDAP_BACKGROUND_SYNC
|
||||
ARG LDAP_BACKGROUND_SYNC_INTERVAL
|
||||
ARG LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED
|
||||
ARG LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS
|
||||
ARG LDAP_ENCRYPTION
|
||||
ARG LDAP_CA_CERT
|
||||
ARG LDAP_REJECT_UNAUTHORIZED
|
||||
ARG LDAP_USER_SEARCH_FILTER
|
||||
ARG LDAP_USER_SEARCH_SCOPE
|
||||
ARG LDAP_USER_SEARCH_FIELD
|
||||
ARG LDAP_SEARCH_PAGE_SIZE
|
||||
ARG LDAP_SEARCH_SIZE_LIMIT
|
||||
ARG LDAP_GROUP_FILTER_ENABLE
|
||||
ARG LDAP_GROUP_FILTER_OBJECTCLASS
|
||||
ARG LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE
|
||||
ARG LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE
|
||||
ARG LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT
|
||||
ARG LDAP_GROUP_FILTER_GROUP_NAME
|
||||
ARG LDAP_UNIQUE_IDENTIFIER_FIELD
|
||||
ARG LDAP_UTF8_NAMES_SLUGIFY
|
||||
ARG LDAP_USERNAME_FIELD
|
||||
ARG LDAP_FULLNAME_FIELD
|
||||
ARG LDAP_EMAIL_FIELD
|
||||
ARG LDAP_EMAIL_MATCH_ENABLE
|
||||
ARG LDAP_EMAIL_MATCH_REQUIRE
|
||||
ARG LDAP_EMAIL_MATCH_VERIFIED
|
||||
ARG LDAP_MERGE_EXISTING_USERS
|
||||
ARG LDAP_SYNC_USER_DATA
|
||||
ARG LDAP_SYNC_USER_DATA_FIELDMAP
|
||||
ARG LDAP_SYNC_GROUP_ROLES
|
||||
ARG LDAP_DEFAULT_DOMAIN
|
||||
ARG LDAP_SYNC_ADMIN_STATUS
|
||||
ARG LDAP_SYNC_ADMIN_GROUPS
|
||||
ARG HEADER_LOGIN_ID
|
||||
ARG HEADER_LOGIN_FIRSTNAME
|
||||
ARG HEADER_LOGIN_LASTNAME
|
||||
ARG HEADER_LOGIN_EMAIL
|
||||
ARG LOGOUT_WITH_TIMER
|
||||
ARG LOGOUT_IN
|
||||
ARG LOGOUT_ON_HOURS
|
||||
ARG LOGOUT_ON_MINUTES
|
||||
ARG CORS
|
||||
ARG DEFAULT_AUTHENTICATION_METHOD
|
||||
|
||||
# Set the environment variables (defaults where required)
|
||||
# DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
# ENV BUILD_DEPS="paxctl"
|
||||
ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 build-essential python python3 python3-distutils git ca-certificates gcc-7" \
|
||||
ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates" \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v8.15.1 \
|
||||
METEOR_RELEASE=1.6.0.1 \
|
||||
NODE_VERSION=v8.16.0 \
|
||||
METEOR_RELEASE=1.8.1 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=latest \
|
||||
FIBERS_VERSION=2.0.0 \
|
||||
FIBERS_VERSION=4.0.1 \
|
||||
ARCHITECTURE=linux-x64 \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
@@ -115,6 +21,12 @@ ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 build-essential pyth
|
||||
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3 \
|
||||
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \
|
||||
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \
|
||||
RICHER_CARD_COMMENT_EDITOR=true \
|
||||
MAX_IMAGE_PIXEL="" \
|
||||
IMAGE_COMPRESS_RATIO="" \
|
||||
BIGEVENTS_PATTERN="" \
|
||||
NOTIFY_DUE_DAYS_BEFORE_AND_AFTER="" \
|
||||
NOTIFY_DUE_AT_HOUR_OF_DAY="" \
|
||||
EMAIL_NOTIFICATION_TIMEOUT=30000 \
|
||||
MATOMO_ADDRESS="" \
|
||||
MATOMO_SITE_ID="" \
|
||||
@@ -134,6 +46,8 @@ ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 build-essential pyth
|
||||
OAUTH2_ID_MAP="" \
|
||||
OAUTH2_USERNAME_MAP="" \
|
||||
OAUTH2_FULLNAME_MAP="" \
|
||||
OAUTH2_ID_TOKEN_WHITELIST_FIELDS="" \
|
||||
OAUTH2_REQUEST_PERMISSIONS='openid profile email' \
|
||||
OAUTH2_EMAIL_MAP="" \
|
||||
LDAP_ENABLE=false \
|
||||
LDAP_PORT=389 \
|
||||
@@ -149,12 +63,14 @@ ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 build-essential pyth
|
||||
LDAP_AUTHENTIFICATION_PASSWORD="" \
|
||||
LDAP_LOG_ENABLED=false \
|
||||
LDAP_BACKGROUND_SYNC=false \
|
||||
LDAP_BACKGROUND_SYNC_INTERVAL=100 \
|
||||
LDAP_BACKGROUND_SYNC_INTERVAL="" \
|
||||
LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false \
|
||||
LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false \
|
||||
LDAP_ENCRYPTION=false \
|
||||
LDAP_CA_CERT="" \
|
||||
LDAP_REJECT_UNAUTHORIZED=false \
|
||||
LDAP_USER_AUTHENTICATION=false \
|
||||
LDAP_USER_AUTHENTICATION_FIELD=uid \
|
||||
LDAP_USER_SEARCH_FILTER="" \
|
||||
LDAP_USER_SEARCH_SCOPE="" \
|
||||
LDAP_USER_SEARCH_FIELD="" \
|
||||
@@ -190,6 +106,8 @@ ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 build-essential pyth
|
||||
LOGOUT_ON_HOURS="" \
|
||||
LOGOUT_ON_MINUTES="" \
|
||||
CORS="" \
|
||||
CORS_ALLOW_HEADERS="" \
|
||||
CORS_EXPOSE_HEADERS="" \
|
||||
DEFAULT_AUTHENTICATION_METHOD=""
|
||||
|
||||
# Copy the app to the image
|
||||
@@ -202,6 +120,7 @@ RUN \
|
||||
\
|
||||
# OS dependencies
|
||||
apt-get update -y && apt-get install -y --no-install-recommends ${BUILD_DEPS} && \
|
||||
#pip3 install -U pip setuptools wheel && \
|
||||
\
|
||||
# Meteor installer doesn't work with the default tar binary, so using bsdtar while installing.
|
||||
# https://github.com/coreos/bugs/issues/1095#issuecomment-350574389
|
||||
@@ -257,70 +176,81 @@ RUN \
|
||||
mv node-${NODE_VERSION}-${ARCHITECTURE} /opt/nodejs && \
|
||||
ln -s /opt/nodejs/bin/node /usr/bin/node && \
|
||||
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
|
||||
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/8.16.0 /home/wekan/.config && \
|
||||
chown wekan --recursive /home/wekan/.config && \
|
||||
\
|
||||
#DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
#paxctl -mC `which node` && \
|
||||
\
|
||||
# Install Node dependencies
|
||||
# Install Node dependencies. Python path for node-gyp.
|
||||
npm install -g npm@${NPM_VERSION} && \
|
||||
npm install -g node-gyp && \
|
||||
npm install -g fibers@${FIBERS_VERSION} && \
|
||||
#npm config set python python2.7 && \
|
||||
#npm install -g node-gyp && \
|
||||
#npm install -g fibers@${FIBERS_VERSION} && \
|
||||
\
|
||||
# Change user to wekan and install meteor
|
||||
cd /home/wekan/ && \
|
||||
chown wekan:wekan --recursive /home/wekan && \
|
||||
curl "https://install.meteor.com" -o /home/wekan/install_meteor.sh && \
|
||||
chown wekan --recursive /home/wekan && \
|
||||
#curl "https://install.meteor.com" -o /home/wekan/install_meteor.sh && \
|
||||
#curl "https://install.meteor.com/?release=${METEOR_RELEASE}" -o /home/wekan/install_meteor.sh && \
|
||||
# OLD: sed -i "s|RELEASE=.*|RELEASE=${METEOR_RELEASE}\"\"|g" ./install_meteor.sh && \
|
||||
# Install Meteor forcing its progress
|
||||
sed -i 's/VERBOSITY="--silent"/VERBOSITY="--progress-bar"/' ./install_meteor.sh && \
|
||||
#sed -i 's/VERBOSITY="--silent"/VERBOSITY="--progress-bar"/' ./install_meteor.sh && \
|
||||
echo "Starting meteor ${METEOR_RELEASE} installation... \n" && \
|
||||
chown wekan:wekan /home/wekan/install_meteor.sh && \
|
||||
gosu wekan:wekan curl https://install.meteor.com/ | /bin/sh && \
|
||||
mv /root/.meteor /home/wekan/ && \
|
||||
chown wekan --recursive /home/wekan/.meteor && \
|
||||
\
|
||||
# Check if opting for a release candidate instead of major release
|
||||
if [ "$USE_EDGE" = false ]; then \
|
||||
gosu wekan:wekan sh /home/wekan/install_meteor.sh; \
|
||||
else \
|
||||
gosu wekan:wekan git clone --recursive --depth 1 -b release/METEOR@${METEOR_EDGE} git://github.com/meteor/meteor.git /home/wekan/.meteor; \
|
||||
fi; \
|
||||
\
|
||||
#if [ "$USE_EDGE" = false ]; then \
|
||||
#gosu wekan:wekan sh /home/wekan/install_meteor.sh; \
|
||||
# gosu wekan:wekan curl https://install.meteor.com/ | sh; \
|
||||
#else \
|
||||
# gosu wekan:wekan git clone --recursive --depth 1 -b release/METEOR@${METEOR_EDGE} https://github.com/meteor/meteor.git /home/wekan/.meteor; \
|
||||
#fi; \
|
||||
#\
|
||||
# Get additional packages
|
||||
mkdir -p /home/wekan/app/packages && \
|
||||
chown wekan:wekan --recursive /home/wekan && \
|
||||
cd /home/wekan/app/packages && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/flow-router.git kadira-flow-router && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/meteor-useraccounts/core.git meteor-useraccounts-core && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/meteor-accounts-cas.git && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/wekan-ldap.git && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/wekan-scrollbar.git && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/meteor-accounts-oidc.git && \
|
||||
gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_accounts-oidc wekan_accounts-oidc && \
|
||||
gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_oidc wekan_oidc && \
|
||||
gosu wekan:wekan rm -rf meteor-accounts-oidc && \
|
||||
#mkdir -p /home/wekan/app/packages && \
|
||||
#chown wekan:wekan --recursive /home/wekan && \
|
||||
# REPOS BELOW ARE INCLUDED TO WEKAN REPO
|
||||
#cd /home/wekan/app/packages && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/flow-router.git kadira-flow-router && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/meteor-useraccounts/core.git meteor-useraccounts-core && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-cas.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/wekan-ldap.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/wekan-scrollbar.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-oidc.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git && \
|
||||
#gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_accounts-oidc wekan-accounts-oidc && \
|
||||
#gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_oidc wekan-oidc && \
|
||||
#gosu wekan:wekan rm -rf meteor-accounts-oidc && \
|
||||
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \
|
||||
cd /home/wekan/.meteor && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor -- help; \
|
||||
\
|
||||
# extract the OpenAPI specification
|
||||
npm install -g api2html && \
|
||||
mkdir -p /home/wekan/python && \
|
||||
chown wekan:wekan --recursive /home/wekan/python && \
|
||||
cd /home/wekan/python && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/Kronuz/esprima-python && \
|
||||
cd /home/wekan/python/esprima-python && \
|
||||
python3 setup.py install --record files.txt && \
|
||||
cd /home/wekan/app &&\
|
||||
mkdir -p ./public/api && \
|
||||
python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml && \
|
||||
/opt/nodejs/bin/api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml; \
|
||||
#npm install -g api2html@0.3.3 && \
|
||||
#mkdir -p /home/wekan/python && \
|
||||
#chown wekan --recursive /home/wekan/python && \
|
||||
#cd /home/wekan/python && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/Kronuz/esprima-python && \
|
||||
#cd /home/wekan/python/esprima-python && \
|
||||
#python3 setup.py install --record files.txt && \
|
||||
#cd /home/wekan/app && \
|
||||
#mkdir -p /home/wekan/app/public/api && \
|
||||
#chown wekan --recursive /home/wekan/app && \
|
||||
#gosu wekan:wekan python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml && \
|
||||
#gosu wekan:wekan /opt/nodejs/bin/api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml; \
|
||||
# Build app
|
||||
cd /home/wekan/app && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor add standard-minifier-js && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor npm install && \
|
||||
mkdir -p /home/wekan/.npm && \
|
||||
chown wekan --recursive /home/wekan/.npm /home/wekan/.config /home/wekan/.meteor && \
|
||||
#gosu wekan:wekan /home/wekan/.meteor/meteor add standard-minifier-js && \
|
||||
gosu wekan:wekan npm install && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build && \
|
||||
cp /home/wekan/app/fix-download-unicode/cfs_access-point.txt /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
|
||||
rm /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs && \
|
||||
chown wekan:wekan /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
|
||||
#rm /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs && \
|
||||
chown wekan /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
|
||||
#Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
|
||||
#https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
|
||||
#https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
|
||||
@@ -342,10 +272,10 @@ RUN \
|
||||
rm -R /var/lib/apt/lists/* && \
|
||||
rm -R /home/wekan/.meteor && \
|
||||
rm -R /home/wekan/app && \
|
||||
rm -R /home/wekan/app_build && \
|
||||
cat /home/wekan/python/esprima-python/files.txt | xargs rm -R && \
|
||||
rm -R /home/wekan/python && \
|
||||
rm /home/wekan/install_meteor.sh
|
||||
rm -R /home/wekan/app_build
|
||||
#cat /home/wekan/python/esprima-python/files.txt | xargs rm -R && \
|
||||
#rm -R /home/wekan/python
|
||||
#rm /home/wekan/install_meteor.sh
|
||||
|
||||
ENV PORT=8080
|
||||
EXPOSE $PORT
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
[](https://codeclimate.com/github/wekan/wekan)
|
||||
[](https://david-dm.org/wekan/wekan)
|
||||
[](https://www.openhub.net/p/wekan)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwekan%2Fwekan?ref=badge_shield)
|
||||
|
||||
## [Translate Wekan at Transifex](https://transifex.com/wekan/wekan)
|
||||
|
||||
@@ -60,7 +61,7 @@ that by providing one-click installation on various platforms.
|
||||
- 64bit: Linux [Snap](https://github.com/wekan/wekan-snap/wiki/Install) or [Sandstorm](https://sandstorm.io) /
|
||||
[Mac](https://github.com/wekan/wekan/wiki/Mac) / [Windows](https://github.com/wekan/wekan/wiki/Install-Wekan-from-source-on-Windows).
|
||||
[More Platforms](https://github.com/wekan/wekan/wiki/Platforms). [ARM progress](https://github.com/wekan/wekan/issues/1053#issuecomment-410919264).
|
||||
- 1 GB RAM minimum free for Wekan. Production server should have miminum total 4 GB RAM.
|
||||
- 1 GB RAM minimum free for Wekan. Production server should have minimum total 4 GB RAM.
|
||||
For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/devel/docker-compose.yml): 3 frontend servers,
|
||||
each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.
|
||||
- Enough disk space and alerts about low disk space. If you run out disk space, MongoDB database gets corrupted.
|
||||
@@ -133,3 +134,6 @@ with [Meteor](https://www.meteor.com).
|
||||
[free_software]: https://en.wikipedia.org/wiki/Free_software
|
||||
[vanila_badge]: https://vanila.io/img/join-chat-button2.png
|
||||
[wekan_chat]: https://community.vanila.io/wekan
|
||||
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwekan%2Fwekan?ref=badge_large)
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v2.52.0"
|
||||
appVersion: "v3.06.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -201,6 +201,19 @@ template(name="cardActivities")
|
||||
.activity-checklist(href="{{ card.absoluteUrl }}")
|
||||
+viewer
|
||||
= checklistItem.title
|
||||
if(currentData.timeKey)
|
||||
| {{{_ activityType }}}
|
||||
= ' '
|
||||
i(title=currentData.timeValue).activity-meta {{ moment currentData.timeValue 'LLL' }}
|
||||
if (currentData.timeOldValue)
|
||||
= ' '
|
||||
| {{{_ "previous_as" }}}
|
||||
= ' '
|
||||
i(title=currentData.timeOldValue).activity-meta {{ moment currentData.timeOldValue 'LLL' }}
|
||||
= ' @'
|
||||
else if(currentData.timeValue)
|
||||
| {{{_ activityType currentData.timeValue}}}
|
||||
|
||||
|
||||
if($eq activityType 'addComment')
|
||||
+inlinedForm(classNames='js-edit-comment')
|
||||
|
||||
@@ -13,7 +13,7 @@ BlazeComponent.extendComponent({
|
||||
let thisId, searchId;
|
||||
if (mode === 'linkedcard' || mode === 'linkedboard') {
|
||||
thisId = Session.get('currentCard');
|
||||
searchId = Cards.findOne({_id: thisId}).linkedId;
|
||||
searchId = Cards.findOne({ _id: thisId }).linkedId;
|
||||
mode = mode.replace('linked', '');
|
||||
} else {
|
||||
thisId = Session.get(`current${capitalizedMode}`);
|
||||
@@ -22,8 +22,7 @@ BlazeComponent.extendComponent({
|
||||
const limit = this.page.get() * activitiesPerPage;
|
||||
const user = Meteor.user();
|
||||
const hideSystem = user ? user.hasHiddenSystemMessages() : false;
|
||||
if (searchId === null)
|
||||
return;
|
||||
if (searchId === null) return;
|
||||
|
||||
this.subscribe('activities', mode, searchId, limit, hideSystem, () => {
|
||||
this.loadNextPageLocked = false;
|
||||
@@ -50,9 +49,9 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
},
|
||||
|
||||
checkItem(){
|
||||
checkItem() {
|
||||
const checkItemId = this.currentData().checklistItemId;
|
||||
const checkItem = ChecklistItems.findOne({_id:checkItemId});
|
||||
const checkItem = ChecklistItems.findOne({ _id: checkItemId });
|
||||
return checkItem.title;
|
||||
},
|
||||
|
||||
@@ -66,42 +65,58 @@ BlazeComponent.extendComponent({
|
||||
|
||||
cardLink() {
|
||||
const card = this.currentData().card();
|
||||
return card && Blaze.toHTML(HTML.A({
|
||||
href: card.absoluteUrl(),
|
||||
'class': 'action-card',
|
||||
}, card.title));
|
||||
return (
|
||||
card &&
|
||||
Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: card.absoluteUrl(),
|
||||
class: 'action-card',
|
||||
},
|
||||
card.title,
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
lastLabel(){
|
||||
lastLabel() {
|
||||
const lastLabelId = this.currentData().labelId;
|
||||
if (!lastLabelId)
|
||||
return null;
|
||||
const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(lastLabelId);
|
||||
if(lastLabel.name === undefined || lastLabel.name === ''){
|
||||
if (!lastLabelId) return null;
|
||||
const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(
|
||||
lastLabelId,
|
||||
);
|
||||
if (lastLabel.name === undefined || lastLabel.name === '') {
|
||||
return lastLabel.color;
|
||||
}else{
|
||||
} else {
|
||||
return lastLabel.name;
|
||||
}
|
||||
},
|
||||
|
||||
lastCustomField(){
|
||||
const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
|
||||
if (!lastCustomField)
|
||||
return null;
|
||||
lastCustomField() {
|
||||
const lastCustomField = CustomFields.findOne(
|
||||
this.currentData().customFieldId,
|
||||
);
|
||||
if (!lastCustomField) return null;
|
||||
return lastCustomField.name;
|
||||
},
|
||||
|
||||
lastCustomFieldValue(){
|
||||
const lastCustomField = CustomFields.findOne(this.currentData().customFieldId);
|
||||
if (!lastCustomField)
|
||||
return null;
|
||||
lastCustomFieldValue() {
|
||||
const lastCustomField = CustomFields.findOne(
|
||||
this.currentData().customFieldId,
|
||||
);
|
||||
if (!lastCustomField) return null;
|
||||
const value = this.currentData().value;
|
||||
if (lastCustomField.settings.dropdownItems && lastCustomField.settings.dropdownItems.length > 0) {
|
||||
const dropDownValue = _.find(lastCustomField.settings.dropdownItems, (item) => {
|
||||
return item._id === value;
|
||||
});
|
||||
if (dropDownValue)
|
||||
return dropDownValue.name;
|
||||
if (
|
||||
lastCustomField.settings.dropdownItems &&
|
||||
lastCustomField.settings.dropdownItems.length > 0
|
||||
) {
|
||||
const dropDownValue = _.find(
|
||||
lastCustomField.settings.dropdownItems,
|
||||
item => {
|
||||
return item._id === value;
|
||||
},
|
||||
);
|
||||
if (dropDownValue) return dropDownValue.name;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
@@ -112,11 +127,16 @@ BlazeComponent.extendComponent({
|
||||
|
||||
sourceLink() {
|
||||
const source = this.currentData().source;
|
||||
if(source) {
|
||||
if(source.url) {
|
||||
return Blaze.toHTML(HTML.A({
|
||||
href: source.url,
|
||||
}, source.system));
|
||||
if (source) {
|
||||
if (source.url) {
|
||||
return Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: source.url,
|
||||
},
|
||||
source.system,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return source.system;
|
||||
}
|
||||
@@ -133,38 +153,50 @@ BlazeComponent.extendComponent({
|
||||
attachmentLink() {
|
||||
const attachment = this.currentData().attachment();
|
||||
// trying to display url before file is stored generates js errors
|
||||
return attachment && attachment.url({ download: true }) && Blaze.toHTML(HTML.A({
|
||||
href: attachment.url({ download: true }),
|
||||
target: '_blank',
|
||||
}, attachment.name()));
|
||||
return (
|
||||
attachment &&
|
||||
attachment.url({ download: true }) &&
|
||||
Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: attachment.url({ download: true }),
|
||||
target: '_blank',
|
||||
},
|
||||
attachment.name(),
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
customField() {
|
||||
const customField = this.currentData().customField();
|
||||
if (!customField)
|
||||
return null;
|
||||
if (!customField) return null;
|
||||
return customField.name;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
// XXX We should use Popup.afterConfirmation here
|
||||
'click .js-delete-comment'() {
|
||||
const commentId = this.currentData().commentId;
|
||||
CardComments.remove(commentId);
|
||||
return [
|
||||
{
|
||||
// XXX We should use Popup.afterConfirmation here
|
||||
'click .js-delete-comment'() {
|
||||
const commentId = this.currentData().commentId;
|
||||
CardComments.remove(commentId);
|
||||
},
|
||||
'submit .js-edit-comment'(evt) {
|
||||
evt.preventDefault();
|
||||
const commentText = this.currentComponent()
|
||||
.getValue()
|
||||
.trim();
|
||||
const commentId = Template.parentData().commentId;
|
||||
if (commentText) {
|
||||
CardComments.update(commentId, {
|
||||
$set: {
|
||||
text: commentText,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'submit .js-edit-comment'(evt) {
|
||||
evt.preventDefault();
|
||||
const commentText = this.currentComponent().getValue().trim();
|
||||
const commentId = Template.parentData().commentId;
|
||||
if (commentText) {
|
||||
CardComments.update(commentId, {
|
||||
$set: {
|
||||
text: commentText,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('activities');
|
||||
@@ -14,45 +14,47 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-new-comment:not(.focus)'() {
|
||||
commentFormIsOpen.set(true);
|
||||
return [
|
||||
{
|
||||
'click .js-new-comment:not(.focus)'() {
|
||||
commentFormIsOpen.set(true);
|
||||
},
|
||||
'submit .js-new-comment-form'(evt) {
|
||||
const input = this.getInput();
|
||||
const text = input.val().trim();
|
||||
const card = this.currentData();
|
||||
let boardId = card.boardId;
|
||||
let cardId = card._id;
|
||||
if (card.isLinkedCard()) {
|
||||
boardId = Cards.findOne(card.linkedId).boardId;
|
||||
cardId = card.linkedId;
|
||||
}
|
||||
if (text) {
|
||||
CardComments.insert({
|
||||
text,
|
||||
boardId,
|
||||
cardId,
|
||||
});
|
||||
resetCommentInput(input);
|
||||
Tracker.flush();
|
||||
autosize.update(input);
|
||||
}
|
||||
evt.preventDefault();
|
||||
},
|
||||
// Pressing Ctrl+Enter should submit the form
|
||||
'keydown form textarea'(evt) {
|
||||
if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
|
||||
this.find('button[type=submit]').click();
|
||||
}
|
||||
},
|
||||
},
|
||||
'submit .js-new-comment-form'(evt) {
|
||||
const input = this.getInput();
|
||||
const text = input.val().trim();
|
||||
const card = this.currentData();
|
||||
let boardId = card.boardId;
|
||||
let cardId = card._id;
|
||||
if (card.isLinkedCard()) {
|
||||
boardId = Cards.findOne(card.linkedId).boardId;
|
||||
cardId = card.linkedId;
|
||||
}
|
||||
if (text) {
|
||||
CardComments.insert({
|
||||
text,
|
||||
boardId,
|
||||
cardId,
|
||||
});
|
||||
resetCommentInput(input);
|
||||
Tracker.flush();
|
||||
autosize.update(input);
|
||||
}
|
||||
evt.preventDefault();
|
||||
},
|
||||
// Pressing Ctrl+Enter should submit the form
|
||||
'keydown form textarea'(evt) {
|
||||
if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
|
||||
this.find('button[type=submit]').click();
|
||||
}
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('commentForm');
|
||||
|
||||
// XXX This should be a static method of the `commentForm` component
|
||||
function resetCommentInput(input) {
|
||||
input.val('');
|
||||
input.val('').trigger('input'); // without manually trigger, input event won't be fired
|
||||
input.blur();
|
||||
commentFormIsOpen.set(false);
|
||||
}
|
||||
@@ -69,7 +71,8 @@ Tracker.autorun(() => {
|
||||
});
|
||||
});
|
||||
|
||||
EscapeActions.register('inlinedForm',
|
||||
EscapeActions.register(
|
||||
'inlinedForm',
|
||||
() => {
|
||||
const draftKey = {
|
||||
fieldName: 'cardComment',
|
||||
@@ -84,7 +87,10 @@ EscapeActions.register('inlinedForm',
|
||||
}
|
||||
resetCommentInput(commentInput);
|
||||
},
|
||||
() => { return commentFormIsOpen.get(); }, {
|
||||
() => {
|
||||
return commentFormIsOpen.get();
|
||||
},
|
||||
{
|
||||
noClickEscapeOn: '.js-new-comment',
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -4,36 +4,45 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
archivedBoards() {
|
||||
return Boards.find({ archived: true }, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return Boards.find(
|
||||
{ archived: true },
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-restore-board'() {
|
||||
// TODO : Make isSandstorm variable global
|
||||
const isSandstorm = Meteor.settings && Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
currentBoard.archive();
|
||||
}
|
||||
const board = this.currentData();
|
||||
board.restore();
|
||||
Utils.goBoardId(board._id);
|
||||
return [
|
||||
{
|
||||
'click .js-restore-board'() {
|
||||
// TODO : Make isSandstorm variable global
|
||||
const isSandstorm =
|
||||
Meteor.settings &&
|
||||
Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
currentBoard.archive();
|
||||
}
|
||||
const board = this.currentData();
|
||||
board.restore();
|
||||
Utils.goBoardId(board._id);
|
||||
},
|
||||
'click .js-delete-board': Popup.afterConfirm('boardDelete', function() {
|
||||
Popup.close();
|
||||
const isSandstorm =
|
||||
Meteor.settings &&
|
||||
Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
Boards.remove(currentBoard._id);
|
||||
}
|
||||
Boards.remove(this._id);
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
},
|
||||
'click .js-delete-board': Popup.afterConfirm('boardDelete', function() {
|
||||
Popup.close();
|
||||
const isSandstorm = Meteor.settings && Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
Boards.remove(currentBoard._id);
|
||||
}
|
||||
Boards.remove(this._id);
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('archivedBoards');
|
||||
@@ -6,8 +6,9 @@ template(name="board")
|
||||
else
|
||||
+boardBody
|
||||
else
|
||||
//- XXX We need a better error message in case the board has been archived
|
||||
+message(label="board-not-found")
|
||||
//-- XXX We need a better error message in case the board has been archived
|
||||
//-- +message(label="board-not-found")
|
||||
| {{goHome}}
|
||||
else
|
||||
+spinner
|
||||
|
||||
@@ -30,6 +31,8 @@ template(name="boardBody")
|
||||
+listsGroup(currentBoard)
|
||||
else if isViewCalendar
|
||||
+calendarView
|
||||
else
|
||||
+listsGroup(currentBoard)
|
||||
|
||||
template(name="calendarView")
|
||||
if isViewCalendar
|
||||
|
||||
@@ -12,9 +12,8 @@ BlazeComponent.extendComponent({
|
||||
// unfortunatly, Blaze doesn't have this notion.
|
||||
this.autorun(() => {
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (!currentBoardId)
|
||||
return;
|
||||
const handle = subManager.subscribe('board', currentBoardId);
|
||||
if (!currentBoardId) return;
|
||||
const handle = subManager.subscribe('board', currentBoardId, false);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isBoardReady.set(handle.ready());
|
||||
@@ -27,6 +26,9 @@ BlazeComponent.extendComponent({
|
||||
return Utils.isMiniScreen() && Session.get('currentCard');
|
||||
},
|
||||
|
||||
goHome() {
|
||||
FlowRouter.go('home');
|
||||
},
|
||||
}).register('board');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -43,7 +45,7 @@ BlazeComponent.extendComponent({
|
||||
if (nullSortSwimlanes.count() > 0) {
|
||||
const swimlanes = currentBoardData.swimlanes();
|
||||
let count = 0;
|
||||
swimlanes.forEach((s) => {
|
||||
swimlanes.forEach(s => {
|
||||
Swimlanes.update(s._id, {
|
||||
$set: {
|
||||
sort: count,
|
||||
@@ -58,7 +60,7 @@ BlazeComponent.extendComponent({
|
||||
if (nullSortLists.count() > 0) {
|
||||
const lists = currentBoardData.lists();
|
||||
let count = 0;
|
||||
lists.forEach((l) => {
|
||||
lists.forEach(l => {
|
||||
Lists.update(l._id, {
|
||||
$set: {
|
||||
sort: count,
|
||||
@@ -106,12 +108,15 @@ BlazeComponent.extendComponent({
|
||||
// resize all swimlanes + headers to be a total of 150 px per row
|
||||
// this could be achieved by setIsDragging(true) but we want immediate
|
||||
// result
|
||||
ui.item.siblings('.js-swimlane').css('height', `${swimlaneWhileSortingHeight - 26}px`);
|
||||
ui.item
|
||||
.siblings('.js-swimlane')
|
||||
.css('height', `${swimlaneWhileSortingHeight - 26}px`);
|
||||
|
||||
// set the new scroll height after the resize and insertion of
|
||||
// the placeholder. We want the element under the cursor to stay
|
||||
// at the same place on the screen
|
||||
ui.item.parent().get(0).scrollTop = ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
|
||||
ui.item.parent().get(0).scrollTop =
|
||||
ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
|
||||
},
|
||||
beforeStop(evt, ui) {
|
||||
const parentOffset = ui.item.parent().offset();
|
||||
@@ -120,7 +125,8 @@ BlazeComponent.extendComponent({
|
||||
|
||||
// compute the new scroll height after the resize and removal of
|
||||
// the placeholder
|
||||
const scrollTop = ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
|
||||
const scrollTop =
|
||||
ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY;
|
||||
|
||||
// then reset the original view of the swimlane
|
||||
siblings.removeClass('moving-swimlane');
|
||||
@@ -150,11 +156,14 @@ BlazeComponent.extendComponent({
|
||||
sort(evt, ui) {
|
||||
// get the mouse position in the sortable
|
||||
const parentOffset = ui.item.parent().offset();
|
||||
const cursorY = evt.pageY - parentOffset.top + ui.item.parent().scrollTop();
|
||||
const cursorY =
|
||||
evt.pageY - parentOffset.top + ui.item.parent().scrollTop();
|
||||
|
||||
// compute the intended index of the placeholder (we need to skip the
|
||||
// slots between the headers and the list of cards)
|
||||
const newplaceholderIndex = Math.floor(cursorY / swimlaneWhileSortingHeight);
|
||||
const newplaceholderIndex = Math.floor(
|
||||
cursorY / swimlaneWhileSortingHeight,
|
||||
);
|
||||
let destPlaceholderIndex = (newplaceholderIndex + 1) * 2;
|
||||
|
||||
// if we are scrolling far away from the bottom of the list
|
||||
@@ -165,9 +174,17 @@ BlazeComponent.extendComponent({
|
||||
// update the placeholder position in the DOM tree
|
||||
if (destPlaceholderIndex !== ui.placeholder.index()) {
|
||||
if (destPlaceholderIndex < boardComponent.origPlaceholderIndex) {
|
||||
ui.placeholder.insertBefore(ui.placeholder.siblings().slice(destPlaceholderIndex - 2, destPlaceholderIndex - 1));
|
||||
ui.placeholder.insertBefore(
|
||||
ui.placeholder
|
||||
.siblings()
|
||||
.slice(destPlaceholderIndex - 2, destPlaceholderIndex - 1),
|
||||
);
|
||||
} else {
|
||||
ui.placeholder.insertAfter(ui.placeholder.siblings().slice(destPlaceholderIndex - 1, destPlaceholderIndex));
|
||||
ui.placeholder.insertAfter(
|
||||
ui.placeholder
|
||||
.siblings()
|
||||
.slice(destPlaceholderIndex - 1, destPlaceholderIndex),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -177,7 +194,11 @@ BlazeComponent.extendComponent({
|
||||
enableClickOnTouch('.js-swimlane:not(.placeholder)');
|
||||
|
||||
function userIsMember() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
}
|
||||
|
||||
// If there is no data in the board (ie, no lists) we autofocus the list
|
||||
@@ -191,45 +212,44 @@ BlazeComponent.extendComponent({
|
||||
isViewSwimlanes() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return (currentUser.profile.boardView === 'board-view-swimlanes');
|
||||
return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
|
||||
},
|
||||
|
||||
isViewLists() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return true;
|
||||
return (currentUser.profile.boardView === 'board-view-lists');
|
||||
return (currentUser.profile || {}).boardView === 'board-view-lists';
|
||||
},
|
||||
|
||||
isViewCalendar() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return (currentUser.profile.boardView === 'board-view-cal');
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
},
|
||||
|
||||
openNewListForm() {
|
||||
if (this.isViewSwimlanes()) {
|
||||
this.childComponents('swimlane')[0]
|
||||
.childComponents('addListAndSwimlaneForm')[0].open();
|
||||
.childComponents('addListAndSwimlaneForm')[0]
|
||||
.open();
|
||||
} else if (this.isViewLists()) {
|
||||
this.childComponents('listsGroup')[0]
|
||||
.childComponents('addListForm')[0].open();
|
||||
.childComponents('addListForm')[0]
|
||||
.open();
|
||||
}
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
// XXX The board-overlay div should probably be moved to the parent
|
||||
// component.
|
||||
'mouseenter .board-overlay'() {
|
||||
if (this.mouseHasEnterCardDetails) {
|
||||
this.showOverlay.set(false);
|
||||
}
|
||||
return [
|
||||
{
|
||||
// XXX The board-overlay div should probably be moved to the parent
|
||||
// component.
|
||||
mouseup() {
|
||||
if (this._isDragging) {
|
||||
this._isDragging = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
'mouseup'() {
|
||||
if (this._isDragging) {
|
||||
this._isDragging = false;
|
||||
}
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
|
||||
// XXX Flow components allow us to avoid creating these two setter methods by
|
||||
@@ -241,23 +261,24 @@ BlazeComponent.extendComponent({
|
||||
|
||||
scrollLeft(position = 0) {
|
||||
const swimlanes = this.$('.js-swimlanes');
|
||||
swimlanes && swimlanes.animate({
|
||||
scrollLeft: position,
|
||||
});
|
||||
swimlanes &&
|
||||
swimlanes.animate({
|
||||
scrollLeft: position,
|
||||
});
|
||||
},
|
||||
|
||||
scrollTop(position = 0) {
|
||||
const swimlanes = this.$('.js-swimlanes');
|
||||
swimlanes && swimlanes.animate({
|
||||
scrollTop: position,
|
||||
});
|
||||
swimlanes &&
|
||||
swimlanes.animate({
|
||||
scrollTop: position,
|
||||
});
|
||||
},
|
||||
|
||||
}).register('boardBody');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
this.autorun(function(){
|
||||
this.autorun(function() {
|
||||
$('#calendar-view').fullCalendar('refetchEvents');
|
||||
});
|
||||
},
|
||||
@@ -269,7 +290,8 @@ BlazeComponent.extendComponent({
|
||||
timezone: 'local',
|
||||
header: {
|
||||
left: 'title today prev,next',
|
||||
center: 'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,timelineMonth timelineYear',
|
||||
center:
|
||||
'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,timelineMonth timelineYear',
|
||||
right: '',
|
||||
},
|
||||
// height: 'parent', nope, doesn't work as the parent might be small
|
||||
@@ -279,7 +301,7 @@ BlazeComponent.extendComponent({
|
||||
nowIndicator: true,
|
||||
businessHours: {
|
||||
// days of week. an array of zero-based day of week integers (0=Sunday)
|
||||
dow: [ 1, 2, 3, 4, 5 ], // Monday - Friday
|
||||
dow: [1, 2, 3, 4, 5], // Monday - Friday
|
||||
start: '8:00',
|
||||
end: '18:00',
|
||||
},
|
||||
@@ -287,20 +309,25 @@ BlazeComponent.extendComponent({
|
||||
events(start, end, timezone, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const events = [];
|
||||
currentBoard.cardsInInterval(start.toDate(), end.toDate()).forEach(function(card){
|
||||
events.push({
|
||||
id: card._id,
|
||||
title: card.title,
|
||||
start: card.startAt,
|
||||
end: card.endAt,
|
||||
allDay: Math.abs(card.endAt.getTime() - card.startAt.getTime()) / 1000 === 24*3600,
|
||||
url: FlowRouter.url('card', {
|
||||
boardId: currentBoard._id,
|
||||
slug: currentBoard.slug,
|
||||
cardId: card._id,
|
||||
}),
|
||||
currentBoard
|
||||
.cardsInInterval(start.toDate(), end.toDate())
|
||||
.forEach(function(card) {
|
||||
events.push({
|
||||
id: card._id,
|
||||
title: card.title,
|
||||
start: card.startAt,
|
||||
end: card.endAt,
|
||||
allDay:
|
||||
Math.abs(card.endAt.getTime() - card.startAt.getTime()) /
|
||||
1000 ===
|
||||
24 * 3600,
|
||||
url: FlowRouter.url('card', {
|
||||
boardId: currentBoard._id,
|
||||
slug: currentBoard.slug,
|
||||
cardId: card._id,
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
callback(events);
|
||||
},
|
||||
eventResize(event, delta, revertFunc) {
|
||||
@@ -335,6 +362,6 @@ BlazeComponent.extendComponent({
|
||||
isViewCalendar() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return (currentUser.profile.boardView === 'board-view-cal');
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
},
|
||||
}).register('calendarView');
|
||||
@@ -50,6 +50,6 @@ position()
|
||||
display: flex
|
||||
flex-direction: column
|
||||
margin: 0
|
||||
padding: 0 40px 0px 0
|
||||
padding: 0 0px 0px 0
|
||||
overflow-x: hidden
|
||||
overflow-y: auto
|
||||
@@ -87,3 +87,208 @@ setBoardColor(color)
|
||||
|
||||
.board-color-pumpkin
|
||||
setBoardColor(#E67E22)
|
||||
|
||||
.board-color-moderatepink
|
||||
setBoardColor(#CD5A91)
|
||||
|
||||
.board-color-strongcyan
|
||||
setBoardColor(#00AECC)
|
||||
|
||||
.board-color-limegreen
|
||||
setBoardColor(#4BBF6B)
|
||||
|
||||
.board-color-dark
|
||||
setBoardColor(#2C3E51)
|
||||
|
||||
/* Not hidden in dark mode.
|
||||
card fields: received, start, due, end, members, requested, assigned
|
||||
.card-details-item.card-details-item-received,
|
||||
.card-details-item.card-details-item-start,
|
||||
.card-details-item.card-details-item-due,
|
||||
.card-details-item.card-details-item-end,
|
||||
.card-details-item.card-details-item-members,
|
||||
.card-details-item.card-details-item-name { display:none; }
|
||||
.card-details-items:empty { display:none; }
|
||||
*/
|
||||
|
||||
// DARK MODE, when dark background mode selected.
|
||||
// Modified version from https://github.com/wekan/wekan/wiki/Custom-CSS-themes#dark-theme
|
||||
// In progress, please send pull requests to fix remaining visibility issues.
|
||||
.ui-sortable,
|
||||
.swimlane,
|
||||
.swimlane >.swimlane-header-wrap,
|
||||
.swimlane >.list.js-list,
|
||||
.swimlane >.list-composer.js-list-composer,
|
||||
.list-body,
|
||||
.list,
|
||||
.list-composer,
|
||||
.sidebar-content,
|
||||
.card-details
|
||||
background-color:#2C3E50
|
||||
|
||||
.card-details h3,
|
||||
.card-details-items,
|
||||
.card-checklist-items .ui-sortable,
|
||||
.card-subtasks-items,
|
||||
.activities,
|
||||
.material-toggle-switch
|
||||
color:#bbbbbb
|
||||
|
||||
.list-header
|
||||
background-color: #888888
|
||||
|
||||
.board-widget,
|
||||
.board-widget-labels,
|
||||
.board-widget-members
|
||||
color: #aaaaaa
|
||||
|
||||
/* popup menu titles (boards, swimlanes, lists, cards, labels) */
|
||||
.pop-over >.header
|
||||
display:none;
|
||||
|
||||
/* HIDE UNTIL HOVER -------------------------------------------------- */
|
||||
|
||||
/* header "+" button */
|
||||
#header-quick-access .fa-plus
|
||||
display:none
|
||||
#header-quick-access:hover .fa-plus
|
||||
display:inherit
|
||||
|
||||
/* "add card" links (use visibility rather than display so items don't jump) */
|
||||
.open-minicard-composer
|
||||
visibility:hidden
|
||||
|
||||
.list.js-list:hover .open-minicard-composer
|
||||
visibility:visible
|
||||
|
||||
.list-header-menu
|
||||
visibility:hidden
|
||||
|
||||
.list.js-list:hover .list-header-menu
|
||||
visibility:visible
|
||||
|
||||
/* "add list/swimlane" links (use visibility rather than display so items don't jump) */
|
||||
.list.js-list-composer >.list-header
|
||||
visibility:hidden
|
||||
|
||||
.list.js-list-composer:hover >.list-header
|
||||
visibility:visible
|
||||
|
||||
/* headers */
|
||||
#header-quick-access, #header
|
||||
background-color:rgba(0,0,0,.75) !important
|
||||
|
||||
#header .board-header-btn:hover
|
||||
background-color:rgba(255,255,255,0.3) !important
|
||||
|
||||
/* foregrounds: swimlanes, lists */
|
||||
.list >.list-header, .swimlane-header
|
||||
color:rgba(255,255,255,.7)
|
||||
|
||||
/* minicards */
|
||||
.minicard
|
||||
background-color:rgba(255,255,255,.4)
|
||||
|
||||
.minicard-wrapper.is-selected .minicard,
|
||||
.minicard:hover,
|
||||
.minicard-composer.js-composer,
|
||||
.open-minicard-composer:hover
|
||||
background-color:rgba(255,255,255,.8) !important
|
||||
color:#000
|
||||
|
||||
.minicard, .minicard .badge
|
||||
color:#fff
|
||||
|
||||
.minicard:hover .badge, .minicard-wrapper.is-selected .badge
|
||||
color:#000
|
||||
|
||||
/* cards */
|
||||
.card-details .card-details-header
|
||||
background-color:#ccc
|
||||
|
||||
/* sidebar */
|
||||
.sidebar-tongue, .sidebar-shadow
|
||||
background-color:#666 !important
|
||||
|
||||
.sidebar-content h3, .sidebar-content h2, .sidebar-content
|
||||
color:rgba(255,255,255,.7) !important
|
||||
|
||||
.board-color-relax
|
||||
setBoardColor(#27AE61)
|
||||
|
||||
// RELAX MODE: light green background, with green background color,
|
||||
// to help this theme users to relax.
|
||||
// Colors and emphasis are specific to this Wekan theme contributor's company.
|
||||
.ui-sortable
|
||||
background-color:#a7e366
|
||||
|
||||
.list-header
|
||||
background-color:#a7e366
|
||||
border-bottom: 6px solid #a7e366
|
||||
|
||||
.list-body
|
||||
background-color:#a7e366
|
||||
|
||||
.list
|
||||
border-left: 1px dotted #000000
|
||||
|
||||
// Card details text emphasis: black border and white background
|
||||
// to make it details text field easier to find for RELAX MODE users,
|
||||
// and focus attention.
|
||||
.card-details .card-details-items
|
||||
& ~ .js-open-inlined-form
|
||||
.viewer
|
||||
background-color #ffffff !important
|
||||
padding 15px !important
|
||||
border 1px solid #000000 !important
|
||||
|
||||
// When card has comment, emphasis on minicard:
|
||||
// bigger red comment icon and number of comments,
|
||||
// to make it easier notice card comments and focus attention.
|
||||
.minicard .badges .badge
|
||||
.badge-icon,
|
||||
.badge-text
|
||||
&.badge-comment
|
||||
display: block
|
||||
border-radius: 4px
|
||||
padding: 1px 3px
|
||||
margin-bottom: 0.3rem
|
||||
color: #ff0000
|
||||
background-color: #ffffff
|
||||
font-weight: bold
|
||||
font-size: 11pt
|
||||
|
||||
.board-color-corteza
|
||||
setBoardColor(#568BA2)
|
||||
|
||||
/*
|
||||
Wekan for Corteza https://cortezaproject.org
|
||||
|
||||
Theme to match Corteza colors from:
|
||||
https://github.com/cortezaproject/corteza-webapp-messaging/blob/master/src/assets/sass/variables.scss
|
||||
|
||||
// Paths
|
||||
$fonts_dir : './assets/fonts/';
|
||||
$icomoon-font-path: $fonts_dir + 'icomoon' !default;
|
||||
$icomoon-font-family: "icomoon" !default;
|
||||
|
||||
// Typography
|
||||
$regular: 'nunito_sansregular';
|
||||
$bold: 'nunito_sansbold';
|
||||
$semibold: 'nunito_sanssemibold';
|
||||
|
||||
// Color system
|
||||
$white: #fff !default;
|
||||
$black: #000 !default;
|
||||
$primary: #568ba2;
|
||||
$secondary: #90A3B1;
|
||||
$success: #719430;
|
||||
$warning: #F5D380;
|
||||
$danger: #E85568;
|
||||
$light: #F3F3F5;
|
||||
$dark: #1e2224;
|
||||
$currentmymessagebgcolor : #a7d0e3;
|
||||
*/
|
||||
|
||||
//.header-quick-access
|
||||
// backgroud-color: #568ba2
|
||||
@@ -85,10 +85,15 @@ template(name="boardHeaderBar")
|
||||
if Filter.isActive
|
||||
a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}")
|
||||
i.fa.fa-times-thin
|
||||
|
||||
if currentUser.isAdmin
|
||||
a.board-header-btn.js-open-rules-view(title="{{_ 'rules'}}")
|
||||
i.fa.fa-magic
|
||||
span {{_ 'rules'}}
|
||||
else if currentUser.isBoardAdmin
|
||||
a.board-header-btn.js-open-rules-view(title="{{_ 'rules'}}")
|
||||
i.fa.fa-magic
|
||||
span {{_ 'rules'}}
|
||||
|
||||
a.board-header-btn.js-open-search-view(title="{{_ 'search'}}")
|
||||
i.fa.fa-search
|
||||
@@ -98,7 +103,7 @@ template(name="boardHeaderBar")
|
||||
a.board-header-btn.js-toggle-board-view(
|
||||
title="{{_ 'board-view'}}")
|
||||
i.fa.fa-th-large
|
||||
span {{_ currentUser.profile.boardView}}
|
||||
span {{#if currentUser.profile.boardView}}{{_ currentUser.profile.boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}}
|
||||
|
||||
if canModifyBoard
|
||||
a.board-header-btn.js-multiselection-activate(
|
||||
@@ -193,10 +198,10 @@ template(name="boardChangeTitlePopup")
|
||||
form
|
||||
label
|
||||
| {{_ 'title'}}
|
||||
input.js-board-name(type="text" value=title autofocus)
|
||||
input.js-board-name(type="text" value=title autofocus dir="auto")
|
||||
label
|
||||
| {{_ 'description'}}
|
||||
textarea.js-board-desc= description
|
||||
textarea.js-board-desc(dir="auto")= description
|
||||
input.primary.wide(type="submit" value="{{_ 'rename'}}")
|
||||
|
||||
template(name="boardCreateRulePopup")
|
||||
|
||||
@@ -45,15 +45,21 @@ Template.boardMenuPopup.helpers({
|
||||
});
|
||||
|
||||
Template.boardChangeTitlePopup.events({
|
||||
submit(evt, tpl) {
|
||||
const newTitle = tpl.$('.js-board-name').val().trim();
|
||||
const newDesc = tpl.$('.js-board-desc').val().trim();
|
||||
submit(event, templateInstance) {
|
||||
const newTitle = templateInstance
|
||||
.$('.js-board-name')
|
||||
.val()
|
||||
.trim();
|
||||
const newDesc = templateInstance
|
||||
.$('.js-board-desc')
|
||||
.val()
|
||||
.trim();
|
||||
if (newTitle) {
|
||||
this.rename(newTitle);
|
||||
this.setDescription(newDesc);
|
||||
Popup.close();
|
||||
}
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -76,65 +82,79 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-edit-board-title': Popup.open('boardChangeTitle'),
|
||||
'click .js-star-board'() {
|
||||
Meteor.user().toggleBoardStar(Session.get('currentBoard'));
|
||||
return [
|
||||
{
|
||||
'click .js-edit-board-title': Popup.open('boardChangeTitle'),
|
||||
'click .js-star-board'() {
|
||||
Meteor.user().toggleBoardStar(Session.get('currentBoard'));
|
||||
},
|
||||
'click .js-open-board-menu': Popup.open('boardMenu'),
|
||||
'click .js-change-visibility': Popup.open('boardChangeVisibility'),
|
||||
'click .js-watch-board': Popup.open('boardChangeWatch'),
|
||||
'click .js-open-archived-board'() {
|
||||
Modal.open('archivedBoards');
|
||||
},
|
||||
'click .js-toggle-board-view'() {
|
||||
const currentUser = Meteor.user();
|
||||
if (
|
||||
(currentUser.profile || {}).boardView === 'board-view-swimlanes'
|
||||
) {
|
||||
currentUser.setBoardView('board-view-cal');
|
||||
} else if (
|
||||
(currentUser.profile || {}).boardView === 'board-view-lists'
|
||||
) {
|
||||
currentUser.setBoardView('board-view-swimlanes');
|
||||
} else if (
|
||||
(currentUser.profile || {}).boardView === 'board-view-cal'
|
||||
) {
|
||||
currentUser.setBoardView('board-view-lists');
|
||||
} else {
|
||||
currentUser.setBoardView('board-view-swimlanes');
|
||||
}
|
||||
},
|
||||
'click .js-toggle-sidebar'() {
|
||||
Sidebar.toggle();
|
||||
},
|
||||
'click .js-open-filter-view'() {
|
||||
Sidebar.setView('filter');
|
||||
},
|
||||
'click .js-filter-reset'(event) {
|
||||
event.stopPropagation();
|
||||
Sidebar.setView();
|
||||
Filter.reset();
|
||||
},
|
||||
'click .js-open-search-view'() {
|
||||
Sidebar.setView('search');
|
||||
},
|
||||
'click .js-open-rules-view'() {
|
||||
Modal.openWide('rulesMain');
|
||||
},
|
||||
'click .js-multiselection-activate'() {
|
||||
const currentCard = Session.get('currentCard');
|
||||
MultiSelection.activate();
|
||||
if (currentCard) {
|
||||
MultiSelection.add(currentCard);
|
||||
}
|
||||
},
|
||||
'click .js-multiselection-reset'(event) {
|
||||
event.stopPropagation();
|
||||
MultiSelection.disable();
|
||||
},
|
||||
'click .js-log-in'() {
|
||||
FlowRouter.go('atSignIn');
|
||||
},
|
||||
},
|
||||
'click .js-open-board-menu': Popup.open('boardMenu'),
|
||||
'click .js-change-visibility': Popup.open('boardChangeVisibility'),
|
||||
'click .js-watch-board': Popup.open('boardChangeWatch'),
|
||||
'click .js-open-archived-board'() {
|
||||
Modal.open('archivedBoards');
|
||||
},
|
||||
'click .js-toggle-board-view'() {
|
||||
const currentUser = Meteor.user();
|
||||
if (currentUser.profile.boardView === 'board-view-swimlanes') {
|
||||
currentUser.setBoardView('board-view-cal');
|
||||
} else if (currentUser.profile.boardView === 'board-view-lists') {
|
||||
currentUser.setBoardView('board-view-swimlanes');
|
||||
} else if (currentUser.profile.boardView === 'board-view-cal') {
|
||||
currentUser.setBoardView('board-view-lists');
|
||||
}
|
||||
},
|
||||
'click .js-toggle-sidebar'() {
|
||||
Sidebar.toggle();
|
||||
},
|
||||
'click .js-open-filter-view'() {
|
||||
Sidebar.setView('filter');
|
||||
},
|
||||
'click .js-filter-reset'(evt) {
|
||||
evt.stopPropagation();
|
||||
Sidebar.setView();
|
||||
Filter.reset();
|
||||
},
|
||||
'click .js-open-search-view'() {
|
||||
Sidebar.setView('search');
|
||||
},
|
||||
'click .js-open-rules-view'() {
|
||||
Modal.openWide('rulesMain');
|
||||
},
|
||||
'click .js-multiselection-activate'() {
|
||||
const currentCard = Session.get('currentCard');
|
||||
MultiSelection.activate();
|
||||
if (currentCard) {
|
||||
MultiSelection.add(currentCard);
|
||||
}
|
||||
},
|
||||
'click .js-multiselection-reset'(evt) {
|
||||
evt.stopPropagation();
|
||||
MultiSelection.disable();
|
||||
},
|
||||
'click .js-log-in'() {
|
||||
FlowRouter.go('atSignIn');
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('boardHeaderBar');
|
||||
|
||||
Template.boardHeaderBar.helpers({
|
||||
canModifyBoard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -162,15 +182,17 @@ const CreateBoard = BlazeComponent.extendComponent({
|
||||
this.visibilityMenuIsOpen.set(!this.visibilityMenuIsOpen.get());
|
||||
},
|
||||
|
||||
onSubmit(evt) {
|
||||
evt.preventDefault();
|
||||
onSubmit(event) {
|
||||
event.preventDefault();
|
||||
const title = this.find('.js-new-board-title').value;
|
||||
const visibility = this.visibility.get();
|
||||
|
||||
this.boardId.set(Boards.insert({
|
||||
title,
|
||||
permission: visibility,
|
||||
}));
|
||||
this.boardId.set(
|
||||
Boards.insert({
|
||||
title,
|
||||
permission: visibility,
|
||||
}),
|
||||
);
|
||||
|
||||
Swimlanes.insert({
|
||||
title: 'Default',
|
||||
@@ -181,26 +203,28 @@ const CreateBoard = BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-visibility'() {
|
||||
this.setVisibility(this.currentData());
|
||||
return [
|
||||
{
|
||||
'click .js-select-visibility'() {
|
||||
this.setVisibility(this.currentData());
|
||||
},
|
||||
'click .js-change-visibility': this.toggleVisibilityMenu,
|
||||
'click .js-import': Popup.open('boardImportBoard'),
|
||||
submit: this.onSubmit,
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
'click .js-board-template': Popup.open('searchElement'),
|
||||
},
|
||||
'click .js-change-visibility': this.toggleVisibilityMenu,
|
||||
'click .js-import': Popup.open('boardImportBoard'),
|
||||
submit: this.onSubmit,
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
'click .js-board-template': Popup.open('searchElement'),
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('createBoardPopup');
|
||||
|
||||
(class HeaderBarCreateBoard extends CreateBoard {
|
||||
onSubmit(evt) {
|
||||
super.onSubmit(evt);
|
||||
onSubmit(event) {
|
||||
super.onSubmit(event);
|
||||
// Immediately star boards crated with the headerbar popup.
|
||||
Meteor.user().toggleBoardStar(this.boardId.get());
|
||||
}
|
||||
}).register('headerBarCreateBoardPopup');
|
||||
}.register('headerBarCreateBoardPopup'));
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
visibilityCheck() {
|
||||
@@ -216,9 +240,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-visibility': this.selectBoardVisibility,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-select-visibility': this.selectBoardVisibility,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('boardChangeVisibilityPopup');
|
||||
|
||||
@@ -233,13 +259,21 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-watch'() {
|
||||
const level = this.currentData();
|
||||
Meteor.call('watch', 'board', Session.get('currentBoard'), level, (err, ret) => {
|
||||
if (!err && ret) Popup.close();
|
||||
});
|
||||
return [
|
||||
{
|
||||
'click .js-select-watch'() {
|
||||
const level = this.currentData();
|
||||
Meteor.call(
|
||||
'watch',
|
||||
'board',
|
||||
Session.get('currentBoard'),
|
||||
level,
|
||||
(err, ret) => {
|
||||
if (!err && ret) Popup.close();
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('boardChangeWatchPopup');
|
||||
@@ -18,17 +18,25 @@ template(name="boardList")
|
||||
else
|
||||
a.js-open-board.board-list-item(href="{{pathFor 'board' id=_id slug=slug}}")
|
||||
span.details
|
||||
span.board-list-item-name= title
|
||||
span.board-list-item-name
|
||||
+viewer
|
||||
= title
|
||||
i.fa.js-star-board(
|
||||
class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
|
||||
title="{{_ 'star-board-title'}}")
|
||||
|
||||
p.board-list-item-desc
|
||||
+viewer
|
||||
= description
|
||||
if hasSpentTimeCards
|
||||
i.fa.js-has-spenttime-cards(
|
||||
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
|
||||
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
|
||||
|
||||
p.board-list-item-desc= description
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
|
||||
template(name="boardListHeaderBar")
|
||||
h1 {{_ 'my-boards'}}
|
||||
|
||||
@@ -21,11 +21,14 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
type: 'board',
|
||||
}, { sort: ['title'] });
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
type: 'board',
|
||||
},
|
||||
{ sort: ['title'] },
|
||||
);
|
||||
},
|
||||
isStarred() {
|
||||
const user = Meteor.user();
|
||||
@@ -33,12 +36,12 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
hasOvertimeCards() {
|
||||
subManager.subscribe('board', this.currentData()._id);
|
||||
subManager.subscribe('board', this.currentData()._id, false);
|
||||
return this.currentData().hasOvertimeCards();
|
||||
},
|
||||
|
||||
hasSpentTimeCards() {
|
||||
subManager.subscribe('board', this.currentData()._id);
|
||||
subManager.subscribe('board', this.currentData()._id, false);
|
||||
return this.currentData().hasSpentTimeCards();
|
||||
},
|
||||
|
||||
@@ -48,26 +51,49 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-board': Popup.open('createBoard'),
|
||||
'click .js-star-board'(evt) {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.user().toggleBoardStar(boardId);
|
||||
evt.preventDefault();
|
||||
return [
|
||||
{
|
||||
'click .js-add-board': Popup.open('createBoard'),
|
||||
'click .js-star-board'(evt) {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.user().toggleBoardStar(boardId);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-clone-board'(evt) {
|
||||
Meteor.call(
|
||||
'cloneBoard',
|
||||
this.currentData()._id,
|
||||
Session.get('fromBoard'),
|
||||
(err, res) => {
|
||||
if (err) {
|
||||
this.setError(err.error);
|
||||
} else {
|
||||
Session.set('fromBoard', null);
|
||||
Utils.goBoardId(res);
|
||||
}
|
||||
},
|
||||
);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-archive-board'(evt) {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.call('archiveBoard', boardId);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-accept-invite'() {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.call('acceptInvite', boardId);
|
||||
},
|
||||
'click .js-decline-invite'() {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.call('quitBoard', boardId, (err, ret) => {
|
||||
if (!err && ret) {
|
||||
Meteor.call('acceptInvite', boardId);
|
||||
FlowRouter.go('home');
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
'click .js-accept-invite'() {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.user().removeInvite(boardId);
|
||||
},
|
||||
'click .js-decline-invite'() {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.call('quitBoard', boardId, (err, ret) => {
|
||||
if (!err && ret) {
|
||||
Meteor.user().removeInvite(boardId);
|
||||
FlowRouter.go('home');
|
||||
}
|
||||
});
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('boardList');
|
||||
@@ -94,13 +94,41 @@ $spaceBetweenTiles = 16px
|
||||
.is-star-active
|
||||
color: white
|
||||
|
||||
.fa-clone
|
||||
position: absolute;
|
||||
bottom: 0
|
||||
font-size: 14px
|
||||
height: 18px
|
||||
line-height: 18px
|
||||
opacity: 0
|
||||
right: 0
|
||||
padding: 9px 9px
|
||||
transition-duration: .15s
|
||||
transition-property: color, font-size, background
|
||||
|
||||
.fa-archive
|
||||
position: absolute;
|
||||
bottom: 0
|
||||
font-size: 14px
|
||||
height: 18px
|
||||
line-height: 18px
|
||||
opacity: 0
|
||||
left: 0
|
||||
padding: 9px 9px
|
||||
transition-duration: .15s
|
||||
transition-property: color, font-size, background
|
||||
|
||||
li:hover a
|
||||
&:hover
|
||||
.fa-star,
|
||||
.fa-clone,
|
||||
.fa-archive,
|
||||
.fa-star-o
|
||||
color: white
|
||||
|
||||
.fa-star,
|
||||
.fa-clone,
|
||||
.fa-archive,
|
||||
.fa-star-o
|
||||
color: white
|
||||
opacity: .75
|
||||
@@ -133,6 +161,7 @@ $spaceBetweenTiles = 16px
|
||||
padding-right: 6px
|
||||
|
||||
.background-box
|
||||
color: white
|
||||
border-radius: 3px
|
||||
background-size: cover
|
||||
display: block
|
||||
@@ -154,10 +183,11 @@ $spaceBetweenTiles = 16px
|
||||
overflow: scroll
|
||||
|
||||
li
|
||||
width: 33.3%
|
||||
width: 50%
|
||||
|
||||
.board-list-item
|
||||
overflow: hidden
|
||||
height: 8rem
|
||||
|
||||
.board-list-item-sub-name
|
||||
position: relative
|
||||
@@ -166,4 +196,4 @@ $spaceBetweenTiles = 16px
|
||||
|
||||
@media screen and (max-width: 360px)
|
||||
li
|
||||
width: 50%
|
||||
width: 100%
|
||||
@@ -37,17 +37,19 @@ template(name="attachmentsGalery")
|
||||
i.fa.fa-download
|
||||
| {{_ 'download'}}
|
||||
if currentUser.isBoardMember
|
||||
if isImage
|
||||
a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}")
|
||||
i.fa.fa-thumb-tack
|
||||
if($eq ../coverId _id)
|
||||
| {{_ 'remove-cover'}}
|
||||
else
|
||||
| {{_ 'add-cover'}}
|
||||
a.js-confirm-delete
|
||||
i.fa.fa-close
|
||||
| {{_ 'delete'}}
|
||||
unless currentUser.isCommentOnly
|
||||
if isImage
|
||||
a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}")
|
||||
i.fa.fa-thumb-tack
|
||||
if($eq ../coverId _id)
|
||||
| {{_ 'remove-cover'}}
|
||||
else
|
||||
| {{_ 'add-cover'}}
|
||||
a.js-confirm-delete
|
||||
i.fa.fa-close
|
||||
| {{_ 'delete'}}
|
||||
|
||||
if currentUser.isBoardMember
|
||||
li.attachment-item.add-attachment
|
||||
a.js-add-attachment {{_ 'add-attachment' }}
|
||||
unless currentUser.isCommentOnly
|
||||
li.attachment-item.add-attachment
|
||||
a.js-add-attachment {{_ 'add-attachment' }}
|
||||
@@ -1,10 +1,11 @@
|
||||
Template.attachmentsGalery.events({
|
||||
'click .js-add-attachment': Popup.open('cardAttachments'),
|
||||
'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete',
|
||||
'click .js-confirm-delete': Popup.afterConfirm(
|
||||
'attachmentDelete',
|
||||
function() {
|
||||
Attachments.remove(this._id);
|
||||
Popup.close();
|
||||
}
|
||||
},
|
||||
),
|
||||
// If we let this event bubble, FlowRouter will handle it and empty the page
|
||||
// content, see #101.
|
||||
@@ -17,8 +18,8 @@ Template.attachmentsGalery.events({
|
||||
'click .js-remove-cover'() {
|
||||
Cards.findOne(this.cardId).unsetCover();
|
||||
},
|
||||
'click .js-preview-image'(evt) {
|
||||
Popup.open('previewAttachedImage').call(this, evt);
|
||||
'click .js-preview-image'(event) {
|
||||
Popup.open('previewAttachedImage').call(this, event);
|
||||
// when multiple thumbnails, if click one then another very fast,
|
||||
// we might get a wrong width from previous img.
|
||||
// when popup reused, onRendered() won't be called, so we cannot get there.
|
||||
@@ -31,41 +32,40 @@ Template.attachmentsGalery.events({
|
||||
// if the image is too large, we resize & center the popup.
|
||||
if (w > 300) {
|
||||
$('div.pop-over').css({
|
||||
width: (w + 20),
|
||||
width: w + 20,
|
||||
position: 'absolute',
|
||||
left: (window.innerWidth - w)/2,
|
||||
top: (window.innerHeight - h)/2,
|
||||
left: (window.innerWidth - w) / 2,
|
||||
top: (window.innerHeight - h) / 2,
|
||||
});
|
||||
}
|
||||
};
|
||||
const url = $(evt.currentTarget).attr('src');
|
||||
if (img.src === url && img.complete)
|
||||
rePosPopup();
|
||||
else
|
||||
img.onload = rePosPopup;
|
||||
const url = $(event.currentTarget).attr('src');
|
||||
if (img.src === url && img.complete) rePosPopup();
|
||||
else img.onload = rePosPopup;
|
||||
},
|
||||
});
|
||||
|
||||
Template.previewAttachedImagePopup.events({
|
||||
'click .js-large-image-clicked'(){
|
||||
'click .js-large-image-clicked'() {
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardAttachmentsPopup.events({
|
||||
'change .js-attach-file'(evt) {
|
||||
'change .js-attach-file'(event) {
|
||||
const card = this;
|
||||
FS.Utility.eachFile(evt, (f) => {
|
||||
const processFile = f => {
|
||||
const file = new FS.File(f);
|
||||
if (card.isLinkedCard()) {
|
||||
file.boardId = Cards.findOne(card.linkedId).boardId;
|
||||
file.cardId = card.linkedId;
|
||||
} else {
|
||||
file.boardId = card.boardId;
|
||||
file.swimlaneId = card.swimlaneId;
|
||||
file.listId = card.listId;
|
||||
file.cardId = card._id;
|
||||
}
|
||||
file.userId = Meteor.userId();
|
||||
|
||||
const attachment = Attachments.insert(file);
|
||||
|
||||
if (attachment && attachment._id && attachment.isImage()) {
|
||||
@@ -73,39 +73,141 @@ Template.cardAttachmentsPopup.events({
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
};
|
||||
|
||||
FS.Utility.eachFile(event, f => {
|
||||
if (
|
||||
MAX_IMAGE_PIXEL > 0 &&
|
||||
typeof f.type === 'string' &&
|
||||
f.type.match(/^image/)
|
||||
) {
|
||||
// is image
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const dataurl = e && e.target && e.target.result;
|
||||
if (dataurl !== undefined) {
|
||||
shrinkImage({
|
||||
dataurl,
|
||||
maxSize: MAX_IMAGE_PIXEL,
|
||||
ratio: COMPRESS_RATIO,
|
||||
toBlob: true,
|
||||
callback(blob) {
|
||||
if (blob === false) {
|
||||
processFile(f);
|
||||
} else {
|
||||
blob.name = f.name;
|
||||
processFile(blob);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// couldn't process it let other function handle it?
|
||||
processFile(f);
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(f);
|
||||
} else {
|
||||
processFile(f);
|
||||
}
|
||||
});
|
||||
},
|
||||
'click .js-computer-upload'(evt, tpl) {
|
||||
tpl.find('.js-attach-file').click();
|
||||
evt.preventDefault();
|
||||
'click .js-computer-upload'(event, templateInstance) {
|
||||
templateInstance.find('.js-attach-file').click();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-upload-clipboard-image': Popup.open('previewClipboardImage'),
|
||||
});
|
||||
|
||||
const MAX_IMAGE_PIXEL = Meteor.settings.public.MAX_IMAGE_PIXEL;
|
||||
const COMPRESS_RATIO = Meteor.settings.public.IMAGE_COMPRESS_RATIO;
|
||||
let pastedResults = null;
|
||||
const shrinkImage = function(options) {
|
||||
// shrink image to certain size
|
||||
const dataurl = options.dataurl,
|
||||
callback = options.callback,
|
||||
toBlob = options.toBlob;
|
||||
let canvas = document.createElement('canvas'),
|
||||
image = document.createElement('img');
|
||||
const maxSize = options.maxSize || 1024;
|
||||
const ratio = options.ratio || 1.0;
|
||||
const next = function(result) {
|
||||
image = null;
|
||||
canvas = null;
|
||||
if (typeof callback === 'function') {
|
||||
callback(result);
|
||||
}
|
||||
};
|
||||
image.onload = function() {
|
||||
let width = this.width,
|
||||
height = this.height;
|
||||
let changed = false;
|
||||
if (width > height) {
|
||||
if (width > maxSize) {
|
||||
height *= maxSize / width;
|
||||
width = maxSize;
|
||||
changed = true;
|
||||
}
|
||||
} else if (height > maxSize) {
|
||||
width *= maxSize / height;
|
||||
height = maxSize;
|
||||
changed = true;
|
||||
}
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.getContext('2d').drawImage(this, 0, 0, width, height);
|
||||
if (changed === true) {
|
||||
const type = 'image/jpeg';
|
||||
if (toBlob) {
|
||||
canvas.toBlob(next, type, ratio);
|
||||
} else {
|
||||
next(canvas.toDataURL(type, ratio));
|
||||
}
|
||||
} else {
|
||||
next(changed);
|
||||
}
|
||||
};
|
||||
image.onerror = function() {
|
||||
next(false);
|
||||
};
|
||||
image.src = dataurl;
|
||||
};
|
||||
|
||||
Template.previewClipboardImagePopup.onRendered(() => {
|
||||
// we can paste image from clipboard
|
||||
$(document.body).pasteImageReader((results) => {
|
||||
const handle = results => {
|
||||
if (results.dataURL.startsWith('data:image/')) {
|
||||
$('img.preview-clipboard-image').attr('src', results.dataURL);
|
||||
pastedResults = results;
|
||||
const direct = results => {
|
||||
$('img.preview-clipboard-image').attr('src', results.dataURL);
|
||||
pastedResults = results;
|
||||
};
|
||||
if (MAX_IMAGE_PIXEL) {
|
||||
// if has size limitation on image we shrink it before uploading
|
||||
shrinkImage({
|
||||
dataurl: results.dataURL,
|
||||
maxSize: MAX_IMAGE_PIXEL,
|
||||
ratio: COMPRESS_RATIO,
|
||||
callback(changed) {
|
||||
if (changed !== false && !!changed) {
|
||||
results.dataURL = changed;
|
||||
}
|
||||
direct(results);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(document.body).pasteImageReader(handle);
|
||||
|
||||
// we can also drag & drop image file to it
|
||||
$(document.body).dropImageReader((results) => {
|
||||
if (results.dataURL.startsWith('data:image/')) {
|
||||
$('img.preview-clipboard-image').attr('src', results.dataURL);
|
||||
pastedResults = results;
|
||||
}
|
||||
});
|
||||
$(document.body).dropImageReader(handle);
|
||||
});
|
||||
|
||||
Template.previewClipboardImagePopup.events({
|
||||
'click .js-upload-pasted-image'() {
|
||||
const results = pastedResults;
|
||||
if (results && results.file) {
|
||||
window.oPasted = pastedResults;
|
||||
const card = this;
|
||||
const file = new FS.File(results.file);
|
||||
if (!results.name) {
|
||||
|
||||
@@ -4,9 +4,10 @@ template(name="cardCustomFieldsPopup")
|
||||
li.item(class="")
|
||||
a.name.js-select-field(href="#")
|
||||
span.full-name
|
||||
+viewer
|
||||
= name
|
||||
if hasCustomField
|
||||
i.fa.fa-check
|
||||
i.fa.fa-check
|
||||
hr
|
||||
a.quiet-button.full.js-settings
|
||||
i.fa.fa-cog
|
||||
@@ -30,6 +31,10 @@ template(name="cardCustomField-text")
|
||||
= value
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
+viewer
|
||||
= value
|
||||
|
||||
|
||||
template(name="cardCustomField-number")
|
||||
if canModifyCard
|
||||
@@ -44,6 +49,9 @@ template(name="cardCustomField-number")
|
||||
= value
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
if value
|
||||
= value
|
||||
|
||||
template(name="cardCustomField-date")
|
||||
if canModifyCard
|
||||
@@ -54,6 +62,11 @@ template(name="cardCustomField-date")
|
||||
| {{showDate}}
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
if value
|
||||
div.card-date
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
|
||||
template(name="cardCustomField-dropdown")
|
||||
if canModifyCard
|
||||
@@ -61,9 +74,13 @@ template(name="cardCustomField-dropdown")
|
||||
select.inline
|
||||
each items
|
||||
if($eq data.value this._id)
|
||||
option(value=_id selected="selected") {{name}}
|
||||
option(value=_id selected="selected")
|
||||
+viewer
|
||||
= name
|
||||
else
|
||||
option(value=_id) {{name}}
|
||||
option(value=_id)
|
||||
+viewer
|
||||
= name
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
@@ -73,4 +90,8 @@ template(name="cardCustomField-dropdown")
|
||||
+viewer
|
||||
= selectedItem
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
if value
|
||||
+viewer
|
||||
= selectedItem
|
||||
@@ -7,22 +7,21 @@ Template.cardCustomFieldsPopup.helpers({
|
||||
});
|
||||
|
||||
Template.cardCustomFieldsPopup.events({
|
||||
'click .js-select-field'(evt) {
|
||||
'click .js-select-field'(event) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const customFieldId = this._id;
|
||||
card.toggleCustomField(customFieldId);
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-settings'(evt) {
|
||||
'click .js-settings'(event) {
|
||||
EscapeActions.executeUpTo('detailsPane');
|
||||
Sidebar.setView('customFields');
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
// cardCustomField
|
||||
const CardCustomField = BlazeComponent.extendComponent({
|
||||
|
||||
getTemplate() {
|
||||
return `cardCustomField-${this.data().definition.type}`;
|
||||
},
|
||||
@@ -34,52 +33,55 @@ const CardCustomField = BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
CardCustomField.register('cardCustomField');
|
||||
|
||||
// cardCustomField-text
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit .js-card-customfield-text'(evt) {
|
||||
evt.preventDefault();
|
||||
const value = this.currentComponent().getValue();
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
return [
|
||||
{
|
||||
'submit .js-card-customfield-text'(event) {
|
||||
event.preventDefault();
|
||||
const value = this.currentComponent().getValue();
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-text');
|
||||
}.register('cardCustomField-text'));
|
||||
|
||||
// cardCustomField-number
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit .js-card-customfield-number'(evt) {
|
||||
evt.preventDefault();
|
||||
const value = parseInt(this.find('input').value, 10);
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
return [
|
||||
{
|
||||
'submit .js-card-customfield-number'(event) {
|
||||
event.preventDefault();
|
||||
const value = parseInt(this.find('input').value, 10);
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-number');
|
||||
}.register('cardCustomField-number'));
|
||||
|
||||
// cardCustomField-date
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
@@ -108,8 +110,10 @@ CardCustomField.register('cardCustomField');
|
||||
}
|
||||
|
||||
classes() {
|
||||
if (this.date.get().isBefore(this.now.get(), 'minute') &&
|
||||
this.now.get().isBefore(this.data().value)) {
|
||||
if (
|
||||
this.date.get().isBefore(this.now.get(), 'minute') &&
|
||||
this.now.get().isBefore(this.data().value)
|
||||
) {
|
||||
return 'current';
|
||||
}
|
||||
return '';
|
||||
@@ -120,12 +124,13 @@ CardCustomField.register('cardCustomField');
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-edit-date': Popup.open('cardCustomField-date'),
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-edit-date': Popup.open('cardCustomField-date'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-date');
|
||||
}.register('cardCustomField-date'));
|
||||
|
||||
// cardCustomField-datePopup
|
||||
(class extends DatePicker {
|
||||
@@ -144,11 +149,10 @@ CardCustomField.register('cardCustomField');
|
||||
_deleteDate() {
|
||||
this.card.setCustomField(this.customFieldId, '');
|
||||
}
|
||||
}).register('cardCustomField-datePopup');
|
||||
}.register('cardCustomField-datePopup'));
|
||||
|
||||
// cardCustomField-dropdown
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this._items = this.data().definition.settings.dropdownItems;
|
||||
@@ -160,20 +164,23 @@ CardCustomField.register('cardCustomField');
|
||||
}
|
||||
|
||||
selectedItem() {
|
||||
const selected = this._items.find((item) => {
|
||||
const selected = this._items.find(item => {
|
||||
return item._id === this.data().value;
|
||||
});
|
||||
return (selected) ? selected.name : TAPi18n.__('custom-field-dropdown-unknown');
|
||||
return selected
|
||||
? selected.name
|
||||
: TAPi18n.__('custom-field-dropdown-unknown');
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit .js-card-customfield-dropdown'(evt) {
|
||||
evt.preventDefault();
|
||||
const value = this.find('select').value;
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
return [
|
||||
{
|
||||
'submit .js-card-customfield-dropdown'(event) {
|
||||
event.preventDefault();
|
||||
const value = this.find('select').value;
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-dropdown');
|
||||
}.register('cardCustomField-dropdown'));
|
||||
@@ -11,15 +11,20 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
const $picker = this.$('.js-datepicker').datepicker({
|
||||
todayHighlight: true,
|
||||
todayBtn: 'linked',
|
||||
language: TAPi18n.getLanguage(),
|
||||
}).on('changeDate', function(evt) {
|
||||
this.find('#date').value = moment(evt.date).format('L');
|
||||
this.error.set('');
|
||||
this.find('#time').focus();
|
||||
}.bind(this));
|
||||
const $picker = this.$('.js-datepicker')
|
||||
.datepicker({
|
||||
todayHighlight: true,
|
||||
todayBtn: 'linked',
|
||||
language: TAPi18n.getLanguage(),
|
||||
})
|
||||
.on(
|
||||
'changeDate',
|
||||
function(evt) {
|
||||
this.find('#date').value = moment(evt.date).format('L');
|
||||
this.error.set('');
|
||||
this.find('#time').focus();
|
||||
}.bind(this),
|
||||
);
|
||||
|
||||
if (this.date.get().isValid()) {
|
||||
$picker.datepicker('update', this.date.get().toDate());
|
||||
@@ -27,13 +32,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
showDate() {
|
||||
if (this.date.get().isValid())
|
||||
return this.date.get().format('L');
|
||||
if (this.date.get().isValid()) return this.date.get().format('L');
|
||||
return '';
|
||||
},
|
||||
showTime() {
|
||||
if (this.date.get().isValid())
|
||||
return this.date.get().format('LT');
|
||||
if (this.date.get().isValid()) return this.date.get().format('LT');
|
||||
return '';
|
||||
},
|
||||
dateFormat() {
|
||||
@@ -44,51 +47,58 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'keyup .js-date-field'() {
|
||||
// parse for localized date format in strict mode
|
||||
const dateMoment = moment(this.find('#date').value, 'L', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
|
||||
}
|
||||
},
|
||||
'keyup .js-time-field'() {
|
||||
// parse for localized time format in strict mode
|
||||
const dateMoment = moment(this.find('#time').value, 'LT', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
}
|
||||
},
|
||||
'submit .edit-date'(evt) {
|
||||
evt.preventDefault();
|
||||
return [
|
||||
{
|
||||
'keyup .js-date-field'() {
|
||||
// parse for localized date format in strict mode
|
||||
const dateMoment = moment(this.find('#date').value, 'L', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
|
||||
}
|
||||
},
|
||||
'keyup .js-time-field'() {
|
||||
// parse for localized time format in strict mode
|
||||
const dateMoment = moment(this.find('#time').value, 'LT', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
}
|
||||
},
|
||||
'submit .edit-date'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
// if no time was given, init with 12:00
|
||||
const time = evt.target.time.value || moment(new Date().setHours(12, 0, 0)).format('LT');
|
||||
// if no time was given, init with 12:00
|
||||
const time =
|
||||
evt.target.time.value ||
|
||||
moment(new Date().setHours(12, 0, 0)).format('LT');
|
||||
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
const newDate = moment(dateString, 'L LT', true);
|
||||
if (newDate.isValid()) {
|
||||
this._storeDate(newDate.toDate());
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
const newDate = moment(dateString, 'L LT', true);
|
||||
if (newDate.isValid()) {
|
||||
this._storeDate(newDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.error.set('invalid-date');
|
||||
evt.target.date.focus();
|
||||
}
|
||||
},
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
this._deleteDate();
|
||||
Popup.close();
|
||||
}
|
||||
else {
|
||||
this.error.set('invalid-date');
|
||||
evt.target.date.focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
this._deleteDate();
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
Template.dateBadge.helpers({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -96,7 +106,8 @@ Template.dateBadge.helpers({
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this.data().getReceived() && this.date.set(moment(this.data().getReceived()));
|
||||
this.data().getReceived() &&
|
||||
this.date.set(moment(this.data().getReceived()));
|
||||
}
|
||||
|
||||
_storeDate(date) {
|
||||
@@ -106,8 +117,7 @@ Template.dateBadge.helpers({
|
||||
_deleteDate() {
|
||||
this.card.setReceived(null);
|
||||
}
|
||||
}).register('editCardReceivedDatePopup');
|
||||
|
||||
}.register('editCardReceivedDatePopup'));
|
||||
|
||||
// editCardStartDatePopup
|
||||
(class extends DatePicker {
|
||||
@@ -119,7 +129,10 @@ Template.dateBadge.helpers({
|
||||
onRendered() {
|
||||
super.onRendered();
|
||||
if (moment.isDate(this.card.getReceived())) {
|
||||
this.$('.js-datepicker').datepicker('setStartDate', this.card.getReceived());
|
||||
this.$('.js-datepicker').datepicker(
|
||||
'setStartDate',
|
||||
this.card.getReceived(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +143,7 @@ Template.dateBadge.helpers({
|
||||
_deleteDate() {
|
||||
this.card.setStart(null);
|
||||
}
|
||||
}).register('editCardStartDatePopup');
|
||||
}.register('editCardStartDatePopup'));
|
||||
|
||||
// editCardDueDatePopup
|
||||
(class extends DatePicker {
|
||||
@@ -153,7 +166,7 @@ Template.dateBadge.helpers({
|
||||
_deleteDate() {
|
||||
this.card.setDue(null);
|
||||
}
|
||||
}).register('editCardDueDatePopup');
|
||||
}.register('editCardDueDatePopup'));
|
||||
|
||||
// editCardEndDatePopup
|
||||
(class extends DatePicker {
|
||||
@@ -176,8 +189,7 @@ Template.dateBadge.helpers({
|
||||
_deleteDate() {
|
||||
this.card.setEnd(null);
|
||||
}
|
||||
}).register('editCardEndDatePopup');
|
||||
|
||||
}.register('editCardEndDatePopup'));
|
||||
|
||||
// Display received, start, due & end dates
|
||||
const CardDate = BlazeComponent.extendComponent({
|
||||
@@ -224,17 +236,20 @@ class CardReceivedDate extends CardDate {
|
||||
const startAt = this.data().getStart();
|
||||
const theDate = this.date.get();
|
||||
// if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged
|
||||
if (((startAt) && (theDate.isAfter(dueAt))) ||
|
||||
((endAt) && (theDate.isAfter(endAt))) ||
|
||||
((dueAt) && (theDate.isAfter(dueAt))))
|
||||
if (
|
||||
(startAt && theDate.isAfter(dueAt)) ||
|
||||
(endAt && theDate.isAfter(endAt)) ||
|
||||
(dueAt && theDate.isAfter(dueAt))
|
||||
)
|
||||
classes += 'long-overdue';
|
||||
else
|
||||
classes += 'current';
|
||||
else classes += 'current';
|
||||
return classes;
|
||||
}
|
||||
|
||||
showTitle() {
|
||||
return `${TAPi18n.__('card-received-on')} ${this.date.get().format('LLLL')}`;
|
||||
return `${TAPi18n.__('card-received-on')} ${this.date
|
||||
.get()
|
||||
.format('LLLL')}`;
|
||||
}
|
||||
|
||||
events() {
|
||||
@@ -261,13 +276,10 @@ class CardStartDate extends CardDate {
|
||||
const theDate = this.date.get();
|
||||
const now = this.now.get();
|
||||
// if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
|
||||
if (((endAt) && (theDate.isAfter(endAt))) ||
|
||||
((dueAt) && (theDate.isAfter(dueAt))))
|
||||
if ((endAt && theDate.isAfter(endAt)) || (dueAt && theDate.isAfter(dueAt)))
|
||||
classes += 'long-overdue';
|
||||
else if (theDate.isBefore(now, 'minute'))
|
||||
classes += 'almost-due';
|
||||
else
|
||||
classes += 'current';
|
||||
else if (theDate.isBefore(now, 'minute')) classes += 'almost-due';
|
||||
else classes += 'current';
|
||||
return classes;
|
||||
}
|
||||
|
||||
@@ -298,17 +310,12 @@ class CardDueDate extends CardDate {
|
||||
const theDate = this.date.get();
|
||||
const now = this.now.get();
|
||||
// if the due date is after the end date, green - done early
|
||||
if ((endAt) && (theDate.isAfter(endAt)))
|
||||
classes += 'current';
|
||||
if (endAt && theDate.isAfter(endAt)) classes += 'current';
|
||||
// if there is an end date, don't need to flag the due date
|
||||
else if (endAt)
|
||||
classes += '';
|
||||
else if (now.diff(theDate, 'days') >= 2)
|
||||
classes += 'long-overdue';
|
||||
else if (now.diff(theDate, 'minute') >= 0)
|
||||
classes += 'due';
|
||||
else if (now.diff(theDate, 'days') >= -1)
|
||||
classes += 'almost-due';
|
||||
else if (endAt) classes += '';
|
||||
else if (now.diff(theDate, 'days') >= 2) classes += 'long-overdue';
|
||||
else if (now.diff(theDate, 'minute') >= 0) classes += 'due';
|
||||
else if (now.diff(theDate, 'days') >= -1) classes += 'almost-due';
|
||||
return classes;
|
||||
}
|
||||
|
||||
@@ -337,12 +344,9 @@ class CardEndDate extends CardDate {
|
||||
let classes = 'end-date' + ' ';
|
||||
const dueAt = this.data().getDue();
|
||||
const theDate = this.date.get();
|
||||
if (theDate.diff(dueAt, 'days') >= 2)
|
||||
classes += 'long-overdue';
|
||||
else if (theDate.diff(dueAt, 'days') >= 0)
|
||||
classes += 'due';
|
||||
else if (theDate.diff(dueAt, 'days') >= -2)
|
||||
classes += 'almost-due';
|
||||
if (theDate.diff(dueAt, 'days') >= 2) classes += 'long-overdue';
|
||||
else if (theDate.diff(dueAt, 'days') >= 0) classes += 'due';
|
||||
else if (theDate.diff(dueAt, 'days') >= -2) classes += 'almost-due';
|
||||
return classes;
|
||||
}
|
||||
|
||||
@@ -362,22 +366,22 @@ CardEndDate.register('cardEndDate');
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardReceivedDate');
|
||||
}.register('minicardReceivedDate'));
|
||||
|
||||
(class extends CardStartDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardStartDate');
|
||||
}.register('minicardStartDate'));
|
||||
|
||||
(class extends CardDueDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardDueDate');
|
||||
}.register('minicardDueDate'));
|
||||
|
||||
(class extends CardEndDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardEndDate');
|
||||
}.register('minicardEndDate'));
|
||||
@@ -77,7 +77,9 @@ template(name="cardDetails")
|
||||
h3.card-details-item-title {{_ 'labels'}}
|
||||
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
|
||||
each labels
|
||||
span.card-label(class="card-label-{{color}}" title=name)= name
|
||||
span.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if canModifyCard
|
||||
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
|
||||
i.fa.fa-plus
|
||||
@@ -86,7 +88,8 @@ template(name="cardDetails")
|
||||
each customFieldsWD
|
||||
.card-details-item.card-details-item-customfield
|
||||
h3.card-details-item-title
|
||||
= definition.name
|
||||
+viewer
|
||||
= definition.name
|
||||
+cardCustomField
|
||||
|
||||
.card-details-items
|
||||
@@ -197,20 +200,20 @@ template(name="cardDetails")
|
||||
+activities(card=this mode="card")
|
||||
|
||||
template(name="editCardTitleForm")
|
||||
textarea.js-edit-card-title(rows='1' autofocus)
|
||||
textarea.js-edit-card-title(rows='1' autofocus dir="auto")
|
||||
= getTitle
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-title-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editCardRequesterForm")
|
||||
input.js-edit-card-requester(type='text' autofocus value=getRequestedBy)
|
||||
input.js-edit-card-requester(type='text' autofocus value=getRequestedBy dir="auto")
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-requester-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editCardAssignerForm")
|
||||
input.js-edit-card-assigner(type='text' autofocus value=getAssignedBy)
|
||||
input.js-edit-card-assigner(type='text' autofocus value=getAssignedBy dir="auto")
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-assigner-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
@@ -221,14 +224,14 @@ template(name="cardDetailsActionsPopup")
|
||||
if canModifyCard
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-members {{_ 'card-edit-members'}}
|
||||
li: a.js-labels {{_ 'card-edit-labels'}}
|
||||
li: a.js-attachments {{_ 'card-edit-attachments'}}
|
||||
//li: a.js-members {{_ 'card-edit-members'}}
|
||||
//li: a.js-labels {{_ 'card-edit-labels'}}
|
||||
//li: a.js-attachments {{_ 'card-edit-attachments'}}
|
||||
li: a.js-custom-fields {{_ 'card-edit-custom-fields'}}
|
||||
li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
|
||||
li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
|
||||
li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
|
||||
li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
|
||||
//li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
|
||||
//li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
|
||||
//li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
|
||||
//li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
|
||||
li: a.js-spent-time {{_ 'editCardSpentTimePopup-title'}}
|
||||
li: a.js-set-card-color {{_ 'setCardColorPopup-title'}}
|
||||
hr
|
||||
|
||||
@@ -27,7 +27,7 @@ BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
this.isLoaded = new ReactiveVar(false);
|
||||
const boardBody = this.parentComponent().parentComponent();
|
||||
const boardBody = this.parentComponent().parentComponent();
|
||||
//in Miniview parent is Board, not BoardBody.
|
||||
if (boardBody !== null) {
|
||||
boardBody.showOverlay.set(true);
|
||||
@@ -48,7 +48,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
|
||||
scrollParentContainer() {
|
||||
@@ -80,18 +84,17 @@ BlazeComponent.extendComponent({
|
||||
const cardContainerScrollTop = $cardContainer.scrollTop();
|
||||
|
||||
let topOffset = false;
|
||||
if(cardViewStartTop !== 100){
|
||||
if (cardViewStartTop !== 100) {
|
||||
topOffset = cardViewStartTop - 100;
|
||||
}
|
||||
if(topOffset !== false) {
|
||||
if (topOffset !== false) {
|
||||
bodyBoardComponent.scrollTop(cardContainerScrollTop + topOffset);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
presentParentTask() {
|
||||
let result = this.currentBoard.presentParentTask;
|
||||
if ((result === null) || (result === undefined)) {
|
||||
if (result === null || result === undefined) {
|
||||
result = 'no-parent';
|
||||
}
|
||||
return result;
|
||||
@@ -116,7 +119,13 @@ BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
if (!Utils.isMiniScreen()) {
|
||||
Meteor.setTimeout(() => {
|
||||
$('.card-details').mCustomScrollbar({theme:'minimal-dark', setWidth: false, setLeft: 0, scrollbarPosition: 'outside', mouseWheel: true });
|
||||
$('.card-details').mCustomScrollbar({
|
||||
theme: 'minimal-dark',
|
||||
setWidth: false,
|
||||
setLeft: 0,
|
||||
scrollbarPosition: 'outside',
|
||||
mouseWheel: true,
|
||||
});
|
||||
this.scrollParentContainer();
|
||||
}, 500);
|
||||
}
|
||||
@@ -212,7 +221,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
onDestroyed() {
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not board body.
|
||||
if (parentComponent === null) return;
|
||||
parentComponent.showOverlay.set(false);
|
||||
@@ -228,56 +237,64 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
};
|
||||
|
||||
return [{
|
||||
...events,
|
||||
'click .js-close-card-details' () {
|
||||
Utils.goBoardId(this.data().boardId);
|
||||
return [
|
||||
{
|
||||
...events,
|
||||
'click .js-close-card-details'() {
|
||||
Utils.goBoardId(this.data().boardId);
|
||||
},
|
||||
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
|
||||
'submit .js-card-description'(event) {
|
||||
event.preventDefault();
|
||||
const description = this.currentComponent().getValue();
|
||||
this.data().setDescription(description);
|
||||
},
|
||||
'submit .js-card-details-title'(event) {
|
||||
event.preventDefault();
|
||||
const title = this.currentComponent()
|
||||
.getValue()
|
||||
.trim();
|
||||
if (title) {
|
||||
this.data().setTitle(title);
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-assigner'(event) {
|
||||
event.preventDefault();
|
||||
const assigner = this.currentComponent()
|
||||
.getValue()
|
||||
.trim();
|
||||
if (assigner) {
|
||||
this.data().setAssignedBy(assigner);
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-requester'(event) {
|
||||
event.preventDefault();
|
||||
const requester = this.currentComponent()
|
||||
.getValue()
|
||||
.trim();
|
||||
if (requester) {
|
||||
this.data().setRequestedBy(requester);
|
||||
}
|
||||
},
|
||||
'click .js-member': Popup.open('cardMember'),
|
||||
'click .js-add-members': Popup.open('cardMembers'),
|
||||
'click .js-add-labels': Popup.open('cardLabels'),
|
||||
'click .js-received-date': Popup.open('editCardReceivedDate'),
|
||||
'click .js-start-date': Popup.open('editCardStartDate'),
|
||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||
'click .js-end-date': Popup.open('editCardEndDate'),
|
||||
'mouseenter .js-card-details'() {
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not BoardBody.
|
||||
if (parentComponent === null) return;
|
||||
parentComponent.showOverlay.set(true);
|
||||
parentComponent.mouseHasEnterCardDetails = true;
|
||||
},
|
||||
'click #toggleButton'() {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
},
|
||||
},
|
||||
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
|
||||
'submit .js-card-description' (evt) {
|
||||
evt.preventDefault();
|
||||
const description = this.currentComponent().getValue();
|
||||
this.data().setDescription(description);
|
||||
},
|
||||
'submit .js-card-details-title' (evt) {
|
||||
evt.preventDefault();
|
||||
const title = this.currentComponent().getValue().trim();
|
||||
if (title) {
|
||||
this.data().setTitle(title);
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-assigner'(evt) {
|
||||
evt.preventDefault();
|
||||
const assigner = this.currentComponent().getValue().trim();
|
||||
if (assigner) {
|
||||
this.data().setAssignedBy(assigner);
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-requester'(evt) {
|
||||
evt.preventDefault();
|
||||
const requester = this.currentComponent().getValue().trim();
|
||||
if (requester) {
|
||||
this.data().setRequestedBy(requester);
|
||||
}
|
||||
},
|
||||
'click .js-member': Popup.open('cardMember'),
|
||||
'click .js-add-members': Popup.open('cardMembers'),
|
||||
'click .js-add-labels': Popup.open('cardLabels'),
|
||||
'click .js-received-date': Popup.open('editCardReceivedDate'),
|
||||
'click .js-start-date': Popup.open('editCardStartDate'),
|
||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||
'click .js-end-date': Popup.open('editCardEndDate'),
|
||||
'mouseenter .js-card-details' () {
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not BoardBody.
|
||||
if (parentComponent === null) return;
|
||||
parentComponent.showOverlay.set(true);
|
||||
parentComponent.mouseHasEnterCardDetails = true;
|
||||
},
|
||||
'click #toggleButton'() {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('cardDetails');
|
||||
|
||||
@@ -297,7 +314,9 @@ BlazeComponent.extendComponent({
|
||||
close(isReset = false) {
|
||||
if (this.isOpen.get() && !isReset) {
|
||||
const draft = this.getValue().trim();
|
||||
if (draft !== Cards.findOne(Session.get('currentCard')).getDescription()) {
|
||||
if (
|
||||
draft !== Cards.findOne(Session.get('currentCard')).getDescription()
|
||||
) {
|
||||
UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue());
|
||||
}
|
||||
}
|
||||
@@ -311,12 +330,14 @@ BlazeComponent.extendComponent({
|
||||
|
||||
events() {
|
||||
const parentEvents = InlinedForm.prototype.events()[0];
|
||||
return [{
|
||||
...parentEvents,
|
||||
'click .js-close-inlined-form': this.reset,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
...parentEvents,
|
||||
'click .js-close-inlined-form': this.reset,
|
||||
},
|
||||
];
|
||||
}
|
||||
}).register('inlinedCardDescription');
|
||||
}.register('inlinedCardDescription'));
|
||||
|
||||
Template.cardDetailsActionsPopup.helpers({
|
||||
isWatching() {
|
||||
@@ -324,7 +345,11 @@ Template.cardDetailsActionsPopup.helpers({
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -342,23 +367,31 @@ Template.cardDetailsActionsPopup.events({
|
||||
'click .js-copy-card': Popup.open('copyCard'),
|
||||
'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
|
||||
'click .js-set-card-color': Popup.open('setCardColor'),
|
||||
'click .js-move-card-to-top' (evt) {
|
||||
evt.preventDefault();
|
||||
const minOrder = _.min(this.list().cards(this.swimlaneId).map((c) => c.sort));
|
||||
this.move(this.swimlaneId, this.listId, minOrder - 1);
|
||||
'click .js-move-card-to-top'(event) {
|
||||
event.preventDefault();
|
||||
const minOrder = _.min(
|
||||
this.list()
|
||||
.cards(this.swimlaneId)
|
||||
.map(c => c.sort),
|
||||
);
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
|
||||
},
|
||||
'click .js-move-card-to-bottom' (evt) {
|
||||
evt.preventDefault();
|
||||
const maxOrder = _.max(this.list().cards(this.swimlaneId).map((c) => c.sort));
|
||||
this.move(this.swimlaneId, this.listId, maxOrder + 1);
|
||||
'click .js-move-card-to-bottom'(event) {
|
||||
event.preventDefault();
|
||||
const maxOrder = _.max(
|
||||
this.list()
|
||||
.cards(this.swimlaneId)
|
||||
.map(c => c.sort),
|
||||
);
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
|
||||
},
|
||||
'click .js-archive' (evt) {
|
||||
evt.preventDefault();
|
||||
'click .js-archive'(event) {
|
||||
event.preventDefault();
|
||||
this.archive();
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-more': Popup.open('cardMore'),
|
||||
'click .js-toggle-watch-card' () {
|
||||
'click .js-toggle-watch-card'() {
|
||||
const currentCard = this;
|
||||
const level = currentCard.findWatcher(Meteor.userId()) ? null : 'watching';
|
||||
Meteor.call('watch', 'card', currentCard._id, level, (err, ret) => {
|
||||
@@ -367,15 +400,15 @@ Template.cardDetailsActionsPopup.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardTitleForm.onRendered(function () {
|
||||
Template.editCardTitleForm.onRendered(function() {
|
||||
autosize(this.$('.js-edit-card-title'));
|
||||
});
|
||||
|
||||
Template.editCardTitleForm.events({
|
||||
'keydown .js-edit-card-title' (evt) {
|
||||
'keydown .js-edit-card-title'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
// Unless the shift key is also being pressed
|
||||
if (evt.keyCode === 13 && !evt.shiftKey) {
|
||||
if (event.keyCode === 13 && !event.shiftKey) {
|
||||
$('.js-submit-edit-card-title-form').click();
|
||||
}
|
||||
},
|
||||
@@ -386,9 +419,9 @@ Template.editCardRequesterForm.onRendered(function() {
|
||||
});
|
||||
|
||||
Template.editCardRequesterForm.events({
|
||||
'keydown .js-edit-card-requester'(evt) {
|
||||
'keydown .js-edit-card-requester'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
if (evt.keyCode === 13) {
|
||||
if (event.keyCode === 13) {
|
||||
$('.js-submit-edit-card-requester-form').click();
|
||||
}
|
||||
},
|
||||
@@ -399,16 +432,16 @@ Template.editCardAssignerForm.onRendered(function() {
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.events({
|
||||
'keydown .js-edit-card-assigner'(evt) {
|
||||
'keydown .js-edit-card-assigner'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
if (evt.keyCode === 13) {
|
||||
if (event.keyCode === 13) {
|
||||
$('.js-submit-edit-card-assigner-form').click();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.moveCardPopup.events({
|
||||
'click .js-done' () {
|
||||
'click .js-done'() {
|
||||
// XXX We should *not* get the currentCard from the global state, but
|
||||
// instead from a “component” state.
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
@@ -424,18 +457,21 @@ Template.moveCardPopup.events({
|
||||
});
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
subManager.subscribe('board', Session.get('currentBoard'));
|
||||
subManager.subscribe('board', Session.get('currentBoard'), false);
|
||||
this.selectedBoardId = new ReactiveVar(Session.get('currentBoard'));
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Meteor.user().getTemplatesBoardId()},
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
const boards = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Meteor.user().getTemplatesBoardId() },
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
@@ -450,12 +486,14 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'change .js-select-boards'(evt) {
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
subManager.subscribe('board', this.selectedBoardId.get());
|
||||
return [
|
||||
{
|
||||
'change .js-select-boards'(event) {
|
||||
this.selectedBoardId.set($(event.currentTarget).val());
|
||||
subManager.subscribe('board', this.selectedBoardId.get(), false);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('boardsAndLists');
|
||||
|
||||
@@ -471,7 +509,9 @@ Template.copyCardPopup.events({
|
||||
const textarea = $('#copy-card-title');
|
||||
const title = textarea.val().trim();
|
||||
// insert new card to the bottom of new list
|
||||
card.sort = Lists.findOne(card.listId).cards().count();
|
||||
card.sort = Lists.findOne(card.listId)
|
||||
.cards()
|
||||
.count();
|
||||
|
||||
if (title) {
|
||||
card.title = title;
|
||||
@@ -489,7 +529,7 @@ Template.copyCardPopup.events({
|
||||
});
|
||||
|
||||
Template.copyChecklistToManyCardsPopup.events({
|
||||
'click .js-done' () {
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const oldId = card._id;
|
||||
card._id = null;
|
||||
@@ -502,11 +542,13 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
const textarea = $('#copy-card-title');
|
||||
const titleEntry = textarea.val().trim();
|
||||
// insert new card to the bottom of new list
|
||||
card.sort = Lists.findOne(card.listId).cards().count();
|
||||
card.sort = Lists.findOne(card.listId)
|
||||
.cards()
|
||||
.count();
|
||||
|
||||
if (titleEntry) {
|
||||
const titleList = JSON.parse(titleEntry);
|
||||
for (let i = 0; i < titleList.length; i++){
|
||||
for (let i = 0; i < titleList.length; i++) {
|
||||
const obj = titleList[i];
|
||||
card.title = obj.title;
|
||||
card.description = obj.description;
|
||||
@@ -519,12 +561,12 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
Filter.addException(_id);
|
||||
|
||||
// copy checklists
|
||||
Checklists.find({cardId: oldId}).forEach((ch) => {
|
||||
Checklists.find({ cardId: oldId }).forEach(ch => {
|
||||
ch.copy(_id);
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
cursor = Cards.find({parentId: oldId});
|
||||
cursor = Cards.find({ parentId: oldId });
|
||||
cursor.forEach(function() {
|
||||
'use strict';
|
||||
const subtask = arguments[0];
|
||||
@@ -534,7 +576,7 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
});
|
||||
|
||||
// copy card comments
|
||||
CardComments.find({cardId: oldId}).forEach((cmt) => {
|
||||
CardComments.find({ cardId: oldId }).forEach(cmt => {
|
||||
cmt.copy(_id);
|
||||
});
|
||||
}
|
||||
@@ -550,7 +592,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
colors() {
|
||||
return cardColors.map((color) => ({ color, name: '' }));
|
||||
return cardColors.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
@@ -561,19 +603,21 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
return [
|
||||
{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
},
|
||||
'click .js-submit'() {
|
||||
this.currentCard.setColor(this.currentColor.get());
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-remove-color'() {
|
||||
this.currentCard.setColor(null);
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
'click .js-submit' () {
|
||||
this.currentCard.setColor(this.currentColor.get());
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-remove-color'() {
|
||||
this.currentCard.setColor(null);
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('setCardColorPopup');
|
||||
|
||||
@@ -592,15 +636,18 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {
|
||||
$ne: Meteor.user().getTemplatesBoardId(),
|
||||
const boards = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {
|
||||
$ne: Meteor.user().getTemplatesBoardId(),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
@@ -609,7 +656,7 @@ BlazeComponent.extendComponent({
|
||||
if (this.parentBoard.get()) {
|
||||
return Cards.find({
|
||||
boardId: this.parentBoard.get(),
|
||||
_id: {$ne: currentId},
|
||||
_id: { $ne: currentId },
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
@@ -642,63 +689,69 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-copy-card-link-to-clipboard' () {
|
||||
// Clipboard code from:
|
||||
// https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
|
||||
const StringToCopyElement = document.getElementById('cardURL');
|
||||
StringToCopyElement.select();
|
||||
if (document.execCommand('copy')) {
|
||||
StringToCopyElement.blur();
|
||||
} else {
|
||||
document.getElementById('cardURL').selectionStart = 0;
|
||||
document.getElementById('cardURL').selectionEnd = 999;
|
||||
document.execCommand('copy');
|
||||
if (window.getSelection) {
|
||||
if (window.getSelection().empty) { // Chrome
|
||||
window.getSelection().empty();
|
||||
} else if (window.getSelection().removeAllRanges) { // Firefox
|
||||
window.getSelection().removeAllRanges();
|
||||
return [
|
||||
{
|
||||
'click .js-copy-card-link-to-clipboard'() {
|
||||
// Clipboard code from:
|
||||
// https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
|
||||
const StringToCopyElement = document.getElementById('cardURL');
|
||||
StringToCopyElement.select();
|
||||
if (document.execCommand('copy')) {
|
||||
StringToCopyElement.blur();
|
||||
} else {
|
||||
document.getElementById('cardURL').selectionStart = 0;
|
||||
document.getElementById('cardURL').selectionEnd = 999;
|
||||
document.execCommand('copy');
|
||||
if (window.getSelection) {
|
||||
if (window.getSelection().empty) {
|
||||
// Chrome
|
||||
window.getSelection().empty();
|
||||
} else if (window.getSelection().removeAllRanges) {
|
||||
// Firefox
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
} else if (document.selection) {
|
||||
// IE?
|
||||
document.selection.empty();
|
||||
}
|
||||
} else if (document.selection) { // IE?
|
||||
document.selection.empty();
|
||||
}
|
||||
}
|
||||
},
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function() {
|
||||
Popup.close();
|
||||
Cards.remove(this._id);
|
||||
Utils.goBoardId(this.boardId);
|
||||
}),
|
||||
'change .js-field-parent-board'(event) {
|
||||
const selection = $(event.currentTarget).val();
|
||||
const list = $('.js-field-parent-card');
|
||||
if (selection === 'none') {
|
||||
this.parentBoard.set(null);
|
||||
} else {
|
||||
subManager.subscribe('board', $(event.currentTarget).val(), false);
|
||||
this.parentBoard.set(selection);
|
||||
list.prop('disabled', false);
|
||||
}
|
||||
this.setParentCardId(null);
|
||||
},
|
||||
'change .js-field-parent-card'(event) {
|
||||
const selection = $(event.currentTarget).val();
|
||||
this.setParentCardId(selection);
|
||||
},
|
||||
},
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function () {
|
||||
Popup.close();
|
||||
Cards.remove(this._id);
|
||||
Utils.goBoardId(this.boardId);
|
||||
}),
|
||||
'change .js-field-parent-board'(evt) {
|
||||
const selection = $(evt.currentTarget).val();
|
||||
const list = $('.js-field-parent-card');
|
||||
if (selection === 'none') {
|
||||
this.parentBoard.set(null);
|
||||
} else {
|
||||
subManager.subscribe('board', $(evt.currentTarget).val());
|
||||
this.parentBoard.set(selection);
|
||||
list.prop('disabled', false);
|
||||
}
|
||||
this.setParentCardId(null);
|
||||
},
|
||||
'change .js-field-parent-card'(evt) {
|
||||
const selection = $(evt.currentTarget).val();
|
||||
this.setParentCardId(selection);
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('cardMorePopup');
|
||||
|
||||
|
||||
// Close the card details pane by pressing escape
|
||||
EscapeActions.register('detailsPane',
|
||||
EscapeActions.register(
|
||||
'detailsPane',
|
||||
() => {
|
||||
Utils.goBoardId(Session.get('currentBoard'));
|
||||
},
|
||||
() => {
|
||||
return !Session.equals('currentCard', null);
|
||||
}, {
|
||||
},
|
||||
{
|
||||
noClickEscapeOn: '.js-card-details,.board-sidebar,#header',
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -126,13 +126,14 @@ input[type="submit"].attachment-add-link-submit
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.card-details
|
||||
width: calc(100% - 40px)
|
||||
width: calc(100% - 1px)
|
||||
padding: 0px 20px 0px 20px
|
||||
margin: 0px
|
||||
transition: none
|
||||
|
||||
.card-details-canvas
|
||||
width: 100%
|
||||
padding-left: 0px;
|
||||
|
||||
.card-details-header
|
||||
.close-card-details
|
||||
|
||||
@@ -20,29 +20,31 @@ BlazeComponent.extendComponent({
|
||||
this.card.setSpentTime(null);
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
//TODO : need checking this portion
|
||||
'submit .edit-time'(evt) {
|
||||
evt.preventDefault();
|
||||
return [
|
||||
{
|
||||
//TODO : need checking this portion
|
||||
'submit .edit-time'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
const spentTime = parseFloat(evt.target.time.value);
|
||||
const isOvertime = this.card.getIsOvertime();
|
||||
const spentTime = parseFloat(evt.target.time.value);
|
||||
const isOvertime = this.card.getIsOvertime();
|
||||
|
||||
if (spentTime >= 0) {
|
||||
this.storeTime(spentTime, isOvertime);
|
||||
if (spentTime >= 0) {
|
||||
this.storeTime(spentTime, isOvertime);
|
||||
Popup.close();
|
||||
} else {
|
||||
this.error.set('invalid-time');
|
||||
evt.target.time.focus();
|
||||
}
|
||||
},
|
||||
'click .js-delete-time'(evt) {
|
||||
evt.preventDefault();
|
||||
this.deleteTime();
|
||||
Popup.close();
|
||||
} else {
|
||||
this.error.set('invalid-time');
|
||||
evt.target.time.focus();
|
||||
}
|
||||
},
|
||||
'click a.js-toggle-overtime': this.toggleOvertime,
|
||||
},
|
||||
'click .js-delete-time'(evt) {
|
||||
evt.preventDefault();
|
||||
this.deleteTime();
|
||||
Popup.close();
|
||||
},
|
||||
'click a.js-toggle-overtime': this.toggleOvertime,
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('editCardSpentTimePopup');
|
||||
|
||||
@@ -56,23 +58,33 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
showTitle() {
|
||||
if (this.data().getIsOvertime()) {
|
||||
return `${TAPi18n.__('overtime')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
return `${TAPi18n.__(
|
||||
'overtime',
|
||||
)} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
} else {
|
||||
return `${TAPi18n.__('card-spent')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
return `${TAPi18n.__(
|
||||
'card-spent',
|
||||
)} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
}
|
||||
},
|
||||
showTime() {
|
||||
return this.data().getSpentTime();
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-edit-time': Popup.open('editCardSpentTime'),
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-edit-time': Popup.open('editCardSpentTime'),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('cardSpentTime');
|
||||
|
||||
Template.timeBadge.helpers({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -56,7 +56,7 @@ template(name="addChecklistItemForm")
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editChecklistItemForm")
|
||||
textarea.js-edit-checklist-item(rows='1' autofocus)
|
||||
textarea.js-edit-checklist-item(rows='1' autofocus dir="auto")
|
||||
if $eq type 'item'
|
||||
= item.title
|
||||
else
|
||||
|
||||
@@ -64,20 +64,22 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
}).register('checklistDetail');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
||||
addChecklist(event) {
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-add-checklist-item');
|
||||
const title = textarea.value.trim();
|
||||
let cardId = this.currentData().cardId;
|
||||
const card = Cards.findOne(cardId);
|
||||
if (card.isLinked())
|
||||
cardId = card.linkedId;
|
||||
if (card.isLinked()) cardId = card.linkedId;
|
||||
|
||||
if (title) {
|
||||
Checklists.insert({
|
||||
@@ -86,7 +88,9 @@ BlazeComponent.extendComponent({
|
||||
sort: card.checklists().count(),
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.$('.add-checklist-item').last().click();
|
||||
this.$('.add-checklist-item')
|
||||
.last()
|
||||
.click();
|
||||
}, 100);
|
||||
}
|
||||
textarea.value = '';
|
||||
@@ -113,7 +117,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
|
||||
deleteChecklist() {
|
||||
@@ -167,37 +175,43 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
const events = {
|
||||
'click .toggle-delete-checklist-dialog'(event) {
|
||||
if($(event.target).hasClass('js-delete-checklist')){
|
||||
if ($(event.target).hasClass('js-delete-checklist')) {
|
||||
this.checklistToDelete = this.currentData().checklist; //Store data context
|
||||
}
|
||||
this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
|
||||
},
|
||||
};
|
||||
|
||||
return [{
|
||||
...events,
|
||||
'submit .js-add-checklist': this.addChecklist,
|
||||
'submit .js-edit-checklist-title': this.editChecklist,
|
||||
'submit .js-add-checklist-item': this.addChecklistItem,
|
||||
'submit .js-edit-checklist-item': this.editChecklistItem,
|
||||
'click .js-delete-checklist-item': this.deleteItem,
|
||||
'click .confirm-checklist-delete': this.deleteChecklist,
|
||||
keydown: this.pressKey,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
...events,
|
||||
'submit .js-add-checklist': this.addChecklist,
|
||||
'submit .js-edit-checklist-title': this.editChecklist,
|
||||
'submit .js-add-checklist-item': this.addChecklistItem,
|
||||
'submit .js-edit-checklist-item': this.editChecklistItem,
|
||||
'click .js-delete-checklist-item': this.deleteItem,
|
||||
'click .confirm-checklist-delete': this.deleteChecklist,
|
||||
keydown: this.pressKey,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('checklists');
|
||||
|
||||
Template.checklistDeleteDialog.onCreated(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
this.scrollState = { position: $cardDetails.scrollTop(), //save current scroll position
|
||||
this.scrollState = {
|
||||
position: $cardDetails.scrollTop(), //save current scroll position
|
||||
top: false, //required for smooth scroll animation
|
||||
};
|
||||
//Callback's purpose is to only prevent scrolling after animation is complete
|
||||
$cardDetails.animate({ scrollTop: 0 }, 500, () => { this.scrollState.top = true; });
|
||||
$cardDetails.animate({ scrollTop: 0 }, 500, () => {
|
||||
this.scrollState.top = true;
|
||||
});
|
||||
|
||||
//Prevent scrolling while dialog is open
|
||||
$cardDetails.on('scroll', () => {
|
||||
if(this.scrollState.top) { //If it's already in position, keep it there. Otherwise let animation scroll
|
||||
if (this.scrollState.top) {
|
||||
//If it's already in position, keep it there. Otherwise let animation scroll
|
||||
$cardDetails.scrollTop(0);
|
||||
}
|
||||
});
|
||||
@@ -206,12 +220,16 @@ Template.checklistDeleteDialog.onCreated(() => {
|
||||
Template.checklistDeleteDialog.onDestroyed(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
$cardDetails.off('scroll'); //Reactivate scrolling
|
||||
$cardDetails.animate( { scrollTop: this.scrollState.position });
|
||||
$cardDetails.animate({ scrollTop: this.scrollState.position });
|
||||
});
|
||||
|
||||
Template.checklistItemDetail.helpers({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -224,8 +242,10 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-checklist-item .check-box': this.toggleItem,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-checklist-item .check-box': this.toggleItem,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('checklistItemDetail');
|
||||
@@ -31,7 +31,8 @@ template(name="cardLabelsPopup")
|
||||
a.card-label-edit-button.fa.fa-pencil.js-edit-label
|
||||
span.card-label.card-label-selectable.js-select-label(class="card-label-{{color}}"
|
||||
class="{{# if isLabelSelected ../_id }}active{{/if}}")
|
||||
= name
|
||||
+viewer
|
||||
= name
|
||||
if(isLabelSelected ../_id)
|
||||
i.card-label-selectable-icon.fa.fa-check
|
||||
if currentUser.isBoardAdmin
|
||||
|
||||
@@ -9,7 +9,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
labels() {
|
||||
return labelColors.map((color) => ({ color, name: '' }));
|
||||
return labelColors.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
@@ -17,11 +17,13 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
return [
|
||||
{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('formLabel');
|
||||
|
||||
@@ -38,19 +40,19 @@ Template.createLabelPopup.helpers({
|
||||
});
|
||||
|
||||
Template.cardLabelsPopup.events({
|
||||
'click .js-select-label'(evt) {
|
||||
'click .js-select-label'(event) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const labelId = this._id;
|
||||
card.toggleLabel(labelId);
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-edit-label': Popup.open('editLabel'),
|
||||
'click .js-add-label': Popup.open('createLabel'),
|
||||
});
|
||||
|
||||
Template.formLabel.events({
|
||||
'click .js-palette-color'(evt) {
|
||||
const $this = $(evt.currentTarget);
|
||||
'click .js-palette-color'(event) {
|
||||
const $this = $(event.currentTarget);
|
||||
|
||||
// hide selected ll colors
|
||||
$('.js-palette-select').addClass('hide');
|
||||
@@ -62,11 +64,14 @@ Template.formLabel.events({
|
||||
|
||||
Template.createLabelPopup.events({
|
||||
// Create the new label
|
||||
'submit .create-label'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
'submit .create-label'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const board = Boards.findOne(Session.get('currentBoard'));
|
||||
const name = tpl.$('#labelName').val().trim();
|
||||
const color = Blaze.getData(tpl.find('.fa-check')).color;
|
||||
const name = templateInstance
|
||||
.$('#labelName')
|
||||
.val()
|
||||
.trim();
|
||||
const color = Blaze.getData(templateInstance.find('.fa-check')).color;
|
||||
board.addLabel(name, color);
|
||||
Popup.back();
|
||||
},
|
||||
@@ -78,11 +83,14 @@ Template.editLabelPopup.events({
|
||||
board.removeLabel(this._id);
|
||||
Popup.back(2);
|
||||
}),
|
||||
'submit .edit-label'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
'submit .edit-label'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const board = Boards.findOne(Session.get('currentBoard'));
|
||||
const name = tpl.$('#labelName').val().trim();
|
||||
const color = Blaze.getData(tpl.find('.fa-check')).color;
|
||||
const name = templateInstance
|
||||
.$('#labelName')
|
||||
.val()
|
||||
.trim();
|
||||
const color = Blaze.getData(templateInstance.find('.fa-check')).color;
|
||||
board.editLabel(this._id, name, color);
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
@@ -8,7 +8,9 @@ template(name="minicard")
|
||||
if labels
|
||||
.minicard-labels
|
||||
each labels
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
span.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
.minicard-title
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
@@ -58,7 +60,8 @@ template(name="minicard")
|
||||
.minicard-custom-field
|
||||
if definition.showLabelOnMiniCard
|
||||
.minicard-custom-field-item
|
||||
= definition.name
|
||||
+viewer
|
||||
= definition.name
|
||||
.minicard-custom-field-item
|
||||
+viewer
|
||||
= trueValue
|
||||
@@ -73,7 +76,10 @@ template(name="minicard")
|
||||
if comments.count
|
||||
.badge(title="{{_ 'card-comments-title' comments.count }}")
|
||||
span.badge-icon.fa.fa-comment-o.badge-comment
|
||||
span.badge-text= comments.count
|
||||
= ' '
|
||||
= comments.count
|
||||
//span.badge-comment.badge-text
|
||||
//| {{_ 'comment'}}
|
||||
if getDescription
|
||||
.badge.badge-state-image-only(title=getDescription)
|
||||
span.badge-icon.fa.fa-align-left
|
||||
|
||||
@@ -8,13 +8,14 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-linked-link' () {
|
||||
if (this.data().isLinkedCard())
|
||||
Utils.goCardId(this.data().linkedId);
|
||||
else if (this.data().isLinkedBoard())
|
||||
Utils.goBoardId(this.data().linkedId);
|
||||
return [
|
||||
{
|
||||
'click .js-linked-link'() {
|
||||
if (this.data().isLinkedCard()) Utils.goCardId(this.data().linkedId);
|
||||
else if (this.data().isLinkedBoard())
|
||||
Utils.goBoardId(this.data().linkedId);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('minicard');
|
||||
@@ -144,7 +144,7 @@
|
||||
.badge-text
|
||||
vertical-align: middle
|
||||
&.badge-comment
|
||||
margin-bottom: 0.1rem
|
||||
margin-bottom: 0.1rem
|
||||
|
||||
.badge-text
|
||||
font-size: 0.9em
|
||||
|
||||
@@ -50,13 +50,13 @@ template(name="subtaskDeleteDialog")
|
||||
button.toggle-delete-subtask-dialog(type="button") {{_ 'cancel'}}
|
||||
|
||||
template(name="addSubtaskItemForm")
|
||||
textarea.js-add-subtask-item(rows='1' autofocus)
|
||||
textarea.js-add-subtask-item(rows='1' autofocus dir="auto")
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-add-subtask-item-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editSubtaskItemForm")
|
||||
textarea.js-edit-subtask-item(rows='1' autofocus)
|
||||
textarea.js-edit-subtask-item(rows='1' autofocus dir="auto")
|
||||
if $eq type 'item'
|
||||
= item.title
|
||||
else
|
||||
@@ -76,7 +76,7 @@ template(name="subtasksItems")
|
||||
else
|
||||
+subtaskItemDetail(item = item subtasks = subtasks)
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-subtask-item" subtasks = subtasks)
|
||||
+inlinedForm(autoclose=false classNames="js-add-subtask-item" subtasks = subtasks dir="auto")
|
||||
+addSubtaskItemForm
|
||||
else
|
||||
a.add-subtask-item.js-open-inlined-form
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
BlazeComponent.extendComponent({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
}).register('subtaskDetail');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
||||
addSubtask(event) {
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-add-subtask-item');
|
||||
@@ -38,9 +41,10 @@ BlazeComponent.extendComponent({
|
||||
// See https://github.com/wekan/wekan/issues/80
|
||||
Filter.addException(_id);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
this.$('.add-subtask-item').last().click();
|
||||
this.$('.add-subtask-item')
|
||||
.last()
|
||||
.click();
|
||||
}, 100);
|
||||
}
|
||||
textarea.value = '';
|
||||
@@ -48,7 +52,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
|
||||
deleteSubtask() {
|
||||
@@ -85,13 +93,13 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
const events = {
|
||||
'click .toggle-delete-subtask-dialog'(event) {
|
||||
if($(event.target).hasClass('js-delete-subtask')){
|
||||
if ($(event.target).hasClass('js-delete-subtask')) {
|
||||
this.subtaskToDelete = this.currentData().subtask; //Store data context
|
||||
}
|
||||
this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
|
||||
},
|
||||
'click .js-view-subtask'(event) {
|
||||
if($(event.target).hasClass('js-view-subtask')){
|
||||
if ($(event.target).hasClass('js-view-subtask')) {
|
||||
const subtask = this.currentData().subtask;
|
||||
const board = subtask.board();
|
||||
FlowRouter.go('card', {
|
||||
@@ -103,27 +111,33 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
};
|
||||
|
||||
return [{
|
||||
...events,
|
||||
'submit .js-add-subtask': this.addSubtask,
|
||||
'submit .js-edit-subtask-title': this.editSubtask,
|
||||
'click .confirm-subtask-delete': this.deleteSubtask,
|
||||
keydown: this.pressKey,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
...events,
|
||||
'submit .js-add-subtask': this.addSubtask,
|
||||
'submit .js-edit-subtask-title': this.editSubtask,
|
||||
'click .confirm-subtask-delete': this.deleteSubtask,
|
||||
keydown: this.pressKey,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('subtasks');
|
||||
|
||||
Template.subtaskDeleteDialog.onCreated(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
this.scrollState = { position: $cardDetails.scrollTop(), //save current scroll position
|
||||
this.scrollState = {
|
||||
position: $cardDetails.scrollTop(), //save current scroll position
|
||||
top: false, //required for smooth scroll animation
|
||||
};
|
||||
//Callback's purpose is to only prevent scrolling after animation is complete
|
||||
$cardDetails.animate({ scrollTop: 0 }, 500, () => { this.scrollState.top = true; });
|
||||
$cardDetails.animate({ scrollTop: 0 }, 500, () => {
|
||||
this.scrollState.top = true;
|
||||
});
|
||||
|
||||
//Prevent scrolling while dialog is open
|
||||
$cardDetails.on('scroll', () => {
|
||||
if(this.scrollState.top) { //If it's already in position, keep it there. Otherwise let animation scroll
|
||||
if (this.scrollState.top) {
|
||||
//If it's already in position, keep it there. Otherwise let animation scroll
|
||||
$cardDetails.scrollTop(0);
|
||||
}
|
||||
});
|
||||
@@ -132,12 +146,16 @@ Template.subtaskDeleteDialog.onCreated(() => {
|
||||
Template.subtaskDeleteDialog.onDestroyed(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
$cardDetails.off('scroll'); //Reactivate scrolling
|
||||
$cardDetails.animate( { scrollTop: this.scrollState.position });
|
||||
$cardDetails.animate({ scrollTop: this.scrollState.position });
|
||||
});
|
||||
|
||||
Template.subtaskItemDetail.helpers({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ BlazeComponent.extendComponent({
|
||||
const membersMapping = this.membersToMap.get();
|
||||
if (membersMapping) {
|
||||
const mappingById = {};
|
||||
membersMapping.forEach((member) => {
|
||||
membersMapping.forEach(member => {
|
||||
if (member.wekanId) {
|
||||
mappingById[member.id] = member.wekanId;
|
||||
}
|
||||
@@ -64,7 +64,8 @@ BlazeComponent.extendComponent({
|
||||
additionalData.membersMapping = mappingById;
|
||||
}
|
||||
this.membersToMap.set([]);
|
||||
Meteor.call('importBoard',
|
||||
Meteor.call(
|
||||
'importBoard',
|
||||
this.importedData.get(),
|
||||
additionalData,
|
||||
this.importSource,
|
||||
@@ -76,7 +77,7 @@ BlazeComponent.extendComponent({
|
||||
Session.set('fromBoard', null);
|
||||
Utils.goBoardId(res);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -84,12 +85,12 @@ BlazeComponent.extendComponent({
|
||||
const importSource = Session.get('importSource');
|
||||
let membersToMap;
|
||||
switch (importSource) {
|
||||
case 'trello':
|
||||
membersToMap = trelloMembersMapper.getMembersToMap(dataObject);
|
||||
break;
|
||||
case 'wekan':
|
||||
membersToMap = wekanMembersMapper.getMembersToMap(dataObject);
|
||||
break;
|
||||
case 'trello':
|
||||
membersToMap = trelloMembersMapper.getMembersToMap(dataObject);
|
||||
break;
|
||||
case 'wekan':
|
||||
membersToMap = wekanMembersMapper.getMembersToMap(dataObject);
|
||||
break;
|
||||
}
|
||||
return membersToMap;
|
||||
},
|
||||
@@ -109,22 +110,26 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
submit(evt) {
|
||||
return this.parentComponent().importData(evt);
|
||||
return [
|
||||
{
|
||||
submit(evt) {
|
||||
return this.parentComponent().importData(evt);
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('importTextarea');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.autorun(() => {
|
||||
this.parentComponent().membersToMap.get().forEach(({ wekanId }) => {
|
||||
if (wekanId) {
|
||||
this.subscribe('user-miniprofile', wekanId);
|
||||
}
|
||||
});
|
||||
this.parentComponent()
|
||||
.membersToMap.get()
|
||||
.forEach(({ wekanId }) => {
|
||||
if (wekanId) {
|
||||
this.subscribe('user-miniprofile', wekanId);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -149,23 +154,23 @@ BlazeComponent.extendComponent({
|
||||
_setPropertyForMember(property, value, memberId, unset = false) {
|
||||
const listOfMembers = this.members();
|
||||
let finder = null;
|
||||
if(memberId) {
|
||||
finder = (member) => member.id === memberId;
|
||||
if (memberId) {
|
||||
finder = member => member.id === memberId;
|
||||
} else {
|
||||
finder = (member) => member.selected;
|
||||
finder = member => member.selected;
|
||||
}
|
||||
listOfMembers.forEach((member) => {
|
||||
if(finder(member)) {
|
||||
if(value !== null) {
|
||||
listOfMembers.forEach(member => {
|
||||
if (finder(member)) {
|
||||
if (value !== null) {
|
||||
member[property] = value;
|
||||
} else {
|
||||
delete member[property];
|
||||
}
|
||||
if(!unset) {
|
||||
if (!unset) {
|
||||
// we shortcut if we don't care about unsetting the others
|
||||
return false;
|
||||
}
|
||||
} else if(unset) {
|
||||
} else if (unset) {
|
||||
delete member[property];
|
||||
}
|
||||
return true;
|
||||
@@ -186,9 +191,9 @@ BlazeComponent.extendComponent({
|
||||
const allMembers = this.members();
|
||||
let finder = null;
|
||||
if (memberId) {
|
||||
finder = (user) => user.id === memberId;
|
||||
finder = user => user.id === memberId;
|
||||
} else {
|
||||
finder = (user) => user.selected;
|
||||
finder = user => user.selected;
|
||||
}
|
||||
return allMembers.find(finder);
|
||||
},
|
||||
@@ -197,7 +202,7 @@ BlazeComponent.extendComponent({
|
||||
return this._setPropertyForMember('wekanId', wekanId, null);
|
||||
},
|
||||
|
||||
unmapMember(memberId){
|
||||
unmapMember(memberId) {
|
||||
return this._setPropertyForMember('wekanId', null, memberId);
|
||||
},
|
||||
|
||||
@@ -208,7 +213,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
onMapMember(evt) {
|
||||
const memberToMap = this.currentData();
|
||||
if(memberToMap.wekan) {
|
||||
if (memberToMap.wekan) {
|
||||
// todo xxx ask for confirmation?
|
||||
this.unmapMember(memberToMap.id);
|
||||
} else {
|
||||
@@ -218,10 +223,12 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit': this.onSubmit,
|
||||
'click .js-select-member': this.onMapMember,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
submit: this.onSubmit,
|
||||
'click .js-select-member': this.onMapMember,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('importMapMembers');
|
||||
|
||||
@@ -230,14 +237,16 @@ BlazeComponent.extendComponent({
|
||||
this.find('.js-map-member input').focus();
|
||||
},
|
||||
|
||||
onSelectUser(){
|
||||
onSelectUser() {
|
||||
Popup.getOpenerComponent().mapSelectedMember(this.currentData()._id);
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-import': this.onSelectUser,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-select-import': this.onSelectUser,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('importMapMembersAddPopup');
|
||||
@@ -4,7 +4,7 @@ export function getMembersToMap(data) {
|
||||
// imported member
|
||||
const membersToMap = data.members;
|
||||
// auto-map based on username
|
||||
membersToMap.forEach((importedMember) => {
|
||||
membersToMap.forEach(importedMember => {
|
||||
const wekanUser = Users.findOne({ username: importedMember.username });
|
||||
if (wekanUser) {
|
||||
importedMember.wekanId = wekanUser._id;
|
||||
|
||||
@@ -5,10 +5,10 @@ export function getMembersToMap(data) {
|
||||
const membersToMap = data.members;
|
||||
const users = data.users;
|
||||
// auto-map based on username
|
||||
membersToMap.forEach((importedMember) => {
|
||||
membersToMap.forEach(importedMember => {
|
||||
importedMember.id = importedMember.userId;
|
||||
delete importedMember.userId;
|
||||
const user = users.filter((user) => {
|
||||
const user = users.filter(user => {
|
||||
return user._id === importedMember.id;
|
||||
})[0];
|
||||
if (user.profile && user.profile.fullname) {
|
||||
|
||||
@@ -21,14 +21,18 @@ BlazeComponent.extendComponent({
|
||||
const boardComponent = this.parentComponent().parentComponent();
|
||||
|
||||
function userIsMember() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
}
|
||||
|
||||
const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
|
||||
const $cards = this.$('.js-minicards');
|
||||
|
||||
if(window.matchMedia('(max-width: 1199px)').matches) {
|
||||
$( '.js-minicards' ).sortable({
|
||||
if (window.matchMedia('(max-width: 1199px)').matches) {
|
||||
$('.js-minicards').sortable({
|
||||
handle: '.handle',
|
||||
});
|
||||
}
|
||||
@@ -42,10 +46,16 @@ BlazeComponent.extendComponent({
|
||||
if (MultiSelection.isActive()) {
|
||||
const andNOthers = $cards.find('.js-minicard.is-checked').length - 1;
|
||||
if (andNOthers > 0) {
|
||||
helper.append($(Blaze.toHTML(HTML.DIV(
|
||||
{ 'class': 'and-n-other' },
|
||||
TAPi18n.__('and-n-other-card', { count: andNOthers })
|
||||
))));
|
||||
helper.append(
|
||||
$(
|
||||
Blaze.toHTML(
|
||||
HTML.DIV(
|
||||
{ class: 'and-n-other' },
|
||||
TAPi18n.__('and-n-other-card', { count: andNOthers }),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return helper;
|
||||
@@ -69,10 +79,17 @@ BlazeComponent.extendComponent({
|
||||
const listId = Blaze.getData(ui.item.parents('.list').get(0))._id;
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let swimlaneId = '';
|
||||
const boardView = Meteor.user().profile.boardView;
|
||||
if (boardView === 'board-view-swimlanes' || currentBoard.isTemplatesBoard())
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (
|
||||
boardView === 'board-view-swimlanes' ||
|
||||
currentBoard.isTemplatesBoard()
|
||||
)
|
||||
swimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))._id;
|
||||
else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal'))
|
||||
else if (
|
||||
boardView === 'board-view-lists' ||
|
||||
boardView === 'board-view-cal' ||
|
||||
!boardView
|
||||
)
|
||||
swimlaneId = currentBoard.getDefaultSwimline()._id;
|
||||
|
||||
// Normally the jquery-ui sortable library moves the dragged DOM element
|
||||
@@ -86,7 +103,12 @@ BlazeComponent.extendComponent({
|
||||
|
||||
if (MultiSelection.isActive()) {
|
||||
Cards.find(MultiSelection.getMongoSelector()).forEach((card, i) => {
|
||||
card.move(currentBoard._id, swimlaneId, listId, sortIndex.base + i * sortIndex.increment);
|
||||
card.move(
|
||||
currentBoard._id,
|
||||
swimlaneId,
|
||||
listId,
|
||||
sortIndex.base + i * sortIndex.increment,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
const cardDomElement = ui.item.get(0);
|
||||
|
||||
@@ -99,6 +99,14 @@
|
||||
.highlight
|
||||
color: #ce1414
|
||||
|
||||
.cardCount
|
||||
color: #8c8c8c
|
||||
font-size: 0.8em
|
||||
|
||||
.list-header .list-header-plus-icon, .js-open-list-menu, .list-header-menu a
|
||||
color #4d4d4d
|
||||
padding-left 4px
|
||||
|
||||
.list-body
|
||||
flex: 1 1 auto
|
||||
flex-direction: column
|
||||
@@ -161,7 +169,7 @@
|
||||
|
||||
.mini-list
|
||||
flex: 0 0 60px
|
||||
height: 60px
|
||||
height: auto
|
||||
width: 100%
|
||||
border-left: 0px
|
||||
border-bottom: 1px solid darken(white, 20%)
|
||||
@@ -170,6 +178,8 @@
|
||||
display: block
|
||||
width: 100%
|
||||
border-left: 0px
|
||||
&:first-child
|
||||
margin-left: 0px
|
||||
|
||||
&.ui-sortable-helper
|
||||
flex: 0 0 60px
|
||||
@@ -188,8 +198,16 @@
|
||||
border-left: 0px
|
||||
border-bottom: 1px solid darken(white, 20%)
|
||||
|
||||
.list-header
|
||||
.list-body
|
||||
padding: 15px 19px;
|
||||
|
||||
.list-header
|
||||
padding: 0 12px 0px
|
||||
border-bottom: 0px solid #e4e4e4
|
||||
height: 60px
|
||||
margin-top: 10px
|
||||
display: flex
|
||||
align-items: center
|
||||
.list-header-left-icon
|
||||
display: inline
|
||||
padding: 7px
|
||||
@@ -201,8 +219,10 @@
|
||||
.list-header-menu-icon
|
||||
position: absolute
|
||||
padding: 7px
|
||||
top: -@padding
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
right: 17px
|
||||
font-size: 20px
|
||||
|
||||
.link-board-wrapper
|
||||
display: flex
|
||||
|
||||
@@ -13,14 +13,7 @@ template(name="listBody")
|
||||
class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
|
||||
+minicard(this)
|
||||
if (showSpinner (idOrNull ../../_id))
|
||||
.sk-spinner.sk-spinner-wave.sk-spinner-list(
|
||||
class=currentBoard.colorClass
|
||||
id="showMoreResults")
|
||||
.sk-rect1
|
||||
.sk-rect2
|
||||
.sk-rect3
|
||||
.sk-rect4
|
||||
.sk-rect5
|
||||
+spinnerList
|
||||
|
||||
if canSeeAddCard
|
||||
+inlinedForm(autoclose=false position="bottom")
|
||||
@@ -30,13 +23,23 @@ template(name="listBody")
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-card'}}
|
||||
|
||||
template(name="spinnerList")
|
||||
.sk-spinner.sk-spinner-wave.sk-spinner-list(
|
||||
class=currentBoard.colorClass
|
||||
id="showMoreResults")
|
||||
.sk-rect1
|
||||
.sk-rect2
|
||||
.sk-rect3
|
||||
.sk-rect4
|
||||
.sk-rect5
|
||||
|
||||
template(name="addCardForm")
|
||||
.minicard.minicard-composer.js-composer
|
||||
if getLabels
|
||||
.minicard-labels
|
||||
each getLabels
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
textarea.minicard-composer-textarea.js-card-title(autofocus)
|
||||
textarea.minicard-composer-textarea.js-card-title(autofocus dir="auto")
|
||||
if members.get
|
||||
.minicard-members.js-minicard-composer-members
|
||||
each members.get
|
||||
@@ -93,7 +96,7 @@ template(name="searchElementPopup")
|
||||
form
|
||||
label
|
||||
| {{_ 'title'}}
|
||||
input.js-element-title(type="text" placeholder="{{_ 'title'}}" autofocus required)
|
||||
input.js-element-title(type="text" placeholder="{{_ 'title'}}" autofocus required dir="auto")
|
||||
unless isTemplateSearch
|
||||
label {{_ 'boards'}}:
|
||||
.link-board-wrapper
|
||||
@@ -102,7 +105,7 @@ template(name="searchElementPopup")
|
||||
each boards
|
||||
option(value="{{_id}}") {{title}}
|
||||
form.js-search-term-form
|
||||
input(type="text" name="searchTerm" placeholder="{{_ 'search-example'}}" autofocus)
|
||||
input(type="text" name="searchTerm" placeholder="{{_ 'search-example'}}" autofocus dir="auto")
|
||||
.list-body.js-perfect-scrollbar.search-card-results
|
||||
.minicards.clearfix.js-minicards
|
||||
if isBoardTemplateSearch
|
||||
|
||||
+372
-283
@@ -7,28 +7,6 @@ BlazeComponent.extendComponent({
|
||||
this.cardlimit = new ReactiveVar(InfiniteScrollIter);
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
const domElement = this.find('.js-perfect-scrollbar');
|
||||
|
||||
this.$(domElement).on('scroll', () => this.updateList(domElement));
|
||||
$(window).on(`resize.${this.data().listId}`, () => this.updateList(domElement));
|
||||
|
||||
// we add a Mutation Observer to allow propagations of cardlimit
|
||||
// when the spinner stays in the current view (infinite scrolling)
|
||||
this.mutationObserver = new MutationObserver(() => this.updateList(domElement));
|
||||
|
||||
this.mutationObserver.observe(domElement, {
|
||||
childList: true,
|
||||
});
|
||||
|
||||
this.updateList(domElement);
|
||||
},
|
||||
|
||||
onDestroyed() {
|
||||
$(window).off(`resize.${this.data().listId}`);
|
||||
this.mutationObserver.disconnect();
|
||||
},
|
||||
|
||||
mixins() {
|
||||
return [Mixins.PerfectScrollbar];
|
||||
},
|
||||
@@ -38,7 +16,7 @@ BlazeComponent.extendComponent({
|
||||
options.position = options.position || 'top';
|
||||
|
||||
const forms = this.childComponents('inlinedForm');
|
||||
let form = forms.find((component) => {
|
||||
let form = forms.find(component => {
|
||||
return component.data().position === options.position;
|
||||
});
|
||||
if (!form && forms.length > 0) {
|
||||
@@ -70,15 +48,16 @@ BlazeComponent.extendComponent({
|
||||
const board = this.data().board();
|
||||
let linkedId = '';
|
||||
let swimlaneId = '';
|
||||
const boardView = Meteor.user().profile.boardView;
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
let cardType = 'cardType-card';
|
||||
if (title) {
|
||||
if (board.isTemplatesBoard()) {
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id; // Always swimlanes view
|
||||
swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id; // Always swimlanes view
|
||||
const swimlane = Swimlanes.findOne(swimlaneId);
|
||||
// If this is the card templates swimlane, insert a card template
|
||||
if (swimlane.isCardTemplatesSwimlane())
|
||||
cardType = 'template-card';
|
||||
if (swimlane.isCardTemplatesSwimlane()) cardType = 'template-card';
|
||||
// If this is the board templates swimlane, insert a board template and a linked card
|
||||
else if (swimlane.isBoardTemplatesSwimlane()) {
|
||||
linkedId = Boards.insert({
|
||||
@@ -93,8 +72,14 @@ BlazeComponent.extendComponent({
|
||||
cardType = 'cardType-linkedBoard';
|
||||
}
|
||||
} else if (boardView === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id;
|
||||
else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal'))
|
||||
swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
else if (
|
||||
boardView === 'board-view-lists' ||
|
||||
boardView === 'board-view-cal' ||
|
||||
!boardView
|
||||
)
|
||||
swimlaneId = board.getDefaultSwimline()._id;
|
||||
|
||||
const _id = Cards.insert({
|
||||
@@ -113,7 +98,9 @@ BlazeComponent.extendComponent({
|
||||
// if the displayed card count is less than the total cards in the list,
|
||||
// we need to increment the displayed card count to prevent the spinner
|
||||
// to appear
|
||||
const cardCount = this.data().cards(this.idOrNull(swimlaneId)).count();
|
||||
const cardCount = this.data()
|
||||
.cards(this.idOrNull(swimlaneId))
|
||||
.count();
|
||||
if (this.cardlimit.get() < cardCount) {
|
||||
this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
|
||||
}
|
||||
@@ -171,8 +158,12 @@ BlazeComponent.extendComponent({
|
||||
|
||||
idOrNull(swimlaneId) {
|
||||
const currentUser = Meteor.user();
|
||||
if (currentUser.profile.boardView === 'board-view-swimlanes' ||
|
||||
this.data().board().isTemplatesBoard())
|
||||
if (
|
||||
(currentUser.profile || {}).boardView === 'board-view-swimlanes' ||
|
||||
this.data()
|
||||
.board()
|
||||
.isTemplatesBoard()
|
||||
)
|
||||
return swimlaneId;
|
||||
return undefined;
|
||||
},
|
||||
@@ -183,62 +174,45 @@ BlazeComponent.extendComponent({
|
||||
listId: this.currentData()._id,
|
||||
archived: false,
|
||||
};
|
||||
if (swimlaneId)
|
||||
selector.swimlaneId = swimlaneId;
|
||||
if (swimlaneId) selector.swimlaneId = swimlaneId;
|
||||
return Cards.find(Filter.mongoSelector(selector), {
|
||||
sort: ['sort'],
|
||||
limit,
|
||||
});
|
||||
},
|
||||
|
||||
spinnerInView(container) {
|
||||
const parentViewHeight = container.clientHeight;
|
||||
const bottomViewPosition = container.scrollTop + parentViewHeight;
|
||||
|
||||
const spinner = this.find('.sk-spinner-list');
|
||||
|
||||
const threshold = spinner.offsetTop;
|
||||
|
||||
return bottomViewPosition > threshold;
|
||||
},
|
||||
|
||||
showSpinner(swimlaneId) {
|
||||
const list = Template.currentData();
|
||||
return list.cards(swimlaneId).count() > this.cardlimit.get();
|
||||
},
|
||||
|
||||
updateList(container) {
|
||||
// first, if the spinner is not rendered, we have reached the end of
|
||||
// the list of cards, so skip and disable firing the events
|
||||
const target = this.find('.sk-spinner-list');
|
||||
if (!target) {
|
||||
this.$(container).off('scroll');
|
||||
$(window).off(`resize.${this.data().listId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.spinnerInView(container)) {
|
||||
this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
|
||||
Ps.update(container);
|
||||
}
|
||||
},
|
||||
|
||||
canSeeAddCard() {
|
||||
return !this.reachedWipLimit() && Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
return (
|
||||
!this.reachedWipLimit() &&
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
|
||||
reachedWipLimit() {
|
||||
const list = Template.currentData();
|
||||
return !list.getWipLimit('soft') && list.getWipLimit('enabled') && list.getWipLimit('value') <= list.cards().count();
|
||||
return (
|
||||
!list.getWipLimit('soft') &&
|
||||
list.getWipLimit('enabled') &&
|
||||
list.getWipLimit('value') <= list.cards().count()
|
||||
);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-minicard': this.clickOnMiniCard,
|
||||
'click .js-toggle-multi-selection': this.toggleMultiSelection,
|
||||
'click .open-minicard-composer': this.scrollToBottom,
|
||||
submit: this.addCard,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-minicard': this.clickOnMiniCard,
|
||||
'click .js-toggle-multi-selection': this.toggleMultiSelection,
|
||||
'click .open-minicard-composer': this.scrollToBottom,
|
||||
submit: this.addCard,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('listBody');
|
||||
|
||||
@@ -261,10 +235,15 @@ BlazeComponent.extendComponent({
|
||||
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
arr = [];
|
||||
_.forEach(Boards.findOne(currentBoardId).customFields().fetch(), function(field){
|
||||
if(field.automaticallyOnCard)
|
||||
arr.push({_id: field._id, value: null});
|
||||
});
|
||||
_.forEach(
|
||||
Boards.findOne(currentBoardId)
|
||||
.customFields()
|
||||
.fetch(),
|
||||
function(field) {
|
||||
if (field.automaticallyOnCard)
|
||||
arr.push({ _id: field._id, value: null });
|
||||
},
|
||||
);
|
||||
this.customFields.set(arr);
|
||||
},
|
||||
|
||||
@@ -276,7 +255,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
getLabels() {
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
return Boards.findOne(currentBoardId).labels.filter((label) => {
|
||||
return Boards.findOne(currentBoardId).labels.filter(label => {
|
||||
return this.labels.get().indexOf(label._id) > -1;
|
||||
});
|
||||
},
|
||||
@@ -306,18 +285,20 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
|
||||
BlazeComponent.getComponentForElement(nextList).openForm({
|
||||
position:this.data().position,
|
||||
position: this.data().position,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
keydown: this.pressKey,
|
||||
'click .js-link': Popup.open('linkCard'),
|
||||
'click .js-search': Popup.open('searchElement'),
|
||||
'click .js-card-template': Popup.open('searchElement'),
|
||||
}];
|
||||
return [
|
||||
{
|
||||
keydown: this.pressKey,
|
||||
'click .js-link': Popup.open('linkCard'),
|
||||
'click .js-search': Popup.open('searchElement'),
|
||||
'click .js-card-template': Popup.open('searchElement'),
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
@@ -326,66 +307,75 @@ BlazeComponent.extendComponent({
|
||||
|
||||
autosize($textarea);
|
||||
|
||||
$textarea.escapeableTextComplete([
|
||||
// User mentions
|
||||
{
|
||||
match: /\B@([\w.]*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback($.map(currentBoard.activeMembers(), (member) => {
|
||||
const user = Users.findOne(member.userId);
|
||||
return user.username.indexOf(term) === 0 ? user : null;
|
||||
}));
|
||||
$textarea.escapeableTextComplete(
|
||||
[
|
||||
// User mentions
|
||||
{
|
||||
match: /\B@([\w.]*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback(
|
||||
$.map(currentBoard.activeMembers(), member => {
|
||||
const user = Users.findOne(member.userId);
|
||||
return user.username.indexOf(term) === 0 ? user : null;
|
||||
}),
|
||||
);
|
||||
},
|
||||
template(user) {
|
||||
return user.username;
|
||||
},
|
||||
replace(user) {
|
||||
toggleValueInReactiveArray(editor.members, user._id);
|
||||
return '';
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
template(user) {
|
||||
return user.username;
|
||||
},
|
||||
replace(user) {
|
||||
toggleValueInReactiveArray(editor.members, user._id);
|
||||
return '';
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
|
||||
// Labels
|
||||
// Labels
|
||||
{
|
||||
match: /\B#(\w*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback(
|
||||
$.map(currentBoard.labels, label => {
|
||||
if (
|
||||
label.name.indexOf(term) > -1 ||
|
||||
label.color.indexOf(term) > -1
|
||||
) {
|
||||
return label;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
);
|
||||
},
|
||||
template(label) {
|
||||
return Blaze.toHTMLWithData(Template.autocompleteLabelLine, {
|
||||
hasNoName: !label.name,
|
||||
colorName: label.color,
|
||||
labelName: label.name || label.color,
|
||||
});
|
||||
},
|
||||
replace(label) {
|
||||
toggleValueInReactiveArray(editor.labels, label._id);
|
||||
return '';
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
],
|
||||
{
|
||||
match: /\B#(\w*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback($.map(currentBoard.labels, (label) => {
|
||||
if (label.name.indexOf(term) > -1 ||
|
||||
label.color.indexOf(term) > -1) {
|
||||
return label;
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
// When the autocomplete menu is shown we want both a press of both `Tab`
|
||||
// or `Enter` to validation the auto-completion. We also need to stop the
|
||||
// event propagation to prevent the card from submitting (on `Enter`) or
|
||||
// going on the next column (on `Tab`).
|
||||
onKeydown(evt, commands) {
|
||||
if (evt.keyCode === 9 || evt.keyCode === 13) {
|
||||
evt.stopPropagation();
|
||||
return commands.KEY_ENTER;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
template(label) {
|
||||
return Blaze.toHTMLWithData(Template.autocompleteLabelLine, {
|
||||
hasNoName: !label.name,
|
||||
colorName: label.color,
|
||||
labelName: label.name || label.color,
|
||||
});
|
||||
},
|
||||
replace(label) {
|
||||
toggleValueInReactiveArray(editor.labels, label._id);
|
||||
return '';
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
], {
|
||||
// When the autocomplete menu is shown we want both a press of both `Tab`
|
||||
// or `Enter` to validation the auto-completion. We also need to stop the
|
||||
// event propagation to prevent the card from submitting (on `Enter`) or
|
||||
// going on the next column (on `Tab`).
|
||||
onKeydown(evt, commands) {
|
||||
if (evt.keyCode === 9 || evt.keyCode === 13) {
|
||||
evt.stopPropagation();
|
||||
return commands.KEY_ENTER;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
});
|
||||
);
|
||||
},
|
||||
}).register('addCardForm');
|
||||
|
||||
@@ -397,30 +387,35 @@ BlazeComponent.extendComponent({
|
||||
|
||||
this.boardId = Session.get('currentBoard');
|
||||
// In order to get current board info
|
||||
subManager.subscribe('board', this.boardId);
|
||||
subManager.subscribe('board', this.boardId, false);
|
||||
this.board = Boards.findOne(this.boardId);
|
||||
// List where to insert card
|
||||
const list = $(Popup._getTopStack().openerElement).closest('.js-list');
|
||||
this.listId = Blaze.getData(list[0])._id;
|
||||
// Swimlane where to insert card
|
||||
const swimlane = $(Popup._getTopStack().openerElement).closest('.js-swimlane');
|
||||
const swimlane = $(Popup._getTopStack().openerElement).closest(
|
||||
'.js-swimlane',
|
||||
);
|
||||
this.swimlaneId = '';
|
||||
const boardView = Meteor.user().profile.boardView;
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else if (boardView === 'board-view-lists')
|
||||
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
|
||||
else if (boardView === 'board-view-lists' || !boardView)
|
||||
this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
type: 'board',
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
const boards = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Session.get('currentBoard') },
|
||||
type: 'board',
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
@@ -428,7 +423,7 @@ BlazeComponent.extendComponent({
|
||||
if (!this.selectedBoardId.get()) {
|
||||
return [];
|
||||
}
|
||||
const swimlanes = Swimlanes.find({boardId: this.selectedBoardId.get()});
|
||||
const swimlanes = Swimlanes.find({ boardId: this.selectedBoardId.get() });
|
||||
if (swimlanes.count())
|
||||
this.selectedSwimlaneId.set(swimlanes.fetch()[0]._id);
|
||||
return swimlanes;
|
||||
@@ -438,9 +433,8 @@ BlazeComponent.extendComponent({
|
||||
if (!this.selectedBoardId.get()) {
|
||||
return [];
|
||||
}
|
||||
const lists = Lists.find({boardId: this.selectedBoardId.get()});
|
||||
if (lists.count())
|
||||
this.selectedListId.set(lists.fetch()[0]._id);
|
||||
const lists = Lists.find({ boardId: this.selectedBoardId.get() });
|
||||
if (lists.count()) this.selectedListId.set(lists.fetch()[0]._id);
|
||||
return lists;
|
||||
},
|
||||
|
||||
@@ -448,73 +442,84 @@ BlazeComponent.extendComponent({
|
||||
if (!this.board) {
|
||||
return [];
|
||||
}
|
||||
const ownCardsIds = this.board.cards().map((card) => { return card.linkedId || card._id; });
|
||||
const ownCardsIds = this.board.cards().map(card => {
|
||||
return card.linkedId || card._id;
|
||||
});
|
||||
return Cards.find({
|
||||
boardId: this.selectedBoardId.get(),
|
||||
swimlaneId: this.selectedSwimlaneId.get(),
|
||||
listId: this.selectedListId.get(),
|
||||
archived: false,
|
||||
linkedId: {$nin: ownCardsIds},
|
||||
_id: {$nin: ownCardsIds},
|
||||
type: {$nin: ['template-card']},
|
||||
linkedId: { $nin: ownCardsIds },
|
||||
_id: { $nin: ownCardsIds },
|
||||
type: { $nin: ['template-card'] },
|
||||
});
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'change .js-select-boards'(evt) {
|
||||
subManager.subscribe('board', $(evt.currentTarget).val());
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'change .js-select-swimlanes'(evt) {
|
||||
this.selectedSwimlaneId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'change .js-select-lists'(evt) {
|
||||
this.selectedListId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'click .js-done' (evt) {
|
||||
// LINK CARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const linkedId = $('.js-select-cards option:selected').val();
|
||||
if (!linkedId) {
|
||||
return [
|
||||
{
|
||||
'change .js-select-boards'(evt) {
|
||||
subManager.subscribe('board', $(evt.currentTarget).val(), false);
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'change .js-select-swimlanes'(evt) {
|
||||
this.selectedSwimlaneId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'change .js-select-lists'(evt) {
|
||||
this.selectedListId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'click .js-done'(evt) {
|
||||
// LINK CARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const linkedId = $('.js-select-cards option:selected').val();
|
||||
if (!linkedId) {
|
||||
Popup.close();
|
||||
return;
|
||||
}
|
||||
const _id = Cards.insert({
|
||||
title: $('.js-select-cards option:selected').text(), //dummy
|
||||
listId: this.listId,
|
||||
swimlaneId: this.swimlaneId,
|
||||
boardId: this.boardId,
|
||||
sort: Lists.findOne(this.listId)
|
||||
.cards()
|
||||
.count(),
|
||||
type: 'cardType-linkedCard',
|
||||
linkedId,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
Popup.close();
|
||||
return;
|
||||
}
|
||||
const _id = Cards.insert({
|
||||
title: $('.js-select-cards option:selected').text(), //dummy
|
||||
listId: this.listId,
|
||||
swimlaneId: this.swimlaneId,
|
||||
boardId: this.boardId,
|
||||
sort: Lists.findOne(this.listId).cards().count(),
|
||||
type: 'cardType-linkedCard',
|
||||
linkedId,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-link-board' (evt) {
|
||||
//LINK BOARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const impBoardId = $('.js-select-boards option:selected').val();
|
||||
if (!impBoardId || Cards.findOne({linkedId: impBoardId, archived: false})) {
|
||||
},
|
||||
'click .js-link-board'(evt) {
|
||||
//LINK BOARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const impBoardId = $('.js-select-boards option:selected').val();
|
||||
if (
|
||||
!impBoardId ||
|
||||
Cards.findOne({ linkedId: impBoardId, archived: false })
|
||||
) {
|
||||
Popup.close();
|
||||
return;
|
||||
}
|
||||
const _id = Cards.insert({
|
||||
title: $('.js-select-boards option:selected').text(), //dummy
|
||||
listId: this.listId,
|
||||
swimlaneId: this.swimlaneId,
|
||||
boardId: this.boardId,
|
||||
sort: Lists.findOne(this.listId)
|
||||
.cards()
|
||||
.count(),
|
||||
type: 'cardType-linkedBoard',
|
||||
linkedId: impBoardId,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
Popup.close();
|
||||
return;
|
||||
}
|
||||
const _id = Cards.insert({
|
||||
title: $('.js-select-boards option:selected').text(), //dummy
|
||||
listId: this.listId,
|
||||
swimlaneId: this.swimlaneId,
|
||||
boardId: this.boardId,
|
||||
sort: Lists.findOne(this.listId).cards().count(),
|
||||
type: 'cardType-linkedBoard',
|
||||
linkedId: impBoardId,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('linkCardPopup');
|
||||
|
||||
@@ -524,23 +529,37 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
this.isCardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-card-template');
|
||||
this.isListTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-list-template');
|
||||
this.isSwimlaneTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-open-add-swimlane-menu');
|
||||
this.isBoardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass('js-add-board');
|
||||
this.isTemplateSearch = this.isCardTemplateSearch ||
|
||||
this.isCardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
|
||||
'js-card-template',
|
||||
);
|
||||
this.isListTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
|
||||
'js-list-template',
|
||||
);
|
||||
this.isSwimlaneTemplateSearch = $(
|
||||
Popup._getTopStack().openerElement,
|
||||
).hasClass('js-open-add-swimlane-menu');
|
||||
this.isBoardTemplateSearch = $(Popup._getTopStack().openerElement).hasClass(
|
||||
'js-add-board',
|
||||
);
|
||||
this.isTemplateSearch =
|
||||
this.isCardTemplateSearch ||
|
||||
this.isListTemplateSearch ||
|
||||
this.isSwimlaneTemplateSearch ||
|
||||
this.isBoardTemplateSearch;
|
||||
let board = {};
|
||||
if (this.isTemplateSearch) {
|
||||
board = Boards.findOne(Meteor.user().profile.templatesBoardId);
|
||||
board = Boards.findOne((Meteor.user().profile || {}).templatesBoardId);
|
||||
} else {
|
||||
// Prefetch first non-current board id
|
||||
board = Boards.findOne({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$nin: [Session.get('currentBoard'), Meteor.user().profile.templatesBoardId]},
|
||||
_id: {
|
||||
$nin: [
|
||||
Session.get('currentBoard'),
|
||||
(Meteor.user().profile || {}).templatesBoardId,
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
if (!board) {
|
||||
@@ -549,20 +568,21 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
const boardId = board._id;
|
||||
// Subscribe to this board
|
||||
subManager.subscribe('board', boardId);
|
||||
subManager.subscribe('board', boardId, false);
|
||||
this.selectedBoardId = new ReactiveVar(boardId);
|
||||
|
||||
if (!this.isBoardTemplateSearch) {
|
||||
this.boardId = Session.get('currentBoard');
|
||||
// In order to get current board info
|
||||
subManager.subscribe('board', this.boardId);
|
||||
subManager.subscribe('board', this.boardId, false);
|
||||
this.swimlaneId = '';
|
||||
// Swimlane where to insert card
|
||||
const swimlane = $(Popup._getTopStack().openerElement).parents('.js-swimlane');
|
||||
if (Meteor.user().profile.boardView === 'board-view-swimlanes')
|
||||
const swimlane = $(Popup._getTopStack().openerElement).parents(
|
||||
'.js-swimlane',
|
||||
);
|
||||
if ((Meteor.user().profile || {}).boardView === 'board-view-swimlanes')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else
|
||||
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
|
||||
else this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
|
||||
// List where to insert card
|
||||
const list = $(Popup._getTopStack().openerElement).closest('.js-list');
|
||||
this.listId = Blaze.getData(list[0])._id;
|
||||
@@ -571,14 +591,17 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
type: 'board',
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
const boards = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Session.get('currentBoard') },
|
||||
type: 'board',
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
@@ -595,8 +618,8 @@ BlazeComponent.extendComponent({
|
||||
return board.searchSwimlanes(this.term.get());
|
||||
} else if (this.isBoardTemplateSearch) {
|
||||
const boards = board.searchBoards(this.term.get());
|
||||
boards.forEach((board) => {
|
||||
subManager.subscribe('board', board.linkedId);
|
||||
boards.forEach(board => {
|
||||
subManager.subscribe('board', board.linkedId, false);
|
||||
});
|
||||
return boards;
|
||||
} else {
|
||||
@@ -605,59 +628,125 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'change .js-select-boards'(evt) {
|
||||
subManager.subscribe('board', $(evt.currentTarget).val());
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'submit .js-search-term-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.term.set(evt.target.searchTerm.value);
|
||||
},
|
||||
'click .js-minicard'(evt) {
|
||||
// 0. Common
|
||||
const title = $('.js-element-title').val().trim();
|
||||
if (!title)
|
||||
return;
|
||||
const element = Blaze.getData(evt.currentTarget);
|
||||
element.title = title;
|
||||
let _id = '';
|
||||
if (!this.isTemplateSearch || this.isCardTemplateSearch) {
|
||||
// Card insertion
|
||||
// 1. Common
|
||||
element.sort = Lists.findOne(this.listId).cards().count();
|
||||
// 1.A From template
|
||||
if (this.isTemplateSearch) {
|
||||
element.type = 'cardType-card';
|
||||
element.linkedId = '';
|
||||
_id = element.copy(this.boardId, this.swimlaneId, this.listId);
|
||||
// 1.B Linked card
|
||||
} else {
|
||||
delete element._id;
|
||||
element.type = 'cardType-linkedCard';
|
||||
element.linkedId = element.linkedId || element._id;
|
||||
_id = Cards.insert(element);
|
||||
return [
|
||||
{
|
||||
'change .js-select-boards'(evt) {
|
||||
subManager.subscribe('board', $(evt.currentTarget).val(), false);
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'submit .js-search-term-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.term.set(evt.target.searchTerm.value);
|
||||
},
|
||||
'click .js-minicard'(evt) {
|
||||
// 0. Common
|
||||
const title = $('.js-element-title')
|
||||
.val()
|
||||
.trim();
|
||||
if (!title) return;
|
||||
const element = Blaze.getData(evt.currentTarget);
|
||||
element.title = title;
|
||||
let _id = '';
|
||||
if (!this.isTemplateSearch || this.isCardTemplateSearch) {
|
||||
// Card insertion
|
||||
// 1. Common
|
||||
element.sort = Lists.findOne(this.listId)
|
||||
.cards()
|
||||
.count();
|
||||
// 1.A From template
|
||||
if (this.isTemplateSearch) {
|
||||
element.type = 'cardType-card';
|
||||
element.linkedId = '';
|
||||
_id = element.copy(this.boardId, this.swimlaneId, this.listId);
|
||||
// 1.B Linked card
|
||||
} else {
|
||||
delete element._id;
|
||||
element.type = 'cardType-linkedCard';
|
||||
element.linkedId = element.linkedId || element._id;
|
||||
_id = Cards.insert(element);
|
||||
}
|
||||
Filter.addException(_id);
|
||||
// List insertion
|
||||
} else if (this.isListTemplateSearch) {
|
||||
element.sort = Swimlanes.findOne(this.swimlaneId)
|
||||
.lists()
|
||||
.count();
|
||||
element.type = 'list';
|
||||
_id = element.copy(this.boardId, this.swimlaneId);
|
||||
} else if (this.isSwimlaneTemplateSearch) {
|
||||
element.sort = Boards.findOne(this.boardId)
|
||||
.swimlanes()
|
||||
.count();
|
||||
element.type = 'swimlalne';
|
||||
_id = element.copy(this.boardId);
|
||||
} else if (this.isBoardTemplateSearch) {
|
||||
board = Boards.findOne(element.linkedId);
|
||||
board.sort = Boards.find({ archived: false }).count();
|
||||
board.type = 'board';
|
||||
board.title = element.title;
|
||||
delete board.slug;
|
||||
_id = board.copy();
|
||||
}
|
||||
Filter.addException(_id);
|
||||
// List insertion
|
||||
} else if (this.isListTemplateSearch) {
|
||||
element.sort = Swimlanes.findOne(this.swimlaneId).lists().count();
|
||||
element.type = 'list';
|
||||
_id = element.copy(this.boardId, this.swimlaneId);
|
||||
} else if (this.isSwimlaneTemplateSearch) {
|
||||
element.sort = Boards.findOne(this.boardId).swimlanes().count();
|
||||
element.type = 'swimlalne';
|
||||
_id = element.copy(this.boardId);
|
||||
} else if (this.isBoardTemplateSearch) {
|
||||
board = Boards.findOne(element.linkedId);
|
||||
board.sort = Boards.find({archived: false}).count();
|
||||
board.type = 'board';
|
||||
board.title = element.title;
|
||||
delete board.slug;
|
||||
_id = board.copy();
|
||||
}
|
||||
Popup.close();
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('searchElementPopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.cardlimit = this.parentComponent().cardlimit;
|
||||
|
||||
this.listId = this.parentComponent().data()._id;
|
||||
this.swimlaneId = '';
|
||||
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
this.swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
this.spinner = this.find('.sk-spinner-list');
|
||||
this.container = this.$(this.spinner).parents('.js-perfect-scrollbar')[0];
|
||||
|
||||
$(this.container).on(
|
||||
`scroll.spinner_${this.swimlaneId}_${this.listId}`,
|
||||
() => this.updateList(),
|
||||
);
|
||||
$(window).on(`resize.spinner_${this.swimlaneId}_${this.listId}`, () =>
|
||||
this.updateList(),
|
||||
);
|
||||
|
||||
this.updateList();
|
||||
},
|
||||
|
||||
onDestroyed() {
|
||||
$(this.container).off(`scroll.spinner_${this.swimlaneId}_${this.listId}`);
|
||||
$(window).off(`resize.spinner_${this.swimlaneId}_${this.listId}`);
|
||||
},
|
||||
|
||||
updateList() {
|
||||
if (this.spinnerInView()) {
|
||||
this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
|
||||
window.requestIdleCallback(() => this.updateList());
|
||||
}
|
||||
},
|
||||
|
||||
spinnerInView() {
|
||||
const parentViewHeight = this.container.clientHeight;
|
||||
const bottomViewPosition = this.container.scrollTop + parentViewHeight;
|
||||
|
||||
const threshold = this.spinner.offsetTop;
|
||||
|
||||
// spinner deleted
|
||||
if (!this.spinner.offsetTop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return bottomViewPosition > threshold;
|
||||
},
|
||||
}).register('spinnerList');
|
||||
@@ -9,8 +9,9 @@ template(name="listHeader")
|
||||
if currentList
|
||||
a.list-header-left-icon.fa.fa-angle-left.js-unselect-list
|
||||
h2.list-header-name(
|
||||
class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
|
||||
= title
|
||||
class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}js-open-inlined-form is-editable{{/unless}}{{/if}}")
|
||||
+viewer
|
||||
= title
|
||||
if wipLimit.enabled
|
||||
| (
|
||||
span(class="{{#if reachedWipLimit}}highlight{{/if}}") {{cards.count}}
|
||||
@@ -18,7 +19,7 @@ template(name="listHeader")
|
||||
|
||||
if showCardsCountForList cards.count
|
||||
|
|
||||
p.quiet.small {{cardsCount}} {{_ 'cards-count'}}
|
||||
span(class="cardCount") {{cardsCount}} {{_ 'cards-count'}}
|
||||
if isMiniScreen
|
||||
if currentList
|
||||
if isWatching
|
||||
|
||||
@@ -6,12 +6,18 @@ Meteor.startup(() => {
|
||||
BlazeComponent.extendComponent({
|
||||
canSeeAddCard() {
|
||||
const list = Template.currentData();
|
||||
return !list.getWipLimit('enabled') || list.getWipLimit('soft') || !this.reachedWipLimit();
|
||||
return (
|
||||
!list.getWipLimit('enabled') ||
|
||||
list.getWipLimit('soft') ||
|
||||
!this.reachedWipLimit()
|
||||
);
|
||||
},
|
||||
|
||||
editTitle(evt) {
|
||||
evt.preventDefault();
|
||||
const newTitle = this.childComponents('inlinedForm')[0].getValue().trim();
|
||||
editTitle(event) {
|
||||
event.preventDefault();
|
||||
const newTitle = this.childComponents('inlinedForm')[0]
|
||||
.getValue()
|
||||
.trim();
|
||||
const list = this.currentData();
|
||||
if (newTitle) {
|
||||
list.rename(newTitle.trim());
|
||||
@@ -30,16 +36,21 @@ BlazeComponent.extendComponent({
|
||||
cardsCount() {
|
||||
const list = Template.currentData();
|
||||
let swimlaneId = '';
|
||||
const boardView = Meteor.user().profile.boardView;
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id;
|
||||
swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
|
||||
return list.cards(swimlaneId).count();
|
||||
},
|
||||
|
||||
reachedWipLimit() {
|
||||
const list = Template.currentData();
|
||||
return list.getWipLimit('enabled') && list.getWipLimit('value') <= list.cards().count();
|
||||
return (
|
||||
list.getWipLimit('enabled') &&
|
||||
list.getWipLimit('value') <= list.cards().count()
|
||||
);
|
||||
},
|
||||
|
||||
showCardsCountForList(count) {
|
||||
@@ -48,20 +59,24 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-open-list-menu': Popup.open('listAction'),
|
||||
'click .js-add-card' (evt) {
|
||||
const listDom = $(evt.target).parents(`#js-list-${this.currentData()._id}`)[0];
|
||||
const listComponent = BlazeComponent.getComponentForElement(listDom);
|
||||
listComponent.openForm({
|
||||
position: 'top',
|
||||
});
|
||||
return [
|
||||
{
|
||||
'click .js-open-list-menu': Popup.open('listAction'),
|
||||
'click .js-add-card'(event) {
|
||||
const listDom = $(event.target).parents(
|
||||
`#js-list-${this.currentData()._id}`,
|
||||
)[0];
|
||||
const listComponent = BlazeComponent.getComponentForElement(listDom);
|
||||
listComponent.openForm({
|
||||
position: 'top',
|
||||
});
|
||||
},
|
||||
'click .js-unselect-list'() {
|
||||
Session.set('currentList', null);
|
||||
},
|
||||
submit: this.editTitle,
|
||||
},
|
||||
'click .js-unselect-list'() {
|
||||
Session.set('currentList', null);
|
||||
},
|
||||
submit: this.editTitle,
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('listHeader');
|
||||
|
||||
@@ -76,22 +91,22 @@ Template.listActionPopup.helpers({
|
||||
});
|
||||
|
||||
Template.listActionPopup.events({
|
||||
'click .js-list-subscribe' () {},
|
||||
'click .js-list-subscribe'() {},
|
||||
'click .js-set-color-list': Popup.open('setListColor'),
|
||||
'click .js-select-cards' () {
|
||||
const cardIds = this.allCards().map((card) => card._id);
|
||||
'click .js-select-cards'() {
|
||||
const cardIds = this.allCards().map(card => card._id);
|
||||
MultiSelection.add(cardIds);
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-toggle-watch-list' () {
|
||||
'click .js-toggle-watch-list'() {
|
||||
const currentList = this;
|
||||
const level = currentList.findWatcher(Meteor.userId()) ? null : 'watching';
|
||||
Meteor.call('watch', 'list', currentList._id, level, (err, ret) => {
|
||||
if (!err && ret) Popup.close();
|
||||
});
|
||||
},
|
||||
'click .js-close-list' (evt) {
|
||||
evt.preventDefault();
|
||||
'click .js-close-list'(event) {
|
||||
event.preventDefault();
|
||||
this.archive();
|
||||
Popup.close();
|
||||
},
|
||||
@@ -102,10 +117,17 @@ Template.listActionPopup.events({
|
||||
BlazeComponent.extendComponent({
|
||||
applyWipLimit() {
|
||||
const list = Template.currentData();
|
||||
const limit = parseInt(Template.instance().$('.wip-limit-value').val(), 10);
|
||||
const limit = parseInt(
|
||||
Template.instance()
|
||||
.$('.wip-limit-value')
|
||||
.val(),
|
||||
10,
|
||||
);
|
||||
|
||||
if(limit < list.cards().count() && !list.getWipLimit('soft')){
|
||||
Template.instance().$('.wip-limit-error').click();
|
||||
if (limit < list.cards().count() && !list.getWipLimit('soft')) {
|
||||
Template.instance()
|
||||
.$('.wip-limit-error')
|
||||
.click();
|
||||
} else {
|
||||
Meteor.call('applyWipLimit', list._id, limit);
|
||||
Popup.back();
|
||||
@@ -115,7 +137,10 @@ BlazeComponent.extendComponent({
|
||||
enableSoftLimit() {
|
||||
const list = Template.currentData();
|
||||
|
||||
if(list.getWipLimit('soft') && list.getWipLimit('value') < list.cards().count()){
|
||||
if (
|
||||
list.getWipLimit('soft') &&
|
||||
list.getWipLimit('value') < list.cards().count()
|
||||
) {
|
||||
list.setWipLimit(list.cards().count());
|
||||
}
|
||||
Meteor.call('enableSoftLimit', Template.currentData()._id);
|
||||
@@ -124,7 +149,10 @@ BlazeComponent.extendComponent({
|
||||
enableWipLimit() {
|
||||
const list = Template.currentData();
|
||||
// Prevent user from using previously stored wipLimit.value if it is less than the current number of cards in the list
|
||||
if(!list.getWipLimit('enabled') && list.getWipLimit('value') < list.cards().count()){
|
||||
if (
|
||||
!list.getWipLimit('enabled') &&
|
||||
list.getWipLimit('value') < list.cards().count()
|
||||
) {
|
||||
list.setWipLimit(list.cards().count());
|
||||
}
|
||||
Meteor.call('enableWipLimit', list._id);
|
||||
@@ -138,24 +166,26 @@ BlazeComponent.extendComponent({
|
||||
return Template.currentData().getWipLimit('enabled');
|
||||
},
|
||||
|
||||
wipLimitValue(){
|
||||
wipLimitValue() {
|
||||
return Template.currentData().getWipLimit('value');
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-enable-wip-limit': this.enableWipLimit,
|
||||
'click .wip-limit-apply': this.applyWipLimit,
|
||||
'click .wip-limit-error': Popup.open('wipLimitError'),
|
||||
'click .materialCheckBox': this.enableSoftLimit,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click .js-enable-wip-limit': this.enableWipLimit,
|
||||
'click .wip-limit-apply': this.applyWipLimit,
|
||||
'click .wip-limit-error': Popup.open('wipLimitError'),
|
||||
'click .materialCheckBox': this.enableSoftLimit,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('setWipLimitPopup');
|
||||
|
||||
Template.listMorePopup.events({
|
||||
'click .js-delete': Popup.afterConfirm('listDelete', function () {
|
||||
'click .js-delete': Popup.afterConfirm('listDelete', function() {
|
||||
Popup.close();
|
||||
this.allCards().map((card) => Cards.remove(card._id));
|
||||
this.allCards().map(card => Cards.remove(card._id));
|
||||
Lists.remove(this._id);
|
||||
Utils.goBoardId(this.boardId);
|
||||
}),
|
||||
@@ -168,7 +198,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
colors() {
|
||||
return listsColors.map((color) => ({ color, name: '' }));
|
||||
return listsColors.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
@@ -176,18 +206,20 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
return [
|
||||
{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
},
|
||||
'click .js-submit'() {
|
||||
this.currentList.setColor(this.currentColor.get());
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-remove-color'() {
|
||||
this.currentList.setColor(null);
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
'click .js-submit' () {
|
||||
this.currentList.setColor(this.currentColor.get());
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-remove-color'() {
|
||||
this.currentList.setColor(null);
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('setListColorPopup');
|
||||
@@ -1,5 +1,6 @@
|
||||
template(name="editor")
|
||||
textarea.editor(
|
||||
dir="auto"
|
||||
class="{{class}}"
|
||||
id=id
|
||||
autofocus=autofocus
|
||||
@@ -7,7 +8,7 @@ template(name="editor")
|
||||
+Template.contentBlock
|
||||
|
||||
template(name="viewer")
|
||||
.viewer
|
||||
.viewer(dir="auto")
|
||||
+mentions
|
||||
+markdown
|
||||
{{> UI.contentBlock }}
|
||||
@@ -1,18 +1,22 @@
|
||||
Template.editor.onRendered(() => {
|
||||
const $textarea = this.$('textarea');
|
||||
|
||||
autosize($textarea);
|
||||
|
||||
$textarea.escapeableTextComplete([
|
||||
const textareaSelector = 'textarea';
|
||||
const enableRicherEditor =
|
||||
Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR || true;
|
||||
const mentions = [
|
||||
// User mentions
|
||||
{
|
||||
match: /\B@([\w.]*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback(currentBoard.activeMembers().map((member) => {
|
||||
const username = Users.findOne(member.userId).username;
|
||||
return username.includes(term) ? username : null;
|
||||
}).filter(Boolean));
|
||||
callback(
|
||||
currentBoard
|
||||
.activeMembers()
|
||||
.map(member => {
|
||||
const username = Users.findOne(member.userId).username;
|
||||
return username.includes(term) ? username : null;
|
||||
})
|
||||
.filter(Boolean),
|
||||
);
|
||||
},
|
||||
template(value) {
|
||||
return value;
|
||||
@@ -22,7 +26,163 @@ Template.editor.onRendered(() => {
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
]);
|
||||
];
|
||||
const enableTextarea = function() {
|
||||
const $textarea = this.$(textareaSelector);
|
||||
autosize($textarea);
|
||||
$textarea.escapeableTextComplete(mentions);
|
||||
};
|
||||
if (enableRicherEditor) {
|
||||
const isSmall = Utils.isMiniScreen();
|
||||
const toolbar = isSmall
|
||||
? [
|
||||
['view', ['fullscreen']],
|
||||
['table', ['table']],
|
||||
['font', ['bold', 'underline']],
|
||||
//['fontsize', ['fontsize']],
|
||||
['color', ['color']],
|
||||
]
|
||||
: [
|
||||
['style', ['style']],
|
||||
['font', ['bold', 'underline', 'clear']],
|
||||
['fontsize', ['fontsize']],
|
||||
['fontname', ['fontname']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
//['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
//['insert', ['link', 'picture']], // modal popup has issue somehow :(
|
||||
['view', ['fullscreen', 'help']],
|
||||
];
|
||||
const cleanPastedHTML = function(input) {
|
||||
const badTags = [
|
||||
'style',
|
||||
'script',
|
||||
'applet',
|
||||
'embed',
|
||||
'noframes',
|
||||
'noscript',
|
||||
'meta',
|
||||
'link',
|
||||
'button',
|
||||
'form',
|
||||
].join('|');
|
||||
const badPatterns = new RegExp(
|
||||
`(?:${[
|
||||
`<(${badTags})s*[^>][\\s\\S]*?<\\/\\1>`,
|
||||
`<(${badTags})[^>]*?\\/>`,
|
||||
].join('|')})`,
|
||||
'gi',
|
||||
);
|
||||
let output = input;
|
||||
// remove bad Tags
|
||||
output = output.replace(badPatterns, '');
|
||||
// remove attributes ' style="..."'
|
||||
const badAttributes = new RegExp(
|
||||
`(?:${[
|
||||
'on\\S+=([\'"]?).*?\\1',
|
||||
'href=([\'"]?)javascript:.*?\\2',
|
||||
'style=([\'"]?).*?\\3',
|
||||
'target=\\S+',
|
||||
].join('|')})`,
|
||||
'gi',
|
||||
);
|
||||
output = output.replace(badAttributes, '');
|
||||
output = output.replace(/(<a )/gi, '$1target=_ '); // always to new target
|
||||
return output;
|
||||
};
|
||||
const editor = '.editor';
|
||||
const selectors = [
|
||||
`.js-new-comment-form ${editor}`,
|
||||
`.js-edit-comment ${editor}`,
|
||||
].join(','); // only new comment and edit comment
|
||||
const inputs = $(selectors);
|
||||
if (inputs.length === 0) {
|
||||
// only enable richereditor to new comment or edit comment no others
|
||||
enableTextarea();
|
||||
} else {
|
||||
const placeholder = inputs.attr('placeholder') || '';
|
||||
const mSummernotes = [];
|
||||
const getSummernote = function(input) {
|
||||
const idx = inputs.index(input);
|
||||
if (idx > -1) {
|
||||
return mSummernotes[idx];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
inputs.each(function(idx, input) {
|
||||
mSummernotes[idx] = $(input).summernote({
|
||||
placeholder,
|
||||
callbacks: {
|
||||
onInit(object) {
|
||||
const originalInput = this;
|
||||
$(originalInput).on('input', function() {
|
||||
// when comment is submitted, the original textarea will be set to '', so shall we
|
||||
if (!this.value) {
|
||||
const sn = getSummernote(this);
|
||||
sn && sn.summernote('reset');
|
||||
object && object.editingArea.find('.note-placeholder').show();
|
||||
}
|
||||
});
|
||||
const jEditor = object && object.editable;
|
||||
const toolbar = object && object.toolbar;
|
||||
if (jEditor !== undefined) {
|
||||
jEditor.escapeableTextComplete(mentions);
|
||||
}
|
||||
if (toolbar !== undefined) {
|
||||
const fBtn = toolbar.find('.btn-fullscreen');
|
||||
fBtn.on('click', function() {
|
||||
const $this = $(this),
|
||||
isActive = $this.hasClass('active');
|
||||
$('.minicards').toggle(!isActive); // mini card is still showing when editor is in fullscreen mode, we hide here manually
|
||||
});
|
||||
}
|
||||
},
|
||||
onPaste() {
|
||||
// clear up unwanted tag info when user pasted in text
|
||||
const thisNote = this;
|
||||
const updatePastedText = function(object) {
|
||||
const someNote = getSummernote(object);
|
||||
const original = someNote.summernote('code');
|
||||
const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
|
||||
someNote.summernote('reset'); //clear original
|
||||
someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code.
|
||||
};
|
||||
setTimeout(function() {
|
||||
//this kinda sucks, but if you don't do a setTimeout,
|
||||
//the function is called before the text is really pasted.
|
||||
updatePastedText(thisNote);
|
||||
}, 10);
|
||||
},
|
||||
},
|
||||
dialogsInBody: true,
|
||||
disableDragAndDrop: true,
|
||||
toolbar,
|
||||
popover: {
|
||||
image: [
|
||||
[
|
||||
'image',
|
||||
['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone'],
|
||||
],
|
||||
['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||
['remove', ['removeMedia']],
|
||||
],
|
||||
table: [
|
||||
['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
|
||||
['delete', ['deleteRow', 'deleteCol', 'deleteTable']],
|
||||
],
|
||||
air: [
|
||||
['color', ['color']],
|
||||
['font', ['bold', 'underline', 'clear']],
|
||||
],
|
||||
},
|
||||
height: 200,
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
enableTextarea();
|
||||
}
|
||||
});
|
||||
|
||||
import sanitizeXss from 'xss';
|
||||
@@ -33,69 +193,73 @@ import sanitizeXss from 'xss';
|
||||
// compiled version to most users -- who don't need to edit.
|
||||
// In the meantime, all the transformation are done on the client using the
|
||||
// Blaze API.
|
||||
const at = HTML.CharRef({html: '@', str: '@'});
|
||||
Blaze.Template.registerHelper('mentions', new Template('mentions', function() {
|
||||
const view = this;
|
||||
let content = Blaze.toHTML(view.templateContentBlock);
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (!currentBoard)
|
||||
const at = HTML.CharRef({ html: '@', str: '@' });
|
||||
Blaze.Template.registerHelper(
|
||||
'mentions',
|
||||
new Template('mentions', function() {
|
||||
const view = this;
|
||||
let content = Blaze.toHTML(view.templateContentBlock);
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (!currentBoard) return HTML.Raw(sanitizeXss(content));
|
||||
const knowedUsers = currentBoard.members.map(member => {
|
||||
const u = Users.findOne(member.userId);
|
||||
if (u) {
|
||||
member.username = u.username;
|
||||
}
|
||||
return member;
|
||||
});
|
||||
const mentionRegex = /\B@([\w.]*)/gi;
|
||||
|
||||
let currentMention;
|
||||
while ((currentMention = mentionRegex.exec(content)) !== null) {
|
||||
const [fullMention, username] = currentMention;
|
||||
const knowedUser = _.findWhere(knowedUsers, { username });
|
||||
if (!knowedUser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const linkValue = [' ', at, knowedUser.username];
|
||||
let linkClass = 'atMention js-open-member';
|
||||
if (knowedUser.userId === Meteor.userId()) {
|
||||
linkClass += ' me';
|
||||
}
|
||||
const link = HTML.A(
|
||||
{
|
||||
class: linkClass,
|
||||
// XXX Hack. Since we stringify this render function result below with
|
||||
// `Blaze.toHTML` we can't rely on blaze data contexts to pass the
|
||||
// `userId` to the popup as usual, and we need to store it in the DOM
|
||||
// using a data attribute.
|
||||
'data-userId': knowedUser.userId,
|
||||
},
|
||||
linkValue,
|
||||
);
|
||||
|
||||
content = content.replace(fullMention, Blaze.toHTML(link));
|
||||
}
|
||||
|
||||
return HTML.Raw(sanitizeXss(content));
|
||||
const knowedUsers = currentBoard.members.map((member) => {
|
||||
const u = Users.findOne(member.userId);
|
||||
if(u){
|
||||
member.username = u.username;
|
||||
}
|
||||
return member;
|
||||
});
|
||||
const mentionRegex = /\B@([\w.]*)/gi;
|
||||
|
||||
let currentMention;
|
||||
while ((currentMention = mentionRegex.exec(content)) !== null) {
|
||||
const [fullMention, username] = currentMention;
|
||||
const knowedUser = _.findWhere(knowedUsers, { username });
|
||||
if (!knowedUser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const linkValue = [' ', at, knowedUser.username];
|
||||
let linkClass = 'atMention js-open-member';
|
||||
if (knowedUser.userId === Meteor.userId()) {
|
||||
linkClass += ' me';
|
||||
}
|
||||
const link = HTML.A({
|
||||
'class': linkClass,
|
||||
// XXX Hack. Since we stringify this render function result below with
|
||||
// `Blaze.toHTML` we can't rely on blaze data contexts to pass the
|
||||
// `userId` to the popup as usual, and we need to store it in the DOM
|
||||
// using a data attribute.
|
||||
'data-userId': knowedUser.userId,
|
||||
}, linkValue);
|
||||
|
||||
content = content.replace(fullMention, Blaze.toHTML(link));
|
||||
}
|
||||
|
||||
return HTML.Raw(sanitizeXss(content));
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
Template.viewer.events({
|
||||
// Viewer sometimes have click-able wrapper around them (for instance to edit
|
||||
// the corresponding text). Clicking a link shouldn't fire these actions, stop
|
||||
// we stop these event at the viewer component level.
|
||||
'click a'(evt, tpl) {
|
||||
evt.stopPropagation();
|
||||
'click a'(event, templateInstance) {
|
||||
event.stopPropagation();
|
||||
|
||||
// XXX We hijack the build-in browser action because we currently don't have
|
||||
// `_blank` attributes in viewer links, and the transformer function is
|
||||
// handled by a third party package that we can't configure easily. Fix that
|
||||
// by using directly `_blank` attribute in the rendered HTML.
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
|
||||
const userId = evt.currentTarget.dataset.userid;
|
||||
const userId = event.currentTarget.dataset.userid;
|
||||
if (userId) {
|
||||
Popup.open('member').call({ userId }, evt, tpl);
|
||||
}
|
||||
else {
|
||||
const href = evt.currentTarget.href;
|
||||
Popup.open('member').call({ userId }, event, templateInstance);
|
||||
} else {
|
||||
const href = event.currentTarget.href;
|
||||
if (href) {
|
||||
window.open(href, '_blank');
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ Template.header.helpers({
|
||||
},
|
||||
|
||||
hasAnnouncement() {
|
||||
const announcements = Announcements.findOne();
|
||||
const announcements = Announcements.findOne();
|
||||
return announcements && announcements.enabled;
|
||||
},
|
||||
|
||||
announcement() {
|
||||
$('.announcement').show();
|
||||
const announcements = Announcements.findOne();
|
||||
const announcements = Announcements.findOne();
|
||||
return announcements && announcements.body;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,37 +1,32 @@
|
||||
head
|
||||
title
|
||||
meta(name="viewport"
|
||||
content="maximum-scale=1.0,width=device-width,initial-scale=1.0,user-scalable=0")
|
||||
meta(name="viewport" content="maximum-scale=1.0,width=device-width,initial-scale=1.0,user-scalable=0")
|
||||
meta(http-equiv="X-UA-Compatible" content="IE=edge")
|
||||
//- XXX We should use pathFor in the following `href` to support the case
|
||||
where the application is deployed with a path prefix, but it seems to be
|
||||
difficult to do that cleanly with Blaze -- at least without adding extra
|
||||
packages.
|
||||
link(rel="shortcut icon" href="/favicon.png")
|
||||
link(rel="apple-touch-icon" href="/favicon.png")
|
||||
link(rel="mask-icon" href="/logo-150.svg")
|
||||
link(rel="manifest" href="/manifest.json")
|
||||
link(rel="shortcut icon" href="/wekan-favicon.png")
|
||||
link(rel="apple-touch-icon" href="/wekan-favicon.png")
|
||||
link(rel="mask-icon" href="/wekan-logo-150.svg")
|
||||
link(rel="manifest" href="/wekan-manifest.json")
|
||||
|
||||
template(name="userFormsLayout")
|
||||
section.auth-layout
|
||||
//if currentSetting.hideLogo
|
||||
h1
|
||||
br
|
||||
br
|
||||
//else
|
||||
// h1.at-form-landing-logo
|
||||
// img(src="{{pathFor '/logo.png'}}" alt="")
|
||||
section.auth-dialog
|
||||
+Template.dynamic(template=content)
|
||||
if currentSetting.displayAuthenticationMethod
|
||||
+connectionMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
|
||||
div.at-form-lang
|
||||
select.select-lang.js-userform-set-language
|
||||
each languages
|
||||
if isCurrentLanguage
|
||||
option(value="{{tag}}" selected="selected") {{name}}
|
||||
else
|
||||
option(value="{{tag}}") {{name}}
|
||||
if isLoading
|
||||
+loader
|
||||
else
|
||||
+Template.dynamic(template=content)
|
||||
if currentSetting.displayAuthenticationMethod
|
||||
+connectionMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
|
||||
div.at-form-lang
|
||||
select.select-lang.js-userform-set-language
|
||||
each languages
|
||||
if isCurrentLanguage
|
||||
option(value="{{tag}}" selected="selected") {{name}}
|
||||
else
|
||||
option(value="{{tag}}") {{name}}
|
||||
|
||||
template(name="defaultLayout")
|
||||
+header
|
||||
@@ -64,3 +59,14 @@ template(name="message")
|
||||
unless currentUser
|
||||
with(pathFor route='atSignIn')
|
||||
p {{{_ 'page-maybe-private' this}}}
|
||||
|
||||
template(name="loader")
|
||||
h1.loadingText {{_ 'loading'}}
|
||||
.lds-roller
|
||||
div
|
||||
div
|
||||
div
|
||||
div
|
||||
div
|
||||
div
|
||||
div
|
||||
@@ -1,6 +1,6 @@
|
||||
BlazeLayout.setRoot('body');
|
||||
|
||||
const i18nTagToT9n = (i18nTag) => {
|
||||
const i18nTagToT9n = i18nTag => {
|
||||
// t9n/i18n tags are same now, see: https://github.com/softwarerero/meteor-accounts-t9n/pull/129
|
||||
// but we keep this conversion function here, to be aware that that they are different system.
|
||||
return i18nTag;
|
||||
@@ -21,19 +21,23 @@ const validator = {
|
||||
};
|
||||
|
||||
Template.userFormsLayout.onCreated(function() {
|
||||
const instance = this;
|
||||
instance.currentSetting = new ReactiveVar();
|
||||
const templateInstance = this;
|
||||
templateInstance.currentSetting = new ReactiveVar();
|
||||
templateInstance.isLoading = new ReactiveVar(false);
|
||||
|
||||
Meteor.subscribe('setting', {
|
||||
onReady() {
|
||||
instance.currentSetting.set(Settings.findOne());
|
||||
templateInstance.currentSetting.set(Settings.findOne());
|
||||
return this.stop();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Template.userFormsLayout.onRendered(() => {
|
||||
AccountsTemplates.state.form.keys = new Proxy(AccountsTemplates.state.form.keys, validator);
|
||||
AccountsTemplates.state.form.keys = new Proxy(
|
||||
AccountsTemplates.state.form.keys,
|
||||
validator,
|
||||
);
|
||||
|
||||
const i18nTag = navigator.language;
|
||||
if (i18nTag) {
|
||||
@@ -47,6 +51,10 @@ Template.userFormsLayout.helpers({
|
||||
return Template.instance().currentSetting.get();
|
||||
},
|
||||
|
||||
isLoading() {
|
||||
return Template.instance().isLoading.get();
|
||||
},
|
||||
|
||||
afterBodyStart() {
|
||||
return currentSetting.customHTMLafterBodyStart;
|
||||
},
|
||||
@@ -63,6 +71,8 @@ Template.userFormsLayout.helpers({
|
||||
name = 'Brezhoneg';
|
||||
} else if (lang.name === 'ig') {
|
||||
name = 'Igbo';
|
||||
} else if (lang.name === 'oc') {
|
||||
name = 'Occitan';
|
||||
}
|
||||
return { tag, name };
|
||||
}).sort(function(a, b) {
|
||||
@@ -82,14 +92,17 @@ Template.userFormsLayout.helpers({
|
||||
});
|
||||
|
||||
Template.userFormsLayout.events({
|
||||
'change .js-userform-set-language'(evt) {
|
||||
const i18nTag = $(evt.currentTarget).val();
|
||||
'change .js-userform-set-language'(event) {
|
||||
const i18nTag = $(event.currentTarget).val();
|
||||
T9n.setLanguage(i18nTagToT9n(i18nTag));
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click #at-btn'(event, instance) {
|
||||
'click #at-btn'(event, templateInstance) {
|
||||
if (FlowRouter.getRouteName() === 'atSignIn') {
|
||||
authentication(event, instance);
|
||||
templateInstance.isLoading.set(true);
|
||||
authentication(event, templateInstance).then(() => {
|
||||
templateInstance.isLoading.set(false);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -100,39 +113,47 @@ Template.defaultLayout.events({
|
||||
},
|
||||
});
|
||||
|
||||
async function authentication(event, instance) {
|
||||
async function authentication(event, templateInstance) {
|
||||
const match = $('#at-field-username_and_email').val();
|
||||
const password = $('#at-field-password').val();
|
||||
|
||||
if (!match || !password) return;
|
||||
if (!match || !password) return undefined;
|
||||
|
||||
const result = await getAuthenticationMethod(instance.currentSetting.get(), match);
|
||||
const result = await getAuthenticationMethod(
|
||||
templateInstance.currentSetting.get(),
|
||||
match,
|
||||
);
|
||||
|
||||
if (result === 'password') return;
|
||||
if (result === 'password') return undefined;
|
||||
|
||||
// Stop submit #at-pwd-form
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
switch (result) {
|
||||
case 'ldap':
|
||||
Meteor.loginWithLDAP(match, password, function() {
|
||||
FlowRouter.go('/');
|
||||
});
|
||||
break;
|
||||
case 'ldap':
|
||||
return new Promise(resolve => {
|
||||
Meteor.loginWithLDAP(match, password, function() {
|
||||
resolve(FlowRouter.go('/'));
|
||||
});
|
||||
});
|
||||
|
||||
case 'cas':
|
||||
Meteor.loginWithCas(function() {
|
||||
FlowRouter.go('/');
|
||||
});
|
||||
break;
|
||||
case 'cas':
|
||||
return new Promise(resolve => {
|
||||
Meteor.loginWithCas(match, password, function() {
|
||||
resolve(FlowRouter.go('/'));
|
||||
});
|
||||
});
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getAuthenticationMethod({displayAuthenticationMethod, defaultAuthenticationMethod}, match) {
|
||||
function getAuthenticationMethod(
|
||||
{ displayAuthenticationMethod, defaultAuthenticationMethod },
|
||||
match,
|
||||
) {
|
||||
if (displayAuthenticationMethod) {
|
||||
return $('.select-authentication').val();
|
||||
}
|
||||
@@ -140,7 +161,7 @@ function getAuthenticationMethod({displayAuthenticationMethod, defaultAuthentica
|
||||
}
|
||||
|
||||
function getUserAuthenticationMethod(defaultAuthenticationMethod, match) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
try {
|
||||
Meteor.subscribe('user-authenticationMethod', match, {
|
||||
onReady() {
|
||||
@@ -153,7 +174,7 @@ function getUserAuthenticationMethod(defaultAuthenticationMethod, match) {
|
||||
resolve(authenticationMethod);
|
||||
},
|
||||
});
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
resolve(defaultAuthenticationMethod);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,6 +2,35 @@
|
||||
|
||||
global-reset()
|
||||
|
||||
*
|
||||
-webkit-box-sizing: unset
|
||||
box-sizing: unset
|
||||
|
||||
.note-popover .popover-content .note-color-palette div .note-color-btn, .panel-heading.note-toolbar .note-color-palette div .note-color-btn
|
||||
background: none
|
||||
|
||||
a:focus
|
||||
outline: unset
|
||||
outline-offset: unset
|
||||
|
||||
a:hover,a:focus
|
||||
color: unset
|
||||
text-decoration: unset
|
||||
|
||||
.badge
|
||||
display: unset
|
||||
min-width: unset
|
||||
padding: unset
|
||||
font-size: unset
|
||||
font-weight: unset
|
||||
line-height: unset
|
||||
color: unset
|
||||
text-align: unset
|
||||
white-space: unset
|
||||
vertical-align: unset
|
||||
background-color: unset
|
||||
border-radius: unset
|
||||
|
||||
html, body, input, select, textarea, button
|
||||
font: 14px Roboto, "Helvetica Neue", Arial, Helvetica, sans-serif
|
||||
line-height: 18px
|
||||
@@ -61,7 +90,7 @@ body
|
||||
display: block
|
||||
float: right
|
||||
font-size: 24px
|
||||
|
||||
|
||||
.modal-content-wide
|
||||
width: 800px
|
||||
min-height: 0px
|
||||
@@ -290,7 +319,7 @@ kbd
|
||||
// Implement a thiner close icon as suggested in
|
||||
// https://github.com/FortAwesome/Font-Awesome/issues/1540#issuecomment-68689950
|
||||
.fa.fa-times-thin:before
|
||||
content: '\00d7';
|
||||
content: '\00d7'
|
||||
|
||||
.fa.fa-globe.colorful, .fa.fa-bell.colorful
|
||||
color: #4caf50
|
||||
@@ -399,3 +428,103 @@ a
|
||||
height: 37px
|
||||
margin: 8px 10px 0 0
|
||||
width: 50px
|
||||
|
||||
.select-authentication
|
||||
width: 100%
|
||||
|
||||
.auth-layout
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
height: 100%
|
||||
|
||||
.auth-dialog
|
||||
margin: 0 !important
|
||||
|
||||
.loadingText
|
||||
text-align: center
|
||||
|
||||
.lds-roller
|
||||
display: block
|
||||
margin: auto
|
||||
position: relative
|
||||
width: 64px
|
||||
height: 64px
|
||||
|
||||
div
|
||||
animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite
|
||||
transform-origin: 32px 32px
|
||||
|
||||
div:after
|
||||
content: " "
|
||||
display: block
|
||||
position: absolute
|
||||
width: 6px
|
||||
height: 6px
|
||||
border-radius: 50%
|
||||
background: #dedede
|
||||
margin: -3px 0 0 -3px
|
||||
|
||||
div:nth-child(1)
|
||||
animation-delay: -0.036s
|
||||
|
||||
div:nth-child(1):after
|
||||
top: 50px
|
||||
left: 50px
|
||||
|
||||
div:nth-child(2)
|
||||
animation-delay: -0.072s
|
||||
|
||||
div:nth-child(2):after
|
||||
top: 54px
|
||||
left: 45px
|
||||
|
||||
div:nth-child(3)
|
||||
animation-delay: -0.108s
|
||||
|
||||
div:nth-child(3):after
|
||||
top: 57px
|
||||
left: 39px
|
||||
|
||||
div:nth-child(4)
|
||||
animation-delay: -0.144s
|
||||
|
||||
div:nth-child(4):after
|
||||
top: 58px
|
||||
left: 32px
|
||||
|
||||
div:nth-child(5)
|
||||
animation-delay: -0.18s
|
||||
|
||||
div:nth-child(5):after
|
||||
top: 57px
|
||||
left: 25px
|
||||
|
||||
div:nth-child(6)
|
||||
animation-delay: -0.216s
|
||||
|
||||
div:nth-child(6):after
|
||||
top: 54px
|
||||
left: 19px
|
||||
|
||||
div:nth-child(7)
|
||||
animation-delay: -0.252s
|
||||
|
||||
div:nth-child(7):after
|
||||
top: 50px
|
||||
left: 14px
|
||||
|
||||
div:nth-child(8)
|
||||
animation-delay: -0.288s
|
||||
|
||||
div:nth-child(8):after
|
||||
top: 45px
|
||||
left: 10px
|
||||
|
||||
@keyframes lds-roller
|
||||
0%
|
||||
transform: rotate(0deg)
|
||||
|
||||
100%
|
||||
transform: rotate(360deg)
|
||||
@@ -18,15 +18,17 @@ Mixins.InfiniteScrolling = BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
scroll(evt) {
|
||||
const domElement = evt.currentTarget;
|
||||
let altitude = domElement.scrollTop + domElement.offsetHeight;
|
||||
altitude += peakAnticipation;
|
||||
if (altitude >= this.callFirstWith(null, 'getNextPeak')) {
|
||||
this.mixinParent().callFirstWith(null, 'reachNextPeak');
|
||||
}
|
||||
return [
|
||||
{
|
||||
scroll(evt) {
|
||||
const domElement = evt.currentTarget;
|
||||
let altitude = domElement.scrollTop + domElement.offsetHeight;
|
||||
altitude += peakAnticipation;
|
||||
if (altitude >= this.callFirstWith(null, 'getNextPeak')) {
|
||||
this.mixinParent().callFirstWith(null, 'reachNextPeak');
|
||||
}
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
});
|
||||
@@ -1,168 +1,165 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
|
||||
},
|
||||
onCreated() {},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-create-card-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const cardName = this.find('#card-name').value;
|
||||
const listName = this.find('#list-name').value;
|
||||
const swimlaneName = this.find('#swimlane-name2').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'createCard',
|
||||
swimlaneName,
|
||||
cardName,
|
||||
listName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
'click .js-create-card-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const cardName = this.find('#card-name').value;
|
||||
const listName = this.find('#list-name').value;
|
||||
const swimlaneName = this.find('#swimlane-name2').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'createCard',
|
||||
swimlaneName,
|
||||
cardName,
|
||||
listName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
'click .js-add-swimlane-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const swimlaneName = this.find('#swimlane-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addSwimlane',
|
||||
swimlaneName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
'click .js-add-spec-move-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#move-spec-action').value;
|
||||
const listTitle = this.find('#listName').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'top') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToTop',
|
||||
listTitle,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'bottom') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToBottom',
|
||||
listTitle,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-move-action'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#move-gen-action').value;
|
||||
if (actionSelected === 'top') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToTop',
|
||||
listTitle: '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'bottom') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToBottom',
|
||||
listTitle: '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-arch-action'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#arch-action').value;
|
||||
if (actionSelected === 'archive') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'archive',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unarchive') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'unarchive',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'click .js-add-swimlane-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const swimlaneName = this.find('#swimlane-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addSwimlane',
|
||||
swimlaneName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
|
||||
},
|
||||
'click .js-add-spec-move-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#move-spec-action').value;
|
||||
const listTitle = this.find('#listName').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'top') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToTop',
|
||||
listTitle,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'bottom') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToBottom',
|
||||
listTitle,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-move-action' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#move-gen-action').value;
|
||||
if (actionSelected === 'top') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToTop',
|
||||
'listTitle': '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'bottom') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToBottom',
|
||||
'listTitle': '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-arch-action' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#arch-action').value;
|
||||
if (actionSelected === 'archive') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'archive',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unarchive') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'unarchive',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
|
||||
}).register('boardActions');
|
||||
/* eslint-no-undef */
|
||||
@@ -1,11 +1,44 @@
|
||||
template(name="cardActions")
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-dropdown
|
||||
select(id="setdate-action")
|
||||
option(value="setDate") {{{_'r-set'}}}
|
||||
option(value="updateDate") {{{_'r-update'}}}
|
||||
div.trigger-text
|
||||
| {{{_'r-datefield'}}}
|
||||
div.trigger-dropdown
|
||||
select(id="setdate-datefield")
|
||||
option(value="startAt") {{{_'r-df-start-at'}}}
|
||||
option(value="dueAt") {{{_'r-df-due-at'}}}
|
||||
option(value="endAt") {{{_'r-df-end-at'}}}
|
||||
option(value="receivedAt") {{{_'r-df-received-at'}}}
|
||||
div.trigger-text
|
||||
| {{{_'r-to-current-datetime'}}}
|
||||
div.trigger-button.js-set-date-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{{_'r-remove-value-from'}}}
|
||||
| {{{_'r-datefield'}}}
|
||||
div.trigger-dropdown
|
||||
select(id="setdate-removedatefieldvalue")
|
||||
option(value="startAt") {{{_'r-df-start-at'}}}
|
||||
option(value="dueAt") {{{_'r-df-due-at'}}}
|
||||
option(value="endAt") {{{_'r-df-end-at'}}}
|
||||
option(value="receivedAt") {{{_'r-df-received-at'}}}
|
||||
div.trigger-button.js-remove-datevalue-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-dropdown
|
||||
select(id="label-action")
|
||||
option(value="add") {{{_'r-add'}}}
|
||||
option(value="remove") {{{_'r-remove'}}}
|
||||
div.trigger-text
|
||||
div.trigger-text
|
||||
| {{{_'r-label'}}}
|
||||
div.trigger-dropdown
|
||||
select(id="label-id")
|
||||
@@ -21,16 +54,16 @@ template(name="cardActions")
|
||||
select(id="member-action")
|
||||
option(value="add") {{{_'r-add'}}}
|
||||
option(value="remove") {{{_'r-remove'}}}
|
||||
div.trigger-text
|
||||
div.trigger-text
|
||||
| {{{_'r-member'}}}
|
||||
div.trigger-dropdown
|
||||
input(id="member-name",type=text,placeholder="{{{_'r-name'}}}")
|
||||
input(id="member-name",type=text,placeholder="{{{_'r-name'}}}")
|
||||
div.trigger-button.js-add-member-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
div.trigger-text
|
||||
| {{{_'r-remove-all'}}}
|
||||
div.trigger-button.js-add-removeall-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
@@ -14,7 +14,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
cardColorButtonText() {
|
||||
return `color-${ this.cardColorButtonValue.get() }`;
|
||||
return `color-${this.cardColorButtonValue.get()}`;
|
||||
},
|
||||
|
||||
labels() {
|
||||
@@ -28,61 +28,24 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-label-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#label-action').value;
|
||||
const labelId = this.find('#label-id').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'add') {
|
||||
return [
|
||||
{
|
||||
'click .js-set-date-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addLabel',
|
||||
labelId,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'remove') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeLabel',
|
||||
labelId,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
const actionSelected = this.find('#setdate-action').value;
|
||||
const dateFieldSelected = this.find('#setdate-datefield').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
|
||||
},
|
||||
'click .js-add-member-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#member-action').value;
|
||||
const username = this.find('#member-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'add') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addMember',
|
||||
username,
|
||||
actionType: actionSelected,
|
||||
dateField: dateFieldSelected,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
@@ -90,12 +53,118 @@ BlazeComponent.extendComponent({
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'remove') {
|
||||
},
|
||||
|
||||
'click .js-remove-datevalue-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const dateFieldSelected = this.find('#setdate-removedatefieldvalue')
|
||||
.value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeDate',
|
||||
dateField: dateFieldSelected,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
},
|
||||
'click .js-add-label-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#label-action').value;
|
||||
const labelId = this.find('#label-id').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'add') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addLabel',
|
||||
labelId,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'remove') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeLabel',
|
||||
labelId,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-member-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#member-action').value;
|
||||
const username = this.find('#member-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'add') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addMember',
|
||||
username,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'remove') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeMember',
|
||||
username,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-removeall-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeMember',
|
||||
username,
|
||||
username: '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
@@ -105,58 +174,38 @@ BlazeComponent.extendComponent({
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-show-color-palette'(event) {
|
||||
const funct = Popup.open('setCardActionsColor');
|
||||
const colorButton = this.find('#color-action');
|
||||
if (colorButton.value === '') {
|
||||
colorButton.value = 'green';
|
||||
}
|
||||
funct.call(this, event);
|
||||
},
|
||||
'click .js-set-color-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const selectedColor = this.cardColorButtonValue.get();
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'setColor',
|
||||
selectedColor,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
},
|
||||
'click .js-add-removeall-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeMember',
|
||||
'username': '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
'click .js-show-color-palette'(event){
|
||||
const funct = Popup.open('setCardActionsColor');
|
||||
const colorButton = this.find('#color-action');
|
||||
if (colorButton.value === '') {
|
||||
colorButton.value = 'green';
|
||||
}
|
||||
funct.call(this, event);
|
||||
},
|
||||
'click .js-set-color-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const selectedColor = this.cardColorButtonValue.get();
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'setColor',
|
||||
selectedColor,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
|
||||
}).register('cardActions');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -167,7 +216,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
colors() {
|
||||
return cardColors.map((color) => ({ color, name: '' }));
|
||||
return cardColors.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
@@ -175,14 +224,16 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
return [
|
||||
{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
},
|
||||
'click .js-submit'() {
|
||||
this.colorButtonValue.set(this.currentColor.get());
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
'click .js-submit' () {
|
||||
this.colorButtonValue.set(this.currentColor.get());
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('setCardActionsColorPopup');
|
||||
@@ -3,149 +3,148 @@ BlazeComponent.extendComponent({
|
||||
this.subscribe('allRules');
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-checklist-items-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const checklistName = this.find('#checklist-name-3').value;
|
||||
const checklistItems = this.find('#checklist-items').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addChecklistWithItems',
|
||||
checklistName,
|
||||
checklistItems,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
'click .js-add-checklist-items-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const checklistName = this.find('#checklist-name-3').value;
|
||||
const checklistItems = this.find('#checklist-items').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addChecklistWithItems',
|
||||
checklistName,
|
||||
checklistItems,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
'click .js-add-checklist-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#check-action').value;
|
||||
const checklistName = this.find('#checklist-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'add') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addChecklist',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'remove') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeChecklist',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-checkall-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#checkall-action').value;
|
||||
const checklistName = this.find('#checklist-name2').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'check') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'checkAll',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncheck') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'uncheckAll',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-check-item-action'(event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const checkItemName = this.find('#checkitem-name');
|
||||
const checklistName = this.find('#checklist-name3');
|
||||
const actionSelected = this.find('#check-item-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'check') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'checkItem',
|
||||
checklistName,
|
||||
checkItemName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncheck') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'uncheckItem',
|
||||
checklistName,
|
||||
checkItemName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'click .js-add-checklist-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#check-action').value;
|
||||
const checklistName = this.find('#checklist-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'add') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addChecklist',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'remove') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeChecklist',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
'click .js-add-checkall-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#checkall-action').value;
|
||||
const checklistName = this.find('#checklist-name2').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'check') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'checkAll',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncheck') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'uncheckAll',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-check-item-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const checkItemName = this.find('#checkitem-name');
|
||||
const checklistName = this.find('#checklist-name3');
|
||||
const actionSelected = this.find('#check-item-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'check') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'checkItem',
|
||||
checklistName,
|
||||
checkItemName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncheck') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'uncheckItem',
|
||||
checklistName,
|
||||
checkItemName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
|
||||
}).register('checklistActions');
|
||||
@@ -1,35 +1,34 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
|
||||
},
|
||||
onCreated() {},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-mail-action' (event) {
|
||||
const emailTo = this.find('#email-to').value;
|
||||
const emailSubject = this.find('#email-subject').value;
|
||||
const emailMsg = this.find('#email-msg').value;
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'sendEmail',
|
||||
emailTo,
|
||||
emailSubject,
|
||||
emailMsg,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
return [
|
||||
{
|
||||
'click .js-mail-action'(event) {
|
||||
const emailTo = this.find('#email-to').value;
|
||||
const emailSubject = this.find('#email-subject').value;
|
||||
const emailMsg = this.find('#email-msg').value;
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'sendEmail',
|
||||
emailTo,
|
||||
emailSubject,
|
||||
emailMsg,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
|
||||
}).register('mailActions');
|
||||
@@ -3,7 +3,6 @@ BlazeComponent.extendComponent({
|
||||
this.subscribe('allRules');
|
||||
this.subscribe('allTriggers');
|
||||
this.subscribe('allActions');
|
||||
|
||||
},
|
||||
|
||||
trigger() {
|
||||
@@ -34,5 +33,4 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [{}];
|
||||
},
|
||||
|
||||
}).register('ruleDetails');
|
||||
@@ -40,19 +40,21 @@ BlazeComponent.extendComponent({
|
||||
// console.log(this.data());
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-set-board-actions'(){
|
||||
this.setBoardActions();
|
||||
return [
|
||||
{
|
||||
'click .js-set-board-actions'() {
|
||||
this.setBoardActions();
|
||||
},
|
||||
'click .js-set-card-actions'() {
|
||||
this.setCardActions();
|
||||
},
|
||||
'click .js-set-mail-actions'() {
|
||||
this.setMailActions();
|
||||
},
|
||||
'click .js-set-checklist-actions'() {
|
||||
this.setChecklistActions();
|
||||
},
|
||||
},
|
||||
'click .js-set-card-actions'() {
|
||||
this.setCardActions();
|
||||
},
|
||||
'click .js-set-mail-actions'() {
|
||||
this.setMailActions();
|
||||
},
|
||||
'click .js-set-checklist-actions'() {
|
||||
this.setChecklistActions();
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('rulesActions');
|
||||
@@ -17,6 +17,10 @@ template(name="rulesList")
|
||||
button.js-delete-rule
|
||||
i.fa.fa-trash-o
|
||||
| {{{_ 'r-delete-rule'}}}
|
||||
else if currentUser.isBoardAdmin
|
||||
button.js-delete-rule
|
||||
i.fa.fa-trash-o
|
||||
| {{{_ 'r-delete-rule'}}}
|
||||
else
|
||||
li.no-items-message {{{_ 'r-no-rules' }}}
|
||||
if currentUser.isAdmin
|
||||
@@ -24,4 +28,10 @@ template(name="rulesList")
|
||||
button.js-goto-trigger
|
||||
i.fa.fa-plus
|
||||
| {{{_ 'r-add-rule'}}}
|
||||
input(type=text,placeholder="{{{_ 'r-new-rule-name' }}}",id="ruleTitle")
|
||||
input(type=text,placeholder="{{{_ 'r-new-rule-name' }}}",id="ruleTitle")
|
||||
else if currentUser.isBoardAdmin
|
||||
div.rules-add
|
||||
button.js-goto-trigger
|
||||
i.fa.fa-plus
|
||||
| {{{_ 'r-add-rule'}}}
|
||||
input(type=text,placeholder="{{{_ 'r-new-rule-name' }}}",id="ruleTitle")
|
||||
@@ -9,12 +9,12 @@ BlazeComponent.extendComponent({
|
||||
setTrigger() {
|
||||
this.rulesCurrentTab.set('trigger');
|
||||
},
|
||||
sanitizeObject(obj){
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if(obj[key] === '' || obj[key] === undefined){
|
||||
sanitizeObject(obj) {
|
||||
Object.keys(obj).forEach(key => {
|
||||
if (obj[key] === '' || obj[key] === undefined) {
|
||||
obj[key] = '*';
|
||||
}}
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
setRulesList() {
|
||||
this.rulesCurrentTab.set('rulesList');
|
||||
@@ -29,69 +29,73 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-delete-rule' () {
|
||||
const rule = this.currentData();
|
||||
Rules.remove(rule._id);
|
||||
Actions.remove(rule.actionId);
|
||||
Triggers.remove(rule.triggerId);
|
||||
|
||||
},
|
||||
'click .js-goto-trigger' (event) {
|
||||
event.preventDefault();
|
||||
const ruleTitle = this.find('#ruleTitle').value;
|
||||
if(ruleTitle !== undefined && ruleTitle !== ''){
|
||||
this.find('#ruleTitle').value = '';
|
||||
this.ruleName.set(ruleTitle);
|
||||
this.setTrigger();
|
||||
}
|
||||
},
|
||||
'click .js-goto-action' (event) {
|
||||
event.preventDefault();
|
||||
// Add user to the trigger
|
||||
const username = $(event.currentTarget.offsetParent).find('.user-name').val();
|
||||
let trigger = this.triggerVar.get();
|
||||
trigger.userId = '*';
|
||||
if(username !== undefined ){
|
||||
const userFound = Users.findOne({username});
|
||||
if(userFound !== undefined){
|
||||
trigger.userId = userFound._id;
|
||||
this.triggerVar.set(trigger);
|
||||
return [
|
||||
{
|
||||
'click .js-delete-rule'() {
|
||||
const rule = this.currentData();
|
||||
Rules.remove(rule._id);
|
||||
Actions.remove(rule.actionId);
|
||||
Triggers.remove(rule.triggerId);
|
||||
},
|
||||
'click .js-goto-trigger'(event) {
|
||||
event.preventDefault();
|
||||
const ruleTitle = this.find('#ruleTitle').value;
|
||||
if (ruleTitle !== undefined && ruleTitle !== '') {
|
||||
this.find('#ruleTitle').value = '';
|
||||
this.ruleName.set(ruleTitle);
|
||||
this.setTrigger();
|
||||
}
|
||||
}
|
||||
// Sanitize trigger
|
||||
trigger = this.triggerVar.get();
|
||||
this.sanitizeObject(trigger);
|
||||
this.triggerVar.set(trigger);
|
||||
this.setAction();
|
||||
},
|
||||
'click .js-show-user-field' (event) {
|
||||
event.preventDefault();
|
||||
$(event.currentTarget.offsetParent).find('.user-details').removeClass('hide-element');
|
||||
},
|
||||
'click .js-goto-rules' (event) {
|
||||
event.preventDefault();
|
||||
this.setRulesList();
|
||||
},
|
||||
'click .js-goback' (event) {
|
||||
event.preventDefault();
|
||||
if(this.rulesCurrentTab.get() === 'trigger' || this.rulesCurrentTab.get() === 'ruleDetails' ){
|
||||
},
|
||||
'click .js-goto-action'(event) {
|
||||
event.preventDefault();
|
||||
// Add user to the trigger
|
||||
const username = $(event.currentTarget.offsetParent)
|
||||
.find('.user-name')
|
||||
.val();
|
||||
let trigger = this.triggerVar.get();
|
||||
trigger.userId = '*';
|
||||
if (username !== undefined) {
|
||||
const userFound = Users.findOne({ username });
|
||||
if (userFound !== undefined) {
|
||||
trigger.userId = userFound._id;
|
||||
this.triggerVar.set(trigger);
|
||||
}
|
||||
}
|
||||
// Sanitize trigger
|
||||
trigger = this.triggerVar.get();
|
||||
this.sanitizeObject(trigger);
|
||||
this.triggerVar.set(trigger);
|
||||
this.setAction();
|
||||
},
|
||||
'click .js-show-user-field'(event) {
|
||||
event.preventDefault();
|
||||
$(event.currentTarget.offsetParent)
|
||||
.find('.user-details')
|
||||
.removeClass('hide-element');
|
||||
},
|
||||
'click .js-goto-rules'(event) {
|
||||
event.preventDefault();
|
||||
this.setRulesList();
|
||||
}
|
||||
if(this.rulesCurrentTab.get() === 'action'){
|
||||
this.setTrigger();
|
||||
}
|
||||
},
|
||||
'click .js-goback'(event) {
|
||||
event.preventDefault();
|
||||
if (
|
||||
this.rulesCurrentTab.get() === 'trigger' ||
|
||||
this.rulesCurrentTab.get() === 'ruleDetails'
|
||||
) {
|
||||
this.setRulesList();
|
||||
}
|
||||
if (this.rulesCurrentTab.get() === 'action') {
|
||||
this.setTrigger();
|
||||
}
|
||||
},
|
||||
'click .js-goto-details'(event) {
|
||||
event.preventDefault();
|
||||
const rule = this.currentData();
|
||||
this.ruleId.set(rule._id);
|
||||
this.setRuleDetails();
|
||||
},
|
||||
},
|
||||
'click .js-goto-details' (event) {
|
||||
event.preventDefault();
|
||||
const rule = this.currentData();
|
||||
this.ruleId.set(rule._id);
|
||||
this.setRuleDetails();
|
||||
},
|
||||
|
||||
}];
|
||||
];
|
||||
},
|
||||
|
||||
}).register('rulesMain');
|
||||
|
||||
|
||||
@@ -38,16 +38,18 @@ BlazeComponent.extendComponent({
|
||||
// console.log(this.data());
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-set-board-triggers' () {
|
||||
this.setBoardTriggers();
|
||||
return [
|
||||
{
|
||||
'click .js-set-board-triggers'() {
|
||||
this.setBoardTriggers();
|
||||
},
|
||||
'click .js-set-card-triggers'() {
|
||||
this.setCardTriggers();
|
||||
},
|
||||
'click .js-set-checklist-triggers'() {
|
||||
this.setChecklistTriggers();
|
||||
},
|
||||
},
|
||||
'click .js-set-card-triggers' () {
|
||||
this.setCardTriggers();
|
||||
},
|
||||
'click .js-set-checklist-triggers' () {
|
||||
this.setChecklistTriggers();
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('rulesTriggers');
|
||||
@@ -64,8 +64,7 @@ template(name="boardTriggers")
|
||||
div.trigger-text
|
||||
| {{_'r-in-swimlane'}}
|
||||
div.trigger-dropdown
|
||||
input(id="create-swimlane-name",type=text,placeholder="{{_'r-swimlane-name'}}")
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
input(id="create-swimlane-name-2",type=text,placeholder="{{_'r-swimlane-name'}}")
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
|
||||
@@ -4,112 +4,118 @@ BlazeComponent.extendComponent({
|
||||
this.currentPopupTriggerId = 'def';
|
||||
this.cardTitleFilters = {};
|
||||
},
|
||||
setNameFilter(name){
|
||||
this.cardTitleFilters[this.currentPopupTriggerId] = name;
|
||||
setNameFilter(name) {
|
||||
this.cardTitleFilters[this.currentPopupTriggerId] = name;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-open-card-title-popup'(event){
|
||||
const funct = Popup.open('boardCardTitle');
|
||||
const divId = $(event.currentTarget.parentNode.parentNode).attr('id');
|
||||
//console.log('current popup');
|
||||
//console.log(this.currentPopupTriggerId);
|
||||
this.currentPopupTriggerId = divId;
|
||||
funct.call(this, event);
|
||||
},
|
||||
'click .js-add-create-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const listName = this.find('#create-list-name').value;
|
||||
const swimlaneName = this.find('#create-swimlane-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const divId = $(event.currentTarget.parentNode).attr('id');
|
||||
const cardTitle = this.cardTitleFilters[divId];
|
||||
// move to generic funciont
|
||||
datas.triggerVar.set({
|
||||
activityType: 'createCard',
|
||||
boardId,
|
||||
cardTitle,
|
||||
swimlaneName,
|
||||
listName,
|
||||
desc,
|
||||
});
|
||||
},
|
||||
'click .js-add-moved-trigger' (event) {
|
||||
const datas = this.data();
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const swimlaneName = this.find('#create-swimlane-name').value;
|
||||
const actionSelected = this.find('#move-action').value;
|
||||
const listName = this.find('#move-list-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'moved-to') {
|
||||
return [
|
||||
{
|
||||
'click .js-open-card-title-popup'(event) {
|
||||
const funct = Popup.open('boardCardTitle');
|
||||
const divId = $(event.currentTarget.parentNode.parentNode).attr('id');
|
||||
//console.log('current popup');
|
||||
//console.log(this.currentPopupTriggerId);
|
||||
this.currentPopupTriggerId = divId;
|
||||
funct.call(this, event);
|
||||
},
|
||||
'click .js-add-create-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const listName = this.find('#create-list-name').value;
|
||||
const swimlaneName = this.find('#create-swimlane-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const divId = $(event.currentTarget.parentNode).attr('id');
|
||||
const cardTitle = this.cardTitleFilters[divId];
|
||||
// move to generic funciont
|
||||
datas.triggerVar.set({
|
||||
activityType: 'moveCard',
|
||||
activityType: 'createCard',
|
||||
boardId,
|
||||
cardTitle,
|
||||
swimlaneName,
|
||||
listName,
|
||||
swimlaneName,
|
||||
'oldListName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'moved-from') {
|
||||
},
|
||||
'click .js-add-moved-trigger'(event) {
|
||||
const datas = this.data();
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const swimlaneName = this.find('#create-swimlane-name-2').value;
|
||||
const actionSelected = this.find('#move-action').value;
|
||||
const listName = this.find('#move-list-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const divId = $(event.currentTarget.parentNode).attr('id');
|
||||
const cardTitle = this.cardTitleFilters[divId];
|
||||
if (actionSelected === 'moved-to') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'moveCard',
|
||||
boardId,
|
||||
listName,
|
||||
cardTitle,
|
||||
swimlaneName,
|
||||
oldListName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'moved-from') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'moveCard',
|
||||
boardId,
|
||||
cardTitle,
|
||||
swimlaneName,
|
||||
listName: '*',
|
||||
oldListName: listName,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-moved-trigger'(event) {
|
||||
const datas = this.data();
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
|
||||
datas.triggerVar.set({
|
||||
activityType: 'moveCard',
|
||||
boardId,
|
||||
swimlaneName,
|
||||
'listName': '*',
|
||||
'oldListName': listName,
|
||||
swimlaneName: '*',
|
||||
listName: '*',
|
||||
oldListName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-arc-trigger'(event) {
|
||||
const datas = this.data();
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const actionSelected = this.find('#arch-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'archived') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'archivedCard',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unarchived') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'restoredCard',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'click .js-add-gen-moved-trigger' (event){
|
||||
const datas = this.data();
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
|
||||
datas.triggerVar.set({
|
||||
'activityType': 'moveCard',
|
||||
boardId,
|
||||
'swimlaneName': '*',
|
||||
'listName':'*',
|
||||
'oldListName': '*',
|
||||
desc,
|
||||
});
|
||||
},
|
||||
'click .js-add-arc-trigger' (event) {
|
||||
const datas = this.data();
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const actionSelected = this.find('#arch-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'archived') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'archivedCard',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unarchived') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'restoredCard',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
}];
|
||||
];
|
||||
},
|
||||
|
||||
}).register('boardTriggers');
|
||||
|
||||
|
||||
Template.boardCardTitlePopup.events({
|
||||
submit(evt, tpl) {
|
||||
const title = tpl.$('.js-card-filter-name').val().trim();
|
||||
submit(event, templateInstance) {
|
||||
const title = templateInstance
|
||||
.$('.js-card-filter-name')
|
||||
.val()
|
||||
.trim();
|
||||
Popup.getOpenerComponent().setNameFilter(title);
|
||||
evt.preventDefault();
|
||||
event.preventDefault();
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
@@ -22,7 +22,7 @@ template(name="cardTriggers")
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-the-label-is'}}
|
||||
| {{_'r-when-the-label'}}
|
||||
div.trigger-dropdown
|
||||
select(id="spec-label")
|
||||
each labels
|
||||
|
||||
@@ -7,7 +7,7 @@ BlazeComponent.extendComponent({
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
if (labels[i].name === '' || labels[i].name === undefined) {
|
||||
labels[i].name = labels[i].color;
|
||||
labels[i].translatedname = `${TAPi18n.__(`color-${ labels[i].color}`)}`;
|
||||
labels[i].translatedname = `${TAPi18n.__(`color-${labels[i].color}`)}`;
|
||||
} else {
|
||||
labels[i].translatedname = labels[i].name;
|
||||
}
|
||||
@@ -15,117 +15,119 @@ BlazeComponent.extendComponent({
|
||||
return labels;
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-gen-label-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#label-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addedLabel',
|
||||
boardId,
|
||||
'labelId': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removedLabel',
|
||||
boardId,
|
||||
'labelId': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
return [
|
||||
{
|
||||
'click .js-add-gen-label-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#label-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addedLabel',
|
||||
boardId,
|
||||
labelId: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removedLabel',
|
||||
boardId,
|
||||
labelId: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-label-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-label-action').value;
|
||||
const labelId = this.find('#spec-label').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addedLabel',
|
||||
boardId,
|
||||
labelId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removedLabel',
|
||||
boardId,
|
||||
labelId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-member-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-member-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'joinMember',
|
||||
boardId,
|
||||
username: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'unjoinMember',
|
||||
boardId,
|
||||
username: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-member-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-member-action').value;
|
||||
const username = this.find('#spec-member').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'joinMember',
|
||||
boardId,
|
||||
username,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'unjoinMember',
|
||||
boardId,
|
||||
username,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-attachment-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#attach-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addAttachment',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'deleteAttachment',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'click .js-add-spec-label-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-label-action').value;
|
||||
const labelId = this.find('#spec-label').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addedLabel',
|
||||
boardId,
|
||||
labelId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removedLabel',
|
||||
boardId,
|
||||
labelId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-member-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-member-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'joinMember',
|
||||
boardId,
|
||||
'username': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'unjoinMember',
|
||||
boardId,
|
||||
'username': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-member-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-member-action').value;
|
||||
const username = this.find('#spec-member').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'joinMember',
|
||||
boardId,
|
||||
username,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'unjoinMember',
|
||||
boardId,
|
||||
username,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-attachment-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#attach-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addAttachment',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'deleteAttachment',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('cardTriggers');
|
||||
@@ -3,144 +3,145 @@ BlazeComponent.extendComponent({
|
||||
this.subscribe('allRules');
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-gen-check-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-check-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-check-action').value;
|
||||
const checklistId = this.find('#check-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-comp-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
return [
|
||||
{
|
||||
'click .js-add-gen-check-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
checklistName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
checklistName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-check-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-check-action').value;
|
||||
const checklistId = this.find('#check-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
checklistName: checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
checklistName: checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-comp-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-comp-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'completed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'completeChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncompleted') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncompleteChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-comp-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'completed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'completeChecklist',
|
||||
boardId,
|
||||
checklistName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncompleted') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncompleteChecklist',
|
||||
boardId,
|
||||
checklistName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-comp-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-comp-check-action').value;
|
||||
const checklistId = this.find('#spec-comp-check-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'completed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'completeChecklist',
|
||||
boardId,
|
||||
checklistName: checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncompleted') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncompleteChecklist',
|
||||
boardId,
|
||||
checklistName: checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-check-item-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#check-item-gen-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'checked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'checkedItem',
|
||||
boardId,
|
||||
checklistItemName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unchecked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncheckedItem',
|
||||
boardId,
|
||||
checklistItemName: '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-check-item-trigger'(event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#check-item-spec-action').value;
|
||||
const checklistItemId = this.find('#check-item-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'checked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'checkedItem',
|
||||
boardId,
|
||||
checklistItemName: checklistItemId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unchecked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncheckedItem',
|
||||
boardId,
|
||||
checklistItemName: checklistItemId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
'click .js-add-spec-comp-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-comp-check-action').value;
|
||||
const checklistId = this.find('#spec-comp-check-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'completed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'completeChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncompleted') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncompleteChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-check-item-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#check-item-gen-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'checked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'checkedItem',
|
||||
boardId,
|
||||
'checklistItemName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unchecked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncheckedItem',
|
||||
boardId,
|
||||
'checklistItemName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-check-item-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#check-item-spec-action').value;
|
||||
const checklistItemId = this.find('#check-item-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'checked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'checkedItem',
|
||||
boardId,
|
||||
'checklistItemName': checklistItemId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unchecked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncheckedItem',
|
||||
boardId,
|
||||
'checklistItemName': checklistItemId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
|
||||
}).register('checklistTriggers');
|
||||
@@ -6,9 +6,11 @@ Template.connectionMethod.onCreated(function() {
|
||||
// TODO : add a management of different languages
|
||||
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
|
||||
this.authenticationMethods.set([
|
||||
{value: 'password'},
|
||||
{ value: 'password' },
|
||||
// Gets only the authentication methods availables
|
||||
...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
|
||||
...Object.entries(result)
|
||||
.filter(e => e[1])
|
||||
.map(e => ({ value: e[0] })),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -24,7 +26,9 @@ Template.connectionMethod.onCreated(function() {
|
||||
|
||||
Template.connectionMethod.onRendered(() => {
|
||||
// Moves the select boxe in the first place of the at-pwd-form div
|
||||
$('.at-form-authentication').detach().prependTo('.at-pwd-form');
|
||||
$('.at-form-authentication')
|
||||
.detach()
|
||||
.prependTo('.at-pwd-form');
|
||||
});
|
||||
|
||||
Template.connectionMethod.helpers({
|
||||
|
||||
@@ -3,8 +3,13 @@ template(name="people")
|
||||
unless currentUser.isAdmin
|
||||
| {{_ 'error-notAuthorized'}}
|
||||
else
|
||||
.content-title
|
||||
span {{_ 'people'}}
|
||||
.content-title.ext-box
|
||||
.ext-box-left
|
||||
span {{_ 'people'}}
|
||||
input#searchInput(placeholder="{{_ 'search'}}")
|
||||
button#searchButton {{_ 'search'}}
|
||||
.ext-box-right
|
||||
span {{_ 'people-number'}} #{peopleNumber}
|
||||
.content-body
|
||||
.side-menu
|
||||
ul
|
||||
@@ -102,5 +107,8 @@ template(name="editUserPopup")
|
||||
label
|
||||
| {{_ 'password'}}
|
||||
input.js-profile-password(type="password")
|
||||
//div.buttonsContainer
|
||||
// input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
// div
|
||||
// input#deleteButton.primary.wide(type="button" value="{{_ 'delete'}}")
|
||||
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
@@ -8,6 +8,8 @@ BlazeComponent.extendComponent({
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.people = new ReactiveVar(true);
|
||||
this.findUsersOptions = new ReactiveVar({});
|
||||
this.number = new ReactiveVar(0);
|
||||
|
||||
this.page = new ReactiveVar(1);
|
||||
this.loadNextPageLocked = false;
|
||||
@@ -26,6 +28,37 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
});
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click #searchButton'() {
|
||||
this.filterPeople();
|
||||
},
|
||||
'keydown #searchInput'(event) {
|
||||
if (event.keyCode === 13 && !event.shiftKey) {
|
||||
this.filterPeople();
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
filterPeople() {
|
||||
const value = $('#searchInput')
|
||||
.first()
|
||||
.val();
|
||||
if (value === '') {
|
||||
this.findUsersOptions.set({});
|
||||
} else {
|
||||
const regex = new RegExp(value, 'i');
|
||||
this.findUsersOptions.set({
|
||||
$or: [
|
||||
{ username: regex },
|
||||
{ 'profile.fullname': regex },
|
||||
{ 'emails.address': regex },
|
||||
],
|
||||
});
|
||||
}
|
||||
},
|
||||
loadNextPage() {
|
||||
if (this.loadNextPageLocked === false) {
|
||||
this.page.set(this.page.get() + 1);
|
||||
@@ -49,9 +82,14 @@ BlazeComponent.extendComponent({
|
||||
this.loading.set(w);
|
||||
},
|
||||
peopleList() {
|
||||
return Users.find({}, {
|
||||
fields: {_id: true},
|
||||
const users = Users.find(this.findUsersOptions.get(), {
|
||||
fields: { _id: true },
|
||||
});
|
||||
this.number.set(users.count());
|
||||
return users;
|
||||
},
|
||||
peopleNumber() {
|
||||
return this.number.get();
|
||||
},
|
||||
}).register('people');
|
||||
|
||||
@@ -64,15 +102,18 @@ Template.peopleRow.helpers({
|
||||
|
||||
Template.editUserPopup.onCreated(function() {
|
||||
this.authenticationMethods = new ReactiveVar([]);
|
||||
this.errorMessage = new ReactiveVar('');
|
||||
|
||||
Meteor.call('getAuthenticationsEnabled', (_, result) => {
|
||||
if (result) {
|
||||
// TODO : add a management of different languages
|
||||
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
|
||||
this.authenticationMethods.set([
|
||||
{value: 'password'},
|
||||
{ value: 'password' },
|
||||
// Gets only the authentication methods availables
|
||||
...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
|
||||
...Object.entries(result)
|
||||
.filter(e => e[1])
|
||||
.map(e => ({ value: e[0] })),
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -95,72 +136,85 @@ Template.editUserPopup.helpers({
|
||||
const selected = Users.findOne(userId).authenticationMethod;
|
||||
return selected === 'ldap';
|
||||
},
|
||||
errorMessage() {
|
||||
return Template.instance().errorMessage.get();
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
},
|
||||
onCreated() {},
|
||||
user() {
|
||||
return Users.findOne(this.userId);
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click a.edit-user': Popup.open('editUser'),
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click a.edit-user': Popup.open('editUser'),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('peopleRow');
|
||||
|
||||
Template.editUserPopup.events({
|
||||
submit(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const user = Users.findOne(this.userId);
|
||||
const fullname = tpl.find('.js-profile-fullname').value.trim();
|
||||
const username = tpl.find('.js-profile-username').value.trim();
|
||||
const password = tpl.find('.js-profile-password').value;
|
||||
const isAdmin = tpl.find('.js-profile-isadmin').value.trim();
|
||||
const isActive = tpl.find('.js-profile-isactive').value.trim();
|
||||
const email = tpl.find('.js-profile-email').value.trim();
|
||||
const authentication = tpl.find('.js-authenticationMethod').value.trim();
|
||||
const fullname = templateInstance.find('.js-profile-fullname').value.trim();
|
||||
const username = templateInstance.find('.js-profile-username').value.trim();
|
||||
const password = templateInstance.find('.js-profile-password').value;
|
||||
const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
|
||||
const isActive = templateInstance.find('.js-profile-isactive').value.trim();
|
||||
const email = templateInstance.find('.js-profile-email').value.trim();
|
||||
const authentication = templateInstance
|
||||
.find('.js-authenticationMethod')
|
||||
.value.trim();
|
||||
|
||||
const isChangePassword = password.length > 0;
|
||||
const isChangeUserName = username !== user.username;
|
||||
const isChangeEmail = email.toLowerCase() !== user.emails[0].address.toLowerCase();
|
||||
const isChangeEmail =
|
||||
email.toLowerCase() !== user.emails[0].address.toLowerCase();
|
||||
|
||||
Users.update(this.userId, {
|
||||
$set: {
|
||||
'profile.fullname': fullname,
|
||||
'isAdmin': isAdmin === 'true',
|
||||
'loginDisabled': isActive === 'true',
|
||||
'authenticationMethod': authentication,
|
||||
isAdmin: isAdmin === 'true',
|
||||
loginDisabled: isActive === 'true',
|
||||
authenticationMethod: authentication,
|
||||
},
|
||||
});
|
||||
|
||||
if(isChangePassword){
|
||||
if (isChangePassword) {
|
||||
Meteor.call('setPassword', password, this.userId);
|
||||
}
|
||||
|
||||
if (isChangeUserName && isChangeEmail) {
|
||||
Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), this.userId, function (error) {
|
||||
const usernameMessageElement = tpl.$('.username-taken');
|
||||
const emailMessageElement = tpl.$('.email-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'username-already-taken') {
|
||||
usernameMessageElement.show();
|
||||
emailMessageElement.hide();
|
||||
} else if (errorElement === 'email-already-taken') {
|
||||
Meteor.call(
|
||||
'setUsernameAndEmail',
|
||||
username,
|
||||
email.toLowerCase(),
|
||||
this.userId,
|
||||
function(error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
const emailMessageElement = templateInstance.$('.email-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'username-already-taken') {
|
||||
usernameMessageElement.show();
|
||||
emailMessageElement.hide();
|
||||
} else if (errorElement === 'email-already-taken') {
|
||||
usernameMessageElement.hide();
|
||||
emailMessageElement.show();
|
||||
}
|
||||
} else {
|
||||
usernameMessageElement.hide();
|
||||
emailMessageElement.show();
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
}
|
||||
} else {
|
||||
usernameMessageElement.hide();
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
} else if (isChangeUserName) {
|
||||
Meteor.call('setUsername', username, this.userId, function (error) {
|
||||
const usernameMessageElement = tpl.$('.username-taken');
|
||||
Meteor.call('setUsername', username, this.userId, function(error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'username-already-taken') {
|
||||
@@ -172,8 +226,10 @@ Template.editUserPopup.events({
|
||||
}
|
||||
});
|
||||
} else if (isChangeEmail) {
|
||||
Meteor.call('setEmail', email.toLowerCase(), this.userId, function (error) {
|
||||
const emailMessageElement = tpl.$('.email-taken');
|
||||
Meteor.call('setEmail', email.toLowerCase(), this.userId, function(
|
||||
error,
|
||||
) {
|
||||
const emailMessageElement = templateInstance.$('.email-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'email-already-taken') {
|
||||
@@ -186,4 +242,9 @@ Template.editUserPopup.events({
|
||||
});
|
||||
} else Popup.close();
|
||||
},
|
||||
|
||||
'click #deleteButton': Popup.afterConfirm('userDelete', function() {
|
||||
Users.remove(this.userId);
|
||||
Popup.close();
|
||||
}),
|
||||
});
|
||||
@@ -13,3 +13,36 @@ table
|
||||
|
||||
tr:nth-child(even)
|
||||
background-color: #dddddd;
|
||||
|
||||
.ext-box
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 34px;
|
||||
|
||||
.ext-box-left
|
||||
display: flex;
|
||||
width: 40%
|
||||
|
||||
span
|
||||
vertical-align: center;
|
||||
line-height: 34px;
|
||||
margin-right: 10px;
|
||||
|
||||
input, button
|
||||
margin: 0 10px 0 0;
|
||||
padding: 0;
|
||||
|
||||
button
|
||||
min-width: 60px;
|
||||
|
||||
.content-wrapper
|
||||
margin-top: 10px
|
||||
|
||||
.buttonsContainer
|
||||
display: flex
|
||||
|
||||
input
|
||||
margin: 0
|
||||
|
||||
div
|
||||
margin: auto
|
||||
@@ -44,7 +44,7 @@ template(name="general")
|
||||
ul
|
||||
li
|
||||
.title {{_ 'invite-people'}}
|
||||
textarea#email-to-invite.form-control(rows='5', placeholder="{{_ 'email-addresses'}}")
|
||||
textarea#email-to-invite.wekan-form-control(rows='5', placeholder="{{_ 'email-addresses'}}")
|
||||
li
|
||||
.title {{_ 'to-boards'}}
|
||||
.bg-white
|
||||
@@ -63,20 +63,20 @@ template(name='email')
|
||||
.title {{_ 'smtp-host'}}
|
||||
.description {{_ 'smtp-host-description'}}
|
||||
.form-group
|
||||
input.form-control#mail-server-host(type="text", placeholder="smtp.domain.com" value="{{currentSetting.mailServer.host}}")
|
||||
input.wekan-form-control#mail-server-host(type="text", placeholder="smtp.domain.com" value="{{currentSetting.mailServer.host}}")
|
||||
li.smtp-form
|
||||
.title {{_ 'smtp-port'}}
|
||||
.description {{_ 'smtp-port-description'}}
|
||||
.form-group
|
||||
input.form-control#mail-server-port(type="text", placeholder="25" value="{{currentSetting.mailServer.port}}")
|
||||
input.wekan-form-control#mail-server-port(type="text", placeholder="25" value="{{currentSetting.mailServer.port}}")
|
||||
li.smtp-form
|
||||
.title {{_ 'smtp-username'}}
|
||||
.form-group
|
||||
input.form-control#mail-server-username(type="text", placeholder="{{_ 'username'}}" value="{{currentSetting.mailServer.username}}")
|
||||
input.wekan-form-control#mail-server-username(type="text", placeholder="{{_ 'username'}}" value="{{currentSetting.mailServer.username}}")
|
||||
li.smtp-form
|
||||
.title {{_ 'smtp-password'}}
|
||||
.form-group
|
||||
input.form-control#mail-server-password(type="password", placeholder="{{_ 'password'}}" value="{{currentSetting.mailServer.password}}")
|
||||
input.wekan-form-control#mail-server-password(type="password", placeholder="{{_ 'password'}}" value="{{currentSetting.mailServer.password}}")
|
||||
li.smtp-form
|
||||
.title {{_ 'smtp-tls'}}
|
||||
.form-group
|
||||
@@ -88,7 +88,7 @@ template(name='email')
|
||||
li.smtp-form
|
||||
.title {{_ 'send-from'}}
|
||||
.form-group
|
||||
input.form-control#mail-server-from(type="email", placeholder="no-reply@domain.com" value="{{currentSetting.mailServer.from}}")
|
||||
input.wekan-form-control#mail-server-from(type="email", placeholder="no-reply@domain.com" value="{{currentSetting.mailServer.from}}")
|
||||
|
||||
li
|
||||
button.js-save.primary {{_ 'save'}}
|
||||
@@ -101,17 +101,25 @@ template(name='accountSettings')
|
||||
li.accounts-form
|
||||
.title {{_ 'accounts-allowEmailChange'}}
|
||||
.form-group.flex
|
||||
input.form-control#accounts-allowEmailChange(type="radio" name="allowEmailChange" value="true" checked="{{#if allowEmailChange}}checked{{/if}}")
|
||||
input.wekan-form-control#accounts-allowEmailChange(type="radio" name="allowEmailChange" value="true" checked="{{#if allowEmailChange}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
input.form-control#accounts-allowEmailChange(type="radio" name="allowEmailChange" value="false" checked="{{#unless allowEmailChange}}checked{{/unless}}")
|
||||
input.wekan-form-control#accounts-allowEmailChange(type="radio" name="allowEmailChange" value="false" checked="{{#unless allowEmailChange}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
li
|
||||
li.accounts-form
|
||||
.title {{_ 'accounts-allowUserNameChange'}}
|
||||
.form-group.flex
|
||||
input.form-control#accounts-allowUserNameChange(type="radio" name="allowUserNameChange" value="true" checked="{{#if allowUserNameChange}}checked{{/if}}")
|
||||
input.wekan-form-control#accounts-allowUserNameChange(type="radio" name="allowUserNameChange" value="true" checked="{{#if allowUserNameChange}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
input.form-control#accounts-allowUserNameChange(type="radio" name="allowUserNameChange" value="false" checked="{{#unless allowUserNameChange}}checked{{/unless}}")
|
||||
input.wekan-form-control#accounts-allowUserNameChange(type="radio" name="allowUserNameChange" value="false" checked="{{#unless allowUserNameChange}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
li
|
||||
li.accounts-form
|
||||
.title {{_ 'accounts-allowUserDelete'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#accounts-allowUserDelete(type="radio" name="allowUserDelete" value="true" checked="{{#if allowUserDelete}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
input.wekan-form-control#accounts-allowUserDelete(type="radio" name="allowUserDelete" value="false" checked="{{#unless allowUserDelete}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
li
|
||||
button.js-accounts-save.primary {{_ 'save'}}
|
||||
@@ -128,7 +136,7 @@ template(name='announcementSettings')
|
||||
ul
|
||||
li
|
||||
.title {{_ 'admin-announcement-title'}}
|
||||
textarea#admin-announcement.form-control= currentSetting.body
|
||||
textarea#admin-announcement.wekan-form-control= currentSetting.body
|
||||
li
|
||||
button.js-announcement-save.primary {{_ 'save'}}
|
||||
|
||||
@@ -137,16 +145,16 @@ template(name='layoutSettings')
|
||||
//li.layout-form
|
||||
.title {{_ 'hide-logo'}}
|
||||
.form-group.flex
|
||||
input.form-control#hide-logo(type="radio" name="hideLogo" value="true" checked="{{#if currentSetting.hideLogo}}checked{{/if}}")
|
||||
input.wekan-form-control#hide-logo(type="radio" name="hideLogo" value="true" checked="{{#if currentSetting.hideLogo}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
input.form-control#hide-logo(type="radio" name="hideLogo" value="false" checked="{{#unless currentSetting.hideLogo}}checked{{/unless}}")
|
||||
input.wekan-form-control#hide-logo(type="radio" name="hideLogo" value="false" checked="{{#unless currentSetting.hideLogo}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
li.layout-form
|
||||
.title {{_ 'display-authentication-method'}}
|
||||
.form-group.flex
|
||||
input.form-control#display-authentication-method(type="radio" name="displayAuthenticationMethod" value="true" checked="{{#if currentSetting.displayAuthenticationMethod}}checked{{/if}}")
|
||||
input.wekan-form-control#display-authentication-method(type="radio" name="displayAuthenticationMethod" value="true" checked="{{#if currentSetting.displayAuthenticationMethod}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
input.form-control#display-authentication-method(type="radio" name="displayAuthenticationMethod" value="false" checked="{{#unless currentSetting.displayAuthenticationMethod}}checked{{/unless}}")
|
||||
input.wekan-form-control#display-authentication-method(type="radio" name="displayAuthenticationMethod" value="false" checked="{{#unless currentSetting.displayAuthenticationMethod}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
li.layout-form
|
||||
.title {{_ 'default-authentication-method'}}
|
||||
@@ -154,13 +162,13 @@ template(name='layoutSettings')
|
||||
li.layout-form
|
||||
.title {{_ 'custom-product-name'}}
|
||||
.form-group
|
||||
input.form-control#product-name(type="text", placeholder="" value="{{currentSetting.productName}}")
|
||||
input.wekan-form-control#product-name(type="text", placeholder="" value="{{currentSetting.productName}}")
|
||||
li.layout-form
|
||||
.title {{_ 'add-custom-html-after-body-start'}}
|
||||
textarea#customHTMLafterBodyStart.form-control= currentSetting.customHTMLafterBodyStart
|
||||
textarea#customHTMLafterBodyStart.wekan-form-control= currentSetting.customHTMLafterBodyStart
|
||||
li.layout-form
|
||||
.title {{_ 'add-custom-html-before-body-end'}}
|
||||
textarea#customHTMLbeforeBodyEnd.form-control= currentSetting.customHTMLbeforeBodyEnd
|
||||
textarea#customHTMLbeforeBodyEnd.wekan-form-control= currentSetting.customHTMLbeforeBodyEnd
|
||||
li
|
||||
button.js-save-layout.primary {{_ 'save'}}
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ BlazeComponent.extendComponent({
|
||||
checkField(selector) {
|
||||
const value = $(selector).val();
|
||||
if (!value || value.trim() === '') {
|
||||
$(selector).parents('li.smtp-form').addClass('has-error');
|
||||
$(selector)
|
||||
.parents('li.smtp-form')
|
||||
.addClass('has-error');
|
||||
throw Error('blank field');
|
||||
} else {
|
||||
return value;
|
||||
@@ -37,18 +39,23 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
'members.isAdmin': true,
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
'members.isAdmin': true,
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
},
|
||||
toggleRegistration() {
|
||||
this.setLoading(true);
|
||||
const registrationClosed = this.currentSetting().disableRegistration;
|
||||
Settings.update(Settings.findOne()._id, {$set: {disableRegistration: !registrationClosed}});
|
||||
Settings.update(Settings.findOne()._id, {
|
||||
$set: { disableRegistration: !registrationClosed },
|
||||
});
|
||||
this.setLoading(false);
|
||||
if (registrationClosed) {
|
||||
$('.invite-people').slideUp();
|
||||
@@ -90,13 +97,19 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
inviteThroughEmail() {
|
||||
const emails = $('#email-to-invite').val().toLowerCase().trim().split('\n').join(',').split(',');
|
||||
const emails = $('#email-to-invite')
|
||||
.val()
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.split('\n')
|
||||
.join(',')
|
||||
.split(',');
|
||||
const boardsToInvite = [];
|
||||
$('.js-toggle-board-choose .materialCheckBox.is-checked').each(function () {
|
||||
$('.js-toggle-board-choose .materialCheckBox.is-checked').each(function() {
|
||||
boardsToInvite.push($(this).data('id'));
|
||||
});
|
||||
const validEmails = [];
|
||||
emails.forEach((email) => {
|
||||
emails.forEach(email => {
|
||||
if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
|
||||
validEmails.push(email.trim());
|
||||
}
|
||||
@@ -119,14 +132,22 @@ BlazeComponent.extendComponent({
|
||||
try {
|
||||
const host = this.checkField('#mail-server-host');
|
||||
const port = this.checkField('#mail-server-port');
|
||||
const username = $('#mail-server-username').val().trim();
|
||||
const password = $('#mail-server-password').val().trim();
|
||||
const username = $('#mail-server-username')
|
||||
.val()
|
||||
.trim();
|
||||
const password = $('#mail-server-password')
|
||||
.val()
|
||||
.trim();
|
||||
const from = this.checkField('#mail-server-from');
|
||||
const tls = $('#mail-server-tls.is-checked').length > 0;
|
||||
Settings.update(Settings.findOne()._id, {
|
||||
$set: {
|
||||
'mailServer.host': host, 'mailServer.port': port, 'mailServer.username': username,
|
||||
'mailServer.password': password, 'mailServer.enableTLS': tls, 'mailServer.from': from,
|
||||
'mailServer.host': host,
|
||||
'mailServer.port': port,
|
||||
'mailServer.username': username,
|
||||
'mailServer.password': password,
|
||||
'mailServer.enableTLS': tls,
|
||||
'mailServer.from': from,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -134,19 +155,25 @@ BlazeComponent.extendComponent({
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
saveLayout() {
|
||||
this.setLoading(true);
|
||||
$('li').removeClass('has-error');
|
||||
|
||||
const productName = $('#product-name').val().trim();
|
||||
const hideLogoChange = ($('input[name=hideLogo]:checked').val() === 'true');
|
||||
const displayAuthenticationMethod = ($('input[name=displayAuthenticationMethod]:checked').val() === 'true');
|
||||
const productName = $('#product-name')
|
||||
.val()
|
||||
.trim();
|
||||
const hideLogoChange = $('input[name=hideLogo]:checked').val() === 'true';
|
||||
const displayAuthenticationMethod =
|
||||
$('input[name=displayAuthenticationMethod]:checked').val() === 'true';
|
||||
const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
|
||||
const customHTMLafterBodyStart = $('#customHTMLafterBodyStart').val().trim();
|
||||
const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd').val().trim();
|
||||
const customHTMLafterBodyStart = $('#customHTMLafterBodyStart')
|
||||
.val()
|
||||
.trim();
|
||||
const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd')
|
||||
.val()
|
||||
.trim();
|
||||
|
||||
try {
|
||||
Settings.update(Settings.findOne()._id, {
|
||||
@@ -166,7 +193,6 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
|
||||
DocHead.setTitle(productName);
|
||||
|
||||
},
|
||||
|
||||
sendSMTPTestEmail() {
|
||||
@@ -183,31 +209,40 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click a.js-toggle-registration': this.toggleRegistration,
|
||||
'click a.js-toggle-tls': this.toggleTLS,
|
||||
'click a.js-setting-menu': this.switchMenu,
|
||||
'click a.js-toggle-board-choose': this.checkBoard,
|
||||
'click button.js-email-invite': this.inviteThroughEmail,
|
||||
'click button.js-save': this.saveMailServerInfo,
|
||||
'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
|
||||
'click a.js-toggle-hide-logo': this.toggleHideLogo,
|
||||
'click button.js-save-layout': this.saveLayout,
|
||||
'click a.js-toggle-display-authentication-method': this.toggleDisplayAuthenticationMethod,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click a.js-toggle-registration': this.toggleRegistration,
|
||||
'click a.js-toggle-tls': this.toggleTLS,
|
||||
'click a.js-setting-menu': this.switchMenu,
|
||||
'click a.js-toggle-board-choose': this.checkBoard,
|
||||
'click button.js-email-invite': this.inviteThroughEmail,
|
||||
'click button.js-save': this.saveMailServerInfo,
|
||||
'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
|
||||
'click a.js-toggle-hide-logo': this.toggleHideLogo,
|
||||
'click button.js-save-layout': this.saveLayout,
|
||||
'click a.js-toggle-display-authentication-method': this
|
||||
.toggleDisplayAuthenticationMethod,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('setting');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
||||
saveAccountsChange() {
|
||||
const allowEmailChange = ($('input[name=allowEmailChange]:checked').val() === 'true');
|
||||
const allowUserNameChange = ($('input[name=allowUserNameChange]:checked').val() === 'true');
|
||||
const allowEmailChange =
|
||||
$('input[name=allowEmailChange]:checked').val() === 'true';
|
||||
const allowUserNameChange =
|
||||
$('input[name=allowUserNameChange]:checked').val() === 'true';
|
||||
const allowUserDelete =
|
||||
$('input[name=allowUserDelete]:checked').val() === 'true';
|
||||
AccountSettings.update('accounts-allowEmailChange', {
|
||||
$set: {'booleanValue': allowEmailChange},
|
||||
$set: { booleanValue: allowEmailChange },
|
||||
});
|
||||
AccountSettings.update('accounts-allowUserNameChange', {
|
||||
$set: {'booleanValue': allowUserNameChange},
|
||||
$set: { booleanValue: allowUserNameChange },
|
||||
});
|
||||
AccountSettings.update('accounts-allowUserDelete', {
|
||||
$set: { booleanValue: allowUserDelete },
|
||||
});
|
||||
},
|
||||
|
||||
@@ -217,11 +252,16 @@ BlazeComponent.extendComponent({
|
||||
allowUserNameChange() {
|
||||
return AccountSettings.findOne('accounts-allowUserNameChange').booleanValue;
|
||||
},
|
||||
allowUserDelete() {
|
||||
return AccountSettings.findOne('accounts-allowUserDelete').booleanValue;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click button.js-accounts-save': this.saveAccountsChange,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click button.js-accounts-save': this.saveAccountsChange,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('accountSettings');
|
||||
|
||||
@@ -239,9 +279,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
saveMessage() {
|
||||
const message = $('#admin-announcement').val().trim();
|
||||
const message = $('#admin-announcement')
|
||||
.val()
|
||||
.trim();
|
||||
Announcements.update(Announcements.findOne()._id, {
|
||||
$set: {'body': message},
|
||||
$set: { body: message },
|
||||
});
|
||||
},
|
||||
|
||||
@@ -249,7 +291,7 @@ BlazeComponent.extendComponent({
|
||||
this.setLoading(true);
|
||||
const isActive = this.currentSetting().enabled;
|
||||
Announcements.update(Announcements.findOne()._id, {
|
||||
$set: {'enabled': !isActive},
|
||||
$set: { enabled: !isActive },
|
||||
});
|
||||
this.setLoading(false);
|
||||
if (isActive) {
|
||||
@@ -260,14 +302,15 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click a.js-toggle-activemessage': this.toggleActive,
|
||||
'click button.js-announcement-save': this.saveMessage,
|
||||
}];
|
||||
return [
|
||||
{
|
||||
'click a.js-toggle-activemessage': this.toggleActive,
|
||||
'click button.js-announcement-save': this.saveMessage,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('announcementSettings');
|
||||
|
||||
|
||||
Template.selectAuthenticationMethod.onCreated(function() {
|
||||
this.authenticationMethods = new ReactiveVar([]);
|
||||
|
||||
@@ -276,9 +319,11 @@ Template.selectAuthenticationMethod.onCreated(function() {
|
||||
// TODO : add a management of different languages
|
||||
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
|
||||
this.authenticationMethods.set([
|
||||
{value: 'password'},
|
||||
{ value: 'password' },
|
||||
// Gets only the authentication methods availables
|
||||
...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
|
||||
...Object.entries(result)
|
||||
.filter(e => e[1])
|
||||
.map(e => ({ value: e[0] })),
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -105,14 +105,14 @@
|
||||
.bg-white
|
||||
background #f9fbfc;
|
||||
|
||||
.form-control.has-error
|
||||
.wekan-form-control.has-error
|
||||
border-color: #a94442;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
||||
|
||||
li.has-error
|
||||
color #a94442
|
||||
.form-group
|
||||
.form-control
|
||||
.wekan-form-control
|
||||
border-color: #a94442;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
||||
|
||||
@@ -10,8 +10,8 @@ template(name="sidebar")
|
||||
i.fa.fa-keyboard-o
|
||||
span {{_ 'keyboard-shortcuts' }}
|
||||
.sidebar-content.js-board-sidebar-content.js-perfect-scrollbar
|
||||
a.hide-btn.js-hide-sidebar
|
||||
i.fa.fa-navicon
|
||||
//a.hide-btn.js-hide-sidebar
|
||||
// i.fa.fa-navicon
|
||||
unless isDefaultView
|
||||
h2
|
||||
a.fa.fa-chevron-left.js-back-home
|
||||
@@ -34,8 +34,9 @@ template(name="membersWidget")
|
||||
h3
|
||||
i.fa.fa-user
|
||||
| {{_ 'members'}}
|
||||
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
|
||||
i.board-header-btn-icon.fa.fa-cog
|
||||
unless currentUser.isCommentOnly
|
||||
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
|
||||
i.board-header-btn-icon.fa.fa-cog
|
||||
|
||||
.board-widget-content
|
||||
each currentBoard.activeMembers
|
||||
@@ -61,6 +62,7 @@ template(name="boardChangeColorPopup")
|
||||
each backgroundColors
|
||||
.board-background-select.js-select-background
|
||||
span.background-box(class="board-color-{{this}}")
|
||||
span {{this}}
|
||||
if isSelected
|
||||
i.fa.fa-check
|
||||
|
||||
@@ -189,7 +191,9 @@ template(name="labelsWidget")
|
||||
each currentBoard.labels
|
||||
a.card-label(class="card-label-{{color}}"
|
||||
class="{{#if currentUser.isNotCommentOnly}}js-label{{/if}}")
|
||||
span.card-label-name= name
|
||||
span.card-label-name
|
||||
+viewer
|
||||
= name
|
||||
if currentUser.isBoardAdmin
|
||||
a.card-label.add-label.js-add-label
|
||||
i.fa.fa-plus
|
||||
|
||||
@@ -91,29 +91,34 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
showTongueTitle() {
|
||||
if (this.isOpen())
|
||||
return `${TAPi18n.__('sidebar-close')}`;
|
||||
else
|
||||
return `${TAPi18n.__('sidebar-open')}`;
|
||||
if (this.isOpen()) return `${TAPi18n.__('sidebar-close')}`;
|
||||
else return `${TAPi18n.__('sidebar-open')}`;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-hide-sidebar': this.hide,
|
||||
'click .js-toggle-sidebar': this.toggle,
|
||||
'click .js-back-home': this.setView,
|
||||
'click .js-shortcuts'() {
|
||||
FlowRouter.go('shortcuts');
|
||||
return [
|
||||
{
|
||||
'click .js-hide-sidebar': this.hide,
|
||||
'click .js-toggle-sidebar': this.toggle,
|
||||
'click .js-back-home': this.setView,
|
||||
'click .js-shortcuts'() {
|
||||
FlowRouter.go('shortcuts');
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('sidebar');
|
||||
|
||||
Blaze.registerHelper('Sidebar', () => Sidebar);
|
||||
|
||||
EscapeActions.register('sidebarView',
|
||||
() => { Sidebar.setView(defaultView); },
|
||||
() => { return Sidebar && Sidebar.getView() !== defaultView; }
|
||||
EscapeActions.register(
|
||||
'sidebarView',
|
||||
() => {
|
||||
Sidebar.setView(defaultView);
|
||||
},
|
||||
() => {
|
||||
return Sidebar && Sidebar.getView() !== defaultView;
|
||||
},
|
||||
);
|
||||
|
||||
Template.memberPopup.helpers({
|
||||
@@ -122,13 +127,13 @@ Template.memberPopup.helpers({
|
||||
},
|
||||
memberType() {
|
||||
const type = Users.findOne(this.userId).isBoardAdmin() ? 'admin' : 'normal';
|
||||
if(type === 'normal'){
|
||||
if (type === 'normal') {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const commentOnly = currentBoard.hasCommentOnly(this.userId);
|
||||
const noComments = currentBoard.hasNoComments(this.userId);
|
||||
if(commentOnly){
|
||||
if (commentOnly) {
|
||||
return TAPi18n.__('comment-only').toLowerCase();
|
||||
} else if(noComments) {
|
||||
} else if (noComments) {
|
||||
return TAPi18n.__('no-comments').toLowerCase();
|
||||
} else {
|
||||
return TAPi18n.__(type).toLowerCase();
|
||||
@@ -197,7 +202,7 @@ Template.memberPopup.events({
|
||||
'click .js-remove-member': Popup.afterConfirm('removeMember', function() {
|
||||
const boardId = Session.get('currentBoard');
|
||||
const memberId = this.userId;
|
||||
Cards.find({ boardId, members: memberId }).forEach((card) => {
|
||||
Cards.find({ boardId, members: memberId }).forEach(card => {
|
||||
card.unassignMember(memberId);
|
||||
});
|
||||
Boards.findOne(boardId).removeMember(memberId);
|
||||
@@ -274,38 +279,40 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit'(evt) {
|
||||
evt.preventDefault();
|
||||
const url = evt.target.url.value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
let id = null;
|
||||
let integration = null;
|
||||
if (evt.target.id) {
|
||||
id = evt.target.id.value;
|
||||
integration = this.integration(id);
|
||||
if (url) {
|
||||
Integrations.update(integration._id, {
|
||||
$set: {
|
||||
url: `${url}`,
|
||||
},
|
||||
return [
|
||||
{
|
||||
submit(evt) {
|
||||
evt.preventDefault();
|
||||
const url = evt.target.url.value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
let id = null;
|
||||
let integration = null;
|
||||
if (evt.target.id) {
|
||||
id = evt.target.id.value;
|
||||
integration = this.integration(id);
|
||||
if (url) {
|
||||
Integrations.update(integration._id, {
|
||||
$set: {
|
||||
url: `${url}`,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
Integrations.remove(integration._id);
|
||||
}
|
||||
} else if (url) {
|
||||
Integrations.insert({
|
||||
userId: Meteor.userId(),
|
||||
enabled: true,
|
||||
type: 'outgoing-webhooks',
|
||||
url: `${url}`,
|
||||
boardId: `${boardId}`,
|
||||
activities: ['all'],
|
||||
});
|
||||
} else {
|
||||
Integrations.remove(integration._id);
|
||||
}
|
||||
} else if (url) {
|
||||
Integrations.insert({
|
||||
userId: Meteor.userId(),
|
||||
enabled: true,
|
||||
type: 'outgoing-webhooks',
|
||||
url: `${url}`,
|
||||
boardId: `${boardId}`,
|
||||
activities: ['all'],
|
||||
});
|
||||
}
|
||||
Popup.close();
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('outgoingWebhooksPopup');
|
||||
|
||||
@@ -375,14 +382,16 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-select-background'(evt) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const newColor = this.currentData().toString();
|
||||
currentBoard.setColor(newColor);
|
||||
evt.preventDefault();
|
||||
return [
|
||||
{
|
||||
'click .js-select-background'(evt) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const newColor = this.currentData().toString();
|
||||
currentBoard.setColor(newColor);
|
||||
evt.preventDefault();
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('boardChangeColorPopup');
|
||||
|
||||
@@ -400,25 +409,34 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
isNullBoardSelected() {
|
||||
return (this.currentBoard.subtasksDefaultBoardId === null) || (this.currentBoard.subtasksDefaultBoardId === undefined);
|
||||
return (
|
||||
this.currentBoard.subtasksDefaultBoardId === null ||
|
||||
this.currentBoard.subtasksDefaultBoardId === undefined
|
||||
);
|
||||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
lists() {
|
||||
return Lists.find({
|
||||
boardId: this.currentBoard._id,
|
||||
archived: false,
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return Lists.find(
|
||||
{
|
||||
boardId: this.currentBoard._id,
|
||||
archived: false,
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
hasLists() {
|
||||
@@ -431,54 +449,74 @@ BlazeComponent.extendComponent({
|
||||
|
||||
presentParentTask() {
|
||||
let result = this.currentBoard.presentParentTask;
|
||||
if ((result === null) || (result === undefined)) {
|
||||
if (result === null || result === undefined) {
|
||||
result = 'no-parent';
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-field-has-subtasks'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
|
||||
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-has-subtasks .materialCheckBox').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-has-subtasks').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-deposit-board').prop('disabled', !this.currentBoard.allowsSubtasks);
|
||||
},
|
||||
'change .js-field-deposit-board'(evt) {
|
||||
let value = evt.target.value;
|
||||
if (value === 'null') {
|
||||
value = null;
|
||||
}
|
||||
this.currentBoard.setSubtasksDefaultBoardId(value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'change .js-field-deposit-list'(evt) {
|
||||
this.currentBoard.setSubtasksDefaultListId(evt.target.value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-field-show-parent-in-minicard'(evt) {
|
||||
const value = evt.target.id || $(evt.target).parent()[0].id || $(evt.target).parent()[0].parent()[0].id;
|
||||
const options = [
|
||||
'prefix-with-full-path',
|
||||
'prefix-with-parent',
|
||||
'subtext-with-full-path',
|
||||
'subtext-with-parent',
|
||||
'no-parent'];
|
||||
options.forEach(function(element) {
|
||||
if (element !== value) {
|
||||
$(`#${element} .materialCheckBox`).toggleClass('is-checked', false);
|
||||
$(`#${element}`).toggleClass('is-checked', false);
|
||||
return [
|
||||
{
|
||||
'click .js-field-has-subtasks'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
|
||||
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-has-subtasks .materialCheckBox').toggleClass(
|
||||
'is-checked',
|
||||
this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
$('.js-field-has-subtasks').toggleClass(
|
||||
'is-checked',
|
||||
this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
$('.js-field-deposit-board').prop(
|
||||
'disabled',
|
||||
!this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
},
|
||||
'change .js-field-deposit-board'(evt) {
|
||||
let value = evt.target.value;
|
||||
if (value === 'null') {
|
||||
value = null;
|
||||
}
|
||||
});
|
||||
$(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
|
||||
$(`#${value}`).toggleClass('is-checked', true);
|
||||
this.currentBoard.setPresentParentTask(value);
|
||||
evt.preventDefault();
|
||||
this.currentBoard.setSubtasksDefaultBoardId(value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'change .js-field-deposit-list'(evt) {
|
||||
this.currentBoard.setSubtasksDefaultListId(evt.target.value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-field-show-parent-in-minicard'(evt) {
|
||||
const value =
|
||||
evt.target.id ||
|
||||
$(evt.target).parent()[0].id ||
|
||||
$(evt.target)
|
||||
.parent()[0]
|
||||
.parent()[0].id;
|
||||
const options = [
|
||||
'prefix-with-full-path',
|
||||
'prefix-with-parent',
|
||||
'subtext-with-full-path',
|
||||
'subtext-with-parent',
|
||||
'no-parent',
|
||||
];
|
||||
options.forEach(function(element) {
|
||||
if (element !== value) {
|
||||
$(`#${element} .materialCheckBox`).toggleClass(
|
||||
'is-checked',
|
||||
false,
|
||||
);
|
||||
$(`#${element}`).toggleClass('is-checked', false);
|
||||
}
|
||||
});
|
||||
$(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
|
||||
$(`#${value}`).toggleClass('is-checked', true);
|
||||
this.currentBoard.setPresentParentTask(value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('boardSubtaskSettingsPopup');
|
||||
|
||||
@@ -528,35 +566,46 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'keyup input'() {
|
||||
this.setError('');
|
||||
return [
|
||||
{
|
||||
'keyup input'() {
|
||||
this.setError('');
|
||||
},
|
||||
'click .js-select-member'() {
|
||||
const userId = this.currentData()._id;
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (!currentBoard.hasMember(userId)) {
|
||||
this.inviteUser(userId);
|
||||
}
|
||||
},
|
||||
'click .js-email-invite'() {
|
||||
const idNameEmail = $('.js-search-member input').val();
|
||||
if (idNameEmail.indexOf('@') < 0 || this.isValidEmail(idNameEmail)) {
|
||||
this.inviteUser(idNameEmail);
|
||||
} else this.setError('email-invalid');
|
||||
},
|
||||
},
|
||||
'click .js-select-member'() {
|
||||
const userId = this.currentData()._id;
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (!currentBoard.hasMember(userId)) {
|
||||
this.inviteUser(userId);
|
||||
}
|
||||
},
|
||||
'click .js-email-invite'() {
|
||||
const idNameEmail = $('.js-search-member input').val();
|
||||
if (idNameEmail.indexOf('@')<0 || this.isValidEmail(idNameEmail)) {
|
||||
this.inviteUser(idNameEmail);
|
||||
} else this.setError('email-invalid');
|
||||
},
|
||||
}];
|
||||
];
|
||||
},
|
||||
}).register('addMemberPopup');
|
||||
|
||||
Template.changePermissionsPopup.events({
|
||||
'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'(event) {
|
||||
'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'(
|
||||
event,
|
||||
) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const memberId = this.userId;
|
||||
const isAdmin = $(event.currentTarget).hasClass('js-set-admin');
|
||||
const isCommentOnly = $(event.currentTarget).hasClass('js-set-comment-only');
|
||||
const isCommentOnly = $(event.currentTarget).hasClass(
|
||||
'js-set-comment-only',
|
||||
);
|
||||
const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
|
||||
currentBoard.setMemberPermission(memberId, isAdmin, isNoComments, isCommentOnly);
|
||||
currentBoard.setMemberPermission(
|
||||
memberId,
|
||||
isAdmin,
|
||||
isNoComments,
|
||||
isCommentOnly,
|
||||
);
|
||||
Popup.back(1);
|
||||
},
|
||||
});
|
||||
@@ -569,21 +618,33 @@ Template.changePermissionsPopup.helpers({
|
||||
|
||||
isNormal() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return !currentBoard.hasAdmin(this.userId) && !currentBoard.hasNoComments(this.userId) && !currentBoard.hasCommentOnly(this.userId);
|
||||
return (
|
||||
!currentBoard.hasAdmin(this.userId) &&
|
||||
!currentBoard.hasNoComments(this.userId) &&
|
||||
!currentBoard.hasCommentOnly(this.userId)
|
||||
);
|
||||
},
|
||||
|
||||
isNoComments() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return !currentBoard.hasAdmin(this.userId) && currentBoard.hasNoComments(this.userId);
|
||||
return (
|
||||
!currentBoard.hasAdmin(this.userId) &&
|
||||
currentBoard.hasNoComments(this.userId)
|
||||
);
|
||||
},
|
||||
|
||||
isCommentOnly() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return !currentBoard.hasAdmin(this.userId) && currentBoard.hasCommentOnly(this.userId);
|
||||
return (
|
||||
!currentBoard.hasAdmin(this.userId) &&
|
||||
currentBoard.hasCommentOnly(this.userId)
|
||||
);
|
||||
},
|
||||
|
||||
isLastAdmin() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return currentBoard.hasAdmin(this.userId) && (currentBoard.activeAdmins() === 1);
|
||||
return (
|
||||
currentBoard.hasAdmin(this.userId) && currentBoard.activeAdmins() === 1
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1,38 +1,56 @@
|
||||
template(name="archivesSidebar")
|
||||
+basicTabs(tabs=tabs)
|
||||
|
||||
+tabContent(slug="cards")
|
||||
each archivedCards
|
||||
.minicard-wrapper.js-minicard
|
||||
+minicard(this)
|
||||
if currentUser.isBoardMember
|
||||
if isArchiveReady.get
|
||||
+basicTabs(tabs=tabs)
|
||||
+tabContent(slug="cards")
|
||||
p.quiet
|
||||
a.js-restore-card {{_ 'restore'}}
|
||||
a.js-restore-all-cards {{_ 'restore-all'}}
|
||||
| -
|
||||
a.js-delete-card {{_ 'delete'}}
|
||||
if cardIsInArchivedList
|
||||
p.quiet.small ({{_ 'warn-list-archived'}})
|
||||
else
|
||||
p.no-items-message {{_ 'no-archived-cards'}}
|
||||
|
||||
+tabContent(slug="lists")
|
||||
ul.archived-lists
|
||||
each archivedLists
|
||||
li.archived-lists-item
|
||||
a.js-delete-all-cards {{_ 'delete-all'}}
|
||||
each archivedCards
|
||||
.minicard-wrapper.js-minicard
|
||||
+minicard(this)
|
||||
if currentUser.isBoardMember
|
||||
button.js-restore-list
|
||||
i.fa.fa-undo
|
||||
= title
|
||||
else
|
||||
li.no-items-message {{_ 'no-archived-lists'}}
|
||||
p.quiet
|
||||
a.js-restore-card {{_ 'restore'}}
|
||||
| -
|
||||
a.js-delete-card {{_ 'delete'}}
|
||||
if cardIsInArchivedList
|
||||
p.quiet.small ({{_ 'warn-list-archived'}})
|
||||
else
|
||||
p.no-items-message {{_ 'no-archived-cards'}}
|
||||
|
||||
+tabContent(slug="swimlanes")
|
||||
ul.archived-lists
|
||||
each archivedSwimlanes
|
||||
li.archived-lists-item
|
||||
if currentUser.isBoardMember
|
||||
button.js-restore-swimlane
|
||||
i.fa.fa-undo
|
||||
= title
|
||||
else
|
||||
li.no-items-message {{_ 'no-archived-swimlanes'}}
|
||||
+tabContent(slug="lists")
|
||||
p.quiet
|
||||
a.js-restore-all-lists {{_ 'restore-all'}}
|
||||
| -
|
||||
a.js-delete-all-lists {{_ 'delete-all'}}
|
||||
ul.archived-lists
|
||||
each archivedLists
|
||||
li.archived-lists-item
|
||||
= title
|
||||
if currentUser.isBoardMember
|
||||
p.quiet
|
||||
a.js-restore-list {{_ 'restore'}}
|
||||
| -
|
||||
a.js-delete-list {{_ 'delete'}}
|
||||
else
|
||||
li.no-items-message {{_ 'no-archived-lists'}}
|
||||
|
||||
+tabContent(slug="swimlanes")
|
||||
p.quiet
|
||||
a.js-restore-all-swimlanes {{_ 'restore-all'}}
|
||||
| -
|
||||
a.js-delete-all-swimlanes {{_ 'delete-all'}}
|
||||
ul.archived-lists
|
||||
each archivedSwimlanes
|
||||
li.archived-lists-item
|
||||
= title
|
||||
if currentUser.isBoardMember
|
||||
p.quiet
|
||||
a.js-restore-swimlane {{_ 'restore'}}
|
||||
| -
|
||||
a.js-delete-swimlane {{_ 'delete'}}
|
||||
else
|
||||
li.no-items-message {{_ 'no-archived-swimlanes'}}
|
||||
else
|
||||
+spinner
|
||||
Loaded 100 of 626 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user