Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60df1fa1ee | ||
|
|
8e93912389 | ||
|
|
064e4a2cf9 | ||
|
|
8ce9939217 | ||
|
|
41a28df430 | ||
|
|
c36a74b956 | ||
|
|
f6f7705f23 | ||
|
|
6209b792aa | ||
|
|
f8873b009b | ||
|
|
1a1f610977 | ||
|
|
3d7871ebfc | ||
|
|
cd253522a3 | ||
|
|
cc70773fd5 | ||
|
|
27943796ad | ||
|
|
9b28847592 | ||
|
|
5376bc7b79 | ||
|
|
19d42c675b | ||
|
|
be3e4af643 | ||
|
|
96b3352b2b | ||
|
|
62408fcbf7 | ||
|
|
2bf004120d | ||
|
|
0709b5abc8 | ||
|
|
ac43b4c1e6 | ||
|
|
5aeb88ee6c | ||
|
|
c25162aac7 | ||
|
|
da6df62042 | ||
|
|
458b9eddb2 | ||
|
|
7af7fe6146 | ||
|
|
340d7c731f | ||
|
|
e142acfdb7 | ||
|
|
e928660bc0 | ||
|
|
7a90e89348 | ||
|
|
1f1aea87a4 | ||
|
|
e9b7c4cdde | ||
|
|
1bf7f95bd8 | ||
|
|
2a065825fa | ||
|
|
492ed2bdc2 | ||
|
|
afe7d4991d | ||
|
|
0649add494 | ||
|
|
2dafde8db9 | ||
|
|
782d0b6209 | ||
|
|
8dcd360f93 | ||
|
|
ea1af474fd | ||
|
|
e3d0aece14 | ||
|
|
75dae5c714 | ||
|
|
f96bba7a63 | ||
|
|
3b3950369c | ||
|
|
a35df88805 | ||
|
|
40c70c439d | ||
|
|
2c4d3fa317 | ||
|
|
a4dbe98299 | ||
|
|
98602dabb0 | ||
|
|
6ae5f0cea5 | ||
|
|
0316802b84 | ||
|
|
25561946ed | ||
|
|
c29aa76d02 | ||
|
|
258af8bfed | ||
|
|
e2a374f0aa | ||
|
|
6a53aaf24a | ||
|
|
b2eccc7b54 | ||
|
|
c2fa23d525 | ||
|
|
e6fa02e0a1 | ||
|
|
e164aafbd8 | ||
|
|
a863194e74 | ||
|
|
f518397f7a | ||
|
|
fc91d866db | ||
|
|
8670561f78 | ||
|
|
d8474056d2 | ||
|
|
e48c5fb066 | ||
|
|
467a3be850 | ||
|
|
1b12d3f04f | ||
|
|
c77979ff13 | ||
|
|
0a1e7006b3 | ||
|
|
d5a8ec1c36 | ||
|
|
d497b75c1d | ||
|
|
07724f99ae | ||
|
|
8898862d36 | ||
|
|
2cc3507740 | ||
|
|
3565437728 | ||
|
|
15ba982810 | ||
|
|
488a895850 | ||
|
|
c7ded51502 | ||
|
|
c66cc3d4da | ||
|
|
c60967e935 | ||
|
|
343343e7e7 | ||
|
|
4f5de87cc4 | ||
|
|
f371cbd5d2 | ||
|
|
99ef6e3a20 | ||
|
|
599ace1db7 | ||
|
|
a06daff92e | ||
|
|
58e505f79a | ||
|
|
788dd0a81a | ||
|
|
82d76e55e5 | ||
|
|
38dfe0b9a7 | ||
|
|
b0f345ba21 | ||
|
|
95a4592346 | ||
|
|
3168c4067d | ||
|
|
115d23f929 | ||
|
|
975258ef39 | ||
|
|
b13d28203b | ||
|
|
4786b0c18d | ||
|
|
bc9512b750 | ||
|
|
abb31cb603 | ||
|
|
351d4767d7 | ||
|
|
f595120e72 | ||
|
|
3f2c3c1af9 | ||
|
|
69f2e8e74d | ||
|
|
72553c1f14 | ||
|
|
96abe3c691 | ||
|
|
2079a5bfa3 | ||
|
|
34bfb09c85 | ||
|
|
f652b677d0 | ||
|
|
c93ea33d05 | ||
|
|
26e0bbce17 | ||
|
|
a636611471 | ||
|
|
9313979db7 | ||
|
|
1c4f93a2ce | ||
|
|
a34f13aa2a | ||
|
|
ab2a721a14 | ||
|
|
16cc4d7cbd | ||
|
|
730352a48d | ||
|
|
5b41d72e8d | ||
|
|
33b8952c0b | ||
|
|
12483350a1 | ||
|
|
bbc3ab3f99 | ||
|
|
a9e53ec088 | ||
|
|
704808a6cb | ||
|
|
5b2f439500 | ||
|
|
688bc063e5 | ||
|
|
1eb3d25b40 | ||
|
|
7206e2528f | ||
|
|
e1029816df | ||
|
|
639c95bade | ||
|
|
0907936d2b | ||
|
|
cdc988c7b1 | ||
|
|
274a997e62 | ||
|
|
537a48bede | ||
|
|
82f0924914 | ||
|
|
7214be488b | ||
|
|
36b5965dd0 | ||
|
|
3a00a59ffb | ||
|
|
6b855ac045 | ||
|
|
9bba0ea283 | ||
|
|
de7509dc60 | ||
|
|
9fd14f7ecb | ||
|
|
3cf09efb13 | ||
|
|
22083787f9 | ||
|
|
1728298659 | ||
|
|
865c8305cb | ||
|
|
31dbdc835d | ||
|
|
56efb5c410 | ||
|
|
ebdb3a0cc3 | ||
|
|
473af2f5e2 | ||
|
|
1d2ab299b0 | ||
|
|
f2eda9d7ed | ||
|
|
52129ed714 | ||
|
|
e868f9dcee | ||
|
|
3f059ec1e0 | ||
|
|
8cb3974eff | ||
|
|
7d62d0920c | ||
|
|
b787854289 | ||
|
|
d84ea7d16f | ||
|
|
7318e42080 | ||
|
|
9f11ea0cc6 | ||
|
|
2ec15602d2 | ||
|
|
6a89605477 | ||
|
|
dff261efee | ||
|
|
931f0d508a | ||
|
|
ea823ab68f | ||
|
|
b56ddf9df9 | ||
|
|
9a468dfce5 | ||
|
|
db3476c8ec | ||
|
|
7a5401d5f0 | ||
|
|
d3ca816762 | ||
|
|
b7492e4a11 | ||
|
|
32ce2b51d8 | ||
|
|
e033959278 | ||
|
|
3e8f9ef1a5 | ||
|
|
92efb8bec4 | ||
|
|
9e1aaf163f | ||
|
|
3f19091a91 | ||
|
|
ae6eb706c7 | ||
|
|
806df30ba3 | ||
|
|
6a88c2f289 | ||
|
|
acc6bc0758 | ||
|
|
f3bc92904c | ||
|
|
3308d90a3a | ||
|
|
5bb99ba096 | ||
|
|
6ac7523d9d | ||
|
|
758bbab441 | ||
|
|
b26504f414 | ||
|
|
e195c731de | ||
|
|
00d581245c | ||
|
|
7faff44825 | ||
|
|
6fc9dd1323 | ||
|
|
7d6d3af54a | ||
|
|
064c4d7ca7 | ||
|
|
0fb15888bc | ||
|
|
32f50e1658 | ||
|
|
d2d4840758 | ||
|
|
bc2a20f04e | ||
|
|
2737d6b23f | ||
|
|
f53c624b0f | ||
|
|
77f8b76d4e | ||
|
|
13a2bd6380 | ||
|
|
bec9903f46 | ||
|
|
36e006fa4e | ||
|
|
5b415c851b | ||
|
|
95fb392692 | ||
|
|
f2453834ac | ||
|
|
3064cdd371 | ||
|
|
417fed8605 | ||
|
|
4eca27498c | ||
|
|
d962ecd77f | ||
|
|
6feb35c079 | ||
|
|
ea90ce8784 | ||
|
|
4a42d683af | ||
|
|
8faa48fe12 | ||
|
|
67c4fae32b | ||
|
|
42d31901b5 | ||
|
|
72b22a73b6 | ||
|
|
4d30503cb5 | ||
|
|
7b45a0fbfe | ||
|
|
42dee10dbe | ||
|
|
68be12d166 | ||
|
|
fd896c29b5 | ||
|
|
98a4be643e | ||
|
|
b650a89728 | ||
|
|
346dba4969 | ||
|
|
f3b858ca21 | ||
|
|
5bc355f9a5 | ||
|
|
21fa26a1be | ||
|
|
66372271c2 | ||
|
|
e60926f847 | ||
|
|
b04df1d54d | ||
|
|
83f81f4586 | ||
|
|
4785086e2f | ||
|
|
98c38fe58f | ||
|
|
44378fe9d1 | ||
|
|
afcca947e3 | ||
|
|
20526756db | ||
|
|
58724f93c9 | ||
|
|
5d6c68a9cf | ||
|
|
ed4c49090a | ||
|
|
75dc5f226c | ||
|
|
020b663836 | ||
|
|
e5f0dd7dd8 | ||
|
|
4f34adbd44 | ||
|
|
62b72a03c4 | ||
|
|
5696e4a4c6 | ||
|
|
814e264521 | ||
|
|
872ed4b2f9 | ||
|
|
1d8633e7e6 | ||
|
|
b3637f2781 | ||
|
|
68d3cdae58 | ||
|
|
620a362244 | ||
|
|
d5cff1ec48 | ||
|
|
8d7714760c | ||
|
|
a37723f8a4 | ||
|
|
e3a727edec | ||
|
|
4ee88e026e | ||
|
|
55a2473d81 | ||
|
|
ba754cfaea | ||
|
|
7a8011f887 | ||
|
|
125231beff | ||
|
|
f70e47a012 | ||
|
|
0c34566aef | ||
|
|
f29d7daa1d | ||
|
|
0120c73a80 | ||
|
|
194b6ad46e | ||
|
|
158ddadf54 | ||
|
|
9fb0ef7eb2 | ||
|
|
503be10d36 | ||
|
|
3021f1cbe1 | ||
|
|
c66eba217b | ||
|
|
8486ccbaa8 | ||
|
|
03d7fc02ec | ||
|
|
ddd00a27a9 | ||
|
|
b31c42acc4 | ||
|
|
616903114f | ||
|
|
6e6e7ded82 | ||
|
|
6c7e1ed3ae | ||
|
|
d896f6dbcf | ||
|
|
5e963375cb | ||
|
|
57119868bb | ||
|
|
7b445dfc93 | ||
|
|
5f27a76312 | ||
|
|
b34960d1bd | ||
|
|
ff550e9110 | ||
|
|
0fcfc3a340 | ||
|
|
f95d19ab49 | ||
|
|
50744dead8 | ||
|
|
a0e5737e4c | ||
|
|
ecd9b9b5f4 | ||
|
|
389ed2f1ad | ||
|
|
273dc5223b | ||
|
|
db32f595fe | ||
|
|
4d967360b2 | ||
|
|
f14dce2f19 | ||
|
|
82f73ab58c | ||
|
|
0b52ba4ded | ||
|
|
e0046032e8 | ||
|
|
f094efa929 | ||
|
|
6ce8eeee6c | ||
|
|
be70c02922 | ||
|
|
a61afda39e | ||
|
|
126ab58e03 | ||
|
|
ca95bba358 | ||
|
|
6407893aa6 | ||
|
|
45ed9b426f | ||
|
|
11d6b77bb1 | ||
|
|
f248dada3f | ||
|
|
a56988c487 | ||
|
|
74b5bdf2aa | ||
|
|
753d5b953d | ||
|
|
ea26f70669 | ||
|
|
682c23e011 | ||
|
|
d4a307f66d | ||
|
|
7c9bf3ce0f | ||
|
|
6cbd4cabc7 | ||
|
|
10b953041f | ||
|
|
36fb31c138 | ||
|
|
3f2490e64d | ||
|
|
37065c1d50 | ||
|
|
9764e03a37 | ||
|
|
0f5226f814 | ||
|
|
3b9f2ca7c2 | ||
|
|
7953e5a52a | ||
|
|
9617c78798 | ||
|
|
28f21487b0 | ||
|
|
4567f08a0b | ||
|
|
b0281e1941 | ||
|
|
efed77b474 | ||
|
|
a70b97676a | ||
|
|
7b54983ec2 | ||
|
|
bc2fcef7c4 | ||
|
|
750349053c | ||
|
|
bfbe32e1ec | ||
|
|
ccfaf879dd | ||
|
|
ebd32af5c1 | ||
|
|
43ea7d761e | ||
|
|
a715e30cba | ||
|
|
8633bd8974 | ||
|
|
9474bee7fa | ||
|
|
92b86ada39 | ||
|
|
03bea6e406 | ||
|
|
6183402754 | ||
|
|
663ba26d4d | ||
|
|
510407467c | ||
|
|
b477fc1b1c | ||
|
|
58155288fb | ||
|
|
dd0682328b | ||
|
|
d1ab787215 | ||
|
|
71d32c6bc8 | ||
|
|
3acc9d4468 | ||
|
|
d5403bbfc5 | ||
|
|
583c369233 | ||
|
|
96efa659a8 | ||
|
|
87af5ecb36 | ||
|
|
3d39793cbf | ||
|
|
79d4cd83b1 | ||
|
|
9bc8040634 | ||
|
|
93ae815946 | ||
|
|
be5f435bc5 | ||
|
|
8a74210c60 | ||
|
|
629a6f0103 | ||
|
|
2522a98f7d | ||
|
|
0ff5ce8fde | ||
|
|
5084102e6e | ||
|
|
b7c5ba3d1b | ||
|
|
e5b9d41e67 | ||
|
|
007c064672 | ||
|
|
9c15fb1e69 | ||
|
|
7761a22bb4 | ||
|
|
16388a5e25 | ||
|
|
43c28a2f15 | ||
|
|
d29b40c0ee | ||
|
|
e21c47d3cf | ||
|
|
971de9ca34 | ||
|
|
3976e02012 | ||
|
|
d4f5717264 | ||
|
|
362220299f | ||
|
|
dbdb26a044 | ||
|
|
60e8deb064 | ||
|
|
4b06e2188c | ||
|
|
3307629c8c | ||
|
|
3978c08757 | ||
|
|
e21b0674e3 | ||
|
|
a97360dcfb | ||
|
|
6705007595 | ||
|
|
8c6164f590 | ||
|
|
bbb9815c30 | ||
|
|
0083215ea3 | ||
|
|
cdea7e8869 | ||
|
|
828822daad | ||
|
|
0cf9a7b552 | ||
|
|
7cffce972d | ||
|
|
ad01526124 | ||
|
|
fa34950ca9 | ||
|
|
9bbeb73db1 | ||
|
|
ff419f0b60 | ||
|
|
537a9c0568 | ||
|
|
843783d6cb | ||
|
|
07abed0067 | ||
|
|
fe885860e3 | ||
|
|
0dd3ff29f2 | ||
|
|
576523021e | ||
|
|
9d8a38f324 | ||
|
|
2c78aab3dc | ||
|
|
45c8b00339 | ||
|
|
9156e5a6b6 | ||
|
|
cc792ddd57 | ||
|
|
b8d60f0069 | ||
|
|
b2deab544b | ||
|
|
f96c1eda70 | ||
|
|
e9bbbb6a04 | ||
|
|
efcdbf2e3d | ||
|
|
6e12e2fa59 | ||
|
|
a31ece0974 | ||
|
|
06d85b66f1 | ||
|
|
af830812db | ||
|
|
3f0600fed7 | ||
|
|
4e21e04a48 | ||
|
|
9779f2a7e6 | ||
|
|
a98f6d8e8a | ||
|
|
d93f33d919 | ||
|
|
564ab219c6 | ||
|
|
026d8f4941 | ||
|
|
4b2193436e | ||
|
|
dfb558ae99 | ||
|
|
76731e852d | ||
|
|
b794c22ac0 | ||
|
|
b778cea34f | ||
|
|
a1582c3e65 | ||
|
|
977503fa79 | ||
|
|
a809284a14 | ||
|
|
8e3cbd2d82 | ||
|
|
f3147af2f7 | ||
|
|
86d686fe1d | ||
|
|
7d52ae16d5 | ||
|
|
c882ebe030 | ||
|
|
d80a258c8b | ||
|
|
96d0447df0 | ||
|
|
65769f3726 | ||
|
|
315b5bad46 | ||
|
|
692138c40b | ||
|
|
5c696e5a3c | ||
|
|
ebf3e2e65b | ||
|
|
d302d6f857 | ||
|
|
a282384ab0 | ||
|
|
c87001fa9f | ||
|
|
8fdb126a99 | ||
|
|
e2e35655c3 | ||
|
|
b8c527d52b | ||
|
|
a8ab7b15d8 | ||
|
|
c9118c21d1 | ||
|
|
16adcb7e5a | ||
|
|
d295cb8385 | ||
|
|
ece011f6ae | ||
|
|
3084f24278 | ||
|
|
843478ac40 | ||
|
|
43d14f8b2b | ||
|
|
7198e6b66e | ||
|
|
a666883986 | ||
|
|
76dbadf1ba | ||
|
|
a7b7fd874d | ||
|
|
ee0fb094e6 | ||
|
|
306301e881 | ||
|
|
411ded513d | ||
|
|
c12496e91d | ||
|
|
0cf8996df9 | ||
|
|
46176f5cec | ||
|
|
b9a7bd3503 | ||
|
|
861868fa29 | ||
|
|
8d76db91b8 | ||
|
|
39ffe1d80d | ||
|
|
3513198416 | ||
|
|
20294d833a | ||
|
|
ad09cdb1d1 | ||
|
|
5aa090e911 | ||
|
|
7ea0912159 | ||
|
|
6d47c2e568 | ||
|
|
b5f74162af | ||
|
|
851246a59b | ||
|
|
446e29e422 | ||
|
|
efdab37f3f | ||
|
|
db1cf5bb64 | ||
|
|
9b7b0cf557 | ||
|
|
32563e053d | ||
|
|
b5b81af103 | ||
|
|
a1d883b22f | ||
|
|
1a7ee618be | ||
|
|
405e0b9bb2 | ||
|
|
d3e37d2912 | ||
|
|
2859457600 | ||
|
|
4857bce5d9 | ||
|
|
16dbbfa98c | ||
|
|
67d23ff8ae | ||
|
|
30680f13d6 | ||
|
|
3b526cad40 | ||
|
|
55f0ac959a | ||
|
|
6dc515210a | ||
|
|
7777dd3b45 | ||
|
|
e9a874144d | ||
|
|
77b5244723 | ||
|
|
af560bc9b4 | ||
|
|
c569565ec0 | ||
|
|
80f1222af4 | ||
|
|
76ff965619 | ||
|
|
2ca01b589f | ||
|
|
46cd1aced9 | ||
|
|
d5d5e4448c | ||
|
|
faa1e73027 | ||
|
|
9f6d615ee5 | ||
|
|
520ae551c6 | ||
|
|
d5d75ae7b8 | ||
|
|
13a13e8eca | ||
|
|
cf65f1b7a1 | ||
|
|
aa6a588376 | ||
|
|
4c92cd6ae1 | ||
|
|
f766c1a61c | ||
|
|
264526e85f | ||
|
|
ecc74583ed | ||
|
|
7dad66fbe6 | ||
|
|
47f6fee279 | ||
|
|
460caa7823 | ||
|
|
71d1d9ad98 | ||
|
|
9ed0c3029f | ||
|
|
b7628050be | ||
|
|
2a53a6cb36 | ||
|
|
ad82a900e8 | ||
|
|
4708764bd9 | ||
|
|
e519f989ee | ||
|
|
32e9aa0dda | ||
|
|
c1905f1db1 | ||
|
|
f7e0b837d3 | ||
|
|
9e60b41438 | ||
|
|
778759b8ca | ||
|
|
b58ddcabaf | ||
|
|
716fc32968 | ||
|
|
5d55b21fb6 | ||
|
|
4f4e0a21f8 | ||
|
|
7a60eef375 | ||
|
|
cf9ad221f8 | ||
|
|
f7ab08aeeb | ||
|
|
7d8f1dee62 | ||
|
|
2eebc5c884 | ||
|
|
93c5f08a25 | ||
|
|
3973c45a1f | ||
|
|
1fc3a1db2e | ||
|
|
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 |
No files matched your search
@@ -0,0 +1,162 @@
|
||||
FROM ubuntu:disco
|
||||
LABEL maintainer="sgr"
|
||||
|
||||
ENV BUILD_DEPS="gnupg gosu bsdtar wget curl bzip2 g++ build-essential python git ca-certificates iproute2"
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=8.17.0 \
|
||||
METEOR_RELEASE=1.8.1 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=latest \
|
||||
FIBERS_VERSION=4.0.1 \
|
||||
ARCHITECTURE=linux-x64 \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
|
||||
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="" \
|
||||
MATOMO_DO_NOT_TRACK=true \
|
||||
MATOMO_WITH_USERNAME=false \
|
||||
BROWSER_POLICY_ENABLED=true \
|
||||
TRUSTED_URL="" \
|
||||
WEBHOOKS_ATTRIBUTES="" \
|
||||
OAUTH2_ENABLED=false \
|
||||
OAUTH2_LOGIN_STYLE=redirect \
|
||||
OAUTH2_CLIENT_ID="" \
|
||||
OAUTH2_SECRET="" \
|
||||
OAUTH2_SERVER_URL="" \
|
||||
OAUTH2_AUTH_ENDPOINT="" \
|
||||
OAUTH2_USERINFO_ENDPOINT="" \
|
||||
OAUTH2_TOKEN_ENDPOINT="" \
|
||||
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 \
|
||||
LDAP_HOST="" \
|
||||
LDAP_BASEDN="" \
|
||||
LDAP_LOGIN_FALLBACK=false \
|
||||
LDAP_RECONNECT=true \
|
||||
LDAP_TIMEOUT=10000 \
|
||||
LDAP_IDLE_TIMEOUT=10000 \
|
||||
LDAP_CONNECT_TIMEOUT=10000 \
|
||||
LDAP_AUTHENTIFICATION=false \
|
||||
LDAP_AUTHENTIFICATION_USERDN="" \
|
||||
LDAP_AUTHENTIFICATION_PASSWORD="" \
|
||||
LDAP_LOG_ENABLED=false \
|
||||
LDAP_BACKGROUND_SYNC=false \
|
||||
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="" \
|
||||
LDAP_SEARCH_PAGE_SIZE=0 \
|
||||
LDAP_SEARCH_SIZE_LIMIT=0 \
|
||||
LDAP_GROUP_FILTER_ENABLE=false \
|
||||
LDAP_GROUP_FILTER_OBJECTCLASS="" \
|
||||
LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" \
|
||||
LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" \
|
||||
LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" \
|
||||
LDAP_GROUP_FILTER_GROUP_NAME="" \
|
||||
LDAP_UNIQUE_IDENTIFIER_FIELD="" \
|
||||
LDAP_UTF8_NAMES_SLUGIFY=true \
|
||||
LDAP_USERNAME_FIELD="" \
|
||||
LDAP_FULLNAME_FIELD="" \
|
||||
LDAP_MERGE_EXISTING_USERS=false \
|
||||
LDAP_EMAIL_FIELD="" \
|
||||
LDAP_EMAIL_MATCH_ENABLE=false \
|
||||
LDAP_EMAIL_MATCH_REQUIRE=false \
|
||||
LDAP_EMAIL_MATCH_VERIFIED=false \
|
||||
LDAP_SYNC_USER_DATA=false \
|
||||
LDAP_SYNC_USER_DATA_FIELDMAP="" \
|
||||
LDAP_SYNC_GROUP_ROLES="" \
|
||||
LDAP_DEFAULT_DOMAIN="" \
|
||||
LDAP_SYNC_ADMIN_STATUS="" \
|
||||
LDAP_SYNC_ADMIN_GROUPS="" \
|
||||
HEADER_LOGIN_ID="" \
|
||||
HEADER_LOGIN_FIRSTNAME="" \
|
||||
HEADER_LOGIN_LASTNAME="" \
|
||||
HEADER_LOGIN_EMAIL="" \
|
||||
LOGOUT_WITH_TIMER=false \
|
||||
LOGOUT_IN="" \
|
||||
LOGOUT_ON_HOURS="" \
|
||||
LOGOUT_ON_MINUTES="" \
|
||||
CORS="" \
|
||||
CORS_ALLOW_HEADERS="" \
|
||||
CORS_EXPOSE_HEADERS="" \
|
||||
DEFAULT_AUTHENTICATION_METHOD=""
|
||||
|
||||
# Install OS
|
||||
RUN set -o xtrace \
|
||||
&& useradd --user-group -m --system --home-dir /home/wekan wekan \
|
||||
&& apt-get update \
|
||||
&& apt-get install --assume-yes --no-install-recommends apt-utils apt-transport-https ca-certificates 2>&1 \
|
||||
&& apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
|
||||
|
||||
# Install NodeJS
|
||||
RUN set -o xtrace \
|
||||
&& cd /tmp \
|
||||
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-$ARCHITECTURE.tar.xz" \
|
||||
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
|
||||
&& grep " node-v$NODE_VERSION-$ARCHITECTURE.tar.xz\$" SHASUMS256.txt.asc | sha256sum -c - \
|
||||
&& tar -xJf "node-v$NODE_VERSION-$ARCHITECTURE.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
|
||||
&& rm "node-v$NODE_VERSION-$ARCHITECTURE.tar.xz" SHASUMS256.txt.asc \
|
||||
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
|
||||
&& mkdir -p /usr/local/lib/node_modules/fibers/.node-gyp /root/.node-gyp/${NODE_VERSION} /home/wekan/.config \
|
||||
&& npm install -g npm@${NPM_VERSION} \
|
||||
&& chown wekan:wekan --recursive /home/wekan/.config
|
||||
|
||||
ENV DEBIAN_FRONTEND=dialog
|
||||
|
||||
USER wekan
|
||||
|
||||
# Install Meteor
|
||||
RUN set -o xtrace \
|
||||
&& cd /home/wekan \
|
||||
&& curl https://install.meteor.com/?release=$METEOR_VERSION --output /home/wekan/install-meteor.sh \
|
||||
# Replace tar with bsdtar in the install script; https://github.com/jshimko/meteor-launchpad/issues/39
|
||||
&& sed --in-place "s/tar -xzf.*/bsdtar -xf \"\$TARBALL_FILE\" -C \"\$INSTALL_TMPDIR\"/g" /home/wekan/install-meteor.sh \
|
||||
&& sed --in-place 's/VERBOSITY="--silent"/VERBOSITY="--progress-bar"/' /home/wekan/install-meteor.sh \
|
||||
&& printf "\n[-] Installing Meteor $METEOR_VERSION...\n\n" \
|
||||
&& sh /home/wekan/install-meteor.sh
|
||||
|
||||
ENV PATH=$PATH:/home/wekan/.meteor/
|
||||
|
||||
# Copy source dir
|
||||
USER root
|
||||
|
||||
RUN echo "export PATH=$PATH" >> /etc/environment
|
||||
|
||||
RUN set -o xtrace \
|
||||
&& mkdir /home/wekan/app
|
||||
|
||||
COPY ${SRC_PATH} /home/wekan/app/
|
||||
|
||||
RUN set -o xtrace \
|
||||
&& chown -R wekan:wekan /home/wekan/app /home/wekan/.meteor
|
||||
|
||||
USER wekan
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd /home/wekan/app
|
||||
rm -rf node_modules
|
||||
/home/wekan/.meteor/meteor npm install
|
||||
rm -rf .build
|
||||
/home/wekan/.meteor/meteor build .build --directory
|
||||
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
|
||||
cd .build/bundle/programs/server
|
||||
rm -rf node_modules
|
||||
/home/wekan/.meteor/meteor npm install
|
||||
cd /home/wekan/app
|
||||
@@ -0,0 +1,17 @@
|
||||
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
|
||||
{
|
||||
"dockerComposeFile": ["docker-compose.yml", "docker-compose.extend.yml"],
|
||||
"service": "wekan-dev",
|
||||
"workspaceFolder": "/home/wekan/app",
|
||||
"extensions": [
|
||||
"mutantdino.resourcemonitor",
|
||||
"editorconfig.editorconfig",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"codezombiech.gitignore",
|
||||
"eamodio.gitlens",
|
||||
"gruntfuggly.todo-tree",
|
||||
"dotjoshjohnson.xml",
|
||||
"redhat.vscode-yaml",
|
||||
"vuhrmeister.vscode-meteor"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
|
||||
wekandb-dev:
|
||||
image: mongo:4.0.12
|
||||
container_name: wekan-dev-db
|
||||
restart: unless-stopped
|
||||
command: mongod --smallfiles --oplogSize 128
|
||||
networks:
|
||||
- wekan-dev-tier
|
||||
expose:
|
||||
- 27017
|
||||
volumes:
|
||||
- wekan-dev-db:/data/db
|
||||
- wekan-dev-db-dump:/dump
|
||||
|
||||
wekan-dev:
|
||||
container_name: wekan-dev-app
|
||||
restart: always
|
||||
networks:
|
||||
- wekan-dev-tier
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: .devcontainer/Dockerfile
|
||||
ports:
|
||||
- 3000:3000
|
||||
- 9229:9229
|
||||
environment:
|
||||
- MONGO_URL=mongodb://wekandb-dev:27017/wekan
|
||||
- ROOT_URL=http://localhost:3000
|
||||
- WITH_API=true
|
||||
- RICHER_CARD_COMMENT_EDITOR=true
|
||||
- BROWSER_POLICY_ENABLED=true
|
||||
depends_on:
|
||||
- wekandb-dev
|
||||
volumes:
|
||||
- ..:/app:delegated
|
||||
command:
|
||||
sleep infinity
|
||||
|
||||
volumes:
|
||||
wekan-dev-db:
|
||||
driver: local
|
||||
wekan-dev-db-dump:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
wekan-dev-tier:
|
||||
driver: bridge
|
||||
@@ -145,6 +145,7 @@
|
||||
"allowIsBoardMemberByCard": true,
|
||||
"allowIsBoardMemberCommentOnly": true,
|
||||
"allowIsBoardMemberNoComments": true,
|
||||
"allowIsBoardMemberWorker": true,
|
||||
"Emoji": true,
|
||||
"Checklists": true,
|
||||
"Settings": true,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
* text=auto eol=lf
|
||||
*.{cmd,[cC][mM][dD]} text eol=crlf
|
||||
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
custom: ['https://wekan.team/commercial-support/']
|
||||
@@ -7,8 +7,9 @@ Other Wekan issues can be added here.
|
||||
|
||||
**Server Setup Information**:
|
||||
|
||||
* Note: Please anonymize info, and do not add to this public issue any of your Wekan board URLs, passwords, API tokens etc, do you understand?:
|
||||
* Did you test in newest Wekan?:
|
||||
* For new Wekan install, did you configure root-url correctly https://github.com/wekan/wekan/wiki/Settings ?
|
||||
* For new Wekan install, did you configure root-url correctly so Wekan cards open correctly https://github.com/wekan/wekan/wiki/Settings ?
|
||||
* Wekan version:
|
||||
* If this is about old version of Wekan, what upgrade problem you have?:
|
||||
* Operating System:
|
||||
@@ -16,12 +17,12 @@ Other Wekan issues can be added here.
|
||||
* Http frontend if any (Caddy, Nginx, Apache, see config examples from Wekan GitHub wiki first):
|
||||
* Node Version:
|
||||
* MongoDB Version:
|
||||
* ROOT_URL environment variable http(s)://(subdomain).example.com(/suburl):
|
||||
* Wekan only works on newest desktop Firefox/Chromium/Chrome/Edge/Chromium Edge and mobile Chrome. What webbrowser version are you using?
|
||||
|
||||
**Problem description**:
|
||||
- *REQUIRED: Add recorded animated gif about how it works currently, and screenshot mockups how it should work. Use peek to record animgif in Linux https://github.com/phw/peek*
|
||||
- *Explain steps how to reproduce*
|
||||
- *In webbrowser, what does show Right Click / Inspect / Console ? Chrome shows more detailed info than Firefox.*
|
||||
- *If using Snap, what does show command `sudo snap logs wekan.wekan` ?*
|
||||
- *If using Docker, what does show command `sudo docker logs wekan-app` ?*
|
||||
- *If using Snap, what does show command `sudo snap logs wekan.wekan` ? Please anonymize logs.*
|
||||
- *If using Docker, what does show command `sudo docker logs wekan-app` ? Please anonymize logs.*
|
||||
- *If logs are very long, attach them in .zip file*
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Docker Image CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Build the Docker image
|
||||
run: docker build . --file Dockerfile --tag wekan:$(date +%s)
|
||||
+4
-1
@@ -8,7 +8,6 @@ npm-debug.log
|
||||
.vscode/
|
||||
.idea/
|
||||
.build/*
|
||||
package-lock.json
|
||||
**/parts/
|
||||
**/stage
|
||||
**/prime
|
||||
@@ -29,3 +28,7 @@ versions.json
|
||||
.Trashes
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
.eslintcache
|
||||
.meteor/local
|
||||
.meteor-1.6-snap/.meteor/local
|
||||
.devcontainer/docker-compose.extend.yml
|
||||
@@ -17,3 +17,4 @@ notices-for-facebook-graph-api-2
|
||||
1.4.3-split-account-service-packages
|
||||
1.5-add-dynamic-import-package
|
||||
1.7-split-underscore-from-meteor-base
|
||||
1.8.3-split-jquery-from-blaze
|
||||
+10
-6
@@ -6,9 +6,9 @@
|
||||
meteor-base@1.4.0
|
||||
|
||||
# Build system
|
||||
ecmascript@0.12.4
|
||||
standard-minifier-css@1.5.3
|
||||
standard-minifier-js@2.4.1
|
||||
ecmascript@0.13.2
|
||||
standard-minifier-css@1.5.4
|
||||
standard-minifier-js@2.5.2
|
||||
mquandalle:jade
|
||||
|
||||
# Polyfills
|
||||
@@ -22,7 +22,7 @@ dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
matb33:collection-hooks
|
||||
matteodem:easy-search
|
||||
mongo@1.6.2
|
||||
mongo@1.7.0
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
@@ -75,8 +75,7 @@ horka:swipebox
|
||||
dynamic-import@0.5.1
|
||||
staringatlights:fast-render
|
||||
|
||||
mixmax:smart-disconnect
|
||||
accounts-password@1.5.1
|
||||
accounts-password@1.5.2
|
||||
cfs:gridfs
|
||||
rzymek:fullcalendar
|
||||
momentjs:moment@2.22.2
|
||||
@@ -93,3 +92,8 @@ meteorhacks:picker
|
||||
lamhieu:unblock
|
||||
meteorhacks:aggregate@1.3.0
|
||||
wekan-markdown
|
||||
konecty:mongo-counter
|
||||
percolate:synced-cron
|
||||
easylogic:summernote
|
||||
cfs:filesystem
|
||||
ostrio:cookies
|
||||
+1
-1
@@ -1 +1 @@
|
||||
METEOR@1.8.1
|
||||
METEOR@1.8.3
|
||||
+32
-27
@@ -1,7 +1,7 @@
|
||||
3stack:presence@1.1.2
|
||||
accounts-base@1.4.4
|
||||
accounts-base@1.4.5
|
||||
accounts-oauth@1.1.16
|
||||
accounts-password@1.5.1
|
||||
accounts-password@1.5.2
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
@@ -12,24 +12,25 @@ allow-deny@1.1.0
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
audit-argument-checks@1.0.7
|
||||
autoupdate@1.6.0
|
||||
babel-compiler@7.3.4
|
||||
babel-runtime@1.3.0
|
||||
base64@1.0.11
|
||||
babel-compiler@7.4.2
|
||||
babel-runtime@1.4.0
|
||||
base64@1.0.12
|
||||
binary-heap@1.0.11
|
||||
blaze@2.3.3
|
||||
blaze@2.3.4
|
||||
blaze-tools@1.0.10
|
||||
boilerplate-generator@1.6.0
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
caching-compiler@1.2.1
|
||||
caching-html-compiler@1.1.3
|
||||
callback-hook@1.1.0
|
||||
callback-hook@1.2.0
|
||||
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:filesystem@0.1.2
|
||||
cfs:gridfs@0.0.34
|
||||
cfs:http-methods@0.0.32
|
||||
cfs:http-publish@0.0.13
|
||||
@@ -55,11 +56,12 @@ ddp-server@2.3.0
|
||||
deps@1.0.12
|
||||
diff-sequence@1.1.1
|
||||
dynamic-import@0.5.1
|
||||
ecmascript@0.12.7
|
||||
easylogic:summernote@0.8.8
|
||||
ecmascript@0.13.2
|
||||
ecmascript-runtime@0.7.0
|
||||
ecmascript-runtime-client@0.8.0
|
||||
ecmascript-runtime-server@0.7.1
|
||||
ejson@1.1.0
|
||||
ecmascript-runtime-client@0.9.0
|
||||
ecmascript-runtime-server@0.8.0
|
||||
ejson@1.1.1
|
||||
email@1.2.3
|
||||
es5-shim@4.8.0
|
||||
fastclick@1.0.13
|
||||
@@ -79,16 +81,17 @@ 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
|
||||
konecty:mongo-counter@0.0.5_3
|
||||
lamhieu:meteorx@2.1.1
|
||||
lamhieu:unblock@1.0.0
|
||||
launch-screen@1.1.1
|
||||
livedata@1.0.18
|
||||
localstorage@1.2.0
|
||||
logging@1.1.20
|
||||
lucasantoniassi:accounts-lockout@1.0.0
|
||||
matb33:collection-hooks@0.8.4
|
||||
matb33:collection-hooks@0.9.1
|
||||
matteodem:easy-search@1.6.4
|
||||
mdg:meteor-apm-agent@3.2.3
|
||||
mdg:meteor-apm-agent@3.2.5
|
||||
mdg:validation-error@0.5.1
|
||||
meteor@1.9.3
|
||||
meteor-base@1.4.0
|
||||
@@ -98,17 +101,16 @@ meteorhacks:collection-utils@1.2.0
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
meteorspark:util@0.2.0
|
||||
minifier-css@1.4.2
|
||||
minifier-js@2.4.1
|
||||
minifier-css@1.4.3
|
||||
minifier-js@2.5.1
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.4.5
|
||||
mixmax:smart-disconnect@0.0.4
|
||||
mobile-status-bar@1.0.14
|
||||
modern-browsers@0.1.4
|
||||
modules@0.13.0
|
||||
modules-runtime@0.10.3
|
||||
modules@0.14.0
|
||||
modules-runtime@0.11.0
|
||||
momentjs:moment@2.24.0
|
||||
mongo@1.6.2
|
||||
mongo@1.7.0
|
||||
mongo-decimal@0.1.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.7
|
||||
@@ -125,17 +127,19 @@ mquandalle:mousetrap-bindglobal@0.0.1
|
||||
mquandalle:perfect-scrollbar@0.6.5_2
|
||||
msavin:usercache@1.8.0
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@3.1.2
|
||||
npm-mongo@3.2.0
|
||||
oauth@1.2.8
|
||||
oauth2@1.2.1
|
||||
observe-sequence@1.0.16
|
||||
ongoworks:speakingurl@1.1.0
|
||||
ordered-dict@1.1.0
|
||||
peerlibrary:assert@0.2.5
|
||||
ostrio:cookies@2.5.0
|
||||
peerlibrary:assert@0.3.0
|
||||
peerlibrary:base-component@0.16.0
|
||||
peerlibrary:blaze-components@0.15.1
|
||||
peerlibrary:computed-field@0.9.0
|
||||
peerlibrary:reactive-field@0.5.0
|
||||
peerlibrary:computed-field@0.10.0
|
||||
peerlibrary:reactive-field@0.6.0
|
||||
percolate:synced-cron@1.3.2
|
||||
promise@0.11.2
|
||||
raix:eventemitter@0.1.3
|
||||
raix:handlebar-helpers@0.2.5
|
||||
@@ -163,8 +167,8 @@ softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.0.15
|
||||
spacebars-compiler@1.1.3
|
||||
srp@1.0.12
|
||||
standard-minifier-css@1.5.3
|
||||
standard-minifier-js@2.4.1
|
||||
standard-minifier-css@1.5.4
|
||||
standard-minifier-js@2.5.2
|
||||
staringatlights:fast-render@3.2.0
|
||||
staringatlights:inject-data@2.3.0
|
||||
tap:i18n@1.8.2
|
||||
@@ -174,6 +178,7 @@ templating-compiler@1.3.3
|
||||
templating-runtime@1.3.2
|
||||
templating-tools@1.1.2
|
||||
tracker@1.2.0
|
||||
twbs:bootstrap@3.3.6
|
||||
ui@1.0.13
|
||||
underscore@1.0.10
|
||||
url@1.2.0
|
||||
@@ -181,7 +186,7 @@ useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.7.4
|
||||
webapp@1.7.5
|
||||
webapp-hashing@1.0.9
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-oidc@1.0.10
|
||||
|
||||
@@ -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:
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
dist: trusty
|
||||
dist: disco
|
||||
sudo: required
|
||||
|
||||
env:
|
||||
TRAVIS_DOCKER_COMPOSE_VERSION: 1.17.0
|
||||
TRAVIS_NODE_VERSION: 8.9.3
|
||||
TRAVIS_NPM_VERSION: 5.5.1
|
||||
TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
|
||||
TRAVIS_NODE_VERSION: 8.17.0
|
||||
TRAVIS_NPM_VERSION: latest
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update -y
|
||||
|
||||
+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, zh_HK:zh-HK
|
||||
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, sl_SI:sl, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
|
||||
|
||||
[wekan.application]
|
||||
file_filter = i18n/<lang>.i18n.json
|
||||
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Meteor: Chrome",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Meteor: Node",
|
||||
"runtimeExecutable": "/home/wekan/.meteor/meteor",
|
||||
"runtimeArgs": ["run", "--inspect-brk=9229"],
|
||||
"outputCapture": "std",
|
||||
"port": 9229,
|
||||
"timeout": 60000
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Meteor: All",
|
||||
"configurations": ["Meteor: Node", "Meteor: Chrome"]
|
||||
}
|
||||
]
|
||||
}
|
||||
+1024
File diff suppressed because it is too large.
Load diff
+57
-34
@@ -1,12 +1,12 @@
|
||||
FROM ubuntu:disco
|
||||
FROM ubuntu:rolling
|
||||
LABEL maintainer="wekan"
|
||||
|
||||
# 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 python3 python3-pip git ca-certificates gcc-8" \
|
||||
ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v8.16.0 \
|
||||
NODE_VERSION=v8.17.0 \
|
||||
METEOR_RELEASE=1.8.1 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
@@ -21,6 +21,14 @@ 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 \
|
||||
CARD_OPENED_WEBHOOK_ENABLED=false \
|
||||
ATTACHMENTS_STORE_PATH="" \
|
||||
MAX_IMAGE_PIXEL="" \
|
||||
IMAGE_COMPRESS_RATIO="" \
|
||||
BIGEVENTS_PATTERN=NONE \
|
||||
NOTIFY_DUE_DAYS_BEFORE_AND_AFTER="" \
|
||||
NOTIFY_DUE_AT_HOUR_OF_DAY="" \
|
||||
EMAIL_NOTIFICATION_TIMEOUT=30000 \
|
||||
MATOMO_ADDRESS="" \
|
||||
MATOMO_SITE_ID="" \
|
||||
@@ -57,13 +65,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="" \
|
||||
@@ -113,7 +122,7 @@ RUN \
|
||||
\
|
||||
# OS dependencies
|
||||
apt-get update -y && apt-get install -y --no-install-recommends ${BUILD_DEPS} && \
|
||||
pip3 install -U pip setuptools wheel && \
|
||||
#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
|
||||
@@ -169,33 +178,39 @@ 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.1 /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 --recursive /home/wekan && \
|
||||
curl "https://install.meteor.com" -o /home/wekan/install_meteor.sh && \
|
||||
#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 /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} https://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 && \
|
||||
@@ -216,21 +231,24 @@ RUN \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor -- help; \
|
||||
\
|
||||
# extract the OpenAPI specification
|
||||
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 &&\
|
||||
gosu wekan:wekan mkdir -p ./public/api && \
|
||||
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; \
|
||||
#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 && \
|
||||
@@ -241,6 +259,11 @@ RUN \
|
||||
#cd /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/npm-bcrypt && \
|
||||
#gosu wekan:wekan rm -rf node_modules/bcrypt && \
|
||||
#gosu wekan:wekan npm install bcrypt && \
|
||||
#
|
||||
# Delete phantomjs
|
||||
#cd /home/wekan/app_build/bundle && \
|
||||
#find . -name "*phantomjs*" | xargs rm -rf && \
|
||||
#
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
gosu wekan:wekan npm install && \
|
||||
#gosu wekan:wekan npm install bcrypt && \
|
||||
@@ -256,10 +279,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
|
||||
|
||||
@@ -54,13 +54,12 @@ that by providing one-click installation on various platforms.
|
||||
- [Features][features]: Wekan has real-time user interface.
|
||||
- [Platforms][platforms]: Wekan supports many platforms.
|
||||
Wekan is critical part of new platforms Wekan is currently being integrated to.
|
||||
- [Integrations][integrations]: Current possible integrations and future plans.
|
||||
|
||||
## Requirements
|
||||
|
||||
- 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).
|
||||
[More Platforms](https://github.com/wekan/wekan/wiki/Platforms), bundle for RasPi3 ARM and other CPUs where Node.js and MongoDB exists.
|
||||
- 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.
|
||||
@@ -74,41 +73,25 @@ that by providing one-click installation on various platforms.
|
||||
Bugs, updates, users deleting list or card, harddrive full, harddrive crash etc can eat your data. There is no undo yet.
|
||||
Some bug can cause Wekan board to not load at all, requiring manual fixing of database content.
|
||||
|
||||
## Roadmap
|
||||
## Roadmap and Demo
|
||||
|
||||
[Roadmap Milestones](https://github.com/wekan/wekan/milestones)
|
||||
[Roadmap][roadmap_wekan] - Public read-only board at Wekan demo.
|
||||
|
||||
[Developer Documentation][dev_docs]
|
||||
|
||||
- There is many companies and individuals contributing code to Wekan, to add features and bugfixes
|
||||
[many times a day](https://github.com/wekan/wekan/blob/devel/CHANGELOG.md).
|
||||
- [Please add Add new Feature Requests and Bug Reports immediately](https://github.com/wekan/wekan/issues).
|
||||
- [Commercial Support](https://wekan.team).
|
||||
- [Bounties](https://wekan.team/bounties/index.html).
|
||||
- [Commercial Support](https://wekan.team/commercial-support/).
|
||||
|
||||
We also welcome sponsors for features and bugfixes.
|
||||
By working directly with Wekan you get the benefit of active maintenance and new features added by growing Wekan developer community.
|
||||
|
||||
## Demo
|
||||
|
||||
[Wekan demo][roadmap_wefork]
|
||||
|
||||
## Screenshot
|
||||
|
||||
[More screenshots at Features page](https://github.com/wekan/wekan/wiki/Features)
|
||||
|
||||
[![Screenshot of Wekan][screenshot_wefork]][roadmap_wefork]
|
||||
|
||||
## Stable
|
||||
|
||||
- master+devel branch. At release, devel is merged to master.
|
||||
- Receives fixes and features that have been tested at edge that they work.
|
||||
- If you want automatic updates, [use Snap](https://github.com/wekan/wekan-snap/wiki/Install).
|
||||
- If you want to test before update, [use Docker quay.io release tags](https://github.com/wekan/wekan/wiki/Docker).
|
||||
|
||||
## Edge
|
||||
|
||||
- edge branch. All new fixes and features are added to here first. [Testing Edge](https://github.com/wekan/wekan-snap/wiki/Snap-Developer-Docs).
|
||||
[![Screenshot of Wekan][screenshot_wekan]][roadmap_wekan]
|
||||
|
||||
## License
|
||||
|
||||
@@ -117,14 +100,11 @@ with [Meteor](https://www.meteor.com).
|
||||
|
||||
[platforms]: https://github.com/wekan/wekan/wiki/Platforms
|
||||
[dev_docs]: https://github.com/wekan/wekan/wiki/Developer-Documentation
|
||||
[screenshot_wekan]: http://i.imgur.com/cI4jW2h.png
|
||||
[screenshot_wefork]: https://wekan.github.io/wekan-markdown.png
|
||||
[screenshot_wekan]: https://wekan.github.io/wekan-markdown.png
|
||||
[features]: https://github.com/wekan/wekan/wiki/Features
|
||||
[integrations]: https://github.com/wekan/wekan/wiki/Integrations
|
||||
[roadmap_wekan]: http://try.wekan.io/b/MeSsFJaSqeuo9M6bs/wekan-roadmap
|
||||
[roadmap_wefork]: https://wekan.indie.host/b/t2YaGmyXgNkppcFBq/wekan-fork-roadmap
|
||||
[roadmap_wekan]: https://boards.wekan.team/b/D2SzJKZDS4Z48yeQH/wekan-open-source-kanban-board-with-mit-license
|
||||
[wekan_issues]: https://github.com/wekan/wekan/issues
|
||||
[wekan_issues]: https://github.com/wekan/wekan/issues
|
||||
[wefork_issues]: https://github.com/wefork/wekan/issues
|
||||
[docker_image]: https://hub.docker.com/r/wekanteam/wekan/
|
||||
[travis_badge]: https://travis-ci.org/wekan/wekan.svg?branch=devel
|
||||
[travis_status]: https://travis-ci.org/wekan/wekan
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v2.98.0"
|
||||
appVersion: "v3.63.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -201,7 +201,25 @@ 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 'deleteComment')
|
||||
| {{{_ 'activity-deleteComment' currentData.commentId}}}.
|
||||
if($eq activityType 'editComment')
|
||||
| {{{_ 'activity-editComment' currentData.commentId}}}.
|
||||
if($eq activityType 'addComment')
|
||||
+inlinedForm(classNames='js-edit-comment')
|
||||
+editor(autofocus=true)
|
||||
|
||||
@@ -85,7 +85,7 @@ BlazeComponent.extendComponent({
|
||||
const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(
|
||||
lastLabelId,
|
||||
);
|
||||
if (lastLabel.name === undefined || lastLabel.name === '') {
|
||||
if (lastLabel && (lastLabel.name === undefined || lastLabel.name === '')) {
|
||||
return lastLabel.color;
|
||||
} else {
|
||||
return lastLabel.name;
|
||||
|
||||
@@ -16,9 +16,6 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
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();
|
||||
@@ -38,6 +35,7 @@ BlazeComponent.extendComponent({
|
||||
resetCommentInput(input);
|
||||
Tracker.flush();
|
||||
autosize.update(input);
|
||||
input.trigger('submitted');
|
||||
}
|
||||
evt.preventDefault();
|
||||
},
|
||||
@@ -54,7 +52,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
// XXX This should be a static method of the `commentForm` component
|
||||
function resetCommentInput(input) {
|
||||
input.val('');
|
||||
input.val(''); // without manually trigger, input event won't be fired
|
||||
input.blur();
|
||||
commentFormIsOpen.set(false);
|
||||
}
|
||||
|
||||
@@ -6,9 +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")
|
||||
{{goHome}}
|
||||
//-- XXX We need a better error message in case the board has been archived
|
||||
+message(label="board-not-found")
|
||||
//-- | {{goHome}}
|
||||
else
|
||||
+spinner
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
const swimlaneWhileSortingHeight = 150;
|
||||
@@ -89,7 +91,6 @@ BlazeComponent.extendComponent({
|
||||
helper.append(list.clone());
|
||||
return helper;
|
||||
},
|
||||
handle: '.js-swimlane-header',
|
||||
items: '.swimlane:not(.placeholder)',
|
||||
placeholder: 'swimlane placeholder',
|
||||
distance: 7,
|
||||
@@ -193,6 +194,34 @@ BlazeComponent.extendComponent({
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.js-swimlane:not(.placeholder)');
|
||||
|
||||
this.autorun(() => {
|
||||
let showDesktopDragHandles = false;
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
showDesktopDragHandles = (currentUser.profile || {})
|
||||
.showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
showDesktopDragHandles = true;
|
||||
} else {
|
||||
showDesktopDragHandles = false;
|
||||
}
|
||||
if (
|
||||
Utils.isMiniScreen() ||
|
||||
(!Utils.isMiniScreen() && showDesktopDragHandles)
|
||||
) {
|
||||
$swimlanesDom.sortable({
|
||||
handle: '.js-swimlane-header-handle',
|
||||
});
|
||||
} else {
|
||||
$swimlanesDom.sortable({
|
||||
handle: '.swimlane-header',
|
||||
});
|
||||
}
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member or is comment only
|
||||
$swimlanesDom.sortable('option', 'disabled', !userIsMember());
|
||||
});
|
||||
|
||||
function userIsMember() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
@@ -210,21 +239,30 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
isViewSwimlanes() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
|
||||
} else {
|
||||
return cookies.get('boardView') === 'board-view-swimlanes';
|
||||
}
|
||||
},
|
||||
|
||||
isViewLists() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return true;
|
||||
return (currentUser.profile || {}).boardView === 'board-view-lists';
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).boardView === 'board-view-lists';
|
||||
} else {
|
||||
return cookies.get('boardView') === 'board-view-lists';
|
||||
}
|
||||
},
|
||||
|
||||
isViewCalendar() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
} else {
|
||||
return cookies.get('boardView') === 'board-view-cal';
|
||||
}
|
||||
},
|
||||
|
||||
openNewListForm() {
|
||||
@@ -243,11 +281,6 @@ BlazeComponent.extendComponent({
|
||||
{
|
||||
// XXX The board-overlay div should probably be moved to the parent
|
||||
// component.
|
||||
'mouseenter .board-overlay'() {
|
||||
if (this.mouseHasEnterCardDetails) {
|
||||
this.showOverlay.set(false);
|
||||
}
|
||||
},
|
||||
mouseup() {
|
||||
if (this._isDragging) {
|
||||
this._isDragging = false;
|
||||
@@ -314,25 +347,46 @@ BlazeComponent.extendComponent({
|
||||
events(start, end, timezone, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const events = [];
|
||||
const pushEvent = function(card, title, start, end, extraCls) {
|
||||
start = start || card.startAt;
|
||||
end = end || card.endAt;
|
||||
title = title || card.title;
|
||||
const className =
|
||||
(extraCls ? `${extraCls} ` : '') +
|
||||
(card.color ? `calendar-event-${card.color}` : '');
|
||||
events.push({
|
||||
id: card._id,
|
||||
title,
|
||||
start,
|
||||
end: end || card.endAt,
|
||||
allDay:
|
||||
Math.abs(end.getTime() - start.getTime()) / 1000 === 24 * 3600,
|
||||
url: FlowRouter.url('card', {
|
||||
boardId: currentBoard._id,
|
||||
slug: currentBoard.slug,
|
||||
cardId: card._id,
|
||||
}),
|
||||
className,
|
||||
});
|
||||
};
|
||||
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,
|
||||
}),
|
||||
});
|
||||
pushEvent(card);
|
||||
});
|
||||
currentBoard
|
||||
.cardsDueInBetween(start.toDate(), end.toDate())
|
||||
.forEach(function(card) {
|
||||
pushEvent(
|
||||
card,
|
||||
`${card.title} ${TAPi18n.__('card-due')}`,
|
||||
card.dueAt,
|
||||
new Date(card.dueAt.getTime() + 36e5),
|
||||
);
|
||||
});
|
||||
events.sort(function(first, second) {
|
||||
return first.id > second.id ? 1 : -1;
|
||||
});
|
||||
callback(events);
|
||||
},
|
||||
eventResize(event, delta, revertFunc) {
|
||||
@@ -365,8 +419,11 @@ BlazeComponent.extendComponent({
|
||||
};
|
||||
},
|
||||
isViewCalendar() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
} else {
|
||||
return cookies.get('boardView') === 'board-view-cal';
|
||||
}
|
||||
},
|
||||
}).register('calendarView');
|
||||
@@ -53,3 +53,81 @@ position()
|
||||
padding: 0 0px 0px 0
|
||||
overflow-x: hidden
|
||||
overflow-y: auto
|
||||
|
||||
calendar-event-color(background, borderColor, color...)
|
||||
background: background !important
|
||||
border-color: borderColor
|
||||
if color
|
||||
color: color !important //overwrite text for better visibility
|
||||
|
||||
.calendar-event-green
|
||||
calendar-event-color(#3cb500, #2a8000, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-yellow
|
||||
calendar-event-color(#fad900, #c7ac00, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-orange
|
||||
calendar-event-color(#ff9f19, #cc7c14, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-red
|
||||
calendar-event-color(#eb4646, #b83737, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-purple
|
||||
calendar-event-color(#a632db, #7d26a6, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-blue
|
||||
calendar-event-color(#0079bf, #005a8a, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-pink
|
||||
calendar-event-color(#ff78cb, #cc62a3, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-sky
|
||||
calendar-event-color(#00c2e0, #0094ab, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-black
|
||||
calendar-event-color(#4d4d4d, #1a1a1a, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-lime
|
||||
calendar-event-color(#51e898, #3eb375, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-silver
|
||||
calendar-event-color(#c0c0c0, #8c8c8c, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-peachpuff
|
||||
calendar-event-color(#ffdab9, #ccaf95, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-crimson
|
||||
calendar-event-color(#dc143c, #a8112f, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-plum
|
||||
calendar-event-color(#dda0dd, #a87ba8, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-darkgreen
|
||||
calendar-event-color(#006400, #003000, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-slateblue
|
||||
calendar-event-color(#6a5acd, #4f4399, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-magenta
|
||||
calendar-event-color(#ff00ff, #cc00cc, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-gold
|
||||
calendar-event-color(#ffd700, #ccaa00, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-navy
|
||||
calendar-event-color(#000080, #000033, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-gray
|
||||
calendar-event-color(#808080, #333333, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-saddlebrown
|
||||
calendar-event-color(#8b4513, #572b0c, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-paleturquoise
|
||||
calendar-event-color(#afeeee, #8ababa, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-mistyrose
|
||||
calendar-event-color(#ffe4e1, #ccb8b6, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-indigo
|
||||
calendar-event-color(#4b0082, #2b004d, #ffffff) //White text for better visibility
|
||||
@@ -216,7 +216,9 @@ setBoardColor(color)
|
||||
.board-color-relax
|
||||
setBoardColor(#27AE61)
|
||||
|
||||
// RELAX MODE: light green backgroud, when green background color selected.
|
||||
// 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
|
||||
|
||||
@@ -229,3 +231,65 @@ setBoardColor(color)
|
||||
|
||||
.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
|
||||
word-wrap: break-word
|
||||
|
||||
// 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
|
||||
@@ -77,6 +77,11 @@ template(name="boardHeaderBar")
|
||||
i.fa.fa-archive
|
||||
span {{_ 'archives'}}
|
||||
|
||||
//if showSort
|
||||
// a.board-header-btn.js-open-sort-view(title="{{_ 'sort-desc'}}")
|
||||
// i.fa(class="{{directionClass}}")
|
||||
// span {{_ 'sort'}}{{_ listSortShortDesc}}
|
||||
|
||||
a.board-header-btn.js-open-filter-view(
|
||||
title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}"
|
||||
class="{{#if Filter.isActive}}emphasis{{/if}}")
|
||||
@@ -85,15 +90,6 @@ 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
|
||||
@@ -102,8 +98,14 @@ template(name="boardHeaderBar")
|
||||
unless currentBoard.isTemplatesBoard
|
||||
a.board-header-btn.js-toggle-board-view(
|
||||
title="{{_ 'board-view'}}")
|
||||
i.fa.fa-th-large
|
||||
span {{#if currentUser.profile.boardView}}{{_ currentUser.profile.boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}}
|
||||
i.fa.fa-caret-down
|
||||
if $eq boardView 'board-view-lists'
|
||||
i.fa.fa-trello
|
||||
if $eq boardView 'board-view-swimlanes'
|
||||
i.fa.fa-th-large
|
||||
if $eq boardView 'board-view-cal'
|
||||
i.fa.fa-calendar
|
||||
span {{#if boardView}}{{_ boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}}
|
||||
|
||||
if canModifyBoard
|
||||
a.board-header-btn.js-multiselection-activate(
|
||||
@@ -168,6 +170,44 @@ template(name="boardChangeWatchPopup")
|
||||
i.fa.fa-check
|
||||
span.sub-name {{_ 'muted-info'}}
|
||||
|
||||
template(name="boardChangeViewPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
with "board-view-lists"
|
||||
a.js-open-lists-view
|
||||
i.fa.fa-trello.colorful
|
||||
| {{_ 'board-view-lists'}}
|
||||
if $eq Utils.boardView "board-view-lists"
|
||||
i.fa.fa-check
|
||||
li
|
||||
with "board-view-swimlanes"
|
||||
a.js-open-swimlanes-view
|
||||
i.fa.fa-th-large.colorful
|
||||
| {{_ 'board-view-swimlanes'}}
|
||||
if $eq Utils.boardView "board-view-swimlanes"
|
||||
i.fa.fa-check
|
||||
li
|
||||
with "board-view-cal"
|
||||
a.js-open-cal-view
|
||||
i.fa.fa-calendar.colorful
|
||||
| {{_ 'board-view-cal'}}
|
||||
if $eq Utils.boardView "board-view-cal"
|
||||
i.fa.fa-check
|
||||
if currentUser.isAdmin
|
||||
hr
|
||||
li
|
||||
with "board-view-rules"
|
||||
a.js-open-rules-view(title="{{_ 'rules'}}")
|
||||
i.fa.fa-magic
|
||||
| {{_ 'rules'}}
|
||||
else if currentUser.isBoardAdmin
|
||||
hr
|
||||
li
|
||||
with "board-view-rules"
|
||||
a.js-open-rules-view(title="{{_ 'rules'}}")
|
||||
i.fa.fa-magic
|
||||
| {{_ 'rules'}}
|
||||
|
||||
template(name="createBoard")
|
||||
form
|
||||
label
|
||||
@@ -194,6 +234,20 @@ template(name="createBoard")
|
||||
| /
|
||||
a.js-board-template {{_ 'template'}}
|
||||
|
||||
//template(name="listsortPopup")
|
||||
// h2
|
||||
// | {{_ 'list-sort-by'}}
|
||||
// hr
|
||||
// ul.pop-over-list
|
||||
// each value in allowedSortValues
|
||||
// li
|
||||
// a.js-sort-by(name="{{value.name}}")
|
||||
// if $eq sortby value.name
|
||||
// i(class="fa {{Direction}}")
|
||||
// | {{_ value.label }}{{_ value.shortLabel}}
|
||||
// if $eq sortby value.name
|
||||
// i(class="fa fa-check")
|
||||
|
||||
template(name="boardChangeTitlePopup")
|
||||
form
|
||||
label
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/*
|
||||
const DOWNCLS = 'fa-sort-down';
|
||||
const UPCLS = 'fa-sort-up';
|
||||
*/
|
||||
Template.boardMenuPopup.events({
|
||||
'click .js-rename-board': Popup.open('boardChangeTitle'),
|
||||
'click .js-custom-fields'() {
|
||||
@@ -80,7 +84,27 @@ BlazeComponent.extendComponent({
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return currentBoard && currentBoard.stars >= 2;
|
||||
},
|
||||
|
||||
/*
|
||||
showSort() {
|
||||
return Meteor.user().hasSortBy();
|
||||
},
|
||||
directionClass() {
|
||||
return this.currentDirection() === -1 ? DOWNCLS : UPCLS;
|
||||
},
|
||||
changeDirection() {
|
||||
const direction = 0 - this.currentDirection() === -1 ? '-' : '';
|
||||
Meteor.call('setListSortBy', direction + this.currentListSortBy());
|
||||
},
|
||||
currentDirection() {
|
||||
return Meteor.user().getListSortByDirection();
|
||||
},
|
||||
currentListSortBy() {
|
||||
return Meteor.user().getListSortBy();
|
||||
},
|
||||
listSortShortDesc() {
|
||||
return `list-label-short-${this.currentListSortBy()}`;
|
||||
},
|
||||
*/
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
@@ -94,30 +118,25 @@ BlazeComponent.extendComponent({
|
||||
'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-board-view': Popup.open('boardChangeView'),
|
||||
'click .js-toggle-sidebar'() {
|
||||
Sidebar.toggle();
|
||||
},
|
||||
'click .js-open-filter-view'() {
|
||||
Sidebar.setView('filter');
|
||||
},
|
||||
/*
|
||||
'click .js-open-sort-view'(evt) {
|
||||
const target = evt.target;
|
||||
if (target.tagName === 'I') {
|
||||
// click on the text, popup choices
|
||||
this.changeDirection();
|
||||
} else {
|
||||
// change the sort order
|
||||
Popup.open('listsort')(evt);
|
||||
}
|
||||
},
|
||||
*/
|
||||
'click .js-filter-reset'(event) {
|
||||
event.stopPropagation();
|
||||
Sidebar.setView();
|
||||
@@ -126,9 +145,6 @@ BlazeComponent.extendComponent({
|
||||
'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();
|
||||
@@ -156,6 +172,28 @@ Template.boardHeaderBar.helpers({
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
boardView() {
|
||||
return Utils.boardView();
|
||||
},
|
||||
});
|
||||
|
||||
Template.boardChangeViewPopup.events({
|
||||
'click .js-open-lists-view'() {
|
||||
Utils.setBoardView('board-view-lists');
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-open-swimlanes-view'() {
|
||||
Utils.setBoardView('board-view-swimlanes');
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-open-cal-view'() {
|
||||
Utils.setBoardView('board-view-cal');
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-open-rules-view'() {
|
||||
Modal.openWide('rulesMain');
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
|
||||
const CreateBoard = BlazeComponent.extendComponent({
|
||||
@@ -277,3 +315,75 @@ BlazeComponent.extendComponent({
|
||||
];
|
||||
},
|
||||
}).register('boardChangeWatchPopup');
|
||||
|
||||
/*
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
//this.sortBy = new ReactiveVar();
|
||||
////this.sortDirection = new ReactiveVar();
|
||||
//this.setSortBy();
|
||||
this.downClass = DOWNCLS;
|
||||
this.upClass = UPCLS;
|
||||
},
|
||||
allowedSortValues() {
|
||||
const types = [];
|
||||
const pushed = {};
|
||||
Meteor.user()
|
||||
.getListSortTypes()
|
||||
.forEach(type => {
|
||||
const key = type.replace(/^-/, '');
|
||||
if (pushed[key] === undefined) {
|
||||
types.push({
|
||||
name: key,
|
||||
label: `list-label-${key}`,
|
||||
shortLabel: `list-label-short-${key}`,
|
||||
});
|
||||
pushed[key] = 1;
|
||||
}
|
||||
});
|
||||
return types;
|
||||
},
|
||||
Direction() {
|
||||
return Meteor.user().getListSortByDirection() === -1
|
||||
? this.downClass
|
||||
: this.upClass;
|
||||
},
|
||||
sortby() {
|
||||
return Meteor.user().getListSortBy();
|
||||
},
|
||||
|
||||
setSortBy(type = null) {
|
||||
const user = Meteor.user();
|
||||
if (type === null) {
|
||||
type = user._getListSortBy();
|
||||
} else {
|
||||
let value = '';
|
||||
if (type.map) {
|
||||
// is an array
|
||||
value = (type[1] === -1 ? '-' : '') + type[0];
|
||||
}
|
||||
Meteor.call('setListSortBy', value);
|
||||
}
|
||||
//this.sortBy.set(type[0]);
|
||||
//this.sortDirection.set(type[1]);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-sort-by'(evt) {
|
||||
evt.preventDefault();
|
||||
const target = evt.target;
|
||||
const sortby = target.getAttribute('name');
|
||||
const down = !!target.querySelector(`.${this.upClass}`);
|
||||
const direction = down ? -1 : 1;
|
||||
this.setSortBy([sortby, direction]);
|
||||
if (Utils.isMiniScreen) {
|
||||
Popup.close();
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('listsortPopup');
|
||||
*/
|
||||
@@ -31,12 +31,28 @@ template(name="boardList")
|
||||
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}}")
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
unless isMiniScreen
|
||||
if isSandstorm
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else if currentUser.isBoardAdmin
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else if currentUser.isAdmin
|
||||
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'}}
|
||||
|
||||
@@ -8,10 +8,10 @@ Template.boardListHeaderBar.events({
|
||||
|
||||
Template.boardListHeaderBar.helpers({
|
||||
templatesBoardId() {
|
||||
return Meteor.user().getTemplatesBoardId();
|
||||
return Meteor.user() && Meteor.user().getTemplatesBoardId();
|
||||
},
|
||||
templatesBoardSlug() {
|
||||
return Meteor.user().getTemplatesBoardSlug();
|
||||
return Meteor.user() && Meteor.user().getTemplatesBoardSlug();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -82,13 +82,13 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
'click .js-accept-invite'() {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.user().removeInvite(boardId);
|
||||
Meteor.call('acceptInvite', boardId);
|
||||
},
|
||||
'click .js-decline-invite'() {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.call('quitBoard', boardId, (err, ret) => {
|
||||
if (!err && ret) {
|
||||
Meteor.user().removeInvite(boardId);
|
||||
Meteor.call('acceptInvite', boardId);
|
||||
FlowRouter.go('home');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -183,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
|
||||
@@ -195,4 +196,4 @@ $spaceBetweenTiles = 16px
|
||||
|
||||
@media screen and (max-width: 360px)
|
||||
li
|
||||
width: 50%
|
||||
width: 100%
|
||||
@@ -37,17 +37,21 @@ 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
|
||||
unless currentUser.isWorker
|
||||
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
|
||||
unless currentUser.isWorker
|
||||
li.attachment-item.add-attachment
|
||||
a.js-add-attachment {{_ 'add-attachment' }}
|
||||
@@ -54,26 +54,49 @@ Template.previewAttachedImagePopup.events({
|
||||
Template.cardAttachmentsPopup.events({
|
||||
'change .js-attach-file'(event) {
|
||||
const card = this;
|
||||
const processFile = f => {
|
||||
Utils.processUploadedAttachment(card, f, attachment => {
|
||||
if (attachment && attachment._id && attachment.isImage()) {
|
||||
card.setCover(attachment._id);
|
||||
}
|
||||
Popup.close();
|
||||
});
|
||||
};
|
||||
|
||||
FS.Utility.eachFile(event, f => {
|
||||
const file = new FS.File(f);
|
||||
if (card.isLinkedCard()) {
|
||||
file.boardId = Cards.findOne(card.linkedId).boardId;
|
||||
file.cardId = card.linkedId;
|
||||
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) {
|
||||
Utils.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 {
|
||||
file.boardId = card.boardId;
|
||||
file.swimlaneId = card.swimlaneId;
|
||||
file.listId = card.listId;
|
||||
file.cardId = card._id;
|
||||
processFile(f);
|
||||
}
|
||||
file.userId = Meteor.userId();
|
||||
|
||||
const attachment = Attachments.insert(file);
|
||||
|
||||
if (attachment && attachment._id && attachment.isImage()) {
|
||||
card.setCover(attachment._id);
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
});
|
||||
},
|
||||
'click .js-computer-upload'(event, templateInstance) {
|
||||
@@ -83,30 +106,48 @@ Template.cardAttachmentsPopup.events({
|
||||
'click .js-upload-clipboard-image': Popup.open('previewClipboardImage'),
|
||||
});
|
||||
|
||||
const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
|
||||
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
|
||||
let pastedResults = null;
|
||||
|
||||
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
|
||||
Utils.shrinkImage({
|
||||
dataurl: results.dataURL,
|
||||
maxSize: MAX_IMAGE_PIXEL,
|
||||
ratio: COMPRESS_RATIO,
|
||||
callback(changed) {
|
||||
if (changed !== false && !!changed) {
|
||||
results.dataURL = changed;
|
||||
}
|
||||
direct(results);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
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) {
|
||||
|
||||
@@ -31,6 +31,10 @@ template(name="cardCustomField-text")
|
||||
= value
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
+viewer
|
||||
= value
|
||||
|
||||
|
||||
template(name="cardCustomField-number")
|
||||
if canModifyCard
|
||||
@@ -45,6 +49,9 @@ template(name="cardCustomField-number")
|
||||
= value
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
if value
|
||||
= value
|
||||
|
||||
template(name="cardCustomField-date")
|
||||
if canModifyCard
|
||||
@@ -55,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
|
||||
@@ -79,3 +91,7 @@ template(name="cardCustomField-dropdown")
|
||||
= selectedItem
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
if value
|
||||
+viewer
|
||||
= selectedItem
|
||||
@@ -97,7 +97,8 @@ Template.dateBadge.helpers({
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -105,7 +106,7 @@ Template.dateBadge.helpers({
|
||||
// editCardReceivedDatePopup
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
|
||||
this.data().getReceived() &&
|
||||
this.date.set(moment(this.data().getReceived()));
|
||||
}
|
||||
@@ -122,7 +123,7 @@ Template.dateBadge.helpers({
|
||||
// editCardStartDatePopup
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
|
||||
this.data().getStart() && this.date.set(moment(this.data().getStart()));
|
||||
}
|
||||
|
||||
@@ -148,7 +149,7 @@ Template.dateBadge.helpers({
|
||||
// editCardDueDatePopup
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
super.onCreated('1970-01-01 17:00:00');
|
||||
this.data().getDue() && this.date.set(moment(this.data().getDue()));
|
||||
}
|
||||
|
||||
@@ -171,7 +172,7 @@ Template.dateBadge.helpers({
|
||||
// editCardEndDatePopup
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
|
||||
this.data().getEnd() && this.date.set(moment(this.data().getEnd()));
|
||||
}
|
||||
|
||||
@@ -237,7 +238,7 @@ class CardReceivedDate extends CardDate {
|
||||
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)) ||
|
||||
(startAt && theDate.isAfter(startAt)) ||
|
||||
(endAt && theDate.isAfter(endAt)) ||
|
||||
(dueAt && theDate.isAfter(dueAt))
|
||||
)
|
||||
@@ -344,9 +345,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 (!dueAt) classes += '';
|
||||
else if (theDate.isBefore(dueAt)) classes += 'current';
|
||||
else if (theDate.isAfter(dueAt)) classes += 'due';
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,15 +4,20 @@ template(name="cardDetails")
|
||||
+inlinedForm(classNames="js-card-details-title")
|
||||
+editCardTitleForm
|
||||
else
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details
|
||||
if currentUser.isBoardMember
|
||||
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
|
||||
unless isMiniScreen
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details
|
||||
if currentUser.isBoardMember
|
||||
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
|
||||
if isMiniScreen
|
||||
a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details
|
||||
if currentUser.isBoardMember
|
||||
a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu
|
||||
h2.card-details-title.js-card-title(
|
||||
class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
|
||||
+viewer
|
||||
= getTitle
|
||||
if isWatching
|
||||
i.fa.fa-eye.card-details-watch
|
||||
if isWatching
|
||||
i.card-details-watch.fa.fa-eye
|
||||
.card-details-path
|
||||
each parentList
|
||||
| >
|
||||
@@ -32,57 +37,91 @@ template(name="cardDetails")
|
||||
|
||||
.card-details-items
|
||||
.card-details-item.card-details-item-received
|
||||
h3.card-details-item-title {{_ 'card-received'}}
|
||||
h3
|
||||
i.fa.fa-sign-out
|
||||
card-details-item-title {{_ 'card-received'}}
|
||||
if getReceived
|
||||
+cardReceivedDate
|
||||
else
|
||||
if canModifyCard
|
||||
a.js-received-date {{_ 'add'}}
|
||||
unless currentUser.isWorker
|
||||
a.js-received-date {{_ 'add'}}
|
||||
|
||||
.card-details-item.card-details-item-start
|
||||
h3.card-details-item-title {{_ 'card-start'}}
|
||||
h3
|
||||
i.fa.fa-hourglass-start
|
||||
card-details-item-title {{_ 'card-start'}}
|
||||
if getStart
|
||||
+cardStartDate
|
||||
else
|
||||
if canModifyCard
|
||||
a.js-start-date {{_ 'add'}}
|
||||
unless currentUser.isWorker
|
||||
a.js-start-date {{_ 'add'}}
|
||||
|
||||
.card-details-item.card-details-item-due
|
||||
h3.card-details-item-title {{_ 'card-due'}}
|
||||
h3
|
||||
i.fa.fa-sign-in
|
||||
card-details-item-title {{_ 'card-due'}}
|
||||
if getDue
|
||||
+cardDueDate
|
||||
else
|
||||
if canModifyCard
|
||||
a.js-due-date {{_ 'add'}}
|
||||
unless currentUser.isWorker
|
||||
a.js-due-date {{_ 'add'}}
|
||||
|
||||
.card-details-item.card-details-item-end
|
||||
h3.card-details-item-title {{_ 'card-end'}}
|
||||
h3
|
||||
i.fa.fa-hourglass-end
|
||||
card-details-item-title {{_ 'card-end'}}
|
||||
if getEnd
|
||||
+cardEndDate
|
||||
else
|
||||
if canModifyCard
|
||||
a.js-end-date {{_ 'add'}}
|
||||
unless currentUser.isWorker
|
||||
a.js-end-date {{_ 'add'}}
|
||||
|
||||
.card-details-items
|
||||
.card-details-item.card-details-item-members
|
||||
h3.card-details-item-title {{_ 'members'}}
|
||||
h3
|
||||
i.fa.fa-users
|
||||
card-details-item-title {{_ 'members'}}
|
||||
each getMembers
|
||||
+userAvatar(userId=this cardId=../_id)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
if canModifyCard
|
||||
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
|
||||
unless currentUser.isWorker
|
||||
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
.card-details-item.card-details-item-assignees
|
||||
h3
|
||||
i.fa.fa-user
|
||||
card-details-item-title {{_ 'assignee'}}
|
||||
each getAssignees
|
||||
+userAvatarAssignee(userId=this cardId=../_id)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
if canModifyCard
|
||||
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
|
||||
i.fa.fa-plus
|
||||
if currentUser.isWorker
|
||||
unless assigneeSelected
|
||||
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
|
||||
.card-details-item.card-details-item-labels
|
||||
h3.card-details-item-title {{_ 'labels'}}
|
||||
h3
|
||||
i.fa.fa-tags
|
||||
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)
|
||||
+viewer
|
||||
= name
|
||||
if canModifyCard
|
||||
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
|
||||
i.fa.fa-plus
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
.card-details-items
|
||||
each customFieldsWD
|
||||
@@ -103,26 +142,29 @@ template(name="cardDetails")
|
||||
|
||||
//- XXX We should use "editable" to avoid repetiting ourselves
|
||||
if canModifyCard
|
||||
h3.card-details-item-title {{_ 'description'}}
|
||||
+inlinedCardDescription(classNames="card-description js-card-description")
|
||||
+editor(autofocus=true)
|
||||
| {{getUnsavedValue 'cardDescription' _id getDescription}}
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getDescription
|
||||
+viewer
|
||||
= getDescription
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
if (hasUnsavedValue 'cardDescription' _id)
|
||||
p.quiet
|
||||
| {{_ 'unsaved-description'}}
|
||||
a.js-open-inlined-form {{_ 'view-it'}}
|
||||
= ' - '
|
||||
a.js-close-inlined-form {{_ 'discard'}}
|
||||
unless currentUser.isWorker
|
||||
h3
|
||||
i.fa.fa-align-left
|
||||
card-details-item-title {{_ 'description'}}
|
||||
+inlinedCardDescription(classNames="card-description js-card-description")
|
||||
+editor(autofocus=true)
|
||||
| {{getUnsavedValue 'cardDescription' _id getDescription}}
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getDescription
|
||||
+viewer
|
||||
= getDescription
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
if (hasUnsavedValue 'cardDescription' _id)
|
||||
p.quiet
|
||||
| {{_ 'unsaved-description'}}
|
||||
a.js-open-inlined-form {{_ 'view-it'}}
|
||||
= ' - '
|
||||
a.js-close-inlined-form {{_ 'discard'}}
|
||||
else if getDescription
|
||||
h3.card-details-item-title {{_ 'description'}}
|
||||
+viewer
|
||||
@@ -130,33 +172,39 @@ template(name="cardDetails")
|
||||
|
||||
.card-details-items
|
||||
.card-details-item.card-details-item-name
|
||||
h3.card-details-item-title {{_ 'requested-by'}}
|
||||
h3
|
||||
i.fa.fa-shopping-cart
|
||||
card-details-item-title {{_ 'requested-by'}}
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-details-requester")
|
||||
+editCardRequesterForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getRequestedBy
|
||||
+viewer
|
||||
= getRequestedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
unless currentUser.isWorker
|
||||
+inlinedForm(classNames="js-card-details-requester")
|
||||
+editCardRequesterForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getRequestedBy
|
||||
+viewer
|
||||
= getRequestedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
else if getRequestedBy
|
||||
+viewer
|
||||
= getRequestedBy
|
||||
|
||||
.card-details-item.card-details-item-name
|
||||
h3.card-details-item-title {{_ 'assigned-by'}}
|
||||
h3
|
||||
i.fa.fa-user-plus
|
||||
card-details-item-title {{_ 'assigned-by'}}
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-details-assigner")
|
||||
+editCardAssignerForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getAssignedBy
|
||||
+viewer
|
||||
= getAssignedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
unless currentUser.isWorker
|
||||
+inlinedForm(classNames="js-card-details-assigner")
|
||||
+editCardAssignerForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getAssignedBy
|
||||
+viewer
|
||||
= getAssignedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
else if getRequestedBy
|
||||
+viewer
|
||||
= getAssignedBy
|
||||
@@ -178,7 +226,9 @@ template(name="cardDetails")
|
||||
hr
|
||||
unless currentUser.isNoComments
|
||||
.activity-title
|
||||
h3 {{ _ 'activity'}}
|
||||
h3
|
||||
i.fa.fa-history
|
||||
| {{ _ 'activity'}}
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch
|
||||
span.toggle-switch-title {{_ 'hide-system-messages'}}
|
||||
@@ -220,32 +270,79 @@ template(name="editCardAssignerForm")
|
||||
|
||||
template(name="cardDetailsActionsPopup")
|
||||
ul.pop-over-list
|
||||
li: a.js-toggle-watch-card {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}}
|
||||
li
|
||||
a.js-toggle-watch-card
|
||||
if isWatching
|
||||
i.fa.fa-eye
|
||||
| {{_ 'unwatch'}}
|
||||
else
|
||||
i.fa.fa-eye-slash
|
||||
| {{_ 'watch'}}
|
||||
if canModifyCard
|
||||
hr
|
||||
unless currentUser.isWorker
|
||||
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-custom-fields
|
||||
i.fa.fa-list-alt
|
||||
| {{_ '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-spent-time
|
||||
i.fa.fa-clock-o
|
||||
| {{_ 'editCardSpentTimePopup-title'}}
|
||||
li
|
||||
a.js-set-card-color
|
||||
i.fa.fa-paint-brush
|
||||
| {{_ 'setCardColorPopup-title'}}
|
||||
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-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-spent-time {{_ 'editCardSpentTimePopup-title'}}
|
||||
li: a.js-set-card-color {{_ 'setCardColorPopup-title'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-move-card-to-top {{_ 'moveCardToTop-title'}}
|
||||
li: a.js-move-card-to-bottom {{_ 'moveCardToBottom-title'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-move-card {{_ 'moveCardPopup-title'}}
|
||||
li: a.js-copy-card {{_ 'copyCardPopup-title'}}
|
||||
li: a.js-copy-checklist-cards {{_ 'copyChecklistToManyCardsPopup-title'}}
|
||||
li
|
||||
a.js-move-card-to-top
|
||||
i.fa.fa-arrow-up
|
||||
| {{_ 'moveCardToTop-title'}}
|
||||
li
|
||||
a.js-move-card-to-bottom
|
||||
i.fa.fa-arrow-down
|
||||
| {{_ 'moveCardToBottom-title'}}
|
||||
unless currentUser.isWorker
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-move-card
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ 'moveCardPopup-title'}}
|
||||
li
|
||||
a.js-copy-card
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyCardPopup-title'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-copy-checklist-cards
|
||||
i.fa.fa-list
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyChecklistToManyCardsPopup-title'}}
|
||||
unless archived
|
||||
li: a.js-archive {{_ 'archive-card'}}
|
||||
li: a.js-more {{_ 'cardMorePopup-title'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-archive
|
||||
i.fa.fa-arrow-right
|
||||
i.fa.fa-archive
|
||||
| {{_ 'archive-card'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-more
|
||||
i.fa.fa-link
|
||||
| {{_ 'cardMorePopup-title'}}
|
||||
|
||||
template(name="moveCardPopup")
|
||||
+boardsAndLists
|
||||
@@ -296,6 +393,67 @@ template(name="cardMembersPopup")
|
||||
if isCardMember
|
||||
i.fa.fa-check
|
||||
|
||||
template(name="cardAssigneesPopup")
|
||||
unless currentUser.isWorker
|
||||
ul.pop-over-list.js-card-assignee-list
|
||||
each board.activeMembers
|
||||
li.item(class="{{#if isCardAssignee}}active{{/if}}")
|
||||
a.name.js-select-assignee(href="#")
|
||||
+userAvatar(userId=user._id)
|
||||
span.full-name
|
||||
= user.profile.fullname
|
||||
| (<span class="username">{{ user.username }}</span>)
|
||||
if isCardAssignee
|
||||
i.fa.fa-check
|
||||
if currentUser.isWorker
|
||||
ul.pop-over-list.js-card-assignee-list
|
||||
li.item(class="{{#if currentUser.isCardAssignee}}active{{/if}}")
|
||||
a.name.js-select-assignee(href="#")
|
||||
+userAvatar(userId=currentUser._id)
|
||||
span.full-name
|
||||
= currentUser.profile.fullname
|
||||
| (<span class="username">{{ currentUser.username }}</span>)
|
||||
if currentUser.isCardAssignee
|
||||
i.fa.fa-check
|
||||
|
||||
template(name="userAvatarAssignee")
|
||||
a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})")
|
||||
if userData.profile.avatarUrl
|
||||
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
|
||||
else
|
||||
+userAvatarAssigneeInitials(userId=userData._id)
|
||||
|
||||
if showStatus
|
||||
span.assignee-presence-status(class=presenceStatusClassName)
|
||||
span.member-type(class=memberType)
|
||||
|
||||
unless isSandstorm
|
||||
if showEdit
|
||||
if $eq currentUser._id userData._id
|
||||
a.edit-avatar.js-change-avatar
|
||||
i.fa.fa-pencil
|
||||
|
||||
template(name="cardAssigneePopup")
|
||||
.board-assignee-menu
|
||||
.mini-profile-info
|
||||
+userAvatar(userId=user._id showEdit=true)
|
||||
.info
|
||||
h3= user.profile.fullname
|
||||
p.quiet @{{ user.username }}
|
||||
ul.pop-over-list
|
||||
if currentUser.isNotCommentOnly
|
||||
unless currentUser.isWorker
|
||||
li: a.js-remove-assignee {{_ 'remove-member-from-card'}}
|
||||
|
||||
unless currentUser.isWorker
|
||||
if $eq currentUser._id user._id
|
||||
with currentUser
|
||||
li: a.js-edit-profile {{_ 'edit-profile'}}
|
||||
|
||||
template(name="userAvatarAssigneeInitials")
|
||||
svg.avatar.avatar-assignee-initials(viewBox="0 0 {{viewPortWidth}} 15")
|
||||
text(x="50%" y="13" text-anchor="middle")= initials
|
||||
|
||||
template(name="cardMorePopup")
|
||||
p.quiet
|
||||
span.clearfix
|
||||
|
||||
@@ -51,7 +51,8 @@ BlazeComponent.extendComponent({
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
@@ -117,6 +118,41 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
if (Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED) {
|
||||
// Send Webhook but not create Activities records ---
|
||||
const card = this.currentData();
|
||||
const userId = Meteor.userId();
|
||||
const params = {
|
||||
userId,
|
||||
cardId: card._id,
|
||||
boardId: card.boardId,
|
||||
listId: card.listId,
|
||||
user: Meteor.user().username,
|
||||
url: '',
|
||||
};
|
||||
|
||||
const integrations = Integrations.find({
|
||||
boardId: { $in: [card.boardId, Integrations.Const.GLOBAL_WEBHOOK_ID] },
|
||||
enabled: true,
|
||||
activities: { $in: ['CardDetailsRendered', 'all'] },
|
||||
}).fetch();
|
||||
|
||||
if (integrations.length > 0) {
|
||||
integrations.forEach(integration => {
|
||||
Meteor.call(
|
||||
'outgoingWebhooks',
|
||||
integration,
|
||||
'CardSelected',
|
||||
params,
|
||||
() => {
|
||||
return;
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
//-------------
|
||||
}
|
||||
|
||||
if (!Utils.isMiniScreen()) {
|
||||
Meteor.setTimeout(() => {
|
||||
$('.card-details').mCustomScrollbar({
|
||||
@@ -256,6 +292,8 @@ BlazeComponent.extendComponent({
|
||||
.trim();
|
||||
if (title) {
|
||||
this.data().setTitle(title);
|
||||
} else {
|
||||
this.data().setTitle('');
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-assigner'(event) {
|
||||
@@ -265,6 +303,8 @@ BlazeComponent.extendComponent({
|
||||
.trim();
|
||||
if (assigner) {
|
||||
this.data().setAssignedBy(assigner);
|
||||
} else {
|
||||
this.data().setAssignedBy('');
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-requester'(event) {
|
||||
@@ -274,10 +314,14 @@ BlazeComponent.extendComponent({
|
||||
.trim();
|
||||
if (requester) {
|
||||
this.data().setRequestedBy(requester);
|
||||
} else {
|
||||
this.data().setRequestedBy('');
|
||||
}
|
||||
},
|
||||
'click .js-member': Popup.open('cardMember'),
|
||||
'click .js-add-members': Popup.open('cardMembers'),
|
||||
'click .js-assignee': Popup.open('cardAssignee'),
|
||||
'click .js-add-assignees': Popup.open('cardAssignees'),
|
||||
'click .js-add-labels': Popup.open('cardLabels'),
|
||||
'click .js-received-date': Popup.open('editCardReceivedDate'),
|
||||
'click .js-start-date': Popup.open('editCardStartDate'),
|
||||
@@ -290,6 +334,19 @@ BlazeComponent.extendComponent({
|
||||
parentComponent.showOverlay.set(true);
|
||||
parentComponent.mouseHasEnterCardDetails = true;
|
||||
},
|
||||
'mousedown .js-card-details'() {
|
||||
Session.set('cardDetailsIsDragging', false);
|
||||
Session.set('cardDetailsIsMouseDown', true);
|
||||
},
|
||||
'mousemove .js-card-details'() {
|
||||
if (Session.get('cardDetailsIsMouseDown')) {
|
||||
Session.set('cardDetailsIsDragging', true);
|
||||
}
|
||||
},
|
||||
'mouseup .js-card-details'() {
|
||||
Session.set('cardDetailsIsDragging', false);
|
||||
Session.set('cardDetailsIsMouseDown', false);
|
||||
},
|
||||
'click #toggleButton'() {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
},
|
||||
@@ -298,6 +355,58 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('cardDetails');
|
||||
|
||||
Template.cardDetails.helpers({
|
||||
userData() {
|
||||
// We need to handle a special case for the search results provided by the
|
||||
// `matteodem:easy-search` package. Since these results gets published in a
|
||||
// separate collection, and not in the standard Meteor.Users collection as
|
||||
// expected, we use a component parameter ("property") to distinguish the
|
||||
// two cases.
|
||||
const userCollection = this.esSearch ? ESSearchResults : Users;
|
||||
return userCollection.findOne(this.userId, {
|
||||
fields: {
|
||||
profile: 1,
|
||||
username: 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
assigneeSelected() {
|
||||
if (this.getAssignees().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
memberType() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return user && user.isBoardAdmin() ? 'admin' : 'normal';
|
||||
},
|
||||
|
||||
presenceStatusClassName() {
|
||||
const user = Users.findOne(this.userId);
|
||||
const userPresence = presences.findOne({ userId: this.userId });
|
||||
if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
|
||||
else if (!userPresence) return 'disconnected';
|
||||
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
|
||||
return 'active';
|
||||
else return 'idle';
|
||||
},
|
||||
});
|
||||
|
||||
Template.userAvatarAssigneeInitials.helpers({
|
||||
initials() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return user && user.getInitials();
|
||||
},
|
||||
|
||||
viewPortWidth() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return ((user && user.getInitials().length) || 1) * 12;
|
||||
},
|
||||
});
|
||||
|
||||
// We extends the normal InlinedForm component to support UnsavedEdits draft
|
||||
// feature.
|
||||
(class extends InlinedForm {
|
||||
@@ -355,6 +464,7 @@ Template.cardDetailsActionsPopup.helpers({
|
||||
|
||||
Template.cardDetailsActionsPopup.events({
|
||||
'click .js-members': Popup.open('cardMembers'),
|
||||
'click .js-assignees': Popup.open('cardAssignees'),
|
||||
'click .js-labels': Popup.open('cardLabels'),
|
||||
'click .js-attachments': Popup.open('cardAttachments'),
|
||||
'click .js-custom-fields': Popup.open('cardCustomFields'),
|
||||
@@ -746,7 +856,14 @@ BlazeComponent.extendComponent({
|
||||
EscapeActions.register(
|
||||
'detailsPane',
|
||||
() => {
|
||||
Utils.goBoardId(Session.get('currentBoard'));
|
||||
if (Session.get('cardDetailsIsDragging')) {
|
||||
// Reset dragging status as the mouse landed outside the cardDetails template area and this will prevent a mousedown event from firing
|
||||
Session.set('cardDetailsIsDragging', false);
|
||||
Session.set('cardDetailsIsMouseDown', false);
|
||||
} else {
|
||||
// Prevent close card when the user is selecting text and moves the mouse cursor outside the card detail area
|
||||
Utils.goBoardId(Session.get('currentBoard'));
|
||||
}
|
||||
},
|
||||
() => {
|
||||
return !Session.equals('currentCard', null);
|
||||
@@ -755,3 +872,76 @@ EscapeActions.register(
|
||||
noClickEscapeOn: '.js-card-details,.board-sidebar,#header',
|
||||
},
|
||||
);
|
||||
|
||||
Template.cardAssigneesPopup.events({
|
||||
'click .js-select-assignee'(event) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const assigneeId = this.userId;
|
||||
card.toggleAssignee(assigneeId);
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardAssigneesPopup.helpers({
|
||||
isCardAssignee() {
|
||||
const card = Template.parentData();
|
||||
const cardAssignees = card.getAssignees();
|
||||
|
||||
return _.contains(cardAssignees, this.userId);
|
||||
},
|
||||
|
||||
user() {
|
||||
return Users.findOne(this.userId);
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardAssigneePopup.helpers({
|
||||
userData() {
|
||||
// We need to handle a special case for the search results provided by the
|
||||
// `matteodem:easy-search` package. Since these results gets published in a
|
||||
// separate collection, and not in the standard Meteor.Users collection as
|
||||
// expected, we use a component parameter ("property") to distinguish the
|
||||
// two cases.
|
||||
const userCollection = this.esSearch ? ESSearchResults : Users;
|
||||
return userCollection.findOne(this.userId, {
|
||||
fields: {
|
||||
profile: 1,
|
||||
username: 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
memberType() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return user && user.isBoardAdmin() ? 'admin' : 'normal';
|
||||
},
|
||||
|
||||
presenceStatusClassName() {
|
||||
const user = Users.findOne(this.userId);
|
||||
const userPresence = presences.findOne({ userId: this.userId });
|
||||
if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
|
||||
else if (!userPresence) return 'disconnected';
|
||||
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
|
||||
return 'active';
|
||||
else return 'idle';
|
||||
},
|
||||
|
||||
isCardAssignee() {
|
||||
const card = Template.parentData();
|
||||
const cardAssignees = card.getAssignees();
|
||||
|
||||
return _.contains(cardAssignees, this.userId);
|
||||
},
|
||||
|
||||
user() {
|
||||
return Users.findOne(this.userId);
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardAssigneePopup.events({
|
||||
'click .js-remove-assignee'() {
|
||||
Cards.findOne(this.cardId).unassignAssignee(this.userId);
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-edit-profile': Popup.open('editProfile'),
|
||||
});
|
||||
@@ -1,5 +1,80 @@
|
||||
@import 'nib'
|
||||
|
||||
// Assignee, code copied from wekan/client/users/userAvatar.styl
|
||||
|
||||
avatar-radius = 50%
|
||||
|
||||
.assignee
|
||||
border-radius: 3px
|
||||
display: block
|
||||
position: relative
|
||||
float: left
|
||||
height: 30px
|
||||
width: @height
|
||||
margin: 0 4px 4px 0
|
||||
cursor: pointer
|
||||
user-select: none
|
||||
z-index: 1
|
||||
text-decoration: none
|
||||
border-radius: avatar-radius
|
||||
|
||||
.avatar
|
||||
overflow: hidden
|
||||
border-radius: avatar-radius
|
||||
|
||||
&.avatar-assignee-initials
|
||||
height: 70%
|
||||
width: @height
|
||||
padding: 15%
|
||||
background-color: #dbdbdb
|
||||
color: #444444
|
||||
position: absolute
|
||||
|
||||
&.avatar-image
|
||||
height: 100%
|
||||
width: @height
|
||||
|
||||
.assignee-presence-status
|
||||
background-color: #b3b3b3
|
||||
border: 1px solid #fff
|
||||
border-radius: 50%
|
||||
height: 7px
|
||||
width: @height
|
||||
position: absolute
|
||||
right: -1px
|
||||
bottom: -1px
|
||||
border: 1px solid white
|
||||
z-index: 15
|
||||
|
||||
&.active
|
||||
background: #64c464
|
||||
border-color: #daf1da
|
||||
|
||||
&.idle
|
||||
background: #e4e467
|
||||
border-color: #f7f7d4
|
||||
|
||||
&.disconnected
|
||||
background: #bdbdbd
|
||||
border-color: #ededed
|
||||
|
||||
&.pending
|
||||
background: #e44242
|
||||
border-color: #f1dada
|
||||
|
||||
|
||||
|
||||
&.add-assignee
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
box-shadow: 0 0 0 2px darken(white, 25%) inset
|
||||
|
||||
&:hover, &.is-active
|
||||
box-shadow: 0 0 0 2px darken(white, 60%) inset
|
||||
|
||||
// Other card details
|
||||
|
||||
.card-details
|
||||
padding: 0
|
||||
flex-shrink: 0
|
||||
@@ -32,7 +107,9 @@
|
||||
border-bottom: 1px solid darken(white, 14%)
|
||||
|
||||
.close-card-details,
|
||||
.card-details-menu
|
||||
.card-details-menu,
|
||||
.close-card-details-mobile-web,
|
||||
.card-details-menu-mobile-web
|
||||
float: right
|
||||
|
||||
.close-card-details
|
||||
@@ -40,10 +117,20 @@
|
||||
padding: 5px
|
||||
margin-right: -8px
|
||||
|
||||
.close-card-details-mobile-web
|
||||
font-size: 24px
|
||||
padding: 5px
|
||||
margin-right: 40px
|
||||
|
||||
.card-details-menu
|
||||
font-size: 17px
|
||||
padding: 10px
|
||||
|
||||
.card-details-menu-mobile-web
|
||||
font-size: 17px
|
||||
padding: 10px
|
||||
margin-right: 30px
|
||||
|
||||
.card-details-watch
|
||||
font-size: 17px
|
||||
padding-left: 7px
|
||||
@@ -86,11 +173,6 @@
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
margin: 15px 0
|
||||
& ~ .js-open-inlined-form
|
||||
.viewer
|
||||
background-color #ffffff !important
|
||||
padding 15px !important
|
||||
border 1px solid #000000 !important
|
||||
|
||||
.card-details-item
|
||||
margin-right: 0.5em
|
||||
@@ -98,13 +180,16 @@
|
||||
margin-right: 0
|
||||
&.card-details-item-labels,
|
||||
&.card-details-item-members,
|
||||
&.card-details-item-assignees,
|
||||
&.card-details-item-received,
|
||||
&.card-details-item-start,
|
||||
&.card-details-item-due,
|
||||
&.card-details-item-end,
|
||||
&.card-details-item-customfield,
|
||||
&.card-details-item-name
|
||||
max-width: 50%
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 48%
|
||||
flex-grow: 1
|
||||
|
||||
.card-details-item-title
|
||||
@@ -131,7 +216,7 @@ 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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
template(name="checklists")
|
||||
h3 {{_ 'checklists'}}
|
||||
h3
|
||||
i.fa.fa-check
|
||||
| {{_ 'checklists'}}
|
||||
if toggleDeleteDialog.get
|
||||
.board-overlay#card-details-overlay
|
||||
+checklistDeleteDialog(checklist = checklistToDelete)
|
||||
|
||||
@@ -67,7 +67,8 @@ BlazeComponent.extendComponent({
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
}).register('checklistDetail');
|
||||
@@ -120,7 +121,8 @@ BlazeComponent.extendComponent({
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
@@ -228,7 +230,8 @@ Template.checklistItemDetail.helpers({
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -128,6 +128,9 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
& .viewer
|
||||
p
|
||||
margin-bottom: 2px
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 420px
|
||||
|
||||
.js-delete-checklist-item
|
||||
margin: 0 0 0.5em 1.33em
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
margin-right: 4px
|
||||
margin-bottom: 5px
|
||||
padding: 3px 8px
|
||||
max-width: 100%
|
||||
max-width: 210px
|
||||
min-width: 8px
|
||||
overflow: ellipsis
|
||||
word-wrap: break-word
|
||||
height: 18px
|
||||
vertical-align: bottom
|
||||
|
||||
|
||||
@@ -3,15 +3,25 @@ template(name="minicard")
|
||||
class="{{#if isLinkedCard}}linked-card{{/if}}"
|
||||
class="{{#if isLinkedBoard}}linked-board{{/if}}"
|
||||
class="minicard-{{colorClass}}")
|
||||
if isMiniScreen
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
unless isMiniScreen
|
||||
if showDesktopDragHandles
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
if cover
|
||||
.minicard-cover(style="background-image: url('{{cover.url}}');")
|
||||
if labels
|
||||
.minicard-labels
|
||||
each labels
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
unless hiddenMinicardLabelText
|
||||
span.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if hiddenMinicardLabelText
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
.minicard-title
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
if $eq 'prefix-with-full-path' currentBoard.presentParentTask
|
||||
.parent-prefix
|
||||
| {{ parentString ' > ' }}
|
||||
@@ -48,6 +58,8 @@ template(name="minicard")
|
||||
if getDue
|
||||
.date
|
||||
+minicardDueDate
|
||||
if getEnd
|
||||
+minicardEndDate
|
||||
if getSpentTime
|
||||
.date
|
||||
+cardSpentTime
|
||||
@@ -55,14 +67,21 @@ template(name="minicard")
|
||||
.minicard-custom-fields
|
||||
each customFieldsWD
|
||||
if definition.showOnCard
|
||||
.minicard-custom-field
|
||||
if definition.showLabelOnMiniCard
|
||||
if trueValue
|
||||
.minicard-custom-field
|
||||
if definition.showLabelOnMiniCard
|
||||
.minicard-custom-field-item
|
||||
+viewer
|
||||
= definition.name
|
||||
.minicard-custom-field-item
|
||||
+viewer
|
||||
= definition.name
|
||||
.minicard-custom-field-item
|
||||
+viewer
|
||||
= trueValue
|
||||
= trueValue
|
||||
|
||||
if getAssignees
|
||||
.minicard-assignees.js-minicard-assignees
|
||||
each getAssignees
|
||||
+userAvatar(userId=this)
|
||||
hr
|
||||
|
||||
if getMembers
|
||||
.minicard-members.js-minicard-members
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
// Template.cards.events({
|
||||
// 'click .member': Popup.open('cardMember')
|
||||
// });
|
||||
@@ -16,6 +18,38 @@ BlazeComponent.extendComponent({
|
||||
Utils.goBoardId(this.data().linkedId);
|
||||
},
|
||||
},
|
||||
{
|
||||
'click .js-toggle-minicard-label-text'() {
|
||||
if (cookies.has('hiddenMinicardLabelText')) {
|
||||
cookies.remove('hiddenMinicardLabelText'); //true
|
||||
} else {
|
||||
cookies.set('hiddenMinicardLabelText', 'true'); //true
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('minicard');
|
||||
|
||||
Template.minicard.helpers({
|
||||
showDesktopDragHandles() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
hiddenMinicardLabelText() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).hiddenMinicardLabelText;
|
||||
} else if (cookies.has('hiddenMinicardLabelText')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -81,6 +81,7 @@
|
||||
.minicard-labels
|
||||
float: right
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
|
||||
.minicard-label
|
||||
width: 11px
|
||||
@@ -92,8 +93,11 @@
|
||||
.minicard-custom-field
|
||||
display:flex;
|
||||
.minicard-custom-field-item
|
||||
max-width:50%;
|
||||
flex-grow:1;
|
||||
flex-grow: 1
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 100px
|
||||
margin-right: 4px
|
||||
.handle
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@@ -101,7 +105,7 @@
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
display:none;
|
||||
@media only screen and (max-width: 1199px) {
|
||||
@media only screen {
|
||||
display:block;
|
||||
}
|
||||
.fa-arrows
|
||||
@@ -111,7 +115,9 @@
|
||||
p:last-child
|
||||
margin-bottom: 0
|
||||
.viewer
|
||||
display: inline-block
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 230px
|
||||
.dates
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -144,14 +150,7 @@
|
||||
.badge-text
|
||||
vertical-align: middle
|
||||
&.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
|
||||
margin-bottom: 0.1rem
|
||||
|
||||
.badge-text
|
||||
font-size: 0.9em
|
||||
@@ -161,9 +160,10 @@
|
||||
padding-left: 0px
|
||||
line-height: 12px
|
||||
|
||||
.minicard-members
|
||||
.minicard-members,
|
||||
.minicard-assignees
|
||||
float: right
|
||||
margin: 2px -8px -2px 0
|
||||
margin: 2px -8px 12px 0
|
||||
|
||||
.member
|
||||
float: right
|
||||
@@ -171,10 +171,17 @@
|
||||
height: 28px
|
||||
width: @height
|
||||
|
||||
.assignee
|
||||
float: right
|
||||
border-radius: 50%
|
||||
height: 28px
|
||||
width: @height
|
||||
|
||||
+ .badges
|
||||
margin-top: 10px
|
||||
|
||||
.minicard-members:empty
|
||||
.minicard-members:empty,
|
||||
.minicard-assignees:empty
|
||||
display: none
|
||||
|
||||
&.minicard-composer
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
template(name="subtasks")
|
||||
h3 {{_ 'subtasks'}}
|
||||
h3
|
||||
i.fa.fa-sitemap
|
||||
| {{_ 'subtasks'}}
|
||||
if toggleDeleteDialog.get
|
||||
.board-overlay#card-details-overlay
|
||||
+subtaskDeleteDialog(subtask = subtaskToDelete)
|
||||
|
||||
@@ -3,7 +3,8 @@ BlazeComponent.extendComponent({
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
}).register('subtaskDetail');
|
||||
@@ -55,7 +56,8 @@ BlazeComponent.extendComponent({
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
@@ -154,7 +156,8 @@ Template.subtaskItemDetail.helpers({
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -211,22 +211,20 @@ BlazeComponent.extendComponent({
|
||||
this.parentComponent().nextStep();
|
||||
},
|
||||
|
||||
onMapMember(evt) {
|
||||
const memberToMap = this.currentData();
|
||||
if (memberToMap.wekan) {
|
||||
// todo xxx ask for confirmation?
|
||||
this.unmapMember(memberToMap.id);
|
||||
} else {
|
||||
this.setSelectedMember(memberToMap.id);
|
||||
Popup.open('importMapMembersAdd')(evt);
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
submit: this.onSubmit,
|
||||
'click .js-select-member': this.onMapMember,
|
||||
'click .js-select-member'(evt) {
|
||||
const memberToMap = this.currentData();
|
||||
if (memberToMap.wekan) {
|
||||
// todo xxx ask for confirmation?
|
||||
this.unmapMember(memberToMap.id);
|
||||
} else {
|
||||
this.setSelectedMember(memberToMap.id);
|
||||
Popup.open('importMapMembersAdd')(evt);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -31,12 +33,6 @@ BlazeComponent.extendComponent({
|
||||
const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
|
||||
const $cards = this.$('.js-minicards');
|
||||
|
||||
if (window.matchMedia('(max-width: 1199px)').matches) {
|
||||
$('.js-minicards').sortable({
|
||||
handle: '.handle',
|
||||
});
|
||||
}
|
||||
|
||||
$cards.sortable({
|
||||
connectWith: '.js-minicards:not(.js-list-full)',
|
||||
tolerance: 'pointer',
|
||||
@@ -79,16 +75,15 @@ 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' ||
|
||||
Utils.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' ||
|
||||
!boardView
|
||||
Utils.boardView() === 'board-view-lists' ||
|
||||
Utils.boardView() === 'board-view-cal' ||
|
||||
!Utils.boardView
|
||||
)
|
||||
swimlaneId = currentBoard.getDefaultSwimline()._id;
|
||||
|
||||
@@ -122,8 +117,29 @@ BlazeComponent.extendComponent({
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch(itemsSelector);
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member or is comment only
|
||||
this.autorun(() => {
|
||||
let showDesktopDragHandles = false;
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
showDesktopDragHandles = (currentUser.profile || {})
|
||||
.showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
showDesktopDragHandles = true;
|
||||
} else {
|
||||
showDesktopDragHandles = false;
|
||||
}
|
||||
|
||||
if (!Utils.isMiniScreen() && showDesktopDragHandles) {
|
||||
$cards.sortable({
|
||||
handle: '.handle',
|
||||
});
|
||||
} else {
|
||||
$cards.sortable({
|
||||
handle: '.minicard',
|
||||
});
|
||||
}
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member or is comment only
|
||||
$cards.sortable('option', 'disabled', !userIsMember());
|
||||
});
|
||||
|
||||
@@ -155,6 +171,19 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('list');
|
||||
|
||||
Template.list.helpers({
|
||||
showDesktopDragHandles() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.miniList.events({
|
||||
'click .js-select-list'() {
|
||||
const listId = this._id;
|
||||
|
||||
@@ -84,17 +84,16 @@
|
||||
padding-left: 10px
|
||||
color: #a6a6a6
|
||||
|
||||
|
||||
.list-header-menu
|
||||
position: absolute
|
||||
padding: 27px 19px
|
||||
margin-top: 1px
|
||||
top: -7px
|
||||
right: -7px
|
||||
right: 3px
|
||||
|
||||
.list-header-plus-icon
|
||||
color: #a6a6a6
|
||||
margin-right: 10px
|
||||
margin-right: 15px
|
||||
|
||||
.highlight
|
||||
color: #ce1414
|
||||
@@ -165,7 +164,16 @@
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.list-header-menu
|
||||
margin-right: 30px
|
||||
position: absolute
|
||||
padding: 27px 19px
|
||||
margin-top: 1px
|
||||
top: -7px
|
||||
margin-right: 7px
|
||||
right: -3px
|
||||
|
||||
.list-header
|
||||
.list-header-name
|
||||
margin-left: 1.4rem
|
||||
|
||||
.mini-list
|
||||
flex: 0 0 60px
|
||||
@@ -221,9 +229,17 @@
|
||||
padding: 7px
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
right: 17px
|
||||
right: 47px
|
||||
font-size: 20px
|
||||
|
||||
.list-header-handle
|
||||
position: absolute
|
||||
padding: 7px
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
right: 10px
|
||||
font-size: 24px
|
||||
|
||||
.link-board-wrapper
|
||||
display: flex
|
||||
align-items: baseline
|
||||
|
||||
@@ -48,7 +48,6 @@ BlazeComponent.extendComponent({
|
||||
const board = this.data().board();
|
||||
let linkedId = '';
|
||||
let swimlaneId = '';
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
let cardType = 'cardType-card';
|
||||
if (title) {
|
||||
if (board.isTemplatesBoard()) {
|
||||
@@ -71,14 +70,14 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
cardType = 'cardType-linkedBoard';
|
||||
}
|
||||
} else if (boardView === 'board-view-swimlanes')
|
||||
} else if (Utils.boardView() === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
else if (
|
||||
boardView === 'board-view-lists' ||
|
||||
boardView === 'board-view-cal' ||
|
||||
!boardView
|
||||
Utils.boardView() === 'board-view-lists' ||
|
||||
Utils.boardView() === 'board-view-cal' ||
|
||||
!Utils.boardView
|
||||
)
|
||||
swimlaneId = board.getDefaultSwimline()._id;
|
||||
|
||||
@@ -157,9 +156,8 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
idOrNull(swimlaneId) {
|
||||
const currentUser = Meteor.user();
|
||||
if (
|
||||
(currentUser.profile || {}).boardView === 'board-view-swimlanes' ||
|
||||
Utils.boardView() === 'board-view-swimlanes' ||
|
||||
this.data()
|
||||
.board()
|
||||
.isTemplatesBoard()
|
||||
@@ -191,7 +189,8 @@ BlazeComponent.extendComponent({
|
||||
!this.reachedWipLimit() &&
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
@@ -397,10 +396,9 @@ BlazeComponent.extendComponent({
|
||||
'.js-swimlane',
|
||||
);
|
||||
this.swimlaneId = '';
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
if (Utils.boardView() === 'board-view-swimlanes')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else if (boardView === 'board-view-lists' || !boardView)
|
||||
else if (Utils.boardView() === 'board-view-lists' || !Utils.boardView)
|
||||
this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
|
||||
},
|
||||
|
||||
@@ -580,7 +578,7 @@ BlazeComponent.extendComponent({
|
||||
const swimlane = $(Popup._getTopStack().openerElement).parents(
|
||||
'.js-swimlane',
|
||||
);
|
||||
if ((Meteor.user().profile || {}).boardView === 'board-view-swimlanes')
|
||||
if (Utils.boardView() === 'board-view-swimlanes')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
|
||||
// List where to insert card
|
||||
@@ -701,12 +699,27 @@ BlazeComponent.extendComponent({
|
||||
this.listId = this.parentComponent().data()._id;
|
||||
this.swimlaneId = '';
|
||||
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
const isSandstorm =
|
||||
Meteor.settings &&
|
||||
Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
|
||||
if (isSandstorm) {
|
||||
const user = Meteor.user();
|
||||
if (user) {
|
||||
if (Utils.boardView() === 'board-view-swimlanes') {
|
||||
this.swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
}
|
||||
}
|
||||
} else if (Utils.boardView() === 'board-view-swimlanes') {
|
||||
this.swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
}
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
|
||||
@@ -9,7 +9,8 @@ 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="{{ moment modifiedAt 'LLL' }}"
|
||||
class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
|
||||
+viewer
|
||||
= title
|
||||
if wipLimit.enabled
|
||||
@@ -29,16 +30,22 @@ template(name="listHeader")
|
||||
if canSeeAddCard
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-icon
|
||||
a.fa.fa-navicon.js-open-list-menu
|
||||
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
else
|
||||
a.list-header-menu-icon.fa.fa-angle-right.js-select-list
|
||||
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
else if currentUser.isBoardMember
|
||||
if isWatching
|
||||
i.list-header-watch-icon.fa.fa-eye
|
||||
div.list-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
//if isBoardAdmin
|
||||
// a.fa.js-list-star.list-header-plus-icon(class="fa-star{{#unless starred}}-o{{/unless}}")
|
||||
if canSeeAddCard
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-icon
|
||||
a.fa.fa-navicon.js-open-list-menu
|
||||
if showDesktopDragHandles
|
||||
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
|
||||
template(name="editListTitleForm")
|
||||
.list-composer
|
||||
@@ -49,25 +56,47 @@ template(name="editListTitleForm")
|
||||
|
||||
template(name="listActionPopup")
|
||||
ul.pop-over-list
|
||||
li: a.js-toggle-watch-list {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}}
|
||||
li
|
||||
a.js-toggle-watch-list
|
||||
if isWatching
|
||||
i.fa.fa-eye
|
||||
| {{_ 'unwatch'}}
|
||||
else
|
||||
i.fa.fa-eye-slash
|
||||
| {{_ 'watch'}}
|
||||
unless currentUser.isCommentOnly
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-set-color-list {{_ 'set-color-list'}}
|
||||
hr
|
||||
unless currentUser.isWorker
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-set-color-list
|
||||
i.fa.fa-paint-brush
|
||||
| {{_ 'set-color-list'}}
|
||||
ul.pop-over-list
|
||||
if cards.count
|
||||
li: a.js-select-cards {{_ 'list-select-cards'}}
|
||||
hr
|
||||
li
|
||||
a.js-select-cards
|
||||
i.fa.fa-check-square
|
||||
| {{_ 'list-select-cards'}}
|
||||
if currentUser.isBoardAdmin
|
||||
ul.pop-over-list
|
||||
li: a.js-set-wip-limit {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}}
|
||||
li
|
||||
a.js-set-wip-limit
|
||||
i.fa.fa-ban
|
||||
| {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}}
|
||||
unless currentUser.isWorker
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-close-list {{_ 'archive-list'}}
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-close-list
|
||||
i.fa.fa-arrow-right
|
||||
i.fa.fa-archive
|
||||
| {{_ 'archive-list'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-more {{_ 'listMorePopup-title'}}
|
||||
li
|
||||
a.js-more
|
||||
i.fa.fa-link
|
||||
| {{_ 'listMorePopup-title'}}
|
||||
|
||||
template(name="boardLists")
|
||||
ul.pop-over-list
|
||||
@@ -87,7 +116,8 @@ template(name="listMorePopup")
|
||||
input.inline-input(type="text" readonly value="{{ rootUrl }}")
|
||||
| {{_ 'added'}}
|
||||
span.date(title=list.createdAt) {{ moment createdAt 'LLL' }}
|
||||
a.js-delete {{_ 'delete'}}
|
||||
unless currentUser.isWorker
|
||||
a.js-delete {{_ 'delete'}}
|
||||
|
||||
template(name="listDeletePopup")
|
||||
p {{_ "list-delete-pop"}}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
let listsColors;
|
||||
Meteor.startup(() => {
|
||||
listsColors = Lists.simpleSchema()._schema.color.allowedValues;
|
||||
@@ -7,12 +9,27 @@ BlazeComponent.extendComponent({
|
||||
canSeeAddCard() {
|
||||
const list = Template.currentData();
|
||||
return (
|
||||
!list.getWipLimit('enabled') ||
|
||||
list.getWipLimit('soft') ||
|
||||
!this.reachedWipLimit()
|
||||
(!list.getWipLimit('enabled') ||
|
||||
list.getWipLimit('soft') ||
|
||||
!this.reachedWipLimit()) &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
isBoardAdmin() {
|
||||
return Meteor.user().isBoardAdmin();
|
||||
},
|
||||
starred(check = undefined) {
|
||||
const list = Template.currentData();
|
||||
const status = list.isStarred();
|
||||
if (check === undefined) {
|
||||
// just check
|
||||
return status;
|
||||
} else {
|
||||
list.star(!status);
|
||||
return !status;
|
||||
}
|
||||
},
|
||||
editTitle(event) {
|
||||
event.preventDefault();
|
||||
const newTitle = this.childComponents('inlinedForm')[0]
|
||||
@@ -30,14 +47,18 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
limitToShowCardsCount() {
|
||||
return Meteor.user().getLimitToShowCardsCount();
|
||||
const currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return Meteor.user().getLimitToShowCardsCount();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
cardsCount() {
|
||||
const list = Template.currentData();
|
||||
let swimlaneId = '';
|
||||
const boardView = (Meteor.user().profile || {}).boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
if (Utils.boardView() === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
@@ -61,6 +82,10 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-list-star'(event) {
|
||||
event.preventDefault();
|
||||
this.starred(!this.starred());
|
||||
},
|
||||
'click .js-open-list-menu': Popup.open('listAction'),
|
||||
'click .js-add-card'(event) {
|
||||
const listDom = $(event.target).parents(
|
||||
@@ -80,6 +105,19 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('listHeader');
|
||||
|
||||
Template.listHeader.helpers({
|
||||
showDesktopDragHandles() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.listActionPopup.helpers({
|
||||
isWipLimitEnabled() {
|
||||
return Template.currentData().getWipLimit('enabled');
|
||||
|
||||
@@ -1,9 +1,90 @@
|
||||
import _sanitizeXss from 'xss';
|
||||
const ASIS = 'asis';
|
||||
const sanitizeXss = (input, options) => {
|
||||
const defaultAllowedIframeSrc = /^(https:){0,1}\/\/.*?(youtube|vimeo|dailymotion|youku)/i;
|
||||
const allowedIframeSrcRegex = (function() {
|
||||
let reg = defaultAllowedIframeSrc;
|
||||
const SAFE_IFRAME_SRC_PATTERN =
|
||||
Meteor.settings.public.SAFE_IFRAME_SRC_PATTERN;
|
||||
try {
|
||||
if (SAFE_IFRAME_SRC_PATTERN !== undefined) {
|
||||
reg = new RegExp(SAFE_IFRAME_SRC_PATTERN, 'i');
|
||||
}
|
||||
} catch (e) {
|
||||
/*eslint no-console: ["error", { allow: ["warn", "error"] }] */
|
||||
|
||||
console.error('Wrong pattern specified', SAFE_IFRAM_SRC_PATTERN, e);
|
||||
}
|
||||
return reg;
|
||||
})();
|
||||
const targetWindow = '_blank';
|
||||
const getHtmlDOM = html => {
|
||||
const i = document.createElement('i');
|
||||
i.innerHTML = html;
|
||||
return i.firstChild;
|
||||
};
|
||||
options = {
|
||||
onTag(tag, html, options) {
|
||||
const htmlDOM = getHtmlDOM(html);
|
||||
const getAttr = attr => {
|
||||
return htmlDOM && attr && htmlDOM.getAttribute(attr);
|
||||
};
|
||||
if (tag === 'iframe') {
|
||||
const clipCls = 'note-vide-clip';
|
||||
if (!options.isClosing) {
|
||||
const iframeCls = getAttr('class');
|
||||
let safe = iframeCls.indexOf(clipCls) > -1;
|
||||
const src = getAttr('src');
|
||||
if (allowedIframeSrcRegex.exec(src)) {
|
||||
safe = true;
|
||||
}
|
||||
if (safe)
|
||||
return `<iframe src='${src}' class="${clipCls}" width=100% height=auto allowfullscreen></iframe>`;
|
||||
} else {
|
||||
// remove </iframe> tag
|
||||
return '';
|
||||
}
|
||||
} else if (tag === 'a') {
|
||||
if (!options.isClosing) {
|
||||
if (getAttr(ASIS) === 'true') {
|
||||
// if has a ASIS attribute, don't do anything, it's a member id
|
||||
return html;
|
||||
} else {
|
||||
const href = getAttr('href');
|
||||
if (href.match(/^((http(s){0,1}:){0,1}\/\/|\/)/)) {
|
||||
// a valid url
|
||||
return `<a href=${href} target=${targetWindow}>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (tag === 'img') {
|
||||
if (!options.isClosing) {
|
||||
const src = getAttr('src');
|
||||
if (src) {
|
||||
return `<a href='${src}' class='swipebox'><img src='${src}' class="attachment-image-preview mCS_img_loaded"></a>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
onTagAttr(tag, name, value) {
|
||||
if (tag === 'img' && name === 'src') {
|
||||
if (value && value.substr(0, 5) === 'data:') {
|
||||
// allow image with dataURI src
|
||||
return `${name}='${value}'`;
|
||||
}
|
||||
} else if (tag === 'a' && name === 'target') {
|
||||
return `${name}='${targetWindow}'`; // always change a href target to a new window
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
...options,
|
||||
};
|
||||
return _sanitizeXss(input, options);
|
||||
};
|
||||
Template.editor.onRendered(() => {
|
||||
const $textarea = this.$('textarea');
|
||||
|
||||
autosize($textarea);
|
||||
|
||||
$textarea.escapeableTextComplete([
|
||||
const textareaSelector = 'textarea';
|
||||
const mentions = [
|
||||
// User mentions
|
||||
{
|
||||
match: /\B@([\w.]*)$/,
|
||||
@@ -13,7 +94,13 @@ Template.editor.onRendered(() => {
|
||||
currentBoard
|
||||
.activeMembers()
|
||||
.map(member => {
|
||||
const username = Users.findOne(member.userId).username;
|
||||
const user = Users.findOne(member.userId);
|
||||
if (user._id === Meteor.userId()) {
|
||||
return null;
|
||||
}
|
||||
const value = user.username;
|
||||
const username =
|
||||
value && value.match(/\s+/) ? `"${value}"` : value;
|
||||
return username.includes(term) ? username : null;
|
||||
})
|
||||
.filter(Boolean),
|
||||
@@ -27,11 +114,223 @@ Template.editor.onRendered(() => {
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
]);
|
||||
];
|
||||
const enableTextarea = function() {
|
||||
const $textarea = this.$(textareaSelector);
|
||||
autosize($textarea);
|
||||
$textarea.escapeableTextComplete(mentions);
|
||||
};
|
||||
if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) {
|
||||
const isSmall = Utils.isMiniScreen();
|
||||
const toolbar = isSmall
|
||||
? [
|
||||
['view', ['fullscreen']],
|
||||
['table', ['table']],
|
||||
['font', ['bold']],
|
||||
['color', ['color']],
|
||||
['insert', ['video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
//['fontsize', ['fontsize']],
|
||||
]
|
||||
: [
|
||||
['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 = sanitizeXss;
|
||||
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;
|
||||
};
|
||||
let popupShown = false;
|
||||
inputs.each(function(idx, input) {
|
||||
mSummernotes[idx] = $(input).summernote({
|
||||
placeholder,
|
||||
callbacks: {
|
||||
onKeydown(e) {
|
||||
if (popupShown) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
onKeyup(e) {
|
||||
if (popupShown) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
onInit(object) {
|
||||
const originalInput = this;
|
||||
const setAutocomplete = function(jEditor) {
|
||||
if (jEditor !== undefined) {
|
||||
jEditor.escapeableTextComplete(mentions).on({
|
||||
'textComplete:show'() {
|
||||
popupShown = true;
|
||||
},
|
||||
'textComplete:hide'() {
|
||||
popupShown = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
$(originalInput).on('submitted', function() {
|
||||
// resetCommentInput has been called
|
||||
if (!this.value) {
|
||||
const sn = getSummernote(this);
|
||||
sn && sn.summernote('code', '');
|
||||
}
|
||||
});
|
||||
const jEditor = object && object.editable;
|
||||
const toolbar = object && object.toolbar;
|
||||
setAutocomplete(jEditor);
|
||||
if (toolbar !== undefined) {
|
||||
const fBtn = toolbar.find('.btn-fullscreen');
|
||||
fBtn.on('click', function() {
|
||||
const $this = $(this),
|
||||
isActive = $this.hasClass('active');
|
||||
$('.minicards,#header-quick-access').toggle(!isActive); // mini card is still showing when editor is in fullscreen mode, we hide here manually
|
||||
});
|
||||
}
|
||||
},
|
||||
onImageUpload(files) {
|
||||
const $summernote = getSummernote(this);
|
||||
if (files && files.length > 0) {
|
||||
const image = files[0];
|
||||
const currentCard = Cards.findOne(Session.get('currentCard'));
|
||||
const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
|
||||
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
|
||||
const insertImage = src => {
|
||||
const img = document.createElement('img');
|
||||
img.src = src;
|
||||
img.setAttribute('width', '100%');
|
||||
$summernote.summernote('insertNode', img);
|
||||
};
|
||||
const processData = function(fileObj) {
|
||||
Utils.processUploadedAttachment(
|
||||
currentCard,
|
||||
fileObj,
|
||||
attachment => {
|
||||
if (
|
||||
attachment &&
|
||||
attachment._id &&
|
||||
attachment.isImage()
|
||||
) {
|
||||
attachment.one('uploaded', function() {
|
||||
const maxTry = 3;
|
||||
const checkItvl = 500;
|
||||
let retry = 0;
|
||||
const checkUrl = function() {
|
||||
// even though uploaded event fired, attachment.url() is still null somehow //TODO
|
||||
const url = attachment.url();
|
||||
if (url) {
|
||||
insertImage(
|
||||
`${location.protocol}//${location.host}${url}`,
|
||||
);
|
||||
} else {
|
||||
retry++;
|
||||
if (retry < maxTry) {
|
||||
setTimeout(checkUrl, checkItvl);
|
||||
}
|
||||
}
|
||||
};
|
||||
checkUrl();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
if (MAX_IMAGE_PIXEL) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const dataurl = e && e.target && e.target.result;
|
||||
if (dataurl !== undefined) {
|
||||
// need to shrink image
|
||||
Utils.shrinkImage({
|
||||
dataurl,
|
||||
maxSize: MAX_IMAGE_PIXEL,
|
||||
ratio: COMPRESS_RATIO,
|
||||
toBlob: true,
|
||||
callback(blob) {
|
||||
if (blob !== false) {
|
||||
blob.name = image.name;
|
||||
processData(blob);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(image);
|
||||
} else {
|
||||
processData(image);
|
||||
}
|
||||
}
|
||||
},
|
||||
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('code', ''); //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';
|
||||
|
||||
// XXX I believe we should compute a HTML rendered field on the server that
|
||||
// would handle markdown and user mentions. We can simply have two
|
||||
// fields, one source, and one compiled version (in HTML) and send only the
|
||||
@@ -53,11 +352,12 @@ Blaze.Template.registerHelper(
|
||||
}
|
||||
return member;
|
||||
});
|
||||
const mentionRegex = /\B@([\w.]*)/gi;
|
||||
const mentionRegex = /\B@(?:(?:"([\w.\s]*)")|([\w.]+))/gi; // including space in username
|
||||
|
||||
let currentMention;
|
||||
while ((currentMention = mentionRegex.exec(content)) !== null) {
|
||||
const [fullMention, username] = currentMention;
|
||||
const [fullMention, quoteduser, simple] = currentMention;
|
||||
const username = quoteduser || simple;
|
||||
const knowedUser = _.findWhere(knowedUsers, { username });
|
||||
if (!knowedUser) {
|
||||
continue;
|
||||
@@ -76,38 +376,42 @@ Blaze.Template.registerHelper(
|
||||
// `userId` to the popup as usual, and we need to store it in the DOM
|
||||
// using a data attribute.
|
||||
'data-userId': knowedUser.userId,
|
||||
[ASIS]: 'true',
|
||||
},
|
||||
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'(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.
|
||||
event.preventDefault();
|
||||
|
||||
let prevent = true;
|
||||
const userId = event.currentTarget.dataset.userid;
|
||||
if (userId) {
|
||||
Popup.open('member').call({ userId }, event, templateInstance);
|
||||
} else {
|
||||
const href = event.currentTarget.href;
|
||||
if (href) {
|
||||
const child = event.currentTarget.firstElementChild;
|
||||
if (child && child.tagName === 'IMG') {
|
||||
prevent = false;
|
||||
} else if (href) {
|
||||
window.open(href, '_blank');
|
||||
}
|
||||
}
|
||||
if (prevent) {
|
||||
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.
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -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
|
||||
@@ -203,6 +232,7 @@ kbd
|
||||
background: darken(white, 2%)
|
||||
border-radius: 3px
|
||||
border: 1px solid darken(white, 10%)
|
||||
color: unset
|
||||
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.15)
|
||||
|
||||
.clear
|
||||
@@ -348,6 +378,12 @@ a
|
||||
|
||||
.viewer
|
||||
min-height: 18px
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
|
||||
table
|
||||
word-wrap: normal
|
||||
word-break: normal
|
||||
|
||||
ol
|
||||
list-style-type: decimal
|
||||
@@ -395,6 +431,9 @@ a
|
||||
height: 100%
|
||||
margin: 0px
|
||||
|
||||
.panel-default
|
||||
width: 83vw
|
||||
|
||||
.inline-input
|
||||
height: 37px
|
||||
margin: 8px 10px 0 0
|
||||
|
||||
@@ -130,7 +130,8 @@ $popupWidth = 300px
|
||||
.popup-container-depth-{depth}
|
||||
transform: translateX(- depth * $popupWidth)
|
||||
|
||||
.select-members-list
|
||||
.select-members-list,
|
||||
.select-avatars-list
|
||||
margin-bottom: 8px
|
||||
|
||||
.pop-over-list
|
||||
@@ -230,7 +231,8 @@ $popupWidth = 300px
|
||||
min-height: 56px
|
||||
position: relative
|
||||
|
||||
.member
|
||||
.member,
|
||||
.avatar
|
||||
position: absolute
|
||||
top: 2px
|
||||
left: 2px
|
||||
|
||||
@@ -4,12 +4,16 @@ template(name='information')
|
||||
| {{_ 'error-notAuthorized'}}
|
||||
else
|
||||
.content-title
|
||||
span {{_ 'info'}}
|
||||
span
|
||||
i.fa.fa-info-circle
|
||||
| {{_ 'info'}}
|
||||
.content-body
|
||||
.side-menu
|
||||
ul
|
||||
li.active
|
||||
a.js-setting-menu(data-id="information-display") {{_ 'info'}}
|
||||
a.js-setting-menu(data-id="information-display")
|
||||
i.fa.fa-info-circle
|
||||
| {{_ 'info'}}
|
||||
.main-body
|
||||
+statistics
|
||||
|
||||
@@ -19,9 +23,21 @@ template(name='statistics')
|
||||
tr
|
||||
th Wekan {{_ 'info'}}
|
||||
td {{statistics.version}}
|
||||
tr
|
||||
th {{_ 'Meteor_version'}}
|
||||
td {{statistics.meteor.meteorVersion}}
|
||||
tr
|
||||
th {{_ 'Node_version'}}
|
||||
td {{statistics.process.nodeVersion}}
|
||||
tr
|
||||
th {{_ 'MongoDB_version'}}
|
||||
td {{statistics.mongo.mongoVersion}}
|
||||
tr
|
||||
th {{_ 'MongoDB_storage_engine'}}
|
||||
td {{statistics.mongo.mongoStorageEngine}}
|
||||
tr
|
||||
th {{_ 'MongoDB_Oplog_enabled'}}
|
||||
td {{statistics.mongo.mongoOplogEnabled}}
|
||||
tr
|
||||
th {{_ 'OS_Type'}}
|
||||
td {{statistics.os.type}}
|
||||
|
||||
@@ -5,16 +5,22 @@ template(name="people")
|
||||
else
|
||||
.content-title.ext-box
|
||||
.ext-box-left
|
||||
span {{_ 'people'}}
|
||||
span
|
||||
i.fa.fa-users
|
||||
| {{_ 'people'}}
|
||||
input#searchInput(placeholder="{{_ 'search'}}")
|
||||
button#searchButton {{_ 'search'}}
|
||||
button#searchButton
|
||||
i.fa.fa-search
|
||||
| {{_ 'search'}}
|
||||
.ext-box-right
|
||||
span {{_ 'people-number'}} #{peopleNumber}
|
||||
.content-body
|
||||
.side-menu
|
||||
ul
|
||||
li.active
|
||||
a.js-setting-menu(data-id="people-setting") {{_ 'people'}}
|
||||
a.js-setting-menu(data-id="people-setting")
|
||||
i.fa.fa-users
|
||||
| {{_ 'people'}}
|
||||
.main-body
|
||||
if loading.get
|
||||
+spinner
|
||||
@@ -39,28 +45,58 @@ template(name="peopleGeneral")
|
||||
|
||||
template(name="peopleRow")
|
||||
tr
|
||||
td.username {{ userData.username }}
|
||||
td {{ userData.profile.fullname }}
|
||||
td
|
||||
if userData.isAdmin
|
||||
| {{_ 'yes'}}
|
||||
else
|
||||
| {{_ 'no'}}
|
||||
td {{ userData.emails.[0].address }}
|
||||
td
|
||||
if userData.emails.[0].verified
|
||||
| {{_ 'yes'}}
|
||||
else
|
||||
| {{_ 'no'}}
|
||||
td {{ moment userData.createdAt 'LLL' }}
|
||||
if userData.loginDisabled
|
||||
td.username <s>{{ userData.username }}</s>
|
||||
else
|
||||
td.username {{ userData.username }}
|
||||
if userData.loginDisabled
|
||||
td <s>{{ userData.profile.fullname }}</s>
|
||||
else
|
||||
td {{ userData.profile.fullname }}
|
||||
if userData.loginDisabled
|
||||
td
|
||||
if userData.isAdmin
|
||||
| <s>{{_ 'yes'}}</s>
|
||||
else
|
||||
| <s>{{_ 'no'}}</s>
|
||||
else
|
||||
td
|
||||
if userData.isAdmin
|
||||
| {{_ 'yes'}}
|
||||
else
|
||||
| {{_ 'no'}}
|
||||
if userData.loginDisabled
|
||||
td <s>{{ userData.emails.[0].address }}</s>
|
||||
else
|
||||
td {{ userData.emails.[0].address }}
|
||||
if userData.loginDisabled
|
||||
td
|
||||
if userData.emails.[0].verified
|
||||
| <s>{{_ 'yes'}}</s>
|
||||
else
|
||||
| <s>{{_ 'no'}}</s>
|
||||
else
|
||||
td
|
||||
if userData.emails.[0].verified
|
||||
| {{_ 'yes'}}
|
||||
else
|
||||
| {{_ 'no'}}
|
||||
if userData.loginDisabled
|
||||
td <s>{{ moment userData.createdAt 'LLL' }}</s>
|
||||
else
|
||||
td {{ moment userData.createdAt 'LLL' }}
|
||||
td
|
||||
if userData.loginDisabled
|
||||
| {{_ 'no'}}
|
||||
else
|
||||
| {{_ 'yes'}}
|
||||
td {{_ userData.authenticationMethod }}
|
||||
if userData.loginDisabled
|
||||
td <s>{{_ userData.authenticationMethod }}</s>
|
||||
else
|
||||
td {{_ userData.authenticationMethod }}
|
||||
td
|
||||
a.edit-user
|
||||
i.fa.fa-edit
|
||||
| {{_ 'edit'}}
|
||||
|
||||
template(name="editUserPopup")
|
||||
@@ -109,6 +145,6 @@ template(name="editUserPopup")
|
||||
input.js-profile-password(type="password")
|
||||
div.buttonsContainer
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
div
|
||||
input#deleteButton.primary.wide(type="button" value="{{_ 'delete'}}")
|
||||
// div
|
||||
// input#deleteButton.primary.wide(type="button" value="{{_ 'delete'}}")
|
||||
|
||||
@@ -17,7 +17,7 @@ BlazeComponent.extendComponent({
|
||||
this.autorun(() => {
|
||||
const limit = this.page.get() * usersPerPage;
|
||||
|
||||
this.subscribe('people', limit, () => {
|
||||
this.subscribe('people', this.findUsersOptions.get(), limit, () => {
|
||||
this.loadNextPageLocked = false;
|
||||
const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
|
||||
this.calculateNextPeak();
|
||||
@@ -85,7 +85,7 @@ BlazeComponent.extendComponent({
|
||||
const users = Users.find(this.findUsersOptions.get(), {
|
||||
fields: { _id: true },
|
||||
});
|
||||
this.number.set(users.count());
|
||||
this.number.set(users.count(false));
|
||||
return users;
|
||||
},
|
||||
peopleNumber() {
|
||||
@@ -243,8 +243,8 @@ Template.editUserPopup.events({
|
||||
} else Popup.close();
|
||||
},
|
||||
|
||||
'click #deleteButton'() {
|
||||
'click #deleteButton': Popup.afterConfirm('userDelete', function() {
|
||||
Users.remove(this.userId);
|
||||
Popup.close();
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -33,7 +33,7 @@ table
|
||||
padding: 0;
|
||||
|
||||
button
|
||||
min-width: 60px;
|
||||
min-width: 90px;
|
||||
|
||||
.content-wrapper
|
||||
margin-top: 10px
|
||||
|
||||
@@ -4,20 +4,35 @@ template(name="setting")
|
||||
| {{_ 'error-notAuthorized'}}
|
||||
else
|
||||
.content-title
|
||||
i.fa.fa-cog
|
||||
span {{_ 'settings'}}
|
||||
.content-body
|
||||
.side-menu
|
||||
ul
|
||||
li.active
|
||||
a.js-setting-menu(data-id="registration-setting") {{_ 'registration'}}
|
||||
a.js-setting-menu(data-id="registration-setting")
|
||||
i.fa.fa-sign-in
|
||||
| {{_ 'registration'}}
|
||||
li
|
||||
a.js-setting-menu(data-id="email-setting") {{_ 'email'}}
|
||||
a.js-setting-menu(data-id="email-setting")
|
||||
i.fa.fa-envelope
|
||||
| {{_ 'email'}}
|
||||
li
|
||||
a.js-setting-menu(data-id="account-setting") {{_ 'accounts'}}
|
||||
a.js-setting-menu(data-id="account-setting")
|
||||
i.fa.fa-users
|
||||
| {{_ 'accounts'}}
|
||||
li
|
||||
a.js-setting-menu(data-id="announcement-setting") {{_ 'admin-announcement'}}
|
||||
a.js-setting-menu(data-id="announcement-setting")
|
||||
i.fa.fa-bullhorn
|
||||
| {{_ 'admin-announcement'}}
|
||||
li
|
||||
a.js-setting-menu(data-id="layout-setting") {{_ 'layout'}}
|
||||
a.js-setting-menu(data-id="layout-setting")
|
||||
i.fa.fa-object-group
|
||||
| {{_ 'layout'}}
|
||||
li
|
||||
a.js-setting-menu(data-id="webhook-setting")
|
||||
i.fa.fa-globe
|
||||
| {{_ 'global-webhook'}}
|
||||
.main-body
|
||||
if loading.get
|
||||
+spinner
|
||||
@@ -31,6 +46,12 @@ template(name="setting")
|
||||
+announcementSettings
|
||||
else if layoutSetting.get
|
||||
+layoutSettings
|
||||
else if webhookSetting.get
|
||||
+webhookSettings
|
||||
|
||||
template(name="webhookSettings")
|
||||
span
|
||||
+outgoingWebhooksPopup
|
||||
|
||||
template(name="general")
|
||||
ul#registration-setting.setting-detail
|
||||
@@ -44,7 +65,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 +84,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 +109,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 +122,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 +157,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 +166,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 +183,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'}}
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ BlazeComponent.extendComponent({
|
||||
this.accountSetting = new ReactiveVar(false);
|
||||
this.announcementSetting = new ReactiveVar(false);
|
||||
this.layoutSetting = new ReactiveVar(false);
|
||||
this.webhookSetting = new ReactiveVar(false);
|
||||
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('mailServer');
|
||||
Meteor.subscribe('accountSettings');
|
||||
Meteor.subscribe('announcements');
|
||||
Meteor.subscribe('globalwebhooks');
|
||||
},
|
||||
|
||||
setError(error) {
|
||||
@@ -83,6 +85,7 @@ BlazeComponent.extendComponent({
|
||||
this.accountSetting.set('account-setting' === targetID);
|
||||
this.announcementSetting.set('announcement-setting' === targetID);
|
||||
this.layoutSetting.set('layout-setting' === targetID);
|
||||
this.webhookSetting.set('webhook-setting' === targetID);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -233,12 +236,17 @@ BlazeComponent.extendComponent({
|
||||
$('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 },
|
||||
});
|
||||
AccountSettings.update('accounts-allowUserNameChange', {
|
||||
$set: { booleanValue: allowUserNameChange },
|
||||
});
|
||||
AccountSettings.update('accounts-allowUserDelete', {
|
||||
$set: { booleanValue: allowUserDelete },
|
||||
});
|
||||
},
|
||||
|
||||
allowEmailChange() {
|
||||
@@ -247,6 +255,9 @@ BlazeComponent.extendComponent({
|
||||
allowUserNameChange() {
|
||||
return AccountSettings.findOne('accounts-allowUserNameChange').booleanValue;
|
||||
},
|
||||
allowUserDelete() {
|
||||
return AccountSettings.findOne('accounts-allowUserDelete').booleanValue;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
|
||||
@@ -41,21 +41,24 @@
|
||||
&:hover
|
||||
background #fff
|
||||
box-shadow 0 1px 2px rgba(0,0,0,0.15);
|
||||
|
||||
a
|
||||
@extends .flex
|
||||
padding: 1rem 0 1rem 1rem
|
||||
width: 100% - 5rem
|
||||
|
||||
|
||||
span
|
||||
font-size: 13px
|
||||
|
||||
i
|
||||
margin-right: 20px
|
||||
|
||||
.main-body
|
||||
padding: 0.1em 1em
|
||||
-webkit-user-select: auto // Safari 3.1+
|
||||
-moz-user-select: auto // Firefox 2+
|
||||
-ms-user-select: auto // IE 10+
|
||||
user-select: auto // Standard syntax
|
||||
-webkit-user-select: text // Safari 3.1+
|
||||
-moz-user-select: text // Firefox 2+
|
||||
-ms-user-select: text // IE 10+
|
||||
user-select: text // Standard syntax
|
||||
|
||||
ul
|
||||
li
|
||||
@@ -105,14 +108,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);
|
||||
|
||||
@@ -22,6 +22,11 @@ template(name='homeSidebar')
|
||||
+membersWidget
|
||||
hr
|
||||
+labelsWidget
|
||||
ul#cards.label-text-hidden
|
||||
a.flex.js-toggle-minicard-label-text
|
||||
span {{_ 'hide-minicard-label-text'}}
|
||||
b
|
||||
.materialCheckBox(class="{{#if hiddenMinicardLabelText}}is-checked{{/if}}")
|
||||
hr
|
||||
unless currentUser.isNoComments
|
||||
h3
|
||||
@@ -32,10 +37,12 @@ template(name='homeSidebar')
|
||||
template(name="membersWidget")
|
||||
.board-widget.board-widget-members
|
||||
h3
|
||||
i.fa.fa-user
|
||||
i.fa.fa-users
|
||||
| {{_ 'members'}}
|
||||
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
|
||||
i.board-header-btn-icon.fa.fa-cog
|
||||
unless currentUser.isCommentOnly
|
||||
unless currentUser.isWorker
|
||||
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
|
||||
@@ -124,62 +131,113 @@ template(name="chooseBoardSource")
|
||||
|
||||
template(name="archiveBoardPopup")
|
||||
p {{_ 'close-board-pop'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
|
||||
button.js-confirm.negate.full(type="submit")
|
||||
i.fa.fa-archive
|
||||
| {{_ 'archive'}}
|
||||
|
||||
template(name="outgoingWebhooksPopup")
|
||||
each integrations
|
||||
form.integration-form
|
||||
if title
|
||||
h4 {{title}}
|
||||
else
|
||||
h4 {{_ 'no-name'}}
|
||||
label
|
||||
| URL
|
||||
input.js-outgoing-webhooks-url(type="text" name="url" value=url)
|
||||
input(type="hidden" value=_id name="id")
|
||||
a.flex
|
||||
span {{_ 'disable-webhook'}}
|
||||
b
|
||||
.materialCheckBox(class="{{#unless enabled}}is-checked{{/unless}}")
|
||||
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" value=title)
|
||||
input.js-outgoing-webhooks-url(type="text" name="url" value=url autofocus)
|
||||
input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" value=token name="token")
|
||||
select.js-outgoing-webhooks-type(name="type")
|
||||
each _type in types
|
||||
if($eq _type this.type)
|
||||
option(value=_type selected="selected") {{_ _type}}
|
||||
else
|
||||
option(value=_type) {{_ _type}}
|
||||
input(type="hidden" value=this.type name="_type")
|
||||
input(type="hidden" value=_id name="id")
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
form.integration-form
|
||||
h4
|
||||
| {{_ 'new-outgoing-webhook'}}
|
||||
label
|
||||
| URL
|
||||
input.js-outgoing-webhooks-url(type="text" name="url" autofocus)
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" autofocus)
|
||||
input.js-outgoing-webhooks-url(placeholder="{{_ 'URL' }}" type="text" name="url")
|
||||
input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" name="token")
|
||||
select.js-outgoing-webhooks-type(name="type")
|
||||
each _type in types
|
||||
option(value=_type) {{_ _type}}
|
||||
input.primary.wide(type="submit" value="{{_ 'create'}}")
|
||||
|
||||
template(name="boardMenuPopup")
|
||||
ul.pop-over-list
|
||||
li: a.js-custom-fields {{_ 'custom-fields'}}
|
||||
li: a.js-open-archives {{_ 'archived-items'}}
|
||||
li
|
||||
a.js-open-archives
|
||||
i.fa.fa-archive
|
||||
| {{_ 'archived-items'}}
|
||||
if currentUser.isBoardAdmin
|
||||
li: a.js-change-board-color {{_ 'board-change-color'}}
|
||||
li
|
||||
a.js-change-board-color
|
||||
i.fa.fa-paint-brush
|
||||
| {{_ 'board-change-color'}}
|
||||
|
||||
//-
|
||||
XXX Language should be handled by sandstorm, but for now display a
|
||||
language selection link in the board menu. This link is normally present
|
||||
in the header bar that is not displayed on sandstorm.
|
||||
if isSandstorm
|
||||
li: a.js-change-language {{_ 'language'}}
|
||||
li
|
||||
a.js-change-language
|
||||
i.fa.fa-flag
|
||||
| {{_ 'language'}}
|
||||
unless isSandstorm
|
||||
if currentUser.isBoardAdmin
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
|
||||
unless currentBoard.isTemplatesBoard
|
||||
li: a.js-archive-board {{_ 'archive-board'}}
|
||||
li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-subtask-settings {{_ 'subtask-settings'}}
|
||||
li
|
||||
a(href="{{exportUrl}}", download="{{exportFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
| {{_ 'export-board'}}
|
||||
li
|
||||
a.js-outgoing-webhooks
|
||||
i.fa.fa-globe
|
||||
| {{_ 'outgoing-webhooks'}}
|
||||
li
|
||||
a.js-subtask-settings
|
||||
i.fa.fa-sitemap
|
||||
| {{_ 'subtask-settings'}}
|
||||
unless currentBoard.isTemplatesBoard
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-archive-board
|
||||
i.fa.fa-arrow-right
|
||||
i.fa.fa-archive
|
||||
| {{_ 'archive-board'}}
|
||||
|
||||
if isSandstorm
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
|
||||
li: a.js-import-board {{_ 'import-board-c'}}
|
||||
li: a.js-archive-board {{_ 'archive-board'}}
|
||||
li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
|
||||
li
|
||||
a(href="{{exportUrl}}", download="{{exportFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
i.fa.fa-sign-out
|
||||
| {{_ 'export-board'}}
|
||||
li
|
||||
a.js-import-board
|
||||
i.fa.fa-share-alt
|
||||
i.fa.fa-sign-in
|
||||
| {{_ 'import-board-c'}}
|
||||
li
|
||||
a.js-archive-board
|
||||
i.fa.fa-arrow-right
|
||||
i.fa.fa-archive
|
||||
| {{_ 'archive-board'}}
|
||||
li
|
||||
a.js-outgoing-webhooks
|
||||
i.fa.fa-globe
|
||||
| {{_ 'outgoing-webhooks'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-subtask-settings {{_ 'subtask-settings'}}
|
||||
li
|
||||
a.js-subtask-settings
|
||||
i.fa.fa-sitemap
|
||||
| {{_ 'subtask-settings'}}
|
||||
|
||||
template(name="labelsWidget")
|
||||
.board-widget.board-widget-labels
|
||||
@@ -189,7 +247,7 @@ template(name="labelsWidget")
|
||||
.board-widget-content
|
||||
each currentBoard.labels
|
||||
a.card-label(class="card-label-{{color}}"
|
||||
class="{{#if currentUser.isNotCommentOnly}}js-label{{/if}}")
|
||||
class="{{#if currentUser.isNotCommentOnly}}{{#if currentUser.isNotWorker}}js-label{{/if}}{{/if}}")
|
||||
span.card-label-name
|
||||
+viewer
|
||||
= name
|
||||
@@ -218,12 +276,12 @@ template(name="memberPopup")
|
||||
a.js-change-role
|
||||
| {{_ 'change-permissions'}}
|
||||
span.quiet (#{memberType})
|
||||
li
|
||||
if $eq currentUser._id userId
|
||||
a.js-leave-member {{_ 'leave-board'}}
|
||||
else if currentUser.isBoardAdmin
|
||||
a.js-remove-member {{_ 'remove-from-board'}}
|
||||
|
||||
unless currentUser.isWorker
|
||||
li
|
||||
if $eq currentUser._id userId
|
||||
a.js-leave-member {{_ 'leave-board'}}
|
||||
else if currentUser.isBoardAdmin
|
||||
a.js-remove-member {{_ 'remove-from-board'}}
|
||||
|
||||
template(name="removeMemberPopup")
|
||||
p {{_ 'remove-member-pop' name=user.profile.fullname username=user.username boardTitle=board.title}}
|
||||
@@ -287,6 +345,12 @@ template(name="changePermissionsPopup")
|
||||
if isCommentOnly
|
||||
i.fa.fa-check
|
||||
span.sub-name {{_ 'comment-only-desc'}}
|
||||
li
|
||||
a(class="{{#if isLastAdmin}}disabled{{else}}js-set-worker{{/if}}")
|
||||
| {{_ 'worker'}}
|
||||
if isWorker
|
||||
i.fa.fa-check
|
||||
span.sub-name {{_ 'worker-desc'}}
|
||||
if isLastAdmin
|
||||
hr
|
||||
p.quiet.bottom {{_ 'last-admin-desc'}}
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
Sidebar = null;
|
||||
|
||||
const defaultView = 'home';
|
||||
const MCB = '.materialCheckBox';
|
||||
const CKCLS = 'is-checked';
|
||||
|
||||
const viewTitles = {
|
||||
filter: 'filter-cards',
|
||||
@@ -47,8 +51,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
calculateNextPeak() {
|
||||
const altitude = this.find('.js-board-sidebar-content').scrollHeight;
|
||||
this.callFirstWith(this, 'setNextPeak', altitude);
|
||||
const sidebarElement = this.find('.js-board-sidebar-content');
|
||||
if (sidebarElement) {
|
||||
const altitude = sidebarElement.scrollHeight;
|
||||
this.callFirstWith(this, 'setNextPeak', altitude);
|
||||
}
|
||||
},
|
||||
|
||||
reachNextPeak() {
|
||||
@@ -101,6 +108,16 @@ BlazeComponent.extendComponent({
|
||||
'click .js-hide-sidebar': this.hide,
|
||||
'click .js-toggle-sidebar': this.toggle,
|
||||
'click .js-back-home': this.setView,
|
||||
'click .js-toggle-minicard-label-text'() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
Meteor.call('toggleMinicardLabelText');
|
||||
} else if (cookies.has('hiddenMinicardLabelText')) {
|
||||
cookies.remove('hiddenMinicardLabelText');
|
||||
} else {
|
||||
cookies.set('hiddenMinicardLabelText', 'true');
|
||||
}
|
||||
},
|
||||
'click .js-shortcuts'() {
|
||||
FlowRouter.go('shortcuts');
|
||||
},
|
||||
@@ -111,6 +128,19 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Blaze.registerHelper('Sidebar', () => Sidebar);
|
||||
|
||||
Template.homeSidebar.helpers({
|
||||
hiddenMinicardLabelText() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).hiddenMinicardLabelText;
|
||||
} else if (cookies.has('hiddenMinicardLabelText')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
EscapeActions.register(
|
||||
'sidebarView',
|
||||
() => {
|
||||
@@ -131,10 +161,13 @@ Template.memberPopup.helpers({
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const commentOnly = currentBoard.hasCommentOnly(this.userId);
|
||||
const noComments = currentBoard.hasNoComments(this.userId);
|
||||
const worker = currentBoard.hasWorker(this.userId);
|
||||
if (commentOnly) {
|
||||
return TAPi18n.__('comment-only').toLowerCase();
|
||||
} else if (noComments) {
|
||||
return TAPi18n.__('no-comments').toLowerCase();
|
||||
} else if (worker) {
|
||||
return TAPi18n.__('worker').toLowerCase();
|
||||
} else {
|
||||
return TAPi18n.__(type).toLowerCase();
|
||||
}
|
||||
@@ -237,6 +270,14 @@ Template.membersWidget.helpers({
|
||||
const user = Meteor.user();
|
||||
return user && user.isInvitedTo(Session.get('currentBoard'));
|
||||
},
|
||||
isWorker() {
|
||||
const user = Meteor.user();
|
||||
if (user) {
|
||||
return Meteor.call(Boards.hasWorker(user.memberId));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.membersWidget.events({
|
||||
@@ -268,44 +309,71 @@ Template.membersWidget.events({
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
boardId() {
|
||||
return Session.get('currentBoard') || Integrations.Const.GLOBAL_WEBHOOK_ID;
|
||||
},
|
||||
integrations() {
|
||||
const boardId = Session.get('currentBoard');
|
||||
const boardId = this.boardId();
|
||||
return Integrations.find({ boardId: `${boardId}` }).fetch();
|
||||
},
|
||||
|
||||
integration(id) {
|
||||
const boardId = Session.get('currentBoard');
|
||||
return Integrations.findOne({ _id: id, boardId: `${boardId}` });
|
||||
types() {
|
||||
return Integrations.Const.WEBHOOK_TYPES;
|
||||
},
|
||||
integration(cond) {
|
||||
const boardId = this.boardId();
|
||||
const condition = { boardId, ...cond };
|
||||
for (const k in condition) {
|
||||
if (!condition[k]) delete condition[k];
|
||||
}
|
||||
return Integrations.findOne(condition);
|
||||
},
|
||||
onCreated() {
|
||||
this.disabled = new ReactiveVar(false);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click a.flex'(evt) {
|
||||
this.disabled.set(!this.disabled.get());
|
||||
$(evt.target).toggleClass(CKCLS, this.disabled.get());
|
||||
},
|
||||
submit(evt) {
|
||||
evt.preventDefault();
|
||||
const url = evt.target.url.value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const boardId = this.boardId();
|
||||
let id = null;
|
||||
let integration = null;
|
||||
const title = evt.target.title.value;
|
||||
const token = evt.target.token.value;
|
||||
const type = evt.target.type.value;
|
||||
const enabled = !this.disabled.get();
|
||||
let remove = false;
|
||||
const values = {
|
||||
url,
|
||||
type,
|
||||
token,
|
||||
title,
|
||||
enabled,
|
||||
};
|
||||
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);
|
||||
}
|
||||
integration = this.integration({ _id: id });
|
||||
remove = !url;
|
||||
} else if (url) {
|
||||
integration = this.integration({ url, token });
|
||||
}
|
||||
if (remove) {
|
||||
Integrations.remove(integration._id);
|
||||
} else if (integration && integration._id) {
|
||||
Integrations.update(integration._id, {
|
||||
$set: values,
|
||||
});
|
||||
} else if (url) {
|
||||
Integrations.insert({
|
||||
...values,
|
||||
userId: Meteor.userId(),
|
||||
enabled: true,
|
||||
type: 'outgoing-webhooks',
|
||||
url: `${url}`,
|
||||
boardId: `${boardId}`,
|
||||
boardId,
|
||||
activities: ['all'],
|
||||
});
|
||||
}
|
||||
@@ -462,12 +530,12 @@ BlazeComponent.extendComponent({
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
|
||||
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-has-subtasks .materialCheckBox').toggleClass(
|
||||
'is-checked',
|
||||
$(`.js-field-has-subtasks ${MCB}`).toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
$('.js-field-has-subtasks').toggleClass(
|
||||
'is-checked',
|
||||
CKCLS,
|
||||
this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
$('.js-field-deposit-board').prop(
|
||||
@@ -503,15 +571,12 @@ BlazeComponent.extendComponent({
|
||||
];
|
||||
options.forEach(function(element) {
|
||||
if (element !== value) {
|
||||
$(`#${element} .materialCheckBox`).toggleClass(
|
||||
'is-checked',
|
||||
false,
|
||||
);
|
||||
$(`#${element}`).toggleClass('is-checked', false);
|
||||
$(`#${element} ${MCB}`).toggleClass(CKCLS, false);
|
||||
$(`#${element}`).toggleClass(CKCLS, false);
|
||||
}
|
||||
});
|
||||
$(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
|
||||
$(`#${value}`).toggleClass('is-checked', true);
|
||||
$(`#${value} ${MCB}`).toggleClass(CKCLS, true);
|
||||
$(`#${value}`).toggleClass(CKCLS, true);
|
||||
this.currentBoard.setPresentParentTask(value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
@@ -590,7 +655,7 @@ BlazeComponent.extendComponent({
|
||||
}).register('addMemberPopup');
|
||||
|
||||
Template.changePermissionsPopup.events({
|
||||
'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'(
|
||||
'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only, click .js-set-worker'(
|
||||
event,
|
||||
) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
@@ -600,11 +665,13 @@ Template.changePermissionsPopup.events({
|
||||
'js-set-comment-only',
|
||||
);
|
||||
const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
|
||||
const isWorker = $(event.currentTarget).hasClass('js-set-worker');
|
||||
currentBoard.setMemberPermission(
|
||||
memberId,
|
||||
isAdmin,
|
||||
isNoComments,
|
||||
isCommentOnly,
|
||||
isWorker,
|
||||
);
|
||||
Popup.back(1);
|
||||
},
|
||||
@@ -621,7 +688,8 @@ Template.changePermissionsPopup.helpers({
|
||||
return (
|
||||
!currentBoard.hasAdmin(this.userId) &&
|
||||
!currentBoard.hasNoComments(this.userId) &&
|
||||
!currentBoard.hasCommentOnly(this.userId)
|
||||
!currentBoard.hasCommentOnly(this.userId) &&
|
||||
!currentBoard.hasWorker(this.userId)
|
||||
);
|
||||
},
|
||||
|
||||
@@ -641,6 +709,13 @@ Template.changePermissionsPopup.helpers({
|
||||
);
|
||||
},
|
||||
|
||||
isWorker() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return (
|
||||
!currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
|
||||
);
|
||||
},
|
||||
|
||||
isLastAdmin() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return (
|
||||
|
||||
@@ -2,54 +2,60 @@ template(name="archivesSidebar")
|
||||
if isArchiveReady.get
|
||||
+basicTabs(tabs=tabs)
|
||||
+tabContent(slug="cards")
|
||||
p.quiet
|
||||
a.js-restore-all-cards {{_ 'restore-all'}}
|
||||
| -
|
||||
a.js-delete-all-cards {{_ 'delete-all'}}
|
||||
unless isWorker
|
||||
p.quiet
|
||||
a.js-restore-all-cards {{_ 'restore-all'}}
|
||||
| -
|
||||
a.js-delete-all-cards {{_ 'delete-all'}}
|
||||
each archivedCards
|
||||
.minicard-wrapper.js-minicard
|
||||
+minicard(this)
|
||||
if currentUser.isBoardMember
|
||||
p.quiet
|
||||
a.js-restore-card {{_ 'restore'}}
|
||||
| -
|
||||
a.js-delete-card {{_ 'delete'}}
|
||||
unless isWorker
|
||||
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="lists")
|
||||
p.quiet
|
||||
a.js-restore-all-lists {{_ 'restore-all'}}
|
||||
| -
|
||||
a.js-delete-all-lists {{_ 'delete-all'}}
|
||||
unless isWorker
|
||||
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'}}
|
||||
unless isWorker
|
||||
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'}}
|
||||
unless isWorker
|
||||
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'}}
|
||||
unless isWorker
|
||||
p.quiet
|
||||
a.js-restore-swimlane {{_ 'restore'}}
|
||||
| -
|
||||
a.js-delete-swimlane {{_ 'delete'}}
|
||||
else
|
||||
li.no-items-message {{_ 'no-archived-swimlanes'}}
|
||||
else
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
archivedRequested = false;
|
||||
const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -12,6 +13,7 @@ BlazeComponent.extendComponent({
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (!currentBoardId) return;
|
||||
const handle = subManager.subscribe('board', currentBoardId, true);
|
||||
archivedRequested = true;
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isArchiveReady.set(handle.ready());
|
||||
@@ -137,3 +139,12 @@ BlazeComponent.extendComponent({
|
||||
];
|
||||
},
|
||||
}).register('archivesSidebar');
|
||||
|
||||
Template.archivesSidebar.helpers({
|
||||
isWorker() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return (
|
||||
!currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -4,6 +4,10 @@
|
||||
and #each x in y constructors to fix this.
|
||||
|
||||
template(name="filterSidebar")
|
||||
ul.sidebar-list
|
||||
span {{_ 'list-filter-label'}}
|
||||
form.js-list-filter
|
||||
input(type="text")
|
||||
ul.sidebar-list
|
||||
li(class="{{#if Filter.labelIds.isSelected undefined}}active{{/if}}")
|
||||
a.name.js-toggle-label-filter
|
||||
@@ -56,6 +60,22 @@ template(name="filterSidebar")
|
||||
if Filter.customFields.isSelected _id
|
||||
i.fa.fa-check
|
||||
hr
|
||||
ul.sidebar-list
|
||||
li(class="{{#if Filter.archive.isSelected _id}}active{{/if}}")
|
||||
a.name.js-toggle-archive-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-show-archive'}}
|
||||
if Filter.archive.isSelected _id
|
||||
i.fa.fa-check
|
||||
hr
|
||||
ul.sidebar-list
|
||||
li(class="{{#if Filter.hideEmpty.isSelected _id}}active{{/if}}")
|
||||
a.name.js-toggle-hideEmpty-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-hide-empty'}}
|
||||
if Filter.hideEmpty.isSelected _id
|
||||
i.fa.fa-check
|
||||
hr
|
||||
span {{_ 'advanced-filter-label'}}
|
||||
input.js-field-advanced-filter(type="text")
|
||||
span {{_ 'advanced-filter-description'}}
|
||||
@@ -97,13 +117,14 @@ template(name="multiselectionSidebar")
|
||||
i.fa.fa-check
|
||||
else if someSelectedElementHave 'member' _id
|
||||
i.fa.fa-ellipsis-h
|
||||
hr
|
||||
a.sidebar-btn.js-move-selection
|
||||
i.fa.fa-share
|
||||
span {{_ 'move-selection'}}
|
||||
a.sidebar-btn.js-archive-selection
|
||||
i.fa.fa-archive
|
||||
span {{_ 'archive-selection'}}
|
||||
unless currentUser.isWorker
|
||||
hr
|
||||
a.sidebar-btn.js-move-selection
|
||||
i.fa.fa-share
|
||||
span {{_ 'move-selection'}}
|
||||
a.sidebar-btn.js-archive-selection
|
||||
i.fa.fa-archive
|
||||
span {{_ 'archive-selection'}}
|
||||
|
||||
template(name="disambiguateMultiLabelPopup")
|
||||
p {{_ 'what-to-do'}}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'submit .js-list-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.lists.set(this.find('.js-list-filter input').value.trim());
|
||||
},
|
||||
'click .js-toggle-label-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.labelIds.toggle(this.currentData()._id);
|
||||
@@ -12,6 +18,23 @@ BlazeComponent.extendComponent({
|
||||
Filter.members.toggle(this.currentData()._id);
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-archive-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.archive.toggle(this.currentData()._id);
|
||||
Filter.resetExceptions();
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (!currentBoardId) return;
|
||||
subManager.subscribe(
|
||||
'board',
|
||||
currentBoardId,
|
||||
Filter.archive.isSelected(),
|
||||
);
|
||||
},
|
||||
'click .js-toggle-hideEmpty-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.hideEmpty.toggle(this.currentData()._id);
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-custom-fields-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.customFields.toggle(this.currentData()._id);
|
||||
|
||||
@@ -2,6 +2,10 @@ template(name="searchSidebar")
|
||||
form.js-search-term-form
|
||||
input(type="text" name="searchTerm" placeholder="{{_ 'search-example'}}" autofocus dir="auto")
|
||||
.list-body.js-perfect-scrollbar
|
||||
.minilists.clearfix.js-minilists
|
||||
each (lists)
|
||||
a.minilist-wrapper.js-minilist(href=absoluteUrl)
|
||||
+minilist(this)
|
||||
.minicards.clearfix.js-minicards
|
||||
each (results)
|
||||
a.minicard-wrapper.js-minicard(href=absoluteUrl)
|
||||
|
||||
@@ -8,6 +8,11 @@ BlazeComponent.extendComponent({
|
||||
return currentBoard.searchCards(this.term.get());
|
||||
},
|
||||
|
||||
lists() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return currentBoard.searchLists(this.term.get());
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -16,6 +16,11 @@ template(name="swimlaneFixedHeader")
|
||||
unless currentUser.isCommentOnly
|
||||
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon
|
||||
a.fa.fa-navicon.js-open-swimlane-menu
|
||||
unless isMiniScreen
|
||||
if showDesktopDragHandles
|
||||
a.swimlane-header-handle.handle.fa.fa-arrows.js-swimlane-header-handle
|
||||
if isMiniScreen
|
||||
a.swimlane-header-miniscreen-handle.handle.fa.fa-arrows.js-swimlane-header-handle
|
||||
|
||||
template(name="editSwimlaneTitleForm")
|
||||
.list-composer
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
const { calculateIndexData } = Utils;
|
||||
|
||||
let swimlaneColors;
|
||||
@@ -28,6 +30,19 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('swimlaneHeader');
|
||||
|
||||
Template.swimlaneHeader.helpers({
|
||||
showDesktopDragHandles() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.swimlaneActionPopup.events({
|
||||
'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
|
||||
'click .js-close-swimlane'(event) {
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
template(name="swimlane")
|
||||
.swimlane
|
||||
+swimlaneHeader
|
||||
.swimlane.js-lists.js-swimlane
|
||||
if isMiniScreen
|
||||
if currentListIsInThisSwimlane _id
|
||||
+list(currentList)
|
||||
unless currentList
|
||||
unless collapseSwimlane
|
||||
.swimlane.js-lists.js-swimlane
|
||||
if isMiniScreen
|
||||
if currentListIsInThisSwimlane _id
|
||||
+list(currentList)
|
||||
unless currentList
|
||||
each lists
|
||||
+miniList(this)
|
||||
if currentUser.isBoardMember
|
||||
unless currentUser.isCommentOnly
|
||||
+addListForm
|
||||
else
|
||||
each lists
|
||||
+miniList(this)
|
||||
+list(this)
|
||||
if currentCardIsInThisList _id ../_id
|
||||
+cardDetails(currentCard)
|
||||
if currentUser.isBoardMember
|
||||
+addListForm
|
||||
else
|
||||
each lists
|
||||
+list(this)
|
||||
if currentCardIsInThisList _id ../_id
|
||||
+cardDetails(currentCard)
|
||||
if currentUser.isBoardMember
|
||||
+addListForm
|
||||
unless currentUser.isCommentOnly
|
||||
+addListForm
|
||||
|
||||
template(name="listsGroup")
|
||||
.swimlane.list-group.js-lists
|
||||
@@ -27,29 +30,33 @@ template(name="listsGroup")
|
||||
each lists
|
||||
+miniList(this)
|
||||
if currentUser.isBoardMember
|
||||
+addListForm
|
||||
unless currentUser.isCommentOnly
|
||||
+addListForm
|
||||
else
|
||||
each lists
|
||||
+list(this)
|
||||
if visible this
|
||||
+list(this)
|
||||
if currentCardIsInThisList _id null
|
||||
+cardDetails(currentCard)
|
||||
if currentUser.isBoardMember
|
||||
+addListForm
|
||||
unless currentUser.isCommentOnly
|
||||
+addListForm
|
||||
|
||||
template(name="addListForm")
|
||||
.list.list-composer.js-list-composer
|
||||
.list-header-add
|
||||
+inlinedForm(autoclose=false)
|
||||
input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}"
|
||||
autocomplete="off" autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm(type="submit") {{_ 'save'}}
|
||||
unless currentBoard.isTemplatesBoard
|
||||
unless currentBoard.isTemplateBoard
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
a.js-list-template {{_ 'template'}}
|
||||
else
|
||||
a.open-list-composer.js-open-inlined-form
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-list'}}
|
||||
unless currentUser.isWorker
|
||||
.list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}")
|
||||
.list-header-add
|
||||
+inlinedForm(autoclose=false)
|
||||
input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}"
|
||||
autocomplete="off" autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm(type="submit") {{_ 'save'}}
|
||||
unless currentBoard.isTemplatesBoard
|
||||
unless currentBoard.isTemplateBoard
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
a.js-list-template {{_ 'template'}}
|
||||
else
|
||||
a.open-list-composer.js-open-inlined-form
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-list'}}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
|
||||
function currentListIsInThisSwimlane(swimlaneId) {
|
||||
@@ -14,7 +16,7 @@ function currentCardIsInThisList(listId, swimlaneId) {
|
||||
if (
|
||||
currentUser &&
|
||||
currentUser.profile &&
|
||||
currentUser.profile.boardView === 'board-view-swimlanes'
|
||||
Utils.boardView() === 'board-view-swimlanes'
|
||||
)
|
||||
return (
|
||||
currentCard &&
|
||||
@@ -56,7 +58,6 @@ function initSortable(boardComponent, $listsDom) {
|
||||
$listsDom.sortable({
|
||||
tolerance: 'pointer',
|
||||
helper: 'clone',
|
||||
handle: '.js-list-header',
|
||||
items: '.js-list:not(.js-list-composer)',
|
||||
placeholder: 'list placeholder',
|
||||
distance: 7,
|
||||
@@ -93,21 +94,53 @@ function initSortable(boardComponent, $listsDom) {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
}
|
||||
|
||||
// Disable drag-dropping while in multi-selection mode, or if the current user
|
||||
// is not a board member
|
||||
boardComponent.autorun(() => {
|
||||
let showDesktopDragHandles = false;
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
showDesktopDragHandles = (currentUser.profile || {})
|
||||
.showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
showDesktopDragHandles = true;
|
||||
} else {
|
||||
showDesktopDragHandles = false;
|
||||
}
|
||||
|
||||
if (!Utils.isMiniScreen() && showDesktopDragHandles) {
|
||||
$listsDom.sortable({
|
||||
handle: '.js-list-handle',
|
||||
});
|
||||
} else {
|
||||
$listsDom.sortable({
|
||||
handle: '.js-list-header',
|
||||
});
|
||||
}
|
||||
|
||||
const $listDom = $listsDom;
|
||||
if ($listDom.data('sortable')) {
|
||||
$listsDom.sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
MultiSelection.isActive() || !userIsMember(),
|
||||
// Disable drag-dropping when user is not member
|
||||
!userIsMember(),
|
||||
// Not disable drag-dropping while in multi-selection mode
|
||||
// MultiSelection.isActive() || !userIsMember(),
|
||||
);
|
||||
}
|
||||
|
||||
$listsDom.sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
// Disable drag-dropping when user is not member
|
||||
Meteor.user().isWorker(),
|
||||
// Not disable drag-dropping while in multi-selection mode
|
||||
// MultiSelection.isActive() || !userIsMember(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -151,13 +184,25 @@ BlazeComponent.extendComponent({
|
||||
// define a list of elements in which we disable the dragging because
|
||||
// the user will legitimately expect to be able to select some text with
|
||||
// his mouse.
|
||||
const noDragInside = [
|
||||
'a',
|
||||
'input',
|
||||
'textarea',
|
||||
'p',
|
||||
'.js-list-header',
|
||||
];
|
||||
|
||||
let showDesktopDragHandles = false;
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
showDesktopDragHandles = (currentUser.profile || {})
|
||||
.showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
showDesktopDragHandles = true;
|
||||
} else {
|
||||
showDesktopDragHandles = false;
|
||||
}
|
||||
|
||||
const noDragInside = ['a', 'input', 'textarea', 'p'].concat(
|
||||
Utils.isMiniScreen() ||
|
||||
(!Utils.isMiniScreen() && showDesktopDragHandles)
|
||||
? ['.js-list-handle', '.js-swimlane-header-handle']
|
||||
: ['.js-list-header'],
|
||||
);
|
||||
|
||||
if (
|
||||
$(evt.target).closest(noDragInside.join(',')).length === 0 &&
|
||||
this.$('.swimlane').prop('clientHeight') > evt.offsetY
|
||||
@@ -233,11 +278,22 @@ BlazeComponent.extendComponent({
|
||||
}).register('addListForm');
|
||||
|
||||
Template.swimlane.helpers({
|
||||
showDesktopDragHandles() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
canSeeAddList() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -246,6 +302,29 @@ BlazeComponent.extendComponent({
|
||||
currentCardIsInThisList(listId, swimlaneId) {
|
||||
return currentCardIsInThisList(listId, swimlaneId);
|
||||
},
|
||||
visible(list) {
|
||||
if (list.archived) {
|
||||
// Show archived list only when filter archive is on or archive is selected
|
||||
if (!(Filter.archive.isSelected() || archivedRequested)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (Filter.lists._isActive()) {
|
||||
if (!list.title.match(Filter.lists.getRegexSelector())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (Filter.hideEmpty.isSelected()) {
|
||||
const swimlaneId = this.parentComponent()
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
const cards = list.cards(swimlaneId);
|
||||
if (cards.count() === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onRendered() {
|
||||
const boardComponent = this.parentComponent();
|
||||
const $listsDom = this.$('.js-lists');
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
@import 'nib'
|
||||
|
||||
/*
|
||||
// Minimize swimlanes start https://www.w3schools.com/howto/howto_js_accordion.asp
|
||||
|
||||
.accordion
|
||||
cursor: pointer
|
||||
width: 30px
|
||||
height: 20px
|
||||
border: none
|
||||
outline: none
|
||||
font-size: 18px
|
||||
transition: 0.4s
|
||||
padding-top: 0px
|
||||
margin-top: 0px
|
||||
|
||||
.accordion:after
|
||||
// Unicode triagle right:
|
||||
content: '\25B6'
|
||||
color: #777
|
||||
font-weight: bold
|
||||
float: left
|
||||
|
||||
.active:after
|
||||
// Unicode triangle down:
|
||||
content: '\25BC'
|
||||
|
||||
.panel
|
||||
width: 100%
|
||||
max-height: 0
|
||||
overflow: hidden
|
||||
transition: max-height 0.2s ease-out
|
||||
margin: 0px
|
||||
padding: 0px
|
||||
|
||||
// Minimize swimlanes end https://www.w3schools.com/howto/howto_js_accordion.asp
|
||||
*/
|
||||
|
||||
.swimlane
|
||||
// Even if this background color is the same as the body we can't leave it
|
||||
// transparent, because that won't work during a swimlane drag.
|
||||
@@ -25,22 +61,22 @@
|
||||
cursor: grabbing
|
||||
|
||||
.swimlane-header-wrap
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1 0 100%;
|
||||
background-color: #ccc;
|
||||
display: flex
|
||||
flex-direction: row
|
||||
flex: 1 0 100%
|
||||
background-color: #ccc
|
||||
|
||||
.swimlane-header
|
||||
font-size: 14px;
|
||||
font-size: 14px
|
||||
padding: 5px 5px
|
||||
font-weight: bold;
|
||||
min-height: 9px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
word-wrap: break-word;
|
||||
text-align: center;
|
||||
font-weight: bold
|
||||
min-height: 9px
|
||||
width: 100%
|
||||
overflow: hidden
|
||||
-o-text-overflow: ellipsis
|
||||
text-overflow: ellipsis
|
||||
word-wrap: break-word
|
||||
text-align: center
|
||||
|
||||
.swimlane-header-menu
|
||||
position: absolute
|
||||
@@ -50,6 +86,22 @@
|
||||
margin-left: 5px
|
||||
margin-right: 10px
|
||||
|
||||
.swimlane-header-handle
|
||||
position: absolute
|
||||
padding: 7px
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
left: 230px
|
||||
font-size: 18px
|
||||
|
||||
.swimlane-header-miniscreen-handle
|
||||
position: absolute
|
||||
padding: 7px
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
left: 87vw
|
||||
font-size: 24px
|
||||
|
||||
.list-group
|
||||
height: 100%
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ template(name="cardMemberPopup")
|
||||
p.quiet @{{ user.username }}
|
||||
ul.pop-over-list
|
||||
if currentUser.isNotCommentOnly
|
||||
if currentUser.isNotWorker
|
||||
li: a.js-remove-member {{_ 'remove-member-from-card'}}
|
||||
|
||||
if $eq currentUser._id user._id
|
||||
|
||||
@@ -13,21 +13,46 @@ template(name="headerUserBar")
|
||||
template(name="memberMenuPopup")
|
||||
ul.pop-over-list
|
||||
with currentUser
|
||||
li: a.js-edit-profile {{_ 'edit-profile'}}
|
||||
li: a.js-change-settings {{_ 'change-settings'}}
|
||||
li: a.js-change-avatar {{_ 'edit-avatar'}}
|
||||
li
|
||||
a.js-edit-profile
|
||||
i.fa.fa-user
|
||||
| {{_ 'edit-profile'}}
|
||||
li
|
||||
a.js-change-settings
|
||||
i.fa.fa-cog
|
||||
| {{_ 'change-settings'}}
|
||||
li
|
||||
a.js-change-avatar
|
||||
i.fa.fa-picture-o
|
||||
| {{_ 'edit-avatar'}}
|
||||
unless isSandstorm
|
||||
li: a.js-change-password {{_ 'changePasswordPopup-title'}}
|
||||
li: a.js-change-language {{_ 'changeLanguagePopup-title'}}
|
||||
li
|
||||
a.js-change-password
|
||||
i.fa.fa-key
|
||||
| {{_ 'changePasswordPopup-title'}}
|
||||
li
|
||||
a.js-change-language
|
||||
i.fa.fa-flag
|
||||
| {{_ 'changeLanguagePopup-title'}}
|
||||
if currentUser.isAdmin
|
||||
li: a.js-go-setting(href="{{pathFor 'setting'}}") {{_ 'admin-panel'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") {{_ 'templates'}}
|
||||
li
|
||||
a.js-go-setting(href="{{pathFor 'setting'}}")
|
||||
i.fa.fa-lock
|
||||
| {{_ 'admin-panel'}}
|
||||
unless currentUser.isWorker
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}")
|
||||
i.fa.fa-clone
|
||||
| {{_ 'templates'}}
|
||||
unless isSandstorm
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-logout {{_ 'log-out'}}
|
||||
li
|
||||
a.js-logout
|
||||
i.fa.fa-sign-out
|
||||
| {{_ 'log-out'}}
|
||||
|
||||
template(name="editProfilePopup")
|
||||
form
|
||||
@@ -55,8 +80,9 @@ template(name="editProfilePopup")
|
||||
input.js-profile-email(type="email" value="{{emails.[0].address}}" readonly)
|
||||
div.buttonsContainer
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
div
|
||||
input#deleteButton.primary.wide(type="button" value="{{_ 'delete'}}")
|
||||
if allowUserDelete
|
||||
div
|
||||
input#deleteButton.primary.wide(type="button" value="{{_ 'delete'}}")
|
||||
|
||||
template(name="changePasswordPopup")
|
||||
+atForm(state='changePwd')
|
||||
@@ -74,11 +100,25 @@ template(name="changeSettingsPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-toggle-system-messages
|
||||
i.fa.fa-comments-o
|
||||
| {{_ 'hide-system-messages'}}
|
||||
if hiddenSystemMessages
|
||||
i.fa.fa-check
|
||||
li
|
||||
label.bold
|
||||
| {{_ 'show-cards-minimum-count'}}
|
||||
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
|
||||
input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
|
||||
a.js-toggle-desktop-drag-handles
|
||||
i.fa.fa-arrows
|
||||
| {{_ 'show-desktop-drag-handles'}}
|
||||
if showDesktopDragHandles
|
||||
i.fa.fa-check
|
||||
unless currentUser.isWorker
|
||||
li
|
||||
label.bold
|
||||
i.fa.fa-sort-numeric-asc
|
||||
| {{_ 'show-cards-minimum-count'}}
|
||||
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
|
||||
input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
|
||||
|
||||
template(name="userDeletePopup")
|
||||
unless currentUser.isWorker
|
||||
p {{_ 'delete-user-confirm-popup'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
@@ -1,3 +1,6 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
|
||||
Template.headerUserBar.events({
|
||||
'click .js-open-header-member-menu': Popup.open('memberMenu'),
|
||||
'click .js-change-avatar': Popup.open('changeAvatar'),
|
||||
@@ -5,10 +8,22 @@ Template.headerUserBar.events({
|
||||
|
||||
Template.memberMenuPopup.helpers({
|
||||
templatesBoardId() {
|
||||
return Meteor.user().getTemplatesBoardId();
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return Meteor.user().getTemplatesBoardId();
|
||||
} else {
|
||||
// No need to getTemplatesBoardId on public board
|
||||
return false;
|
||||
}
|
||||
},
|
||||
templatesBoardSlug() {
|
||||
return Meteor.user().getTemplatesBoardSlug();
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return Meteor.user().getTemplatesBoardSlug();
|
||||
} else {
|
||||
// No need to getTemplatesBoardSlug() on public board
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -30,10 +45,31 @@ Template.memberMenuPopup.events({
|
||||
|
||||
Template.editProfilePopup.helpers({
|
||||
allowEmailChange() {
|
||||
return AccountSettings.findOne('accounts-allowEmailChange').booleanValue;
|
||||
Meteor.call('AccountSettings.allowEmailChange', (_, result) => {
|
||||
if (result) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
allowUserNameChange() {
|
||||
return AccountSettings.findOne('accounts-allowUserNameChange').booleanValue;
|
||||
Meteor.call('AccountSettings.allowUserNameChange', (_, result) => {
|
||||
if (result) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
allowUserDelete() {
|
||||
Meteor.call('AccountSettings.allowUserDelete', (_, result) => {
|
||||
if (result) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -104,11 +140,11 @@ Template.editProfilePopup.events({
|
||||
});
|
||||
} else Popup.back();
|
||||
},
|
||||
'click #deleteButton'() {
|
||||
Users.remove(Meteor.userId());
|
||||
'click #deleteButton': Popup.afterConfirm('userDelete', function() {
|
||||
Popup.close();
|
||||
Users.remove(Meteor.userId());
|
||||
AccountsTemplates.logout();
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// XXX For some reason the useraccounts autofocus isnt working in this case.
|
||||
@@ -158,17 +194,56 @@ Template.changeLanguagePopup.events({
|
||||
});
|
||||
|
||||
Template.changeSettingsPopup.helpers({
|
||||
showDesktopDragHandles() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).showDesktopDragHandles;
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
hiddenSystemMessages() {
|
||||
return Meteor.user().hasHiddenSystemMessages();
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).hasHiddenSystemMessages;
|
||||
} else if (cookies.has('hasHiddenSystemMessages')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
showCardsCountAt() {
|
||||
return Meteor.user().getLimitToShowCardsCount();
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return Meteor.user().getLimitToShowCardsCount();
|
||||
} else {
|
||||
return cookies.get('limitToShowCardsCount');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.changeSettingsPopup.events({
|
||||
'click .js-toggle-desktop-drag-handles'() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
Meteor.call('toggleDesktopDragHandles');
|
||||
} else if (cookies.has('showDesktopDragHandles')) {
|
||||
cookies.remove('showDesktopDragHandles');
|
||||
} else {
|
||||
cookies.set('showDesktopDragHandles', 'true');
|
||||
}
|
||||
},
|
||||
'click .js-toggle-system-messages'() {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
} else if (cookies.has('hasHiddenSystemMessages')) {
|
||||
cookies.remove('hasHiddenSystemMessages');
|
||||
} else {
|
||||
cookies.set('hasHiddenSystemMessages', 'true');
|
||||
}
|
||||
},
|
||||
'click .js-apply-show-cards-at'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
@@ -177,7 +252,12 @@ Template.changeSettingsPopup.events({
|
||||
10,
|
||||
);
|
||||
if (!isNaN(minLimit)) {
|
||||
Meteor.call('changeLimitToShowCardsCount', minLimit);
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
Meteor.call('changeLimitToShowCardsCount', minLimit);
|
||||
} else {
|
||||
cookies.set('limitToShowCardsCount', minLimit);
|
||||
}
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,10 +3,11 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
return 'datepicker';
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
onCreated(defaultTime = '1970-01-01 08:00:00') {
|
||||
this.error = new ReactiveVar('');
|
||||
this.card = this.data();
|
||||
this.date = new ReactiveVar(moment.invalid());
|
||||
this.defaultTime = defaultTime;
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
@@ -21,7 +22,15 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
function(evt) {
|
||||
this.find('#date').value = moment(evt.date).format('L');
|
||||
this.error.set('');
|
||||
this.find('#time').focus();
|
||||
const timeInput = this.find('#time');
|
||||
timeInput.focus();
|
||||
if (!timeInput.value) {
|
||||
const currentHour = evt.date.getHours();
|
||||
const defaultMoment = moment(
|
||||
currentHour > 0 ? evt.date : this.defaultTime,
|
||||
); // default to 8:00 am local time
|
||||
timeInput.value = defaultMoment.format('LT');
|
||||
}
|
||||
}.bind(this),
|
||||
);
|
||||
|
||||
|
||||
+16
-2
@@ -439,6 +439,14 @@ class AdvancedFilter {
|
||||
const commands = this._filterToCommands();
|
||||
return this._arrayToSelector(commands);
|
||||
}
|
||||
getRegexSelector() {
|
||||
// generate a regex for filter list
|
||||
this._dep.depend();
|
||||
return new RegExp(
|
||||
`^.*${this._filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}.*$`,
|
||||
'i',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// The global Filter object.
|
||||
@@ -451,10 +459,13 @@ Filter = {
|
||||
// before changing the schema.
|
||||
labelIds: new SetFilter(),
|
||||
members: new SetFilter(),
|
||||
archive: new SetFilter(),
|
||||
hideEmpty: new SetFilter(),
|
||||
customFields: new SetFilter('_id'),
|
||||
advanced: new AdvancedFilter(),
|
||||
lists: new AdvancedFilter(), // we need the ability to filter list by name as well
|
||||
|
||||
_fields: ['labelIds', 'members', 'customFields'],
|
||||
_fields: ['labelIds', 'members', 'archive', 'hideEmpty', 'customFields'],
|
||||
|
||||
// We don't filter cards that have been added after the last filter change. To
|
||||
// implement this we keep the id of these cards in this `_exceptions` fields
|
||||
@@ -466,7 +477,9 @@ Filter = {
|
||||
return (
|
||||
_.any(this._fields, fieldName => {
|
||||
return this[fieldName]._isActive();
|
||||
}) || this.advanced._isActive()
|
||||
}) ||
|
||||
this.advanced._isActive() ||
|
||||
this.lists._isActive()
|
||||
);
|
||||
},
|
||||
|
||||
@@ -531,6 +544,7 @@ Filter = {
|
||||
const filter = this[fieldName];
|
||||
filter.reset();
|
||||
});
|
||||
this.lists.reset();
|
||||
this.advanced.reset();
|
||||
this.resetExceptions();
|
||||
},
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ window.Popup = new (class {
|
||||
title: self._getTitle(popupName),
|
||||
depth: self._stack.length,
|
||||
offset: self._getOffset(openerElement),
|
||||
dataContext: (this.currentData && this.currentData()) || this,
|
||||
dataContext: (this && this.currentData && this.currentData()) || this,
|
||||
});
|
||||
|
||||
// If there are no popup currently opened we use the Blaze API to render
|
||||
|
||||
@@ -45,6 +45,7 @@ $.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) {
|
||||
});
|
||||
},
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
EscapeActions.register('textcomplete', () => {}, () => dropdownMenuIsOpened, {
|
||||
|
||||
+113
-1
@@ -1,4 +1,40 @@
|
||||
import { Cookies } from 'meteor/ostrio:cookies';
|
||||
const cookies = new Cookies();
|
||||
|
||||
Utils = {
|
||||
setBoardView(view) {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
Meteor.user().setBoardView(view);
|
||||
} else if (view === 'board-view-lists') {
|
||||
cookies.set('boardView', 'board-view-lists'); //true
|
||||
} else if (view === 'board-view-swimlanes') {
|
||||
cookies.set('boardView', 'board-view-swimlanes'); //true
|
||||
} else if (view === 'board-view-cal') {
|
||||
cookies.set('boardView', 'board-view-cal'); //true
|
||||
}
|
||||
},
|
||||
|
||||
unsetBoardView() {
|
||||
cookies.remove('boardView');
|
||||
cookies.remove('collapseSwimlane');
|
||||
},
|
||||
|
||||
boardView() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).boardView;
|
||||
} else if (cookies.get('boardView') === 'board-view-lists') {
|
||||
return 'board-view-lists';
|
||||
} else if (cookies.get('boardView') === 'board-view-swimlanes') {
|
||||
return 'board-view-swimlanes';
|
||||
} else if (cookies.get('boardView') === 'board-view-cal') {
|
||||
return 'board-view-cal';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// XXX We should remove these two methods
|
||||
goBoardId(_id) {
|
||||
const board = Boards.findOne(_id);
|
||||
@@ -23,7 +59,83 @@ Utils = {
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
MAX_IMAGE_PIXEL: Meteor.settings.public.MAX_IMAGE_PIXEL,
|
||||
COMPRESS_RATIO: Meteor.settings.public.IMAGE_COMPRESS_RATIO,
|
||||
processUploadedAttachment(card, fileObj, callback) {
|
||||
const next = attachment => {
|
||||
if (typeof callback === 'function') {
|
||||
callback(attachment);
|
||||
}
|
||||
};
|
||||
if (!card) {
|
||||
return next();
|
||||
}
|
||||
const file = new FS.File(fileObj);
|
||||
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();
|
||||
if (file.original) {
|
||||
file.original.name = fileObj.name;
|
||||
}
|
||||
return next(Attachments.insert(file));
|
||||
},
|
||||
shrinkImage(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;
|
||||
},
|
||||
capitalize(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
},
|
||||
|
||||
+68
-16
@@ -9,6 +9,8 @@ version: '2'
|
||||
# https://github.com/wekan/wekan/wiki/Forgot-Password
|
||||
#---------------------------------------------------------------------------------------------------------
|
||||
# ==== Upgrading Wekan to new version =====
|
||||
# NOTE: MongoDB has changed from 3.x to 4.x, in that case you need backup/restore with --noIndexRestore
|
||||
# see https://github.com/wekan/wekan/wiki/Backup
|
||||
# 1) Stop Wekan:
|
||||
# docker-compose stop
|
||||
# 2) Download new version:
|
||||
@@ -90,9 +92,9 @@ services:
|
||||
wekandb:
|
||||
#-------------------------------------------------------------------------------------
|
||||
# ==== MONGODB AND METEOR VERSION ====
|
||||
# a) For Wekan Meteor 1.8.x version at meteor-1.8 branch, use mongo 4.x
|
||||
image: mongo:4.0.10
|
||||
# b) For Wekan Meteor 1.6.x version at master/devel/edge branches.
|
||||
# a) For Wekan Meteor 1.8.x version at master branch, use mongo 4.x
|
||||
image: mongo:4.0.12
|
||||
# b) For Wekan Meteor 1.6.x version at devel branch.
|
||||
# Only for Snap and Sandstorm while they are not upgraded yet to Meteor 1.8.x
|
||||
#image: mongo:3.2.21
|
||||
#-------------------------------------------------------------------------------------
|
||||
@@ -110,17 +112,15 @@ services:
|
||||
wekan:
|
||||
#-------------------------------------------------------------------------------------
|
||||
# ==== MONGODB AND METEOR VERSION ====
|
||||
# a) For Wekan Meteor 1.8.x version at meteor-1.8 branch,
|
||||
# NOTE: Quay is currently not updated, use Docker Hub image below c)
|
||||
# a) For Wekan Meteor 1.8.x version at master branch,
|
||||
# using https://quay.io/wekan/wekan automatic builds
|
||||
#image: quay.io/wekan/wekan:meteor-1.8
|
||||
# b) For Wekan Meteor 1.6.x version at master/devel/edge branches.
|
||||
# Only for Snap and Sandstorm while they are not upgraded yet to Meteor 1.8.x
|
||||
image: quay.io/wekan/wekan:meteor-1.8
|
||||
# c) Using specific Meteor 1.6.x version tag:
|
||||
image: quay.io/wekan/wekan
|
||||
# b) Using specific Meteor 1.6.x version tag:
|
||||
# image: quay.io/wekan/wekan:v1.95
|
||||
# c) Using Docker Hub automatic builds https://hub.docker.com/r/wekanteam/wekan
|
||||
# image: wekanteam/wekan:meteor-1.8
|
||||
# image: wekanteam/wekan:v1.95
|
||||
#image: wekanteam/wekan
|
||||
# image: wekanteam/wekan:v2.99
|
||||
#-------------------------------------------------------------------------------------
|
||||
container_name: wekan-app
|
||||
restart: always
|
||||
@@ -232,6 +232,52 @@ services:
|
||||
#- ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60
|
||||
#- ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15
|
||||
#---------------------------------------------------------------
|
||||
# ==== STORE ATTACHMENT ON SERVER FILESYSTEM INSTEAD OF MONGODB ====
|
||||
# https://github.com/wekan/wekan/pull/2603
|
||||
#- ATTACHMENTS_STORE_PATH = <pathname> # pathname can be relative or fullpath
|
||||
#---------------------------------------------------------------
|
||||
# ==== RICH TEXT EDITOR IN CARD COMMENTS ====
|
||||
# https://github.com/wekan/wekan/pull/2560
|
||||
- RICHER_CARD_COMMENT_EDITOR=true
|
||||
#---------------------------------------------------------------
|
||||
# ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
|
||||
# https://github.com/wekan/wekan/issues/2518
|
||||
- CARD_OPENED_WEBHOOK_ENABLED=false
|
||||
#---------------------------------------------------------------
|
||||
# ==== Allow to shrink attached/pasted image ====
|
||||
# https://github.com/wekan/wekan/pull/2544
|
||||
#-MAX_IMAGE_PIXEL=1024
|
||||
#-IMAGE_COMPRESS_RATIO=80
|
||||
#---------------------------------------------------------------
|
||||
# ==== BIGEVENTS DUE ETC NOTIFICATIONS =====
|
||||
# https://github.com/wekan/wekan/pull/2541
|
||||
# Introduced a system env var BIGEVENTS_PATTERN default as "NONE",
|
||||
# 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.
|
||||
# a) Example
|
||||
#- BIGEVENTS_PATTERN=due
|
||||
# b) All
|
||||
#- BIGEVENTS_PATTERN=received|start|due|end
|
||||
# c) Disabled
|
||||
- BIGEVENTS_PATTERN=NONE
|
||||
#---------------------------------------------------------------
|
||||
# ==== EMAIL DUE DATE NOTIFICATION =====
|
||||
# https://github.com/wekan/wekan/pull/2536
|
||||
# System timelines will be showing any user modification for
|
||||
# dueat startat endat receivedat, also notification to
|
||||
# the watchers and if any card is due, about due or past due.
|
||||
#
|
||||
# Notify due days, default is None, 2 days before and on the event day
|
||||
#- NOTIFY_DUE_DAYS_BEFORE_AND_AFTER=2,0
|
||||
#
|
||||
# Notify due at hour of day. Default every morning at 8am. Can be 0-23.
|
||||
# If env variable has parsing error, use default. Notification sent to watchers.
|
||||
#- NOTIFY_DUE_AT_HOUR_OF_DAY=8
|
||||
#-----------------------------------------------------------------
|
||||
# ==== EMAIL NOTIFICATION TIMEOUT, ms =====
|
||||
# Defaut: 30000 ms = 30s
|
||||
#- EMAIL_NOTIFICATION_TIMEOUT=30000
|
||||
@@ -380,8 +426,10 @@ services:
|
||||
# If the LDAP needs a user account to search
|
||||
#- LDAP_AUTHENTIFICATION=true
|
||||
#
|
||||
# The search user DN
|
||||
#- LDAP_AUTHENTIFICATION_USERDN=cn=wekan_adm,ou=serviceaccounts,ou=admin,ou=prod,dc=mydomain,dc=com
|
||||
# The search user DN - You need quotes when you have spaces in parameters
|
||||
# 2 examples:
|
||||
#- LDAP_AUTHENTIFICATION_USERDN="CN=ldap admin,CN=users,DC=domainmatter,DC=lan"
|
||||
#- LDAP_AUTHENTIFICATION_USERDN="CN=wekan_adm,OU=serviceaccounts,OU=admin,OU=prod,DC=mydomain,DC=com"
|
||||
#
|
||||
# The password for the search user
|
||||
#- LDAP_AUTHENTIFICATION_PASSWORD=pwd
|
||||
@@ -392,8 +440,10 @@ services:
|
||||
# If the sync of the users should be done in the background
|
||||
#- LDAP_BACKGROUND_SYNC=false
|
||||
#
|
||||
# At which interval does the background task sync in milliseconds
|
||||
#- LDAP_BACKGROUND_SYNC_INTERVAL=100
|
||||
# At which interval does the background task sync.
|
||||
# The format must be as specified in:
|
||||
# https://bunkat.github.io/later/parsers.html#text
|
||||
#- LDAP_BACKGROUND_SYNC_INTERVAL='every 1 hour'
|
||||
#
|
||||
#- LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false
|
||||
#
|
||||
@@ -411,6 +461,9 @@ services:
|
||||
# Option to login to the LDAP server with the user's own username and password, instead of an administrator key. Default: false (use administrator key).
|
||||
#- LDAP_USER_AUTHENTICATION="true"
|
||||
#
|
||||
# Which field is used to find the user for the user authentication. Default: uid.
|
||||
#- LDAP_USER_AUTHENTICATION_FIELD="uid"
|
||||
#
|
||||
# Optional extra LDAP filters. Don't forget the outmost enclosing parentheses if needed
|
||||
#- LDAP_USER_SEARCH_FILTER=
|
||||
#
|
||||
@@ -506,7 +559,6 @@ services:
|
||||
# example : LOGOUT_ON_MINUTES=55
|
||||
#- LOGOUT_ON_MINUTES=
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
depends_on:
|
||||
- wekandb
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
dependencies:
|
||||
- name: mongodb-replicaset
|
||||
version: 3.6.x
|
||||
version: 3.11.x
|
||||
repository: "https://kubernetes-charts.storage.googleapis.com/"
|
||||
condition: mongodb-replicaset.enabled
|
||||
@@ -75,7 +75,7 @@ else use user-provided URL.
|
||||
{{- if (index .Values "mongodb-replicaset" "enabled") -}}
|
||||
{{- $count := (int (index .Values "mongodb-replicaset" "replicas")) -}}
|
||||
{{- $release := .Release.Name -}}
|
||||
mongodb://{{- range $v := until $count }}{{ $release }}-mongodb-replicaset-{{ $v }}.{{ $release }}-mongodb-replicaset:27017{{ if ne $v (sub $count 1) }},{{- end -}}{{- end -}}?replicaSet={{ index .Values "mongodb-replicaset" "replicaSetName" }}
|
||||
mongodb://{{ $release }}-mongodb-replicaset:27017/admin?replicaSet={{ index .Values "mongodb-replicaset" "replicaSetName" }}
|
||||
{{- else -}}
|
||||
{{- index .Values "mongodb-replicaset" "url" -}}
|
||||
{{- end -}}
|
||||
|
||||
@@ -35,6 +35,6 @@ spec:
|
||||
- path: {{ $ingressPath }}
|
||||
backend:
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: http
|
||||
servicePort: 80
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
+756
-705
File diff suppressed because it is too large.
Load diff
+756
-705
File diff suppressed because it is too large.
Load diff
+756
-705
File diff suppressed because it is too large.
Load diff
+756
-705
File diff suppressed because it is too large.
Load diff
+756
-705
File diff suppressed because it is too large.
Load diff
+756
-705
File diff suppressed because it is too large.
Load diff
+55
-4
@@ -73,6 +73,8 @@
|
||||
"activity-unchecked-item-card": "hat %s in Checkliste %s abgewählt",
|
||||
"activity-checklist-completed-card": "hat Checkliste __checklist__ der Karte __card__ auf der Liste __list__ in Swimlane __swimlane__ in Board __board__ vervollständigt",
|
||||
"activity-checklist-uncompleted-card": "unvervollständigte die Checkliste %s",
|
||||
"activity-editComment": "editierte Kommentar",
|
||||
"activity-deleteComment": "löschte Kommentar",
|
||||
"add-attachment": "Datei anhängen",
|
||||
"add-board": "neues Board",
|
||||
"add-card": "Karte hinzufügen",
|
||||
@@ -130,10 +132,12 @@
|
||||
"boardChangeVisibilityPopup-title": "Sichtbarkeit ändern",
|
||||
"boardChangeWatchPopup-title": "Beobachtung ändern",
|
||||
"boardMenuPopup-title": "Boardeinstellungen",
|
||||
"boardChangeViewPopup-title": "Boardansicht",
|
||||
"boards": "Boards",
|
||||
"board-view": "Boardansicht",
|
||||
"board-view-cal": "Kalender",
|
||||
"board-view-swimlanes": "Swimlanes",
|
||||
"board-view-collapse": "Einklappen",
|
||||
"board-view-lists": "Listen",
|
||||
"bucket-example": "z.B. \"Löffelliste\"",
|
||||
"cancel": "Abbrechen",
|
||||
@@ -219,6 +223,8 @@
|
||||
"comment-only-desc": "Kann Karten nur kommentieren.",
|
||||
"no-comments": "Keine Kommentare",
|
||||
"no-comments-desc": "Kann keine Kommentare und Aktivitäten sehen.",
|
||||
"worker": "Arbeiter",
|
||||
"worker-desc": "Kann nur Karten verschieben, sich selbst zuweisen und kommentieren.",
|
||||
"computer": "Computer",
|
||||
"confirm-subtask-delete-dialog": "Wollen Sie die Teilaufgabe wirklich löschen?",
|
||||
"confirm-checklist-delete-dialog": "Wollen Sie die Checkliste wirklich löschen?",
|
||||
@@ -298,12 +304,24 @@
|
||||
"error-username-taken": "Dieser Benutzername ist bereits vergeben",
|
||||
"error-email-taken": "E-Mail wird schon verwendet",
|
||||
"export-board": "Board exportieren",
|
||||
"sort": "Sortieren",
|
||||
"sort-desc": "Zum Sortieren der Liste klicken",
|
||||
"list-sort-by": "Sortieren der Liste nach:",
|
||||
"list-label-modifiedAt": "Letzte Zugriffszeit",
|
||||
"list-label-title": "Name der Liste",
|
||||
"list-label-sort": "Ihre manuelle Sortierung",
|
||||
"list-label-short-modifiedAt": "(Z)",
|
||||
"list-label-short-title": "(N)",
|
||||
"list-label-short-sort": "(M)",
|
||||
"filter": "Filter",
|
||||
"filter-cards": "Karten filtern",
|
||||
"filter-cards": "Karten oder Listen filtern",
|
||||
"list-filter-label": "Liste nach Titel filtern",
|
||||
"filter-clear": "Filter entfernen",
|
||||
"filter-no-label": "Kein Label",
|
||||
"filter-no-member": "Kein Mitglied",
|
||||
"filter-no-custom-fields": "Keine benutzerdefinierten Felder",
|
||||
"filter-show-archive": "Archivierte Listen anzeigen",
|
||||
"filter-hide-empty": "Leere Listen verstecken",
|
||||
"filter-on": "Filter ist aktiv",
|
||||
"filter-on-desc": "Sie filtern die Karten in diesem Board. Klicken Sie, um den Filter zu bearbeiten.",
|
||||
"filter-to-selection": "Ergebnisse auswählen",
|
||||
@@ -422,7 +440,7 @@
|
||||
"save": "Speichern",
|
||||
"search": "Suchen",
|
||||
"rules": "Regeln",
|
||||
"search-cards": "Suche nach Kartentiteln und Beschreibungen auf diesem Board",
|
||||
"search-cards": "Suche nach Karten-/Listentiteln und Beschreibungen auf diesem Board",
|
||||
"search-example": "Suchbegriff",
|
||||
"select-color": "Farbe auswählen",
|
||||
"set-wip-limit-value": "Setzen Sie ein Limit für die maximale Anzahl von Aufgaben in dieser Liste",
|
||||
@@ -506,12 +524,21 @@
|
||||
"email-smtp-test-text": "Sie haben erfolgreich eine E-Mail versandt",
|
||||
"error-invitation-code-not-exist": "Ungültiger Einladungscode",
|
||||
"error-notAuthorized": "Sie sind nicht berechtigt diese Seite zu sehen.",
|
||||
"webhook-title": "Webhook Name",
|
||||
"webhook-token": "Token (Optional für Authentifizierung)",
|
||||
"outgoing-webhooks": "Ausgehende Webhooks",
|
||||
"bidirectional-webhooks": "Zwei-Wege Webhooks",
|
||||
"outgoingWebhooksPopup-title": "Ausgehende Webhooks",
|
||||
"boardCardTitlePopup-title": "Kartentitelfilter",
|
||||
"disable-webhook": "Diesen Webhook deaktivieren",
|
||||
"global-webhook": "Globale Webhooks",
|
||||
"new-outgoing-webhook": "Neuer ausgehender Webhook",
|
||||
"no-name": "(Unbekannt)",
|
||||
"Node_version": "Node-Version",
|
||||
"Meteor_version": "Meteor-Version",
|
||||
"MongoDB_version": "MongoDB-Version",
|
||||
"MongoDB_storage_engine": "MongoDB-Speicher-Engine",
|
||||
"MongoDB_Oplog_enabled": "MongoDB-Oplog aktiviert",
|
||||
"OS_Arch": "Betriebssystem-Architektur",
|
||||
"OS_Cpus": "Anzahl Prozessoren",
|
||||
"OS_Freemem": "Freier Arbeitsspeicher",
|
||||
@@ -699,8 +726,32 @@
|
||||
"duplicate-board": "Board duplizieren",
|
||||
"people-number": "Anzahl der Personen:",
|
||||
"swimlaneDeletePopup-title": "Swimlane löschen?",
|
||||
"swimlane-delete-pop": "Alle Aktionen werden aus dem Aktivitätenfeed entfernt und die Swimlane kann nicht wiederhergestellt werden. Es gibt kein Rückgängig!",
|
||||
"swimlane-delete-pop": "Alle Aktionen werden aus dem Aktivitätenfeed entfernt und die Swimlane kann nicht wiederhergestellt werden. Die Aktion kann nicht rückgängig gemacht werden.",
|
||||
"restore-all": "Alles wiederherstellen",
|
||||
"delete-all": "Alles löschen",
|
||||
"loading": "Laden, bitte warten."
|
||||
"loading": "Laden, bitte warten.",
|
||||
"previous_as": "letzter Zeitpunkt war",
|
||||
"act-a-dueAt": "hat Fälligkeit geändert auf\nWann: __timeValue__\nWo: __card__\nvorheriger Fälligkeitszeitpunkt war __timeOldValue__",
|
||||
"act-a-endAt": "hat Ende auf __timeValue__ von (__timeOldValue__) geändert",
|
||||
"act-a-startAt": "hat Start auf __timeValue__ von (__timeOldValue__) geändert",
|
||||
"act-a-receivedAt": "hat Empfangszeit auf __timeValue__ von (__timeOldValue__) geändert",
|
||||
"a-dueAt": "hat Fälligkeit geändert auf",
|
||||
"a-endAt": "hat Ende geändert auf",
|
||||
"a-startAt": "hat Startzeit geändert auf",
|
||||
"a-receivedAt": "hat Empfangszeit geändert auf",
|
||||
"almostdue": "aktuelles Fälligkeitsdatum %s bevorstehend",
|
||||
"pastdue": "aktuelles Fälligkeitsdatum %s überschritten",
|
||||
"duenow": "aktuelles Fälligkeitsdatum %s heute",
|
||||
"act-newDue": "__list__/__card__ hat seine 1. fällige Erinnerung [__board__]",
|
||||
"act-withDue": "Erinnerung an Fällikgeit von __card__ [__board__]",
|
||||
"act-almostdue": "erinnernd an das aktuelle Fälligkeitszeitpunkt (__timeValue__) von __card__ ist bevorstehend",
|
||||
"act-pastdue": "erinnernd an das aktuelle Fälligkeitszeitpunkt (__timeValue__) von __card__ ist vorbei",
|
||||
"act-duenow": "erinnernd an das aktuelle Fälligkeitszeitpunkt (__timeValue__) von __card__ ist jetzt",
|
||||
"act-atUserComment": "Sie wurden in [__board__] __list__/__card__ erwähnt",
|
||||
"delete-user-confirm-popup": "Sind Sie sicher, dass Sie diesen Account löschen wollen? Die Aktion kann nicht rückgängig gemacht werden.",
|
||||
"accounts-allowUserDelete": "Erlaube Benutzern ihren eigenen Account zu löschen",
|
||||
"hide-minicard-label-text": "Labeltext auf Minikarte ausblenden",
|
||||
"show-desktop-drag-handles": "Desktop-Ziehpunkte anzeigen",
|
||||
"assignee": "Zugewiesen",
|
||||
"cardAssigneesPopup-title": "Zugewiesen"
|
||||
}
|
||||
+756
-705
File diff suppressed because it is too large.
Load diff
+756
-705
File diff suppressed because it is too large.
Load diff
Loaded 100 of 216 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user