Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b445ad789 | ||
|
|
9baed4256a | ||
|
|
2b4df7e8c7 | ||
|
|
6e28dec712 | ||
|
|
ba9f0ca672 | ||
|
|
7261ccdc90 | ||
|
|
6fbadbe5bc | ||
|
|
26d7ba72aa | ||
|
|
44e4df2492 | ||
|
|
d8482b533a | ||
|
|
2082480ddd | ||
|
|
e8c4e394fd | ||
|
|
0c5052ebae | ||
|
|
d3b2ae1975 | ||
|
|
f4f0f489eb | ||
|
|
3368ebf067 | ||
|
|
b0ac10d94a | ||
|
|
c87a8b86ae | ||
|
|
d5d71d7097 | ||
|
|
c960a8b909 | ||
|
|
3257f78d24 | ||
|
|
a9133bf933 | ||
|
|
8e8147b5ac | ||
|
|
711f4de3d7 | ||
|
|
68998e062e | ||
|
|
f40d1f6bd5 | ||
|
|
048c3cd14d | ||
|
|
08ca353205 | ||
|
|
c83cdc9335 | ||
|
|
e91e3c076d | ||
|
|
8be7eec2ca | ||
|
|
acc4493517 | ||
|
|
ff467402c0 | ||
|
|
49d3eb5a3f | ||
|
|
889aa6d652 | ||
|
|
78e0457843 | ||
|
|
7f568faef6 | ||
|
|
be045f3156 | ||
|
|
3b5e81d514 | ||
|
|
346bdf2ca8 | ||
|
|
501289786c | ||
|
|
24ffe223be | ||
|
|
32c561d78e | ||
|
|
ff65b37336 | ||
|
|
3f698ca012 | ||
|
|
785bc2556e | ||
|
|
e198282b4d | ||
|
|
24e59c378b | ||
|
|
c1b4aae573 | ||
|
|
f5339ef97f | ||
|
|
2b53fae16f | ||
|
|
1d3466c7a0 | ||
|
|
4d8b2029d2 | ||
|
|
8ad0da2109 | ||
|
|
12827ef0a6 | ||
|
|
4c399a41f7 | ||
|
|
25968a35cc | ||
|
|
548d095223 | ||
|
|
1683bcb3e5 | ||
|
|
991843acb0 | ||
|
|
8b85437b6b | ||
|
|
6259f0de70 | ||
|
|
d86bc1d84d | ||
|
|
88bdb0815d | ||
|
|
97f64fe5e6 | ||
|
|
196fef3a1b | ||
|
|
a2d756074f | ||
|
|
b2f23d619d | ||
|
|
9d1d66b0f5 | ||
|
|
c61e44d55b | ||
|
|
76c5c283b9 | ||
|
|
2d88afb77e | ||
|
|
7fd09761cb | ||
|
|
cb53c53c2b | ||
|
|
77ff6349f6 | ||
|
|
879ad4001d | ||
|
|
e1436ab964 | ||
|
|
007bea0a4b | ||
|
|
67b76558fc | ||
|
|
f60429efdf | ||
|
|
f140e20c6f | ||
|
|
c502ab9500 | ||
|
|
425ae55422 | ||
|
|
e9006a234b | ||
|
|
ef6aa4e415 | ||
|
|
1901c3c798 | ||
|
|
6cb046f29e | ||
|
|
e7e67e300b | ||
|
|
3f948ba49b | ||
|
|
96f33e4052 | ||
|
|
68d60f4f64 | ||
|
|
f7153da83d | ||
|
|
16d53897c7 | ||
|
|
32d405bca4 | ||
|
|
2f747fc09d | ||
|
|
32d5a1d3f7 | ||
|
|
db44b8ea54 | ||
|
|
1f512c8ba1 | ||
|
|
417dc9dc42 | ||
|
|
bd66ea8acc | ||
|
|
dba9d13b00 | ||
|
|
1e034a794f | ||
|
|
a261c66f34 | ||
|
|
b94bd73d33 | ||
|
|
a9be6b17b9 | ||
|
|
ac6f8e8551 | ||
|
|
23fb2bfc7f | ||
|
|
ff1c3722a8 | ||
|
|
1712368f6a | ||
|
|
d7529bf6b5 | ||
|
|
6b145bb3cc | ||
|
|
72e905675d | ||
|
|
2fbcf7e9b6 | ||
|
|
0ade4dbf00 | ||
|
|
d70ad9d733 | ||
|
|
51a19878dd | ||
|
|
ddca6a1ad8 | ||
|
|
cac74122f2 | ||
|
|
991b2b9721 | ||
|
|
b6663acf91 | ||
|
|
7f74e72cea | ||
|
|
bd1df642fa | ||
|
|
575b0241e4 | ||
|
|
f1ed6304a4 | ||
|
|
a42d9871bd | ||
|
|
7a2ea7cace | ||
|
|
fb39811d95 | ||
|
|
78e88f4c42 | ||
|
|
db63af5f7a | ||
|
|
c3e04da796 | ||
|
|
2a59043ef1 | ||
|
|
3d76a35a68 | ||
|
|
c1733fc89c | ||
|
|
388c1bec45 | ||
|
|
a2869e9d2a | ||
|
|
3e5ff42474 | ||
|
|
9366eaf374 | ||
|
|
cb4c93a632 | ||
|
|
adaffd7189 | ||
|
|
821dc22e80 | ||
|
|
b0a71e96cc | ||
|
|
40e156992a | ||
|
|
ab031d9da1 | ||
|
|
209a76c5ce | ||
|
|
4bd74b5817 | ||
|
|
dbb1a86ca3 | ||
|
|
c066883dbd | ||
|
|
0b6b6dc7fd | ||
|
|
c546464d9f | ||
|
|
7b3e2c58e2 | ||
|
|
d6127082e9 | ||
|
|
67a5fdaf96 | ||
|
|
af9156b18a | ||
|
|
7af20fd2d9 | ||
|
|
2a5a428bc0 | ||
|
|
4b7d47465a | ||
|
|
5f4e2d8e06 | ||
|
|
f8ef8507b5 | ||
|
|
2ae143e6da | ||
|
|
38e8f52293 | ||
|
|
b143fc9297 | ||
|
|
dd10f78401 | ||
|
|
90208b774a | ||
|
|
bc35d9fe1a | ||
|
|
472d637c9f | ||
|
|
20c86613a7 | ||
|
|
5ac69ed7de | ||
|
|
0620fe5e11 | ||
|
|
44a819226f | ||
|
|
cf2cf7898a | ||
|
|
ee212b69a9 | ||
|
|
87d91560f8 | ||
|
|
bccca00aaf | ||
|
|
d6e36ed50a | ||
|
|
83f7fa768d | ||
|
|
2d49006aee | ||
|
|
c2e58dcb62 | ||
|
|
f154c5e521 | ||
|
|
eadbebb4fa | ||
|
|
40cf1c7549 | ||
|
|
9c4e305a84 | ||
|
|
167197fcda | ||
|
|
e3a40aca6f | ||
|
|
0611e0c4bd | ||
|
|
d6857693bb | ||
|
|
b5ccf7a58f | ||
|
|
bfca1822a0 | ||
|
|
30151de269 | ||
|
|
fe7c4528d7 | ||
|
|
40367995e7 | ||
|
|
b75fafc5f2 | ||
|
|
b788deb002 | ||
|
|
fa6b2e5679 | ||
|
|
70fd1ce57d | ||
|
|
97158de70f | ||
|
|
926faf0031 | ||
|
|
228996412c | ||
|
|
c9c1464c79 | ||
|
|
30c80f167c | ||
|
|
af9ca9b6c4 | ||
|
|
3e03f557cf | ||
|
|
f19dc77d2c | ||
|
|
1bad630641 | ||
|
|
8a9a82fc80 | ||
|
|
745bd7e806 | ||
|
|
cdb44da71e | ||
|
|
064a2deaaf | ||
|
|
a085ed1e1b | ||
|
|
f23448be33 | ||
|
|
a27c16e434 | ||
|
|
d5be48b905 | ||
|
|
7432af67c1 | ||
|
|
24b61cf772 | ||
|
|
cc64ef903f | ||
|
|
8e3f530217 | ||
|
|
f9b272c223 | ||
|
|
f0232fb5cb | ||
|
|
f84bad13b4 | ||
|
|
b72a68705c | ||
|
|
dd6ba152a0 | ||
|
|
cb9ced756f | ||
|
|
dbf3a48b5f | ||
|
|
b5d1799f5a | ||
|
|
d4ec0cc647 | ||
|
|
21a8c7cc45 | ||
|
|
aa691b0af1 | ||
|
|
7a75d82114 | ||
|
|
4daf69e4b6 | ||
|
|
960b33c3d9 | ||
|
|
685eb0f22c | ||
|
|
81a51c8dfc | ||
|
|
75c1dff1f8 | ||
|
|
cb24b85191 | ||
|
|
3aec8087f7 | ||
|
|
39ff51ef96 | ||
|
|
033a9482fc | ||
|
|
017017623f | ||
|
|
4b53a532bd | ||
|
|
6df296da47 | ||
|
|
9f5d04de27 | ||
|
|
c11b91d32a | ||
|
|
e266902ba9 | ||
|
|
b919eb23ab | ||
|
|
22a9e783c9 | ||
|
|
d4e75165dd | ||
|
|
73daf191ae | ||
|
|
329471e7e3 | ||
|
|
644e2c334f | ||
|
|
16d925e588 | ||
|
|
0daa6d3f7b | ||
|
|
e3b3e8b8c6 | ||
|
|
ddef1ea89c | ||
|
|
8ddf136928 | ||
|
|
893329d9c6 | ||
|
|
cb091c8a54 | ||
|
|
043f9813ef | ||
|
|
717891bee9 | ||
|
|
508f47f940 | ||
|
|
e4c0623cf7 | ||
|
|
4cf9813449 | ||
|
|
d44fb6bedd | ||
|
|
f2dd725eff | ||
|
|
3646a9c259 | ||
|
|
8c497efb46 | ||
|
|
a82aa87850 | ||
|
|
68e5909c96 | ||
|
|
4c2857b6e8 | ||
|
|
6f2275e8cb | ||
|
|
d42d7a89fb | ||
|
|
cba8b56cb5 | ||
|
|
ece009f556 | ||
|
|
53ed1f1a2c | ||
|
|
22a09f3e7d | ||
|
|
dda36f4eb3 | ||
|
|
8c6c089363 | ||
|
|
997a902ae4 | ||
|
|
defcfed272 | ||
|
|
0f1b157bda | ||
|
|
7188879567 | ||
|
|
c753d40f6c | ||
|
|
706436b638 | ||
|
|
13aab4d410 | ||
|
|
a06fd63690 | ||
|
|
07369b9784 | ||
|
|
415e28b471 | ||
|
|
f62d0530ec | ||
|
|
d12561aae9 | ||
|
|
d1ab38e57e | ||
|
|
27bc03d452 | ||
|
|
9cff76f110 | ||
|
|
a5a487caf8 | ||
|
|
9cfef8c337 | ||
|
|
46e2679f82 | ||
|
|
08383ed22d | ||
|
|
3602d59125 | ||
|
|
c72b3a870e | ||
|
|
078bab405b | ||
|
|
e57269ed57 | ||
|
|
05e0fb8fbe | ||
|
|
500ba88781 | ||
|
|
7f941840cb | ||
|
|
b4670f12a5 | ||
|
|
2a4eb0bcc2 | ||
|
|
f2b64c1aaa | ||
|
|
05b9e825bb | ||
|
|
187b4632e6 | ||
|
|
ce2b8a8d1a | ||
|
|
d3517f03f6 | ||
|
|
0583bf6df0 | ||
|
|
d446075239 | ||
|
|
e5949504b7 | ||
|
|
2ce1ba37a1 | ||
|
|
6d4ffdd8b4 | ||
|
|
8ff2e1f16a | ||
|
|
09b05ea3fb | ||
|
|
90a0478d0b | ||
|
|
ecb3a18206 | ||
|
|
e580109dd6 | ||
|
|
9561049668 | ||
|
|
d4dd646bf4 | ||
|
|
4cb25a5bcf | ||
|
|
82e90f7b94 | ||
|
|
760c008f5b | ||
|
|
54f8d4f308 | ||
|
|
0de0135619 | ||
|
|
dfdba25ea0 | ||
|
|
f61942e5cb | ||
|
|
33d4ad76ca | ||
|
|
53c8e63a09 | ||
|
|
9aa5584c9c | ||
|
|
24f66c9f88 | ||
|
|
fa548a4c32 | ||
|
|
10bc2c0d1a | ||
|
|
5188f2f2c9 | ||
|
|
1019f6272e | ||
|
|
dbd34c3254 | ||
|
|
20af78e50b | ||
|
|
96151a54fb | ||
|
|
ccf66905e7 | ||
|
|
ced56ed83f | ||
|
|
790234775f | ||
|
|
aac8933186 | ||
|
|
73edfd90a6 | ||
|
|
a168f885d9 | ||
|
|
50136fe314 | ||
|
|
6308352f90 | ||
|
|
ba5ee34d83 | ||
|
|
849f15ceee | ||
|
|
5979fc1d3d | ||
|
|
81f98e2da1 | ||
|
|
50edffee47 | ||
|
|
41330b15dc | ||
|
|
f5125b76bc | ||
|
|
ad663d2df9 | ||
|
|
9faa85a1c3 | ||
|
|
cb98b1326d | ||
|
|
e9f8e150e7 | ||
|
|
a90f648d5e | ||
|
|
8cd5f7c185 | ||
|
|
bedd3767ce | ||
|
|
63ee764a9b | ||
|
|
3dcebe8a9e | ||
|
|
232685919f | ||
|
|
b7f59db03c | ||
|
|
9e5d016017 | ||
|
|
8c8f51a84b | ||
|
|
de146f9aaf | ||
|
|
25bb0bfa22 | ||
|
|
00eeb86332 | ||
|
|
25ab20bcb4 | ||
|
|
5c588b2424 | ||
|
|
e77cda938c | ||
|
|
99793c0a12 | ||
|
|
178beee476 | ||
|
|
b4402b72ac | ||
|
|
23a35d9707 | ||
|
|
b925b8e74d | ||
|
|
6742dd0585 | ||
|
|
769b1b8110 | ||
|
|
f6040ab43c | ||
|
|
11b7ade355 | ||
|
|
7d39283aec | ||
|
|
f21afdc5f1 | ||
|
|
a864223f4a | ||
|
|
b7a74e25bc | ||
|
|
219091eb5e | ||
|
|
74a89e6610 | ||
|
|
371ce30462 | ||
|
|
1b65cbd3db | ||
|
|
b63ba110b8 | ||
|
|
5f4f447539 | ||
|
|
809c8f64f6 | ||
|
|
beaf20d256 | ||
|
|
644376a356 | ||
|
|
db757f20b3 | ||
|
|
961d9dcbdb | ||
|
|
0b971b6ddb | ||
|
|
ffdd6ff806 | ||
|
|
2b2315032d | ||
|
|
dd47d46f43 | ||
|
|
079e45eb52 | ||
|
|
55aea69948 | ||
|
|
fc5c2f40ba | ||
|
|
f599391419 | ||
|
|
ab0e1b3203 | ||
|
|
1dda8bff5e | ||
|
|
1db6efa9c8 | ||
|
|
d4774d398e | ||
|
|
04f5bda70a | ||
|
|
f11c8727d9 | ||
|
|
9e120cd44b | ||
|
|
bf42372eef | ||
|
|
5e9bf61d3f | ||
|
|
3a7ae7c5f2 | ||
|
|
be42b8d4cb | ||
|
|
28a01862d0 | ||
|
|
3b4f285fea | ||
|
|
58855ada97 | ||
|
|
5d0b23ee3f | ||
|
|
9cd980d9e6 | ||
|
|
d516ecb20c | ||
|
|
d91e0358f3 | ||
|
|
6931b88e40 | ||
|
|
d0735e1d8e | ||
|
|
506b95107c | ||
|
|
02422cc8f2 | ||
|
|
f19948f993 | ||
|
|
ebd884e654 | ||
|
|
a2866ce6f3 | ||
|
|
4c6c6ffb16 | ||
|
|
525a6defd9 | ||
|
|
1623ef65bb | ||
|
|
76bff73121 | ||
|
|
22077af137 | ||
|
|
a26f9bc1e7 | ||
|
|
56c7b3d00f | ||
|
|
fcc3560df4 | ||
|
|
f79f2733a0 | ||
|
|
4c0eb7dcc1 | ||
|
|
35677d0c55 | ||
|
|
2206c5c84c | ||
|
|
0d22f683fe | ||
|
|
001c8f2b01 | ||
|
|
463b4f2442 | ||
|
|
f437d8370e | ||
|
|
29cebc8d3d | ||
|
|
5704dd9f8b | ||
|
|
3d150b63dd | ||
|
|
7ca21d5260 | ||
|
|
111e4d8478 | ||
|
|
0aac9405ee | ||
|
|
f8b89129a3 | ||
|
|
5b8c642d8f | ||
|
|
6c1ddf58ed | ||
|
|
f32b27d2e3 | ||
|
|
288800eafc | ||
|
|
cb9c94cf9d | ||
|
|
18a1d4c5c6 | ||
|
|
ef4292b559 | ||
|
|
6a5e053da1 | ||
|
|
33caf1809a | ||
|
|
1d803d4b51 | ||
|
|
2ad318b57c | ||
|
|
5923585584 | ||
|
|
fccdffb9cc | ||
|
|
181c988706 | ||
|
|
51ac6c839e | ||
|
|
3a3666312f | ||
|
|
c433e3f80f | ||
|
|
6b86b3b6e7 | ||
|
|
d61d9994e3 | ||
|
|
a10b6fb173 | ||
|
|
f6e8e3d107 | ||
|
|
fb46a88a0f | ||
|
|
a8f1711890 | ||
|
|
d671a4bb32 | ||
|
|
cf5b962349 | ||
|
|
07f155d82e | ||
|
|
c6cea2fb4e | ||
|
|
7ee453648c | ||
|
|
b0826e2d7b | ||
|
|
bd6e4a351b | ||
|
|
209da19566 | ||
|
|
b342cfed3a | ||
|
|
a404612174 | ||
|
|
4dd66eed8e | ||
|
|
1ccacf60b2 | ||
|
|
6d88baebc7 | ||
|
|
e52cfc84b4 | ||
|
|
98003e26b1 | ||
|
|
3d4352ba0f | ||
|
|
c060f2322b | ||
|
|
734e4e5f3f | ||
|
|
b21a0caa4f | ||
|
|
c55563696e | ||
|
|
b8043d2dfc | ||
|
|
3942b584bf | ||
|
|
dce63842bb | ||
|
|
8c1630c638 | ||
|
|
f6db23b469 | ||
|
|
8c85955e6b | ||
|
|
86e3df0e5f | ||
|
|
fd805399aa | ||
|
|
6ac726e198 | ||
|
|
73480a3e1c | ||
|
|
12656ee9a1 | ||
|
|
fff11d7fc9 | ||
|
|
dba5de1882 | ||
|
|
6201809810 | ||
|
|
99d38f2d61 | ||
|
|
80e06b8ffc | ||
|
|
93e69c94a2 | ||
|
|
1f02321e27 | ||
|
|
37a53e7466 | ||
|
|
254c7fe951 | ||
|
|
02a35f94a7 | ||
|
|
39099106c6 | ||
|
|
4f54442947 | ||
|
|
1c4ce56b0f | ||
|
|
44f4a1c3bf | ||
|
|
d652eb5cee | ||
|
|
f7731f4f5e | ||
|
|
94cd2ce690 | ||
|
|
156164b5fd | ||
|
|
1bad81ca86 | ||
|
|
ff2d467d42 | ||
|
|
352e9033b6 | ||
|
|
60ffe5ab9b | ||
|
|
51c21fe639 | ||
|
|
bfabd63460 | ||
|
|
e7a03bb187 | ||
|
|
9083d5f88d | ||
|
|
e4cf94606c | ||
|
|
df4498f845 | ||
|
|
7ec7a5f27c | ||
|
|
4e21004c42 | ||
|
|
ddb8c28c38 | ||
|
|
eb8c1530a5 | ||
|
|
70623df899 | ||
|
|
6d11f3345f | ||
|
|
3941066ba5 | ||
|
|
53a020a23a | ||
|
|
2e52ff9e9d | ||
|
|
5445955a77 | ||
|
|
338e18870f | ||
|
|
6516b96ee0 | ||
|
|
0c28c5b087 | ||
|
|
b23cbbf925 | ||
|
|
4f299b72f7 | ||
|
|
ef04c2a826 | ||
|
|
b42649c777 | ||
|
|
5474099916 | ||
|
|
983a56b89b | ||
|
|
6fe7f3c553 | ||
|
|
df84a2be9a | ||
|
|
b3a752ef34 | ||
|
|
6ab5f093d5 | ||
|
|
6673b79738 | ||
|
|
053757f135 | ||
|
|
c17bef2cce | ||
|
|
36c04edb9f | ||
|
|
4f7fd3bb7b | ||
|
|
1983d3422b | ||
|
|
8cb132f492 | ||
|
|
a57806b054 | ||
|
|
25da8376ca | ||
|
|
30a3daa6af | ||
|
|
fc73dc5bbc | ||
|
|
9b43dd4fae | ||
|
|
e9f449e680 | ||
|
|
2e7a31f872 | ||
|
|
2d546627e9 | ||
|
|
f0cb948046 | ||
|
|
31f73057bf | ||
|
|
6aa7a2abef | ||
|
|
e649c79bb7 | ||
|
|
34b37116cf | ||
|
|
4e7f33b9e2 | ||
|
|
1e2582f043 | ||
|
|
63d4e0489f | ||
|
|
f1ab46d517 | ||
|
|
4608c057d5 | ||
|
|
0cb3aee803 | ||
|
|
b39458d2ee | ||
|
|
ca90220d18 | ||
|
|
45c0343f45 | ||
|
|
eb166737e3 | ||
|
|
a58ee144ac | ||
|
|
54260ca325 | ||
|
|
cb7eebaed0 | ||
|
|
6eeb708e4d | ||
|
|
1c49469b33 | ||
|
|
d18cd59c05 | ||
|
|
960cb27eef | ||
|
|
cdab6076cc | ||
|
|
be9cf34462 | ||
|
|
1e0fdf8abc | ||
|
|
2ef8803dfd | ||
|
|
1d2896886e | ||
|
|
e52351dcbf | ||
|
|
0a001d505d | ||
|
|
55072c529a | ||
|
|
66e22a2c87 | ||
|
|
07b505aa32 | ||
|
|
3c86cf854e | ||
|
|
334c15ff71 | ||
|
|
1aecf8da6b | ||
|
|
9cea76e4ef | ||
|
|
e74fb2f5b0 | ||
|
|
6c0b6ca670 | ||
|
|
9492daf9bc | ||
|
|
f346ce04f5 | ||
|
|
b465500afa | ||
|
|
adf969f6ff | ||
|
|
6462f855c9 | ||
|
|
3721742327 | ||
|
|
910771708a | ||
|
|
7ba361e2c9 | ||
|
|
cc2e0a7c7c | ||
|
|
77efcf7137 | ||
|
|
1298a07838 | ||
|
|
bcbe1aaaf5 | ||
|
|
0c351f4864 | ||
|
|
066237de08 | ||
|
|
ec3902021e | ||
|
|
6928fd71a9 | ||
|
|
035334125b | ||
|
|
70f37c55fd | ||
|
|
670ace6a8d | ||
|
|
ebaccbccca | ||
|
|
652f4e693b | ||
|
|
555d31cca6 | ||
|
|
534b20feda | ||
|
|
24f59b1855 | ||
|
|
c0df1dd4aa | ||
|
|
ba63627ebf | ||
|
|
da636ed17d | ||
|
|
4e12bcc378 | ||
|
|
6cec3b36de | ||
|
|
3e7247891d | ||
|
|
6bb2f311e2 | ||
|
|
0c5fc6d7fd | ||
|
|
8fae676551 | ||
|
|
379f1af89e | ||
|
|
5e128583ac | ||
|
|
23c27f9e07 | ||
|
|
c36509095a | ||
|
|
f85a05e454 | ||
|
|
067e4d15f3 | ||
|
|
65d1330d35 | ||
|
|
39312a075e | ||
|
|
96173ad431 | ||
|
|
c0957ce173 | ||
|
|
fdc168a476 | ||
|
|
3d76b8e1c1 | ||
|
|
1296fce72a | ||
|
|
c34b1a9ec5 | ||
|
|
13db2fd7b2 | ||
|
|
511d1973eb | ||
|
|
dfd26e4843 | ||
|
|
d2c3ecdbcd | ||
|
|
1249bef93e | ||
|
|
71b642a2ba | ||
|
|
f170c116bb | ||
|
|
4ed2f29e30 | ||
|
|
d0731a4bf4 | ||
|
|
d165733242 | ||
|
|
a91f0b5c3b | ||
|
|
cc84bf2de3 | ||
|
|
7e0bc1e33a | ||
|
|
1b94b919fe | ||
|
|
d68ad5af04 | ||
|
|
fcbbb93bb6 | ||
|
|
b3b8d3c086 | ||
|
|
676140f5c8 | ||
|
|
20757efc7e | ||
|
|
4d615057fe | ||
|
|
9bdb5aea87 | ||
|
|
d67c435132 | ||
|
|
18de911a7f | ||
|
|
39b1871e4d | ||
|
|
eecac0e37f | ||
|
|
3e0352c586 | ||
|
|
485f6b5ac2 | ||
|
|
3e715bbcc9 | ||
|
|
b881c3b908 | ||
|
|
9e29d4c815 | ||
|
|
f09d9b63de | ||
|
|
0588c2f605 | ||
|
|
b216c63c13 | ||
|
|
1f5f429fc4 | ||
|
|
3b62b5ec5d | ||
|
|
2f24dcfc7d | ||
|
|
fda4e954eb | ||
|
|
cc285afd59 | ||
|
|
b186561355 | ||
|
|
c9ca79aac8 | ||
|
|
6b4e6d0c63 | ||
|
|
c60ae13b5f | ||
|
|
4e1fedb236 | ||
|
|
3c9aa65ca3 | ||
|
|
f7446ba934 | ||
|
|
9c6d374b95 | ||
|
|
365e2e5b4b | ||
|
|
4064429047 | ||
|
|
62dd28d91c | ||
|
|
b3005f828d | ||
|
|
99e7c65907 | ||
|
|
6828ccd7f1 | ||
|
|
36447ba1c0 | ||
|
|
c694505885 | ||
|
|
807c6ce09e | ||
|
|
9fc3de8502 | ||
|
|
be00465e67 | ||
|
|
5c33a85341 | ||
|
|
e55d7e4f72 | ||
|
|
9b0eb0a9f1 | ||
|
|
53c0a52788 | ||
|
|
89b535ac0f | ||
|
|
c2eb300e21 | ||
|
|
eea077e57e | ||
|
|
f725291e14 | ||
|
|
bec1256727 | ||
|
|
9407992312 | ||
|
|
c7e02ad2ee | ||
|
|
ac12aa7ede | ||
|
|
cec5f45459 | ||
|
|
ab73afe17a | ||
|
|
dc4e93ad56 | ||
|
|
2f557ae3a5 | ||
|
|
be25e372a3 | ||
|
|
3f937764cb | ||
|
|
0b9748dff6 | ||
|
|
b9929dc682 | ||
|
|
034981cd3d | ||
|
|
79e464bf90 | ||
|
|
b015b5b724 | ||
|
|
f5515cb95f | ||
|
|
16bbd892aa | ||
|
|
32d9a2c10b | ||
|
|
9967859f42 | ||
|
|
eac0b3e7bb | ||
|
|
8177a1ed0a | ||
|
|
17308dc5f3 | ||
|
|
67301e07e2 | ||
|
|
315db00e83 | ||
|
|
692a1bc369 | ||
|
|
f76d8e47a8 | ||
|
|
6adfcb3513 | ||
|
|
bce2242528 | ||
|
|
f0597ef0c4 | ||
|
|
10aab8a733 | ||
|
|
fb75a487fc | ||
|
|
37306c8d22 | ||
|
|
4ffa8784a2 | ||
|
|
74a01691e3 | ||
|
|
49c415f023 | ||
|
|
b2e175ba8c | ||
|
|
724d26379c | ||
|
|
0a62089df0 | ||
|
|
a93de07fb9 | ||
|
|
64367a01dd | ||
|
|
5644ef66af | ||
|
|
061a13e46e | ||
|
|
dcc7b2970f | ||
|
|
d509b2608f | ||
|
|
193af893ee | ||
|
|
acd105e61b | ||
|
|
a300b73d56 | ||
|
|
5168fb7717 | ||
|
|
945ffeda34 | ||
|
|
66adc397bf | ||
|
|
82a6b4245c | ||
|
|
ae5ea46d7f | ||
|
|
b5022529ac | ||
|
|
49c471d816 | ||
|
|
445aa89e4f | ||
|
|
787941857d | ||
|
|
6a2b494713 | ||
|
|
04d7c47f4c | ||
|
|
9c94ea58b4 | ||
|
|
9f65696fcc | ||
|
|
93cc7f0232 | ||
|
|
7e4bd4a0a7 | ||
|
|
f63482b587 | ||
|
|
95b21943ee | ||
|
|
d1400f48c3 | ||
|
|
53d545eeef | ||
|
|
afeb2de412 | ||
|
|
83d6ef7301 | ||
|
|
d2e93c4927 | ||
|
|
fd67bb8e67 | ||
|
|
da6c2b5503 | ||
|
|
57d43f916f | ||
|
|
10a6dbd080 | ||
|
|
0498671cd4 | ||
|
|
b2eeff9697 | ||
|
|
a8146e61f3 | ||
|
|
6a23f73d94 | ||
|
|
b9bcb8f979 | ||
|
|
0416fbdcc4 | ||
|
|
064d125a1c | ||
|
|
1cc8452752 | ||
|
|
5bfb6c6411 | ||
|
|
a48f560a85 | ||
|
|
92bb646977 | ||
|
|
43c38f87a5 | ||
|
|
01c12d7abc | ||
|
|
63e3f6907a | ||
|
|
2e62524d5e | ||
|
|
ec59af3777 | ||
|
|
aa080a7506 | ||
|
|
397dda11ce | ||
|
|
6173a73381 | ||
|
|
dafe1e39a5 | ||
|
|
b77be874e0 | ||
|
|
aca4bffac8 | ||
|
|
2bb80956dd | ||
|
|
e5148b4571 | ||
|
|
6204edd76e | ||
|
|
4eed23afe0 | ||
|
|
2b02e09b07 | ||
|
|
c708877dde | ||
|
|
e2ae5d6b8e | ||
|
|
a798713919 | ||
|
|
19402f5850 | ||
|
|
2beb91ba5d | ||
|
|
1c35f05224 | ||
|
|
758ec48e5c | ||
|
|
f8d5c91659 | ||
|
|
a8f41f7994 | ||
|
|
bf5596b201 | ||
|
|
b0f410f82d | ||
|
|
df54f15ecb | ||
|
|
6a0e4c9b21 | ||
|
|
c0ddecb2ee | ||
|
|
1a7796f2ca | ||
|
|
7119896a3c | ||
|
|
1b32509496 | ||
|
|
a0d2df0647 | ||
|
|
d8c62cf242 | ||
|
|
980fd4f61e | ||
|
|
0dd82fedaf | ||
|
|
058f7e430b | ||
|
|
9487406fc3 | ||
|
|
bf35b09998 | ||
|
|
d50e78c787 | ||
|
|
5dd43fa147 | ||
|
|
5f345e851a | ||
|
|
687c7923e7 | ||
|
|
90707aaab3 | ||
|
|
d5870472fb | ||
|
|
efb083aabf | ||
|
|
5c77407061 | ||
|
|
9c204d9bbe | ||
|
|
616dade81c | ||
|
|
43d86d7d5d | ||
|
|
dea9df2a3b | ||
|
|
6c7eab4456 | ||
|
|
35a3b0b62e | ||
|
|
7683f98b7b | ||
|
|
09dbcf3bea | ||
|
|
369da1e09d | ||
|
|
94a1446a08 | ||
|
|
884cd0e6b8 | ||
|
|
5e17b01077 | ||
|
|
644b235bbb | ||
|
|
3d3096104e | ||
|
|
a41190cdf0 | ||
|
|
1189259a55 | ||
|
|
9a053164ac | ||
|
|
c31602da42 | ||
|
|
074a1218fd | ||
|
|
5d573a7aa1 | ||
|
|
c26a31f498 | ||
|
|
4b65c5cc33 | ||
|
|
af8bf1af2e | ||
|
|
aaae3150c4 | ||
|
|
5220111822 | ||
|
|
62e5e5db49 | ||
|
|
eb6373a879 | ||
|
|
a4ca6dce25 | ||
|
|
5986f64d72 | ||
|
|
b1b9170afe | ||
|
|
13e5ccb56e | ||
|
|
8d24fc4bcf | ||
|
|
cc63a575ab | ||
|
|
a02c51cc3b | ||
|
|
ca6cadbe6d | ||
|
|
cc753cf4e4 | ||
|
|
7e4fddd932 | ||
|
|
06423164fb | ||
|
|
515c9be051 | ||
|
|
243797d2e9 | ||
|
|
c997786e64 | ||
|
|
dd324aa581 | ||
|
|
177c00e974 | ||
|
|
067aef9de9 | ||
|
|
02f14d967f | ||
|
|
122a61b333 | ||
|
|
49a89b80cf | ||
|
|
ee81775dc8 | ||
|
|
05869792ad | ||
|
|
2ac7660f29 | ||
|
|
abf7890941 | ||
|
|
e139e140f4 | ||
|
|
1f7db171d9 | ||
|
|
bf06c715fe | ||
|
|
44e20023cc | ||
|
|
e03ee1bd2f | ||
|
|
aea5ed7848 | ||
|
|
bbdb6a90b2 | ||
|
|
f4eac833ed | ||
|
|
8fbbc104f7 | ||
|
|
6ba4574c67 | ||
|
|
928d88cfe1 | ||
|
|
4f5086c5cd | ||
|
|
fe69ab9030 | ||
|
|
08a1927c0c | ||
|
|
f02d5e64f0 | ||
|
|
5bca2f802b | ||
|
|
3eba6ef285 | ||
|
|
5f81b73a49 | ||
|
|
ad54a8a484 | ||
|
|
5adce79842 | ||
|
|
9fceb212d9 | ||
|
|
e044769d85 | ||
|
|
a86a8eff2d | ||
|
|
7b9aaddb5b | ||
|
|
541dc67a02 | ||
|
|
db5ff4e1e2 | ||
|
|
d0611e1bff | ||
|
|
523ebea4dd | ||
|
|
bccfa320a9 | ||
|
|
72d0db7a59 | ||
|
|
ed0ef5b7d3 | ||
|
|
5619eef620 | ||
|
|
ae2e42e1b4 | ||
|
|
864ad3827d | ||
|
|
9cb8aab3ba | ||
|
|
374e986579 | ||
|
|
3c4549fe64 | ||
|
|
0394a78ece | ||
|
|
19d239f4cf | ||
|
|
d23ac2b385 | ||
|
|
ae5d2b7908 | ||
|
|
766b58d91b | ||
|
|
663a4040f9 | ||
|
|
18467dfe40 | ||
|
|
2a165dccd4 | ||
|
|
d501e5832a | ||
|
|
b198a3ed99 | ||
|
|
226d25ca94 | ||
|
|
b7d508e8c4 | ||
|
|
439d7c3dbc | ||
|
|
bac490d2f3 | ||
|
|
3a4a075dba | ||
|
|
c0ffd6c20f | ||
|
|
94a52080cf | ||
|
|
c9f70cf382 | ||
|
|
04745f0c2f | ||
|
|
aead18eb58 | ||
|
|
a0c02f4a50 | ||
|
|
50f170b75d | ||
|
|
989b026b33 | ||
|
|
4ac6a507cd | ||
|
|
cd36194477 | ||
|
|
6ab1cbb341 | ||
|
|
5a023e4315 | ||
|
|
adb7f5b2ca | ||
|
|
fd465fbb60 | ||
|
|
879a84184f | ||
|
|
f89de026c4 | ||
|
|
5f20e56721 | ||
|
|
d59583915c | ||
|
|
b627ced605 | ||
|
|
c3037b155f | ||
|
|
c3e839c885 | ||
|
|
b0444ae338 | ||
|
|
43dde4a10f | ||
|
|
97922c90cb | ||
|
|
c372ea02f7 | ||
|
|
daa95c582e | ||
|
|
aa2a15bf1b | ||
|
|
f629a7b3f7 | ||
|
|
03921a969a | ||
|
|
b7d51456eb | ||
|
|
6a587299b8 | ||
|
|
f5aa1b795b | ||
|
|
2efc5ab4e9 | ||
|
|
fde26c2183 | ||
|
|
8ad8f17731 | ||
|
|
e9d56b0804 | ||
|
|
43a967b274 | ||
|
|
c1ecaf3959 | ||
|
|
0b5ecd24f9 | ||
|
|
9a55f38d90 | ||
|
|
09c8f29de8 | ||
|
|
620bbb3394 | ||
|
|
77ae6c17e7 | ||
|
|
b8bbdcc4c5 | ||
|
|
18e936fd28 | ||
|
|
c94edb6bb0 | ||
|
|
615f66c820 | ||
|
|
484d495259 | ||
|
|
bec0c4b4d0 | ||
|
|
dfbcca2a2d | ||
|
|
4526845fd1 | ||
|
|
73c6c07c47 | ||
|
|
896a149fb9 | ||
|
|
0034d2d3be | ||
|
|
880dcab0b1 | ||
|
|
98eb4453c5 | ||
|
|
3e686e4379 | ||
|
|
8509dad58f | ||
|
|
0f53220122 | ||
|
|
1214af04a9 | ||
|
|
8c0d61c4a5 | ||
|
|
ba2012af43 | ||
|
|
b5e6e6ee10 | ||
|
|
61ee6cf09f | ||
|
|
04925f7347 | ||
|
|
37817fcaf8 | ||
|
|
8a7ad0df54 | ||
|
|
a433f7d9fe | ||
|
|
991e74bfc2 | ||
|
|
c46817f23a | ||
|
|
94bd63c8a7 | ||
|
|
3e8b2620a1 | ||
|
|
ce8c78eb34 | ||
|
|
85e02010d4 | ||
|
|
a912960a58 | ||
|
|
aeec9f6ef8 | ||
|
|
718adc6c47 | ||
|
|
454aa05987 | ||
|
|
23726136bd | ||
|
|
fa82efe7d2 | ||
|
|
011fad6701 | ||
|
|
fcf262cc98 | ||
|
|
c5f0e87dd0 | ||
|
|
6a154ee9b0 | ||
|
|
558539e21b | ||
|
|
571f55f904 | ||
|
|
259614b647 | ||
|
|
8ec60879dc | ||
|
|
edd2e9db3c | ||
|
|
67454d0491 | ||
|
|
9e14733478 | ||
|
|
848d8ac95d | ||
|
|
93e7c8aecf | ||
|
|
a98ae8dc71 | ||
|
|
193ff20c23 | ||
|
|
92a85a991b | ||
|
|
38af71d9c9 | ||
|
|
246267276a | ||
|
|
ea19176d3c | ||
|
|
f28889de59 | ||
|
|
aa7199af2d | ||
|
|
fba0a52e4a | ||
|
|
6d7bdad140 | ||
|
|
aef871833b | ||
|
|
dda49d2f07 | ||
|
|
199ea3d6f9 | ||
|
|
6478dd7da8 | ||
|
|
53bd527947 | ||
|
|
2156e45869 | ||
|
|
72a66c1d5b | ||
|
|
1ced919dfb | ||
|
|
d948e78d1e | ||
|
|
bd5fd77655 | ||
|
|
45cf2843a6 | ||
|
|
ca51ace22d | ||
|
|
93a7040c77 | ||
|
|
d4df405862 | ||
|
|
dc687848e6 | ||
|
|
85187817a7 | ||
|
|
5f5a1b780e | ||
|
|
e31741cd7b | ||
|
|
d5bfa468f7 | ||
|
|
9990f9856b | ||
|
|
b3bb03fed4 | ||
|
|
1332a57890 | ||
|
|
123d9659c1 | ||
|
|
90d55777f7 | ||
|
|
ec4e1b6eff | ||
|
|
c72b769f82 | ||
|
|
8471f389eb | ||
|
|
23e6b4ec0a | ||
|
|
8f364281d2 | ||
|
|
1f17cee39b | ||
|
|
4787a1186b | ||
|
|
3c4b09157e | ||
|
|
87ee0ca646 | ||
|
|
de59758471 | ||
|
|
ea109cfdd7 | ||
|
|
9035989abf | ||
|
|
aa6076e087 | ||
|
|
78b9436f38 | ||
|
|
3ce3fa74b3 | ||
|
|
d6eff2a074 | ||
|
|
ccf1e8bc3c | ||
|
|
6d75fe4d86 | ||
|
|
82a01edc54 | ||
|
|
9ff2340cdb | ||
|
|
b727204716 | ||
|
|
5b4f2f55aa | ||
|
|
60119153de | ||
|
|
2b2d596da6 | ||
|
|
63c1ef9f9e | ||
|
|
0cc183682e | ||
|
|
4d193f5650 | ||
|
|
73c4806864 | ||
|
|
b0cf60a3d9 | ||
|
|
3ca3d063a7 | ||
|
|
01741750f8 | ||
|
|
4377381fd3 | ||
|
|
9cf1b62cd6 | ||
|
|
5ded2e5121 | ||
|
|
7eeabf14be | ||
|
|
be507e567a | ||
|
|
1117d1c2ae | ||
|
|
9eaac52b73 | ||
|
|
4b20102139 | ||
|
|
edae64b1c7 | ||
|
|
9242bdb10b | ||
|
|
171d0c8629 | ||
|
|
a69997d31e | ||
|
|
31e4400c66 | ||
|
|
3bcd578fcb | ||
|
|
4c5daf5d85 | ||
|
|
5b4d75a976 | ||
|
|
05020241aa | ||
|
|
b9c63a8c8f | ||
|
|
f75f686271 | ||
|
|
505fc71665 | ||
|
|
529594ef55 | ||
|
|
3d1f004e90 | ||
|
|
0195044ba1 | ||
|
|
e8c24aeaf9 | ||
|
|
ad8f227880 | ||
|
|
ddfcb235cc | ||
|
|
c4d83c6e66 | ||
|
|
90b589d993 | ||
|
|
94fc172366 | ||
|
|
d4cc337577 | ||
|
|
25695bdef2 | ||
|
|
af496839f5 | ||
|
|
a8feb8ed32 | ||
|
|
9c09d926e3 | ||
|
|
cba480d3ae | ||
|
|
dbfdba75f9 | ||
|
|
d46afc12be | ||
|
|
531cea489e | ||
|
|
9e4758ec9e | ||
|
|
e8206f2983 | ||
|
|
e5c6da5a39 | ||
|
|
2119a6568b | ||
|
|
dd7c9997a9 | ||
|
|
256ca129f0 | ||
|
|
6d9ac3ae48 | ||
|
|
59046d448f | ||
|
|
d9e291635a | ||
|
|
b8158fadfc | ||
|
|
1133b7a04a | ||
|
|
43fe9ef34a | ||
|
|
f910b66ef2 | ||
|
|
bfac626fa4 | ||
|
|
ecbb8ef071 | ||
|
|
1c0c5bde0f | ||
|
|
c3044a6b8b | ||
|
|
778a29855f | ||
|
|
eb859dac3a | ||
|
|
9ac1164440 | ||
|
|
39974c9532 | ||
|
|
4ff8989e8f | ||
|
|
2bf13a50c4 | ||
|
|
382b14ea27 | ||
|
|
17fcfd3697 | ||
|
|
811ccf0f10 | ||
|
|
422424d21c | ||
|
|
0bdc7efc8b | ||
|
|
32058e8018 | ||
|
|
ba12b53e49 | ||
|
|
b9ead144fb | ||
|
|
1d58e40133 | ||
|
|
cd749bc385 | ||
|
|
01cd2df369 | ||
|
|
1854f5c061 | ||
|
|
b00bd04baa | ||
|
|
8b32cf42cb | ||
|
|
3075791a93 | ||
|
|
38ab2b766d | ||
|
|
410e5aa28d | ||
|
|
82f7541131 | ||
|
|
106b135eba | ||
|
|
7dfec58128 | ||
|
|
977bce9eb2 | ||
|
|
bc0aef3332 | ||
|
|
8101d7ade8 | ||
|
|
f6d19d2833 | ||
|
|
c431af27c2 | ||
|
|
973a0d93ae | ||
|
|
cbdb7b4f64 | ||
|
|
9c5bcd7321 | ||
|
|
8f0c3b45c0 | ||
|
|
78317ef792 | ||
|
|
1ab4f5b913 | ||
|
|
04f09b6042 | ||
|
|
f48e2fbd14 | ||
|
|
27f9e9e104 | ||
|
|
9a509778d3 | ||
|
|
12b08b9915 | ||
|
|
bd8a8ab90e | ||
|
|
642507fb42 | ||
|
|
4600e4e237 | ||
|
|
c2e6f67689 | ||
|
|
9dec10bad3 | ||
|
|
3e390503fc | ||
|
|
a7f8614f04 | ||
|
|
0be7a5d783 | ||
|
|
41b938ea69 | ||
|
|
12a374bec7 | ||
|
|
8dd1566316 | ||
|
|
50f751e0a9 | ||
|
|
1984a6b1c8 | ||
|
|
1d904adc61 | ||
|
|
22882a0603 | ||
|
|
9b465bc98b | ||
|
|
ef99e6a6b1 | ||
|
|
61b240d126 | ||
|
|
1f3b5604c0 | ||
|
|
3bf4f3ef40 | ||
|
|
838578657d | ||
|
|
d6cfac0122 | ||
|
|
c1d2e27c09 | ||
|
|
b43779e408 | ||
|
|
f9df38d333 | ||
|
|
80088174fc | ||
|
|
0dfc62c025 | ||
|
|
7186b947b2 | ||
|
|
98df01060d | ||
|
|
9184bf3946 | ||
|
|
9518a5c11e | ||
|
|
9a766c0f74 | ||
|
|
c0c7b269a7 | ||
|
|
5dafea405d | ||
|
|
77a5f8f339 | ||
|
|
be97efb791 | ||
|
|
7e34dd0c2b | ||
|
|
0ecbb73419 | ||
|
|
f3aa524b77 | ||
|
|
792f187d06 | ||
|
|
4c18df757f | ||
|
|
9d47d3e397 | ||
|
|
0b9328a1be | ||
|
|
fc033c30e6 | ||
|
|
da180472c1 | ||
|
|
cdc2b920ee | ||
|
|
16d75f549d | ||
|
|
c43508cacb | ||
|
|
6b2d022a93 | ||
|
|
1cb8a6e7fe | ||
|
|
5e109de8d5 | ||
|
|
c4b5145db4 | ||
|
|
e5638e5e5a | ||
|
|
7e49ab5fea | ||
|
|
682eab74d9 | ||
|
|
9edbc349b9 | ||
|
|
c751f05962 | ||
|
|
2fa1b3122d | ||
|
|
c48c18c4e9 | ||
|
|
2da157dbb0 | ||
|
|
daeb06f7b7 | ||
|
|
dcd5af0a76 | ||
|
|
e576e0f9cf | ||
|
|
83e7e4fb27 | ||
|
|
01804daf24 | ||
|
|
d77e54fff6 | ||
|
|
0aa78d9847 | ||
|
|
e5c1351e82 | ||
|
|
f6656a4791 | ||
|
|
d2354827da | ||
|
|
814164e53e | ||
|
|
bde9cc7cea | ||
|
|
7b0cf760e9 | ||
|
|
6a16063f7d | ||
|
|
130aa1161e | ||
|
|
eaebe8b893 | ||
|
|
c4b898633e | ||
|
|
88fca396d4 | ||
|
|
cfa3c4bfcb | ||
|
|
bf7520c430 | ||
|
|
166324df01 | ||
|
|
539c1ab87a | ||
|
|
e5cb190693 | ||
|
|
359d0b376f | ||
|
|
a6cf57e988 | ||
|
|
1124f4120c | ||
|
|
6ee6dbade5 | ||
|
|
f219e2151f | ||
|
|
c5b7f30d65 | ||
|
|
e3f1fbc967 | ||
|
|
e6df7d81cf | ||
|
|
24ad97cddb | ||
|
|
b70252468a | ||
|
|
f9941c0f9c | ||
|
|
26c8505d9e | ||
|
|
e7e558e865 | ||
|
|
6167d447fd | ||
|
|
30b17ff6c9 | ||
|
|
df91d1e632 | ||
|
|
1f42aac19f | ||
|
|
212440862b | ||
|
|
21e68f3e54 | ||
|
|
116bc38136 | ||
|
|
e721dba1e2 | ||
|
|
3b6ec34bf5 | ||
|
|
be61904a1e | ||
|
|
170acf6948 | ||
|
|
d69f8f4cda | ||
|
|
db80e73804 | ||
|
|
25668eb7c4 | ||
|
|
11177196e3 | ||
|
|
6d21ffe660 | ||
|
|
ce79645455 | ||
|
|
7b91b341fe | ||
|
|
6856f4d95f | ||
|
|
075ea1c43d | ||
|
|
b7247f8d52 | ||
|
|
87d2cee02a | ||
|
|
fa758a73bd | ||
|
|
4191ede176 | ||
|
|
ded98d73d7 | ||
|
|
bbfacc06e3 | ||
|
|
e45b8ea48c | ||
|
|
179ff7a124 | ||
|
|
fc4d7a0bde | ||
|
|
5415a7c4b9 | ||
|
|
84fd00c566 | ||
|
|
f41589ba82 | ||
|
|
ce65963217 | ||
|
|
1e0fcfd20d | ||
|
|
8d5cbf1e6c | ||
|
|
7cc930f6b9 | ||
|
|
e9bb7e0041 | ||
|
|
61538ab577 | ||
|
|
bca4f0b842 | ||
|
|
0723a204e8 | ||
|
|
2cd1fbf9d9 | ||
|
|
29c3d939a7 | ||
|
|
02e8749540 | ||
|
|
e402200b87 | ||
|
|
ea289c7810 | ||
|
|
f3058133b3 | ||
|
|
e26a4824cf | ||
|
|
e5995477b8 | ||
|
|
c12e003fd3 | ||
|
|
8e11d278d3 | ||
|
|
de13705289 | ||
|
|
09bc8ac44c | ||
|
|
eae4bb2d37 | ||
|
|
31584e849c | ||
|
|
b735817901 | ||
|
|
4dbe6feb8c | ||
|
|
9eef5112dc | ||
|
|
4861da52a0 | ||
|
|
aede4dd9c5 | ||
|
|
4505e6edc9 | ||
|
|
85e86136e9 | ||
|
|
9b961edb36 | ||
|
|
82fc4d011f | ||
|
|
bfe03ac89f | ||
|
|
db74c86e55 | ||
|
|
b7ee0173ec | ||
|
|
ce177117a0 | ||
|
|
b05f3595f0 | ||
|
|
3e927b4e58 | ||
|
|
1c8a00943c | ||
|
|
f00797de5c | ||
|
|
c115046a7c | ||
|
|
8051f6affa | ||
|
|
973b416885 | ||
|
|
b501d35b55 | ||
|
|
8fc4011bf8 | ||
|
|
7b94b04701 | ||
|
|
217ae207b9 | ||
|
|
4f1eaa6b82 | ||
|
|
49c7a6c223 | ||
|
|
0e883675fc | ||
|
|
5a6be37d52 | ||
|
|
c295e47f76 | ||
|
|
9711945e48 | ||
|
|
fb87e4b1f2 | ||
|
|
a320a71467 | ||
|
|
bda6ee26d7 | ||
|
|
40d438a517 | ||
|
|
5d2dd66450 | ||
|
|
f9539aa2a8 | ||
|
|
66e56849a4 | ||
|
|
75512043e8 | ||
|
|
3631cd5026 | ||
|
|
09c59ff562 | ||
|
|
e3d1fbc7f4 | ||
|
|
05f1d197f5 | ||
|
|
485ccf4007 | ||
|
|
fe79a3e763 | ||
|
|
30a4494af2 | ||
|
|
568a1a11fd | ||
|
|
75b3d51b39 | ||
|
|
3d0bac1f49 | ||
|
|
578bdf9e67 | ||
|
|
a7f88d24a1 | ||
|
|
fa0fd5f21f | ||
|
|
dfc5537107 | ||
|
|
5b98f139d1 | ||
|
|
862239019a | ||
|
|
983649a6cb | ||
|
|
660b578ab3 | ||
|
|
594daa9cf8 | ||
|
|
ebb48f3ee8 | ||
|
|
4a2b30d08b | ||
|
|
0cee496270 | ||
|
|
2c1dadf712 | ||
|
|
6fc89a04de | ||
|
|
4994f66e9c | ||
|
|
91bab1c274 | ||
|
|
daf1a67dc2 | ||
|
|
e44c46bcd2 | ||
|
|
57cf86a79f | ||
|
|
1ef663531b | ||
|
|
becdcbd5a7 | ||
|
|
d3bdc8edbc | ||
|
|
ffc825e348 | ||
|
|
9db64a95b6 | ||
|
|
c5a707a7f5 | ||
|
|
b2d3138220 | ||
|
|
405514fd6b | ||
|
|
efa2d99bf4 | ||
|
|
8d816cdb0f | ||
|
|
918bd0befd | ||
|
|
bf2a08555b | ||
|
|
cf97123512 | ||
|
|
6aca70f0d1 | ||
|
|
963e319345 | ||
|
|
b21021501c | ||
|
|
638e2d87eb | ||
|
|
d94393c764 | ||
|
|
faa95a0f8c | ||
|
|
047e22571e | ||
|
|
ed95542b67 | ||
|
|
9c283112ed | ||
|
|
fbfe6590b7 | ||
|
|
2824873511 | ||
|
|
dc9ac5b58a | ||
|
|
ee69eb03d4 | ||
|
|
c040603786 | ||
|
|
275bb0b131 | ||
|
|
8b16955cc2 | ||
|
|
3753337d60 | ||
|
|
caad952bc1 | ||
|
|
6ff89b43b6 | ||
|
|
733b14dcd8 | ||
|
|
d87191f17e | ||
|
|
afd87e3caa | ||
|
|
ade3c02122 |
No files matched your search
+6
-2
@@ -14,7 +14,7 @@
|
||||
},
|
||||
"rules": {
|
||||
"strict": 0,
|
||||
"no-undef": 2,
|
||||
"no-undef": 0,
|
||||
"accessor-pairs": 2,
|
||||
"comma-dangle": [2, "always-multiline"],
|
||||
"consistent-return": 2,
|
||||
@@ -100,7 +100,9 @@
|
||||
"Attachments": true,
|
||||
"Boards": true,
|
||||
"CardComments": true,
|
||||
"DatePicker" : true,
|
||||
"Cards": true,
|
||||
"CustomFields": true,
|
||||
"Lists": true,
|
||||
"UnsavedEditCollection": true,
|
||||
"Users": true,
|
||||
@@ -119,7 +121,8 @@
|
||||
"allowIsBoardAdmin": true,
|
||||
"allowIsBoardMember": true,
|
||||
"allowIsBoardMemberByCard": true,
|
||||
"allowIsBoardMemberNonComment": true,
|
||||
"allowIsBoardMemberCommentOnly": true,
|
||||
"allowIsBoardMemberNoComments": true,
|
||||
"Emoji": true,
|
||||
"Checklists": true,
|
||||
"Settings": true,
|
||||
@@ -132,6 +135,7 @@
|
||||
"Announcements": true,
|
||||
"Swimlanes": true,
|
||||
"ChecklistItems": true,
|
||||
"Subtasks": true,
|
||||
"Npm": true
|
||||
}
|
||||
}
|
||||
+16
-11
@@ -1,23 +1,28 @@
|
||||
## Issue
|
||||
**DO NOT ADD ISSUES ABOUT DOCKER**
|
||||
|
||||
* [Docker destroys your data](https://github.com/wekan/wekan-mongodb/issues/9)
|
||||
* Docker Hub image is broken. Quay image has problems too.
|
||||
* If you would like for Docker to work, please add fix as pull request to devel branch
|
||||
Add these issues to elsewhere:
|
||||
- LDAP: https://github.com/wekan/wekan-ldap/issues
|
||||
- Snap: https://github.com/wekan/wekan-snap/issues
|
||||
|
||||
**Impacted version**:
|
||||
Other Wekan issues can be added here.
|
||||
|
||||
**Server Setup Information**:
|
||||
|
||||
* Did you test in newest Wekan?:
|
||||
* For new Wekan install, did you configure root-url 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:
|
||||
* Deployment Method(snap/sandstorm/mongodb bundle):
|
||||
* Http frontend (Caddy, Nginx, Apache, see config examples from Wekan GitHub wiki first):
|
||||
* Deployment Method(snap/docker/sandstorm/mongodb bundle/source):
|
||||
* 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):
|
||||
|
||||
**Problem description**:
|
||||
- *be as explicit as you can*
|
||||
- *describe the problem and its symptoms*
|
||||
- *explain how to reproduce*
|
||||
- *attach whatever information that can help understanding the context (screen capture, log files in .zip file)*
|
||||
- *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 logs are very long, attach them in .zip file*
|
||||
+4
-2
@@ -4,14 +4,16 @@
|
||||
*.sublime-workspace
|
||||
tmp/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
.vscode/
|
||||
.idea/
|
||||
.build/*
|
||||
packages/kadira-flow-router/
|
||||
packages/meteor-useraccounts-core/
|
||||
packages/
|
||||
package-lock.json
|
||||
**/parts/
|
||||
**/stage
|
||||
**/prime
|
||||
**/*.snap
|
||||
snap/.snapcraft/
|
||||
.idea
|
||||
.DS_Store
|
||||
+12
-4
@@ -6,7 +6,7 @@
|
||||
meteor-base@1.2.0
|
||||
|
||||
# Build system
|
||||
ecmascript@0.9.0
|
||||
ecmascript
|
||||
stylus@2.513.13
|
||||
standard-minifier-css@1.3.5
|
||||
standard-minifier-js@2.2.0
|
||||
@@ -31,6 +31,7 @@ kenton:accounts-sandstorm
|
||||
service-configuration@1.0.11
|
||||
useraccounts:unstyled
|
||||
useraccounts:flow-routing
|
||||
salleman:accounts-oidc
|
||||
|
||||
# Utilities
|
||||
check@1.2.5
|
||||
@@ -49,7 +50,6 @@ kadira:dochead
|
||||
meteorhacks:picker
|
||||
meteorhacks:subs-manager
|
||||
mquandalle:autofocus
|
||||
mquandalle:moment
|
||||
ongoworks:speakingurl
|
||||
raix:handlebar-helpers
|
||||
tap:i18n
|
||||
@@ -63,7 +63,6 @@ mousetrap:mousetrap
|
||||
mquandalle:jquery-textcomplete
|
||||
mquandalle:jquery-ui-drag-drop-sort
|
||||
mquandalle:mousetrap-bindglobal
|
||||
mquandalle:perfect-scrollbar
|
||||
peerlibrary:blaze-components@=0.15.1
|
||||
perak:markdown
|
||||
templates:tabs
|
||||
@@ -77,8 +76,17 @@ email@1.2.3
|
||||
horka:swipebox
|
||||
dynamic-import@0.2.0
|
||||
staringatlights:fast-render
|
||||
staringatlights:flow-router
|
||||
|
||||
mixmax:smart-disconnect
|
||||
accounts-password@1.5.0
|
||||
cfs:gridfs
|
||||
eluck:accounts-lockout
|
||||
rzymek:fullcalendar
|
||||
momentjs:moment@2.22.2
|
||||
browser-policy-framing
|
||||
mquandalle:moment
|
||||
msavin:usercache
|
||||
wekan:wekan-ldap
|
||||
wekan:accounts-cas
|
||||
wekan-scrollbar
|
||||
mquandalle:perfect-scrollbar
|
||||
+15
-1
@@ -1,5 +1,6 @@
|
||||
3stack:presence@1.1.2
|
||||
accounts-base@1.4.0
|
||||
accounts-oauth@1.1.15
|
||||
accounts-password@1.5.0
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
@@ -18,6 +19,8 @@ binary-heap@1.0.10
|
||||
blaze@2.3.2
|
||||
blaze-tools@1.0.10
|
||||
boilerplate-generator@1.3.1
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
caching-compiler@1.1.9
|
||||
caching-html-compiler@1.1.2
|
||||
callback-hook@1.0.10
|
||||
@@ -57,6 +60,7 @@ ecmascript-runtime@0.5.0
|
||||
ecmascript-runtime-client@0.5.0
|
||||
ecmascript-runtime-server@0.5.0
|
||||
ejson@1.1.0
|
||||
eluck:accounts-lockout@0.9.0
|
||||
email@1.2.3
|
||||
es5-shim@4.6.15
|
||||
fastclick@1.0.13
|
||||
@@ -98,6 +102,7 @@ mixmax:smart-disconnect@0.0.4
|
||||
mobile-status-bar@1.0.14
|
||||
modules@0.11.0
|
||||
modules-runtime@0.9.1
|
||||
momentjs:moment@2.22.2
|
||||
mongo@1.3.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.6
|
||||
@@ -112,8 +117,11 @@ mquandalle:jquery-ui-drag-drop-sort@0.2.0
|
||||
mquandalle:moment@1.0.1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
mquandalle:perfect-scrollbar@0.6.5_2
|
||||
msavin:usercache@1.0.0
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@2.2.33
|
||||
oauth@1.2.1
|
||||
oauth2@1.2.0
|
||||
observe-sequence@1.0.16
|
||||
ongoworks:speakingurl@1.1.0
|
||||
ordered-dict@1.0.9
|
||||
@@ -134,6 +142,9 @@ reactive-var@1.0.11
|
||||
reload@1.1.11
|
||||
retry@1.0.9
|
||||
routepolicy@1.0.12
|
||||
rzymek:fullcalendar@3.8.0
|
||||
salleman:accounts-oidc@1.0.9
|
||||
salleman:oidc@1.0.9
|
||||
service-configuration@1.0.11
|
||||
session@1.1.7
|
||||
sha@1.0.9
|
||||
@@ -150,7 +161,6 @@ srp@1.0.10
|
||||
standard-minifier-css@1.3.5
|
||||
standard-minifier-js@2.2.3
|
||||
staringatlights:fast-render@2.16.5
|
||||
staringatlights:flow-router@2.12.2
|
||||
staringatlights:inject-data@2.0.5
|
||||
stylus@2.513.13
|
||||
tap:i18n@1.8.2
|
||||
@@ -169,4 +179,8 @@ useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.4.0
|
||||
webapp-hashing@1.0.9
|
||||
wekan-scrollbar@3.1.3
|
||||
wekan:accounts-cas@0.1.0
|
||||
wekan:wekan-ldap@0.0.2
|
||||
yasaricli:slugify@0.0.7
|
||||
zimme:active-route@2.3.2
|
||||
+1455
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,4 @@
|
||||
To get started, [please sign the Contributor License Agreement](https://www.clahub.com/agreements/wekan/wekan).
|
||||
|
||||
[Then, please read documentation at wiki](https://github.com/wekan/wekan/wiki).
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
Please see wiki for all documentation:
|
||||
|
||||
<https://github.com/wekan/wekan/wiki>
|
||||
+207
-38
@@ -1,5 +1,5 @@
|
||||
FROM debian:buster-slim
|
||||
MAINTAINER wekan
|
||||
LABEL maintainer="wekan"
|
||||
|
||||
# Declare Arguments
|
||||
ARG NODE_VERSION
|
||||
@@ -10,58 +10,199 @@ ARG NPM_VERSION
|
||||
ARG FIBERS_VERSION
|
||||
ARG ARCHITECTURE
|
||||
ARG SRC_PATH
|
||||
ARG WITH_API
|
||||
ARG MATOMO_ADDRESS
|
||||
ARG MATOMO_SITE_ID
|
||||
ARG MATOMO_DO_NOT_TRACK
|
||||
ARG MATOMO_WITH_USERNAME
|
||||
ARG BROWSER_POLICY_ENABLED
|
||||
ARG TRUSTED_URL
|
||||
ARG WEBHOOKS_ATTRIBUTES
|
||||
ARG OAUTH2_ENABLED
|
||||
ARG OAUTH2_CLIENT_ID
|
||||
ARG OAUTH2_SECRET
|
||||
ARG OAUTH2_SERVER_URL
|
||||
ARG OAUTH2_AUTH_ENDPOINT
|
||||
ARG OAUTH2_USERINFO_ENDPOINT
|
||||
ARG OAUTH2_TOKEN_ENDPOINT
|
||||
ARG LDAP_ENABLE
|
||||
ARG LDAP_PORT
|
||||
ARG LDAP_HOST
|
||||
ARG LDAP_BASEDN
|
||||
ARG LDAP_LOGIN_FALLBACK
|
||||
ARG LDAP_RECONNECT
|
||||
ARG LDAP_TIMEOUT
|
||||
ARG LDAP_IDLE_TIMEOUT
|
||||
ARG LDAP_CONNECT_TIMEOUT
|
||||
ARG LDAP_AUTHENTIFICATION
|
||||
ARG LDAP_AUTHENTIFICATION_USERDN
|
||||
ARG LDAP_AUTHENTIFICATION_PASSWORD
|
||||
ARG LDAP_LOG_ENABLED
|
||||
ARG LDAP_BACKGROUND_SYNC
|
||||
ARG LDAP_BACKGROUND_SYNC_INTERVAL
|
||||
ARG LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED
|
||||
ARG LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS
|
||||
ARG LDAP_ENCRYPTION
|
||||
ARG LDAP_CA_CERT
|
||||
ARG LDAP_REJECT_UNAUTHORIZED
|
||||
ARG LDAP_USER_SEARCH_FILTER
|
||||
ARG LDAP_USER_SEARCH_SCOPE
|
||||
ARG LDAP_USER_SEARCH_FIELD
|
||||
ARG LDAP_SEARCH_PAGE_SIZE
|
||||
ARG LDAP_SEARCH_SIZE_LIMIT
|
||||
ARG LDAP_GROUP_FILTER_ENABLE
|
||||
ARG LDAP_GROUP_FILTER_OBJECTCLASS
|
||||
ARG LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE
|
||||
ARG LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE
|
||||
ARG LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT
|
||||
ARG LDAP_GROUP_FILTER_GROUP_NAME
|
||||
ARG LDAP_UNIQUE_IDENTIFIER_FIELD
|
||||
ARG LDAP_UTF8_NAMES_SLUGIFY
|
||||
ARG LDAP_USERNAME_FIELD
|
||||
ARG LDAP_FULLNAME_FIELD
|
||||
ARG LDAP_MERGE_EXISTING_USERS
|
||||
ARG LDAP_SYNC_USER_DATA
|
||||
ARG LDAP_SYNC_USER_DATA_FIELDMAP
|
||||
ARG LDAP_SYNC_GROUP_ROLES
|
||||
ARG LDAP_DEFAULT_DOMAIN
|
||||
ARG LOGOUT_WITH_TIMER
|
||||
ARG LOGOUT_IN
|
||||
ARG LOGOUT_ON_HOURS
|
||||
ARG LOGOUT_ON_MINUTES
|
||||
ARG CORS
|
||||
ARG DEFAULT_AUTHENTICATION_METHOD
|
||||
|
||||
# Set the environment variables (defaults where required)
|
||||
# paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
ENV BUILD_DEPS="apt-utils gnupg gosu wget curl bzip2 build-essential python git ca-certificates gcc-7 paxctl"
|
||||
ENV NODE_VERSION ${NODE_VERSION:-v8.9.3}
|
||||
ENV METEOR_RELEASE ${METEOR_RELEASE:-1.6.0.1}
|
||||
ENV USE_EDGE ${USE_EDGE:-false}
|
||||
ENV METEOR_EDGE ${METEOR_EDGE:-1.5-beta.17}
|
||||
ENV NPM_VERSION ${NPM_VERSION:-5.5.1}
|
||||
ENV FIBERS_VERSION ${FIBERS_VERSION:-2.0.0}
|
||||
ENV ARCHITECTURE ${ARCHITECTURE:-linux-x64}
|
||||
ENV SRC_PATH ${SRC_PATH:-./}
|
||||
# DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
# ENV BUILD_DEPS="paxctl"
|
||||
ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 build-essential python python3 python3-distutils git ca-certificates gcc-7" \
|
||||
NODE_VERSION=v8.15.0 \
|
||||
METEOR_RELEASE=1.6.0.1 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=latest \
|
||||
FIBERS_VERSION=2.0.0 \
|
||||
ARCHITECTURE=linux-x64 \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
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_CLIENT_ID="" \
|
||||
OAUTH2_SECRET="" \
|
||||
OAUTH2_SERVER_URL="" \
|
||||
OAUTH2_AUTH_ENDPOINT="" \
|
||||
OAUTH2_USERINFO_ENDPOINT="" \
|
||||
OAUTH2_TOKEN_ENDPOINT="" \
|
||||
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=100 \
|
||||
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_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_SYNC_USER_DATA=false \
|
||||
LDAP_SYNC_USER_DATA_FIELDMAP="" \
|
||||
LDAP_SYNC_GROUP_ROLES="" \
|
||||
LDAP_DEFAULT_DOMAIN="" \
|
||||
LOGOUT_WITH_TIMER=false \
|
||||
LOGOUT_IN="" \
|
||||
LOGOUT_ON_HOURS="" \
|
||||
LOGOUT_ON_MINUTES="" \
|
||||
CORS="" \
|
||||
DEFAULT_AUTHENTICATION_METHOD=""
|
||||
|
||||
# Copy the app to the image
|
||||
COPY ${SRC_PATH} /home/wekan/app
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
# Add non-root user wekan
|
||||
useradd --user-group --system --home-dir /home/wekan wekan && \
|
||||
\
|
||||
# OS dependencies
|
||||
apt-get update -y && apt-get install -y --no-install-recommends ${BUILD_DEPS} && \
|
||||
\
|
||||
# Meteor installer doesn't work with the default tar binary, so using bsdtar while installing.
|
||||
# https://github.com/coreos/bugs/issues/1095#issuecomment-350574389
|
||||
cp $(which tar) $(which tar)~ && \
|
||||
ln -sf $(which bsdtar) $(which tar) && \
|
||||
\
|
||||
# Download nodejs
|
||||
wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
|
||||
#---------------------------------------------------------------------------------------------
|
||||
# Node Fibers 100% CPU usage issue:
|
||||
# https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
|
||||
# https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
|
||||
# https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129
|
||||
# Also see beginning of wekan/server/authentication.js
|
||||
# import Fiber from "fibers";
|
||||
# Fiber.poolSize = 1e9;
|
||||
# OLD: Download node version 8.12.0 prerelease that has fix included, => Official 8.12.0 has been released
|
||||
# Description at https://releases.wekan.team/node.txt
|
||||
#wget https://releases.wekan.team/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
#echo "1ed54adb8497ad8967075a0b5d03dd5d0a502be43d4a4d84e5af489c613d7795 node-v8.12.0-linux-x64.tar.gz" >> SHASUMS256.txt.asc && \
|
||||
\
|
||||
# Verify nodejs authenticity
|
||||
grep ${NODE_VERSION}-${ARCHITECTURE}.tar.gz SHASUMS256.txt.asc | shasum -a 256 -c - && \
|
||||
export GNUPGHOME="$(mktemp -d)" && \
|
||||
\
|
||||
#export GNUPGHOME="$(mktemp -d)" && \
|
||||
#\
|
||||
# Try other key servers if ha.pool.sks-keyservers.net is unreachable
|
||||
# Code from https://github.com/chorrell/docker-node/commit/2b673e17547c34f17f24553db02beefbac98d23c
|
||||
# gpg keys listed at https://github.com/nodejs/node#release-team
|
||||
# and keys listed here from previous version of this Dockerfile
|
||||
for key in \
|
||||
9554F04D7259F04124DE6B476D5A82AC7E37093B \
|
||||
94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
|
||||
FD3A5288F042B6850C66B31F09FE44734EB7990E \
|
||||
71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
|
||||
DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
|
||||
C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
|
||||
B9AE9905FFD7803F25714661B63B535A4C206CA9 \
|
||||
; do \
|
||||
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \
|
||||
gpg --keyserver pgp.mit.edu --recv-keys "$key" || \
|
||||
gpg --keyserver keyserver.pgp.com --recv-keys "$key" ; \
|
||||
done && \
|
||||
gpg --verify SHASUMS256.txt.asc && \
|
||||
#for key in \
|
||||
#9554F04D7259F04124DE6B476D5A82AC7E37093B \
|
||||
#94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
|
||||
#FD3A5288F042B6850C66B31F09FE44734EB7990E \
|
||||
#71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
|
||||
#DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
|
||||
#C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
|
||||
#B9AE9905FFD7803F25714661B63B535A4C206CA9 \
|
||||
#; do \
|
||||
#gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \
|
||||
#gpg --keyserver pgp.mit.edu --recv-keys "$key" || \
|
||||
#gpg --keyserver keyserver.pgp.com --recv-keys "$key" ; \
|
||||
#done && \
|
||||
#gpg --verify SHASUMS256.txt.asc && \
|
||||
# Ignore socket files then delete files then delete directories
|
||||
find "$GNUPGHOME" -type f | xargs rm -f && \
|
||||
find "$GNUPGHOME" -type d | xargs rm -fR && \
|
||||
#find "$GNUPGHOME" -type f | xargs rm -f && \
|
||||
#find "$GNUPGHOME" -type d | xargs rm -fR && \
|
||||
rm -f SHASUMS256.txt.asc && \
|
||||
\
|
||||
# Install Node
|
||||
@@ -71,8 +212,8 @@ RUN \
|
||||
ln -s /opt/nodejs/bin/node /usr/bin/node && \
|
||||
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
|
||||
\
|
||||
# paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
paxctl -mC `which node` && \
|
||||
#DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
#paxctl -mC `which node` && \
|
||||
\
|
||||
# Install Node dependencies
|
||||
npm install -g npm@${NPM_VERSION} && \
|
||||
@@ -82,8 +223,11 @@ RUN \
|
||||
# Change user to wekan and install meteor
|
||||
cd /home/wekan/ && \
|
||||
chown wekan:wekan --recursive /home/wekan && \
|
||||
curl https://install.meteor.com -o /home/wekan/install_meteor.sh && \
|
||||
sed -i "s|RELEASE=.*|RELEASE=${METEOR_RELEASE}\"\"|g" ./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 && \
|
||||
echo "Starting meteor ${METEOR_RELEASE} installation... \n" && \
|
||||
chown wekan:wekan /home/wekan/install_meteor.sh && \
|
||||
\
|
||||
@@ -100,10 +244,25 @@ RUN \
|
||||
cd /home/wekan/app/packages && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/flow-router.git kadira-flow-router && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/meteor-useraccounts/core.git meteor-useraccounts-core && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/meteor-accounts-cas.git && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/wekan-ldap.git && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/wekan-scrollbar.git && \
|
||||
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \
|
||||
cd /home/wekan/.meteor && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor -- help; \
|
||||
\
|
||||
# extract the OpenAPI specification
|
||||
npm install -g api2html && \
|
||||
mkdir -p /home/wekan/python && \
|
||||
chown wekan:wekan --recursive /home/wekan/python && \
|
||||
cd /home/wekan/python && \
|
||||
gosu wekan:wekan git clone --depth 1 -b master git://github.com/Kronuz/esprima-python && \
|
||||
cd /home/wekan/python/esprima-python && \
|
||||
python3 setup.py install --record files.txt && \
|
||||
cd /home/wekan/app &&\
|
||||
mkdir -p ./public/api && \
|
||||
python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml && \
|
||||
/opt/nodejs/bin/api2html -c ./public/wekan-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 && \
|
||||
@@ -111,24 +270,34 @@ RUN \
|
||||
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 && \
|
||||
chown wekan:wekan /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
|
||||
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 && \
|
||||
#Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
|
||||
#https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
|
||||
#https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
|
||||
#cd /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 && \
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
gosu wekan:wekan npm install && \
|
||||
gosu wekan:wekan npm install bcrypt && \
|
||||
#gosu wekan:wekan npm install bcrypt && \
|
||||
mv /home/wekan/app_build/bundle /build && \
|
||||
\
|
||||
# Put back the original tar
|
||||
mv $(which tar)~ $(which tar) && \
|
||||
\
|
||||
# Cleanup
|
||||
apt-get remove --purge -y ${BUILD_DEPS} && \
|
||||
apt-get autoremove -y && \
|
||||
npm uninstall -g api2html &&\
|
||||
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
|
||||
|
||||
ENV PORT=80
|
||||
ENV PORT=8080
|
||||
EXPOSE $PORT
|
||||
USER wekan
|
||||
|
||||
CMD ["node", "/build/main.js"]
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2018 The Wekan Team
|
||||
Copyright (c) 2014-2019 The Wekan Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,66 +1,71 @@
|
||||
# Wekan
|
||||
# Wekan - Open Source kanban
|
||||
|
||||
[](https://github.com/wekan/wekan/graphs/contributors)
|
||||
[](https://quay.io/repository/wekan/wekan)
|
||||
|
||||
<!--
|
||||
[](https://hub.docker.com/r/wekanteam/wekan)
|
||||
[](https://hub.docker.com/r/wekanteam/wekan)
|
||||
[![Wekan Build Status][travis_badge]][travis_status]
|
||||
-->
|
||||
[](https://www.codacy.com/app/xet7/wekan?utm_source=github.com&utm_medium=referral&utm_content=wekan/wekan&utm_campaign=Badge_Grade)
|
||||
[](https://codeclimate.com/github/wekan/wekan)
|
||||
[](https://david-dm.org/wekan/wekan)
|
||||
[](https://www.openhub.net/p/wekan)
|
||||
|
||||
Freenode IRC #wekan
|
||||
## [Translate Wekan at Transifex](https://transifex.com/wekan/wekan)
|
||||
|
||||
[![Wekan chat][vanila_badge]][vanila_chat]
|
||||
Translations to non-English languages are accepted only at [Transifex](https://transifex.com/wekan/wekan) using webbrowser.
|
||||
New English strings of new features can be added as PRs to edge branch file wekan/i18n/en.i18n.json .
|
||||
|
||||
[Translate Wekan at Transifex][translate_wekan]
|
||||
## [Wekan feature requests and bugs](https://github.com/wekan/wekan/issues)
|
||||
|
||||
[Open Hub analysis of Wekan code](https://www.openhub.net/p/wekan)
|
||||
Please add most of your questions as GitHub issue: [Wekan feature requests and bugs](https://github.com/wekan/wekan/issues).
|
||||
It's better than at chat where details get lost when chat scrolls up.
|
||||
|
||||
Please read [FAQ](https://github.com/wekan/wekan/wiki/FAQ).
|
||||
Please don't feed the trolls and spammers that are mentioned in the FAQ :)
|
||||
## Chat
|
||||
|
||||
[![Wekan Vanila Chat][vanila_badge]][vanila_chat] - Most Wekan community and developers are here at #wekan chat channel.
|
||||
Use webbrowser to register, and after that you can also alternatively use mobile app Rocket.Chat by Rocket.Chat with
|
||||
address https://chat.vanila.io and same username and password.
|
||||
|
||||
[Wekan IRC FAQ](https://github.com/wekan/wekan/wiki/IRC-FAQ)
|
||||
|
||||
## FAQ
|
||||
|
||||
**NOTE**:
|
||||
- Please read the [FAQ](https://github.com/wekan/wekan/wiki/FAQ) first
|
||||
- Please don't feed the trolls and spammers that are mentioned in the FAQ :)
|
||||
|
||||
## About Wekan
|
||||
|
||||
Wekan is an completely [Open Source][open_source] and [Free software][free_software]
|
||||
collaborative kanban board application with MIT license.
|
||||
|
||||
Whether you’re maintaining a personal todo list, planning your holidays with
|
||||
some friends, or working in a team on your next revolutionary idea, Kanban
|
||||
boards are an unbeatable tool to keep your things organized. They give you a
|
||||
visual overview of the current state of your project, and make you productive by
|
||||
allowing you to focus on the few items that matter the most.
|
||||
Whether you’re maintaining a personal todo list, planning your holidays with some friends, or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool to keep your things organized. They give you a visual overview of the current state of your project, and make you productive by allowing you to focus on the few items that matter the most.
|
||||
|
||||
Wekan has real-time user interface. Not all features are implemented.
|
||||
Since Wekan is a free software, you don’t have to trust us with your data and can
|
||||
install Wekan on your own computer or server. In fact we encourage you to do
|
||||
that by providing one-click installation on various platforms.
|
||||
|
||||
[Features][features]
|
||||
|
||||
Wekan supports many [Platforms][platforms], and plan is to add more.
|
||||
|
||||
[Integrations][integrations]
|
||||
|
||||
[Team](https://github.com/wekan/wekan/wiki/Team)
|
||||
|
||||
You don’t have to trust us with your data and can install Wekan on your own
|
||||
computer or server. In fact we encourage you to do that by providing
|
||||
one-click installation on various platforms.
|
||||
- [Features][features]: Wekan has real-time user interface. Not all features are implemented, yet.
|
||||
- [Platforms][platforms]: Wekan supports many platforms and plan is to add more. This will be the first place to look if you want to **install** it, test out and learn more in depth.
|
||||
- [Integrations][integrations]: Current possible integrations and future plans.
|
||||
- [Team](https://github.com/wekan/wekan/wiki/Team): The people who spends their time and make Wekan into what it is right now.
|
||||
|
||||
## Roadmap
|
||||
|
||||
[Roadmap](https://github.com/wekan/wekan/wiki/Roadmap)
|
||||
|
||||
Upcoming Wekan App Development Platform will make possible
|
||||
many use cases. If you don't find your feature or integration in
|
||||
GitHub issues and [Features][features] or [Integrations][integrations]
|
||||
page at wiki, please add them.
|
||||
Upcoming Wekan App Development Platform will make possible many use cases. If you don't find your feature or integration in
|
||||
GitHub issues and [Features][features] or [Integrations][integrations] page at wiki, please add them.
|
||||
|
||||
We are very welcoming to new developers and teams to submit new pull
|
||||
requests to devel branch to make this Wekan App Development Platform possible
|
||||
faster. Please see [Developer Documentation][dev_docs] to get started.
|
||||
We also welcome sponsors for features, although we don't have any yet.
|
||||
By working directly with Wekan you get the benefit of active maintenance
|
||||
and new features added by growing Wekan developer community.
|
||||
We are very welcoming to new developers and teams to submit new pull requests to devel branch to make this Wekan App Development Platform possible faster. Please see [Developer Documentation][dev_docs] to get started.
|
||||
|
||||
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.
|
||||
|
||||
Actual work happens at [Wekan GitHub issues][wekan_issues].
|
||||
|
||||
See [Development links on Wekan
|
||||
wiki](https://github.com/wekan/wekan/wiki#Development)
|
||||
bottom of the page for more info.
|
||||
See [Development links on Wekan wiki](https://github.com/wekan/wekan/wiki#Development) bottom of the page for more info.
|
||||
|
||||
If you want to know what is going on exactly this moment, you can check out the [project page](https://github.com/wekan/wekan/projects/2).
|
||||
|
||||
## Demo
|
||||
|
||||
@@ -72,9 +77,16 @@ bottom of the page for more info.
|
||||
|
||||
[![Screenshot of Wekan][screenshot_wefork]][roadmap_wefork]
|
||||
|
||||
Since Wekan is a free software, you don’t have to trust us with your data and can
|
||||
install Wekan on your own computer or server. In fact we encourage you to do
|
||||
that by providing one-click installation on various platforms.
|
||||
## 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).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
Security is very important to us. If you discover any issue regarding security, please disclose
|
||||
the information responsibly by sending an email to security (at) wekan.team and not by
|
||||
creating a GitHub issue. We will respond swiftly to fix verifiable security issues.
|
||||
|
||||
We thank you with a place at our hall of fame page, that is
|
||||
at https://wekan.github.io/hall-of-fame . Others have just posted public GitHub issue,
|
||||
so they are not at that hall-of-fame page.
|
||||
|
||||
## How should reports be formatted?
|
||||
|
||||
```
|
||||
Name: %name
|
||||
Twitter: %twitter
|
||||
Bug type: %bugtype
|
||||
Domain: %domain
|
||||
Severity: %severity
|
||||
URL: %url
|
||||
PoC: %poc
|
||||
CVSS (optional): %cvss
|
||||
CWSS (optional): %cwss
|
||||
```
|
||||
|
||||
## Who can participate in the program
|
||||
|
||||
Anyone who reports a unique security issue in scope and does not disclose it to
|
||||
a third party before we have patched and updated may be upon their approval
|
||||
added to the Wekan Hall of Fame.
|
||||
|
||||
## Which domains are in scope?
|
||||
|
||||
No public domains, because all those are donated to Wekan Open Source project,
|
||||
and we don't have any permissions to do security scans on those donated servers
|
||||
|
||||
Please don't perform research that could impact other users. Secondly, please keep
|
||||
the reports short and succinct. If we fail to understand the logics of your bug, we will tell you.
|
||||
|
||||
You can [Install Wekan](https://github.com/wekan/wekan/releases) to your own computer
|
||||
and scan it's vulnerabilities there.
|
||||
|
||||
## About Wekan versions
|
||||
|
||||
There are only 2 versions of Wekan: Standalone Wekan, and Sandstorm Wekan.
|
||||
|
||||
### Standalone Wekan Security
|
||||
|
||||
Standalone Wekan includes all non-Sandstorm platforms. Some Standalone Wekan platforms
|
||||
like Snap and Docker have their own specific sandboxing etc features.
|
||||
|
||||
Standalone Wekan by default does not load any files from Internet, like fonts, CSS, etc.
|
||||
This also means all Standalone Wekan functionality works in offline local networks.
|
||||
Wekan is used by companies that have [thousands of users](https://github.com/wekan/wekan/wiki/AWS) and at healthcare.
|
||||
|
||||
Wekan uses xss package for input fields like cards, as you can see from
|
||||
[package.json](https://github.com/wekan/wekan/blob/devel/package.json). Other used versions can be seen from
|
||||
[Meteor versions file](https://github.com/wekan/wekan/blob/devel/.meteor/versions).
|
||||
Forms can include markdown links, html, image tags etc like you see at https://wekan.github.io .
|
||||
It's possible to add attachments to cards, and markdown/html links to files.
|
||||
|
||||
Wekan attachments are not accessible without logging in. Import from Trello works by copying
|
||||
Trello export JSON to Wekan Trello import page, and in Trello JSON file there is direct links to all publicly
|
||||
accessible Trello attachment files, that Standalone Wekan downloads directly to Wekan MongoDB database in
|
||||
[CollectionFS](https://github.com/wekan/wekan/pull/875) format. When Wekan board is exported in
|
||||
Wekan JSON format, all board attachments are included in Wekan JSON file as base64 encoded text.
|
||||
That Wekan JSON format file can be imported to Sandstorm Wekan with all the attachments, when we get
|
||||
latest Wekan version working on Sandstorm, only couple of bugs are left before that. In Sandstorm it's not
|
||||
possible yet to import from Trello with attachments, because Wekan does not implement Sandstorm-compatible
|
||||
access to outside of Wekan grain.
|
||||
|
||||
Standalone Wekan only has password auth currently, there is work in progress to add
|
||||
[oauth2](https://github.com/wekan/wekan/pull/1578), [Openid](https://github.com/wekan/wekan/issues/538),
|
||||
[LDAP](https://github.com/wekan/wekan/issues/119) etc. If you need more login security for Standalone Wekan now,
|
||||
it's possible add additional [Google Auth proxybouncer](https://github.com/wekan/wekan/wiki/Let's-Encrypt-and-Google-Auth) in front of password auth, and then use Google Authenticator for Google Auth. Standalone Wekan does have [brute force protection with eluck:accounts-lockout and browser-policy clickjacking protection](https://github.com/wekan/wekan/blob/devel/CHANGELOG.md#v080-2018-04-04-wekan-release). You can also optionally use some [WAF](https://en.wikipedia.org/wiki/Web_application_firewall)
|
||||
like for example [AWS WAF](https://aws.amazon.com/waf/).
|
||||
|
||||
[All Wekan Platforms](https://github.com/wekan/wekan/wiki/Platforms)
|
||||
|
||||
### Sandstorm Wekan Security
|
||||
|
||||
On Sandstorm platform using environment variable Standalone Wekan features like Admin Panel etc are
|
||||
turned off, because Sandstorm platform provides SSO for all apps running on Sandstorm.
|
||||
|
||||
[Sandstorm](https://sandstorm.io) is separate Open Source platform that has been
|
||||
[security audited](https://sandstorm.io/news/2017-03-02-security-review) and found bugs fixed.
|
||||
Sandstorm also has passwordless login, LDAP, SAML, Google etc auth options already.
|
||||
At Sandstorm code is read-only and signed by app maintainers, only grain content can be modified.
|
||||
Wekan at Sandstorm runs in sandboxed grain, it does not have access elsewhere without user-visible
|
||||
PowerBox request or opening randomly-generated API key URL.
|
||||
Also read [Sandstorm Security Practices](https://docs.sandstorm.io/en/latest/using/security-practices/) and
|
||||
[Sandstorm Security non-events](https://docs.sandstorm.io/en/latest/using/security-non-events/).
|
||||
For Sandstorm specific security issues you can contact [kentonv](https://github.com/kentonv) by email.
|
||||
|
||||
## What Wekan bugs are eligible?
|
||||
|
||||
Any typical web security bugs. If any of the previously mentioned is somehow problematic and
|
||||
a security issue, we'd like to know about it, and also how to fix it:
|
||||
|
||||
- Cross-site Scripting
|
||||
- Open redirect
|
||||
- Cross-site request forgery
|
||||
- File inclusion
|
||||
- Authentication bypass
|
||||
- Server-side code execution
|
||||
|
||||
## What Wekan bugs are NOT eligible?
|
||||
|
||||
Typical already known or "no impact" bugs such as:
|
||||
|
||||
- Brute force password guessign. Currently there is
|
||||
[brute force protection with eluck:accounts-lockout](https://github.com/wekan/wekan/blob/devel/CHANGELOG.md#v080-2018-04-04-wekan-release).
|
||||
- Security issues related to that Wekan uses Meteor 1.6.0.1 related packages, and upgrading to newer
|
||||
Meteor 1.6.1 is complicated process that requires lots of changes to many dependency packages.
|
||||
Upgrading [has been tried many times, spending a lot of time](https://github.com/meteor/meteor/issues/9609)
|
||||
but there still is issues. Helping with package upgrades is very welcome.
|
||||
- [Wekan API old tokens not replaced correctly](https://github.com/wekan/wekan/issues/1437)
|
||||
- Missing Cookie flags on non-session cookies or 3rd party cookies
|
||||
- Logout CSRF
|
||||
- Social engineering
|
||||
- Denial of service
|
||||
- SSL BEAST/CRIME/etc. Wekan does not have SSL built-in, it uses Caddy/Nginx/Apache etc at front.
|
||||
Integrated Caddy support is updated often.
|
||||
- Email spoofing, SPF, DMARC & DKIM. Wekan does not include email server.
|
||||
|
||||
Wekan is Open Source with MIT license, and free to use also for commercial use.
|
||||
We welcome all fixes to improve security by email to security (at) wekan.team .
|
||||
|
||||
## Bonus Points
|
||||
|
||||
If your Responsible Security Disclosure includes code for fixing security issue,
|
||||
you get bonus points, as seen on [Hall of Fame](https://wekan.github.io/hall-of-fame).
|
||||
@@ -0,0 +1,9 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v2.02.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
userScripts:
|
||||
build: stacksmith/user-scripts/build.sh
|
||||
boot: stacksmith/user-scripts/boot.sh
|
||||
run: stacksmith/user-scripts/run.sh
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Wekan",
|
||||
"description": "The open-source Trello-like kanban",
|
||||
"description": "The open-source kanban",
|
||||
"repository": "https://github.com/wekan/wekan",
|
||||
"logo": "https://raw.githubusercontent.com/wekan/wekan/master/meta/icons/wekan-150.png",
|
||||
"keywords": ["productivity", "tool", "team", "kanban"],
|
||||
|
||||
@@ -14,6 +14,9 @@ template(name="boardActivities")
|
||||
p.activity-desc
|
||||
+memberName(user=user)
|
||||
|
||||
if($eq activityType 'deleteAttachment')
|
||||
| {{{_ 'activity-delete-attach' cardLink}}}.
|
||||
|
||||
if($eq activityType 'addAttachment')
|
||||
| {{{_ 'activity-attached' attachmentLink cardLink}}}.
|
||||
|
||||
@@ -31,12 +34,28 @@ template(name="boardActivities")
|
||||
.activity-checklist(href="{{ card.absoluteUrl }}")
|
||||
+viewer
|
||||
= checklist.title
|
||||
if($eq activityType 'removeChecklist')
|
||||
| {{{_ 'activity-checklist-removed' cardLink}}}.
|
||||
|
||||
if($eq activityType 'checkedItem')
|
||||
| {{{_ 'activity-checked-item' checkItem checklist.title cardLink}}}.
|
||||
|
||||
if($eq activityType 'uncheckedItem')
|
||||
| {{{_ 'activity-unchecked-item' checkItem checklist.title cardLink}}}.
|
||||
|
||||
if($eq activityType 'checklistCompleted')
|
||||
| {{{_ 'activity-checklist-completed' checklist.title cardLink}}}.
|
||||
|
||||
if($eq activityType 'checklistUncompleted')
|
||||
| {{{_ 'activity-checklist-uncompleted' checklist.title cardLink}}}.
|
||||
|
||||
if($eq activityType 'addChecklistItem')
|
||||
| {{{_ 'activity-checklist-item-added' checklist.title cardLink}}}.
|
||||
.activity-checklist(href="{{ card.absoluteUrl }}")
|
||||
+viewer
|
||||
= checklistItem.title
|
||||
if($eq activityType 'removedChecklistItem')
|
||||
| {{{_ 'activity-checklist-item-removed' checklist.title cardLink}}}.
|
||||
|
||||
if($eq activityType 'archivedCard')
|
||||
| {{{_ 'activity-archived' cardLink}}}.
|
||||
@@ -53,6 +72,9 @@ template(name="boardActivities")
|
||||
if($eq activityType 'createCard')
|
||||
| {{{_ 'activity-added' cardLink boardLabel}}}.
|
||||
|
||||
if($eq activityType 'createCustomField')
|
||||
| {{_ 'activity-customfield-created' customField}}.
|
||||
|
||||
if($eq activityType 'createList')
|
||||
| {{_ 'activity-added' list.title boardLabel}}.
|
||||
|
||||
@@ -86,6 +108,12 @@ template(name="boardActivities")
|
||||
if($eq activityType 'restoredCard')
|
||||
| {{{_ 'activity-sent' cardLink boardLabel}}}.
|
||||
|
||||
if($eq activityType 'addedLabel')
|
||||
| {{{_ 'activity-added-label' lastLabel cardLink}}}.
|
||||
|
||||
if($eq activityType 'removedLabel')
|
||||
| {{{_ 'activity-removed-label' lastLabel cardLink}}}.
|
||||
|
||||
if($eq activityType 'unjoinMember')
|
||||
if($eq user._id member._id)
|
||||
| {{{_ 'activity-unjoined' cardLink}}}.
|
||||
@@ -116,6 +144,28 @@ template(name="cardActivities")
|
||||
| {{{_ 'activity-removed' cardLabel memberLink}}}.
|
||||
if($eq activityType 'archivedCard')
|
||||
| {{_ 'activity-archived' cardLabel}}.
|
||||
|
||||
if($eq activityType 'addedLabel')
|
||||
| {{{_ 'activity-added-label-card' lastLabel }}}.
|
||||
|
||||
if($eq activityType 'removedLabel')
|
||||
| {{{_ 'activity-removed-label-card' lastLabel }}}.
|
||||
|
||||
if($eq activityType 'removeChecklist')
|
||||
| {{{_ 'activity-checklist-removed' cardLabel}}}.
|
||||
|
||||
if($eq activityType 'checkedItem')
|
||||
| {{{_ 'activity-checked-item-card' checkItem checklist.title }}}.
|
||||
|
||||
if($eq activityType 'uncheckedItem')
|
||||
| {{{_ 'activity-unchecked-item-card' checkItem checklist.title }}}.
|
||||
|
||||
if($eq activityType 'checklistCompleted')
|
||||
| {{{_ 'activity-checklist-completed-card' checklist.title }}}.
|
||||
|
||||
if($eq activityType 'checklistUncompleted')
|
||||
| {{{_ 'activity-checklist-uncompleted-card' checklist.title }}}.
|
||||
|
||||
if($eq activityType 'restoredCard')
|
||||
| {{_ 'activity-sent' cardLabel boardLabel}}.
|
||||
if($eq activityType 'moveCard')
|
||||
@@ -124,6 +174,10 @@ template(name="cardActivities")
|
||||
| {{{_ 'activity-attached' attachmentLink cardLabel}}}.
|
||||
if attachment.isImage
|
||||
img.attachment-image-preview(src=attachment.url)
|
||||
if($eq activityType 'deleteAttachment')
|
||||
| {{{_ 'activity-delete-attach' cardLabel}}}.
|
||||
if($eq activityType 'removedChecklist')
|
||||
| {{{_ 'activity-checklist-removed' cardLabel}}}.
|
||||
if($eq activityType 'addChecklist')
|
||||
| {{{_ 'activity-checklist-added' cardLabel}}}.
|
||||
.activity-checklist
|
||||
|
||||
@@ -8,16 +8,24 @@ BlazeComponent.extendComponent({
|
||||
const sidebar = this.parentComponent(); // XXX for some reason not working
|
||||
sidebar.callFirstWith(null, 'resetNextPeak');
|
||||
this.autorun(() => {
|
||||
const mode = this.data().mode;
|
||||
let mode = this.data().mode;
|
||||
const capitalizedMode = Utils.capitalize(mode);
|
||||
const id = Session.get(`current${capitalizedMode}`);
|
||||
let thisId, searchId;
|
||||
if (mode === 'linkedcard' || mode === 'linkedboard') {
|
||||
thisId = Session.get('currentCard');
|
||||
searchId = Cards.findOne({_id: thisId}).linkedId;
|
||||
mode = mode.replace('linked', '');
|
||||
} else {
|
||||
thisId = Session.get(`current${capitalizedMode}`);
|
||||
searchId = thisId;
|
||||
}
|
||||
const limit = this.page.get() * activitiesPerPage;
|
||||
const user = Meteor.user();
|
||||
const hideSystem = user ? user.hasHiddenSystemMessages() : false;
|
||||
if (id === null)
|
||||
if (searchId === null)
|
||||
return;
|
||||
|
||||
this.subscribe('activities', mode, id, limit, hideSystem, () => {
|
||||
this.subscribe('activities', mode, searchId, limit, hideSystem, () => {
|
||||
this.loadNextPageLocked = false;
|
||||
|
||||
// If the sibear peak hasn't increased, that mean that there are no more
|
||||
@@ -42,6 +50,12 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
},
|
||||
|
||||
checkItem(){
|
||||
const checkItemId = this.currentData().checklistItemId;
|
||||
const checkItem = ChecklistItems.findOne({_id:checkItemId});
|
||||
return checkItem.title;
|
||||
},
|
||||
|
||||
boardLabel() {
|
||||
return TAPi18n.__('this-board');
|
||||
},
|
||||
@@ -58,6 +72,16 @@ BlazeComponent.extendComponent({
|
||||
}, card.title));
|
||||
},
|
||||
|
||||
lastLabel(){
|
||||
const lastLabelId = this.currentData().labelId;
|
||||
const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(lastLabelId);
|
||||
if(lastLabel.name === undefined || lastLabel.name === ''){
|
||||
return lastLabel.color;
|
||||
}else{
|
||||
return lastLabel.name;
|
||||
}
|
||||
},
|
||||
|
||||
listLabel() {
|
||||
return this.currentData().list().title;
|
||||
},
|
||||
@@ -91,6 +115,11 @@ BlazeComponent.extendComponent({
|
||||
}, attachment.name()));
|
||||
},
|
||||
|
||||
customField() {
|
||||
const customField = this.currentData().customField();
|
||||
return customField.name;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
// XXX We should use Popup.afterConfirmation here
|
||||
|
||||
@@ -21,11 +21,18 @@ BlazeComponent.extendComponent({
|
||||
'submit .js-new-comment-form'(evt) {
|
||||
const input = this.getInput();
|
||||
const text = input.val().trim();
|
||||
const card = this.currentData();
|
||||
let boardId = card.boardId;
|
||||
let cardId = card._id;
|
||||
if (card.isLinkedCard()) {
|
||||
boardId = Cards.findOne(card.linkedId).boardId;
|
||||
cardId = card.linkedId;
|
||||
}
|
||||
if (text) {
|
||||
CardComments.insert({
|
||||
text,
|
||||
boardId: this.currentData().boardId,
|
||||
cardId: this.currentData()._id,
|
||||
boardId,
|
||||
cardId,
|
||||
});
|
||||
resetCommentInput(input);
|
||||
Tracker.flush();
|
||||
|
||||
@@ -6,9 +6,17 @@ template(name="archivedBoards")
|
||||
ul.archived-lists
|
||||
each archivedBoards
|
||||
li.archived-lists-item
|
||||
button.js-restore-board
|
||||
i.fa.fa-undo
|
||||
| {{_ 'restore-board'}}
|
||||
= title
|
||||
div.board-header-btns
|
||||
button.board-header-btn.js-delete-board
|
||||
i.fa.fa-trash-o
|
||||
| {{_ 'delete-board'}}
|
||||
button.board-header-btn.js-restore-board
|
||||
i.fa.fa-undo
|
||||
| {{_ 'restore-board'}}
|
||||
= title
|
||||
else
|
||||
li.no-items-message {{_ 'no-archived-boards'}}
|
||||
|
||||
template(name="boardDeletePopup")
|
||||
p {{_ 'delete-board-confirm-popup'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
@@ -29,6 +29,17 @@ BlazeComponent.extendComponent({
|
||||
board.restore();
|
||||
Utils.goBoardId(board._id);
|
||||
},
|
||||
'click .js-delete-board': Popup.afterConfirm('boardDelete', function() {
|
||||
Popup.close();
|
||||
const isSandstorm = Meteor.settings && Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
Boards.remove(currentBoard._id);
|
||||
}
|
||||
Boards.remove(this._id);
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
}];
|
||||
},
|
||||
}).register('archivedBoards');
|
||||
@@ -25,3 +25,11 @@ template(name="boardBody")
|
||||
+swimlane(this)
|
||||
if isViewLists
|
||||
+listsGroup
|
||||
if isViewCalendar
|
||||
+calendarView
|
||||
|
||||
template(name="calendarView")
|
||||
.calendar-view.swimlane
|
||||
if currentCard
|
||||
+cardDetails(currentCard)
|
||||
+fullcalendar(calendarOptions)
|
||||
@@ -1,5 +1,5 @@
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndex } = Utils;
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
@@ -74,6 +74,9 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.js-swimlane:not(.placeholder)');
|
||||
|
||||
function userIsMember() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
}
|
||||
@@ -87,15 +90,21 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
isViewSwimlanes() {
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
const board = Boards.findOne(currentBoardId);
|
||||
return (board.view === 'board-view-swimlanes');
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return false;
|
||||
return (currentUser.profile.boardView === 'board-view-swimlanes');
|
||||
},
|
||||
|
||||
isViewLists() {
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
const board = Boards.findOne(currentBoardId);
|
||||
return (board.view === 'board-view-lists');
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return true;
|
||||
return (currentUser.profile.boardView === 'board-view-lists');
|
||||
},
|
||||
|
||||
isViewCalendar() {
|
||||
const currentUser = Meteor.user();
|
||||
if (!currentUser) return true;
|
||||
return (currentUser.profile.boardView === 'board-view-cal');
|
||||
},
|
||||
|
||||
openNewListForm() {
|
||||
@@ -107,7 +116,6 @@ BlazeComponent.extendComponent({
|
||||
.childComponents('addListForm')[0].open();
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
// XXX The board-overlay div should probably be moved to the parent
|
||||
@@ -139,4 +147,90 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
},
|
||||
|
||||
scrollTop(position = 0) {
|
||||
const swimlanes = this.$('.js-swimlanes');
|
||||
swimlanes && swimlanes.animate({
|
||||
scrollTop: position,
|
||||
});
|
||||
},
|
||||
|
||||
}).register('boardBody');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
this.autorun(function(){
|
||||
$('#calendar-view').fullCalendar('refetchEvents');
|
||||
});
|
||||
},
|
||||
calendarOptions() {
|
||||
return {
|
||||
id: 'calendar-view',
|
||||
defaultView: 'agendaDay',
|
||||
editable: true,
|
||||
timezone: 'local',
|
||||
header: {
|
||||
left: 'title today prev,next',
|
||||
center: 'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,timelineMonth timelineYear',
|
||||
right: '',
|
||||
},
|
||||
// height: 'parent', nope, doesn't work as the parent might be small
|
||||
height: 'auto',
|
||||
/* TODO: lists as resources: https://fullcalendar.io/docs/vertical-resource-view */
|
||||
navLinks: true,
|
||||
nowIndicator: true,
|
||||
businessHours: {
|
||||
// days of week. an array of zero-based day of week integers (0=Sunday)
|
||||
dow: [ 1, 2, 3, 4, 5 ], // Monday - Friday
|
||||
start: '8:00',
|
||||
end: '18:00',
|
||||
},
|
||||
locale: TAPi18n.getLanguage(),
|
||||
events(start, end, timezone, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const events = [];
|
||||
currentBoard.cardsInInterval(start.toDate(), end.toDate()).forEach(function(card){
|
||||
events.push({
|
||||
id: card._id,
|
||||
title: card.title,
|
||||
start: card.startAt,
|
||||
end: card.endAt,
|
||||
allDay: Math.abs(card.endAt.getTime() - card.startAt.getTime()) / 1000 === 24*3600,
|
||||
url: FlowRouter.url('card', {
|
||||
boardId: currentBoard._id,
|
||||
slug: currentBoard.slug,
|
||||
cardId: card._id,
|
||||
}),
|
||||
});
|
||||
});
|
||||
callback(events);
|
||||
},
|
||||
eventResize(event, delta, revertFunc) {
|
||||
let isOk = false;
|
||||
const card = Cards.findOne(event.id);
|
||||
|
||||
if (card) {
|
||||
card.setEnd(event.end.toDate());
|
||||
isOk = true;
|
||||
}
|
||||
if (!isOk) {
|
||||
revertFunc();
|
||||
}
|
||||
},
|
||||
eventDrop(event, delta, revertFunc) {
|
||||
let isOk = false;
|
||||
const card = Cards.findOne(event.id);
|
||||
if (card) {
|
||||
// TODO: add a flag for allDay events
|
||||
if (!event.allDay) {
|
||||
card.setStart(event.start.toDate());
|
||||
card.setEnd(event.end.toDate());
|
||||
isOk = true;
|
||||
}
|
||||
}
|
||||
if (!isOk) {
|
||||
revertFunc();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
}).register('calendarView');
|
||||
@@ -15,12 +15,13 @@ position()
|
||||
|
||||
.board-wrapper
|
||||
position: cover
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden
|
||||
overflow-y: hidden
|
||||
|
||||
.board-canvas
|
||||
position: cover
|
||||
transition: margin .1s
|
||||
overflow-y: auto;
|
||||
overflow-y: auto
|
||||
|
||||
&.is-sibling-sidebar-open
|
||||
margin-right: 248px
|
||||
|
||||
@@ -2,7 +2,8 @@ template(name="boardHeaderBar")
|
||||
h1.header-board-menu
|
||||
with currentBoard
|
||||
a(class="{{#if currentUser.isBoardAdmin}}js-edit-board-title{{else}}is-disabled{{/if}}")
|
||||
= title
|
||||
+viewer
|
||||
= title
|
||||
|
||||
.board-header-btns.left
|
||||
unless isMiniScreen
|
||||
@@ -71,7 +72,7 @@ template(name="boardHeaderBar")
|
||||
title="{{_ 'log-in'}}")
|
||||
i.fa.fa-sign-in
|
||||
span {{_ 'log-in'}}
|
||||
|
||||
|
||||
if isSandstorm
|
||||
if currentUser
|
||||
a.board-header-btn.js-open-archived-board
|
||||
@@ -86,6 +87,10 @@ 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'}}
|
||||
|
||||
a.board-header-btn.js-open-search-view(title="{{_ 'search'}}")
|
||||
i.fa.fa-search
|
||||
@@ -94,7 +99,7 @@ template(name="boardHeaderBar")
|
||||
a.board-header-btn.js-toggle-board-view(
|
||||
title="{{_ 'board-view'}}")
|
||||
i.fa.fa-th-large
|
||||
span {{_ currentBoard.view}}
|
||||
span {{_ currentUser.profile.boardView}}
|
||||
|
||||
if canModifyBoard
|
||||
a.board-header-btn.js-multiselection-activate(
|
||||
@@ -112,6 +117,7 @@ template(name="boardHeaderBar")
|
||||
|
||||
template(name="boardMenuPopup")
|
||||
ul.pop-over-list
|
||||
li: a.js-custom-fields {{_ 'custom-fields'}}
|
||||
li: a.js-open-archives {{_ 'archived-items'}}
|
||||
if currentUser.isBoardAdmin
|
||||
li: a.js-change-board-color {{_ 'board-change-color'}}
|
||||
@@ -128,11 +134,18 @@ template(name="boardMenuPopup")
|
||||
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
|
||||
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'}}
|
||||
|
||||
if isSandstorm
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
|
||||
li: a.js-import-board {{_ 'import-board-c'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-subtask-settings {{_ 'subtask-settings'}}
|
||||
|
||||
template(name="boardVisibilityList")
|
||||
ul.pop-over-list
|
||||
@@ -191,6 +204,56 @@ template(name="boardChangeColorPopup")
|
||||
if isSelected
|
||||
i.fa.fa-check
|
||||
|
||||
template(name="boardSubtaskSettingsPopup")
|
||||
form.board-subtask-settings
|
||||
h3 {{_ 'show-parent-in-minicard'}}
|
||||
a#prefix-with-full-path.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'prefix-with-full-path'}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if $eq presentParentTask 'prefix-with-full-path'}}is-checked{{/if}}")
|
||||
span {{_ 'prefix-with-full-path'}}
|
||||
a#prefix-with-parent.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'prefix-with-parent'}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if $eq presentParentTask 'prefix-with-parent'}}is-checked{{/if}}")
|
||||
span {{_ 'prefix-with-parent'}}
|
||||
a#subtext-with-full-path.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'subtext-with-full-path'}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if $eq presentParentTask 'subtext-with-full-path'}}is-checked{{/if}}")
|
||||
span {{_ 'subtext-with-full-path'}}
|
||||
a#subtext-with-parent.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'subtext-with-parent'}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if $eq presentParentTask 'subtext-with-parent'}}is-checked{{/if}}")
|
||||
span {{_ 'subtext-with-parent'}}
|
||||
a#no-parent.flex.js-field-show-parent-in-minicard(class="{{#if $eq presentParentTask 'no-parent'}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if $eq presentParentTask 'no-parent'}}is-checked{{/if}}")
|
||||
span {{_ 'no-parent'}}
|
||||
div
|
||||
hr
|
||||
|
||||
div.check-div
|
||||
a.flex.js-field-has-subtasks(class="{{#if allowsSubtasks}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsSubtasks}}is-checked{{/if}}")
|
||||
span {{_ 'show-subtasks-field'}}
|
||||
|
||||
label
|
||||
| {{_ 'deposit-subtasks-board'}}
|
||||
select.js-field-deposit-board(disabled="{{#unless allowsSubtasks}}disabled{{/unless}}")
|
||||
each boards
|
||||
if isBoardSelected
|
||||
option(value=_id selected="selected") {{title}}
|
||||
else
|
||||
option(value=_id) {{title}}
|
||||
if isNullBoardSelected
|
||||
option(value='null' selected="selected") {{_ 'custom-field-dropdown-none'}}
|
||||
else
|
||||
option(value='null') {{_ 'custom-field-dropdown-none'}}
|
||||
div
|
||||
hr
|
||||
|
||||
label
|
||||
| {{_ 'deposit-subtasks-list'}}
|
||||
select.js-field-deposit-list(disabled="{{#unless hasLists}}disabled{{/unless}}")
|
||||
each lists
|
||||
if isListSelected
|
||||
option(value=_id selected="selected") {{title}}
|
||||
else
|
||||
option(value=_id) {{title}}
|
||||
|
||||
template(name="createBoard")
|
||||
form
|
||||
label
|
||||
@@ -231,6 +294,11 @@ template(name="boardChangeTitlePopup")
|
||||
textarea.js-board-desc= description
|
||||
input.primary.wide(type="submit" value="{{_ 'rename'}}")
|
||||
|
||||
template(name="boardCreateRulePopup")
|
||||
p {{_ 'close-board-pop'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
|
||||
|
||||
|
||||
template(name="archiveBoardPopup")
|
||||
p {{_ 'close-board-pop'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
Template.boardMenuPopup.events({
|
||||
'click .js-rename-board': Popup.open('boardChangeTitle'),
|
||||
'click .js-custom-fields'() {
|
||||
Sidebar.setView('customFields');
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-open-archives'() {
|
||||
Sidebar.setView('archives');
|
||||
Popup.close();
|
||||
@@ -13,8 +17,15 @@ Template.boardMenuPopup.events({
|
||||
// confirm that the board was successfully archived.
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
Popup.close();
|
||||
Boards.remove(currentBoard._id);
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
|
||||
});
|
||||
|
||||
Template.boardMenuPopup.helpers({
|
||||
@@ -77,19 +88,13 @@ BlazeComponent.extendComponent({
|
||||
Modal.open('archivedBoards');
|
||||
},
|
||||
'click .js-toggle-board-view'() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (currentBoard.view === 'board-view-swimlanes') {
|
||||
Boards.update(currentBoard._id, {
|
||||
$set: {
|
||||
view: 'board-view-lists',
|
||||
},
|
||||
});
|
||||
} else if (currentBoard.view === 'board-view-lists') {
|
||||
Boards.update(currentBoard._id, {
|
||||
$set: {
|
||||
view: 'board-view-swimlanes',
|
||||
},
|
||||
});
|
||||
const currentUser = Meteor.user();
|
||||
if (currentUser.profile.boardView === 'board-view-swimlanes') {
|
||||
currentUser.setBoardView('board-view-cal');
|
||||
} else if (currentUser.profile.boardView === 'board-view-lists') {
|
||||
currentUser.setBoardView('board-view-swimlanes');
|
||||
} else if (currentUser.profile.boardView === 'board-view-cal') {
|
||||
currentUser.setBoardView('board-view-lists');
|
||||
}
|
||||
},
|
||||
'click .js-open-filter-view'() {
|
||||
@@ -103,6 +108,9 @@ 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();
|
||||
@@ -149,6 +157,102 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('boardChangeColorPopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
},
|
||||
|
||||
allowsSubtasks() {
|
||||
return this.currentBoard.allowsSubtasks;
|
||||
},
|
||||
|
||||
isBoardSelected() {
|
||||
return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
|
||||
},
|
||||
|
||||
isNullBoardSelected() {
|
||||
return (this.currentBoard.subtasksDefaultBoardId === null) || (this.currentBoard.subtasksDefaultBoardId === undefined);
|
||||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
},
|
||||
|
||||
lists() {
|
||||
return Lists.find({
|
||||
boardId: this.currentBoard._id,
|
||||
archived: false,
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
},
|
||||
|
||||
hasLists() {
|
||||
return this.lists().count() > 0;
|
||||
},
|
||||
|
||||
isListSelected() {
|
||||
return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
|
||||
},
|
||||
|
||||
presentParentTask() {
|
||||
let result = this.currentBoard.presentParentTask;
|
||||
if ((result === null) || (result === undefined)) {
|
||||
result = 'no-parent';
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-field-has-subtasks'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
|
||||
this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-has-subtasks .materialCheckBox').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-has-subtasks').toggleClass('is-checked', this.currentBoard.allowsSubtasks);
|
||||
$('.js-field-deposit-board').prop('disabled', !this.currentBoard.allowsSubtasks);
|
||||
},
|
||||
'change .js-field-deposit-board'(evt) {
|
||||
let value = evt.target.value;
|
||||
if (value === 'null') {
|
||||
value = null;
|
||||
}
|
||||
this.currentBoard.setSubtasksDefaultBoardId(value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'change .js-field-deposit-list'(evt) {
|
||||
this.currentBoard.setSubtasksDefaultListId(evt.target.value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-field-show-parent-in-minicard'(evt) {
|
||||
const value = evt.target.id || $(evt.target).parent()[0].id || $(evt.target).parent()[0].parent()[0].id;
|
||||
const options = [
|
||||
'prefix-with-full-path',
|
||||
'prefix-with-parent',
|
||||
'subtext-with-full-path',
|
||||
'subtext-with-parent',
|
||||
'no-parent'];
|
||||
options.forEach(function(element) {
|
||||
if (element !== value) {
|
||||
$(`#${element} .materialCheckBox`).toggleClass('is-checked', false);
|
||||
$(`#${element}`).toggleClass('is-checked', false);
|
||||
}
|
||||
});
|
||||
$(`#${value} .materialCheckBox`).toggleClass('is-checked', true);
|
||||
$(`#${value}`).toggleClass('is-checked', true);
|
||||
this.currentBoard.setPresentParentTask(value);
|
||||
evt.preventDefault();
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('boardSubtaskSettingsPopup');
|
||||
|
||||
const CreateBoard = BlazeComponent.extendComponent({
|
||||
template() {
|
||||
return 'createBoard';
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
.integration-form
|
||||
padding: 5px
|
||||
border-bottom: 1px solid #ccc
|
||||
|
||||
.flex
|
||||
display: -webkit-box
|
||||
display: -moz-box
|
||||
display: -webkit-flex
|
||||
display: -moz-flex
|
||||
display: -ms-flexbox
|
||||
display: flex
|
||||
|
||||
.option
|
||||
@extends .flex
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
text-decoration: none;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
@@ -1,6 +1,8 @@
|
||||
template(name="boardList")
|
||||
.wrapper
|
||||
ul.board-list.clearfix
|
||||
li.js-add-board
|
||||
a.board-list-item.label {{_ 'add-board'}}
|
||||
each boards
|
||||
li(class="{{#if isStarred}}starred{{/if}}" class=colorClass)
|
||||
if isInvited
|
||||
@@ -27,9 +29,6 @@ template(name="boardList")
|
||||
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
|
||||
|
||||
p.board-list-item-desc= description
|
||||
li.js-add-board
|
||||
a.board-list-item.label {{_ 'add-board'}}
|
||||
|
||||
|
||||
template(name="boardListHeaderBar")
|
||||
h1 {{_ 'my-boards'}}
|
||||
|
||||
@@ -44,9 +44,9 @@ $spaceBetweenTiles = 16px
|
||||
line-height: 22px
|
||||
|
||||
.board-list-item-desc
|
||||
color: rgba(255, 255, 255, .5)
|
||||
color: #fff
|
||||
display: block
|
||||
font-size: 10px
|
||||
font-size: 14px
|
||||
font-weight: 400
|
||||
line-height: 18px
|
||||
|
||||
|
||||
@@ -57,8 +57,13 @@ Template.cardAttachmentsPopup.events({
|
||||
const card = this;
|
||||
FS.Utility.eachFile(evt, (f) => {
|
||||
const file = new FS.File(f);
|
||||
file.boardId = card.boardId;
|
||||
file.cardId = card._id;
|
||||
if (card.isLinkedCard()) {
|
||||
file.boardId = Cards.findOne(card.linkedId).boardId;
|
||||
file.cardId = card.linkedId;
|
||||
} else {
|
||||
file.boardId = card.boardId;
|
||||
file.cardId = card._id;
|
||||
}
|
||||
file.userId = Meteor.userId();
|
||||
|
||||
const attachment = Attachments.insert(file);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
template(name="cardCustomFieldsPopup")
|
||||
ul.pop-over-list
|
||||
each board.customFields
|
||||
li.item(class="")
|
||||
a.name.js-select-field(href="#")
|
||||
span.full-name
|
||||
= name
|
||||
if hasCustomField
|
||||
i.fa.fa-check
|
||||
hr
|
||||
a.quiet-button.full.js-settings
|
||||
i.fa.fa-cog
|
||||
span {{_ 'settings'}}
|
||||
|
||||
template(name="cardCustomField")
|
||||
+Template.dynamic(template=getTemplate)
|
||||
|
||||
template(name="cardCustomField-text")
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-customfield-text")
|
||||
+editor(autofocus=true)
|
||||
= value
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if value
|
||||
+viewer
|
||||
= value
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
|
||||
template(name="cardCustomField-number")
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-customfield-number")
|
||||
input(type="number" value=data.value)
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if value
|
||||
= value
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
|
||||
template(name="cardCustomField-date")
|
||||
if canModifyCard
|
||||
a.js-edit-date(title="{{showTitle}}" class="{{classes}}")
|
||||
if value
|
||||
div.card-date
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
|
||||
template(name="cardCustomField-dropdown")
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-customfield-dropdown")
|
||||
select.inline
|
||||
each items
|
||||
if($eq data.value this._id)
|
||||
option(value=_id selected="selected") {{name}}
|
||||
else
|
||||
option(value=_id) {{name}}
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if value
|
||||
+viewer
|
||||
= selectedItem
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
@@ -0,0 +1,179 @@
|
||||
Template.cardCustomFieldsPopup.helpers({
|
||||
hasCustomField() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const customFieldId = this._id;
|
||||
return card.customFieldIndex(customFieldId) > -1;
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardCustomFieldsPopup.events({
|
||||
'click .js-select-field'(evt) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const customFieldId = this._id;
|
||||
card.toggleCustomField(customFieldId);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-settings'(evt) {
|
||||
EscapeActions.executeUpTo('detailsPane');
|
||||
Sidebar.setView('customFields');
|
||||
evt.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
// cardCustomField
|
||||
const CardCustomField = BlazeComponent.extendComponent({
|
||||
|
||||
getTemplate() {
|
||||
return `cardCustomField-${this.data().definition.type}`;
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
const self = this;
|
||||
self.card = Cards.findOne(Session.get('currentCard'));
|
||||
self.customFieldId = this.data()._id;
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
},
|
||||
});
|
||||
CardCustomField.register('cardCustomField');
|
||||
|
||||
// cardCustomField-text
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit .js-card-customfield-text'(evt) {
|
||||
evt.preventDefault();
|
||||
const value = this.currentComponent().getValue();
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
}];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-text');
|
||||
|
||||
// cardCustomField-number
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit .js-card-customfield-number'(evt) {
|
||||
evt.preventDefault();
|
||||
const value = parseInt(this.find('input').value, 10);
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
}];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-number');
|
||||
|
||||
// cardCustomField-date
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
self.date = ReactiveVar();
|
||||
self.now = ReactiveVar(moment());
|
||||
window.setInterval(() => {
|
||||
self.now.set(moment());
|
||||
}, 60000);
|
||||
|
||||
self.autorun(() => {
|
||||
self.date.set(moment(self.data().value));
|
||||
});
|
||||
}
|
||||
|
||||
showDate() {
|
||||
// this will start working once mquandalle:moment
|
||||
// is updated to at least moment.js 2.10.5
|
||||
// until then, the date is displayed in the "L" format
|
||||
return this.date.get().calendar(null, {
|
||||
sameElse: 'llll',
|
||||
});
|
||||
}
|
||||
|
||||
showISODate() {
|
||||
return this.date.get().toISOString();
|
||||
}
|
||||
|
||||
classes() {
|
||||
if (this.date.get().isBefore(this.now.get(), 'minute') &&
|
||||
this.now.get().isBefore(this.data().value)) {
|
||||
return 'current';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
showTitle() {
|
||||
return `${TAPi18n.__('card-start-on')} ${this.date.get().format('LLLL')}`;
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-edit-date': Popup.open('cardCustomField-date'),
|
||||
}];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-date');
|
||||
|
||||
// cardCustomField-datePopup
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
self.card = Cards.findOne(Session.get('currentCard'));
|
||||
self.customFieldId = this.data()._id;
|
||||
this.data().value && this.date.set(moment(this.data().value));
|
||||
}
|
||||
|
||||
_storeDate(date) {
|
||||
this.card.setCustomField(this.customFieldId, date);
|
||||
}
|
||||
|
||||
_deleteDate() {
|
||||
this.card.setCustomField(this.customFieldId, '');
|
||||
}
|
||||
}).register('cardCustomField-datePopup');
|
||||
|
||||
// cardCustomField-dropdown
|
||||
(class extends CardCustomField {
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this._items = this.data().definition.settings.dropdownItems;
|
||||
this.items = this._items.slice(0);
|
||||
this.items.unshift({
|
||||
_id: '',
|
||||
name: TAPi18n.__('custom-field-dropdown-none'),
|
||||
});
|
||||
}
|
||||
|
||||
selectedItem() {
|
||||
const selected = this._items.find((item) => {
|
||||
return item._id === this.data().value;
|
||||
});
|
||||
return (selected) ? selected.name : TAPi18n.__('custom-field-dropdown-unknown');
|
||||
}
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'submit .js-card-customfield-dropdown'(evt) {
|
||||
evt.preventDefault();
|
||||
const value = this.find('select').value;
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
}];
|
||||
}
|
||||
|
||||
}).register('cardCustomField-dropdown');
|
||||
@@ -1,19 +1,3 @@
|
||||
template(name="editCardDate")
|
||||
.edit-card-date
|
||||
form.edit-date
|
||||
.fields
|
||||
.left
|
||||
label(for="date") {{_ 'date'}}
|
||||
input.js-date-field#date(type="text" name="date" value=showDate placeholder=dateFormat autofocus)
|
||||
.right
|
||||
label(for="time") {{_ 'time'}}
|
||||
input.js-time-field#time(type="text" name="time" value=showTime placeholder=timeFormat)
|
||||
.js-datepicker
|
||||
if error.get
|
||||
.warning {{_ error.get}}
|
||||
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
|
||||
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
|
||||
|
||||
template(name="dateBadge")
|
||||
if canModifyCard
|
||||
a.js-edit-date.card-date(title="{{showTitle}}" class="{{classes}}")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Edit received, start, due & end dates
|
||||
const EditCardDate = BlazeComponent.extendComponent({
|
||||
BlazeComponent.extendComponent({
|
||||
template() {
|
||||
return 'editCardDate';
|
||||
},
|
||||
@@ -93,10 +93,10 @@ Template.dateBadge.helpers({
|
||||
});
|
||||
|
||||
// editCardReceivedDatePopup
|
||||
(class extends EditCardDate {
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this.data().receivedAt && this.date.set(moment(this.data().receivedAt));
|
||||
this.data().getReceived() && this.date.set(moment(this.data().getReceived()));
|
||||
}
|
||||
|
||||
_storeDate(date) {
|
||||
@@ -104,22 +104,22 @@ Template.dateBadge.helpers({
|
||||
}
|
||||
|
||||
_deleteDate() {
|
||||
this.card.unsetReceived();
|
||||
this.card.setReceived(null);
|
||||
}
|
||||
}).register('editCardReceivedDatePopup');
|
||||
|
||||
|
||||
// editCardStartDatePopup
|
||||
(class extends EditCardDate {
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this.data().startAt && this.date.set(moment(this.data().startAt));
|
||||
this.data().getStart() && this.date.set(moment(this.data().getStart()));
|
||||
}
|
||||
|
||||
onRendered() {
|
||||
super.onRendered();
|
||||
if (moment.isDate(this.card.receivedAt)) {
|
||||
this.$('.js-datepicker').datepicker('setStartDate', this.card.receivedAt);
|
||||
if (moment.isDate(this.card.getReceived())) {
|
||||
this.$('.js-datepicker').datepicker('setStartDate', this.card.getReceived());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,21 +128,21 @@ Template.dateBadge.helpers({
|
||||
}
|
||||
|
||||
_deleteDate() {
|
||||
this.card.unsetStart();
|
||||
this.card.setStart(null);
|
||||
}
|
||||
}).register('editCardStartDatePopup');
|
||||
|
||||
// editCardDueDatePopup
|
||||
(class extends EditCardDate {
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this.data().dueAt && this.date.set(moment(this.data().dueAt));
|
||||
this.data().getDue() && this.date.set(moment(this.data().getDue()));
|
||||
}
|
||||
|
||||
onRendered() {
|
||||
super.onRendered();
|
||||
if (moment.isDate(this.card.startAt)) {
|
||||
this.$('.js-datepicker').datepicker('setStartDate', this.card.startAt);
|
||||
if (moment.isDate(this.card.getStart())) {
|
||||
this.$('.js-datepicker').datepicker('setStartDate', this.card.getStart());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,21 +151,21 @@ Template.dateBadge.helpers({
|
||||
}
|
||||
|
||||
_deleteDate() {
|
||||
this.card.unsetDue();
|
||||
this.card.setDue(null);
|
||||
}
|
||||
}).register('editCardDueDatePopup');
|
||||
|
||||
// editCardEndDatePopup
|
||||
(class extends EditCardDate {
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this.data().endAt && this.date.set(moment(this.data().endAt));
|
||||
this.data().getEnd() && this.date.set(moment(this.data().getEnd()));
|
||||
}
|
||||
|
||||
onRendered() {
|
||||
super.onRendered();
|
||||
if (moment.isDate(this.card.startAt)) {
|
||||
this.$('.js-datepicker').datepicker('setStartDate', this.card.startAt);
|
||||
if (moment.isDate(this.card.getStart())) {
|
||||
this.$('.js-datepicker').datepicker('setStartDate', this.card.getStart());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ Template.dateBadge.helpers({
|
||||
}
|
||||
|
||||
_deleteDate() {
|
||||
this.card.unsetEnd();
|
||||
this.card.setEnd(null);
|
||||
}
|
||||
}).register('editCardEndDatePopup');
|
||||
|
||||
@@ -213,16 +213,23 @@ class CardReceivedDate extends CardDate {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
self.autorun(() => {
|
||||
self.date.set(moment(self.data().receivedAt));
|
||||
self.date.set(moment(self.data().getReceived()));
|
||||
});
|
||||
}
|
||||
|
||||
classes() {
|
||||
let classes = 'received-date' + ' ';
|
||||
if (this.date.get().isBefore(this.now.get(), 'minute') &&
|
||||
this.now.get().isBefore(this.data().dueAt)) {
|
||||
let classes = 'received-date ';
|
||||
const dueAt = this.data().getDue();
|
||||
const endAt = this.data().getEnd();
|
||||
const startAt = this.data().getStart();
|
||||
const theDate = this.date.get();
|
||||
// if dueAt, endAt and startAt exist & are > receivedAt, receivedAt doesn't need to be flagged
|
||||
if (((startAt) && (theDate.isAfter(dueAt))) ||
|
||||
((endAt) && (theDate.isAfter(endAt))) ||
|
||||
((dueAt) && (theDate.isAfter(dueAt))))
|
||||
classes += 'long-overdue';
|
||||
else
|
||||
classes += 'current';
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
@@ -243,16 +250,24 @@ class CardStartDate extends CardDate {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
self.autorun(() => {
|
||||
self.date.set(moment(self.data().startAt));
|
||||
self.date.set(moment(self.data().getStart()));
|
||||
});
|
||||
}
|
||||
|
||||
classes() {
|
||||
let classes = 'start-date' + ' ';
|
||||
if (this.date.get().isBefore(this.now.get(), 'minute') &&
|
||||
this.now.get().isBefore(this.data().dueAt)) {
|
||||
const dueAt = this.data().getDue();
|
||||
const endAt = this.data().getEnd();
|
||||
const theDate = this.date.get();
|
||||
const now = this.now.get();
|
||||
// if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
|
||||
if (((endAt) && (theDate.isAfter(endAt))) ||
|
||||
((dueAt) && (theDate.isAfter(dueAt))))
|
||||
classes += 'long-overdue';
|
||||
else if (theDate.isBefore(now, 'minute'))
|
||||
classes += 'almost-due';
|
||||
else
|
||||
classes += 'current';
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
@@ -273,17 +288,26 @@ class CardDueDate extends CardDate {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
self.autorun(() => {
|
||||
self.date.set(moment(self.data().dueAt));
|
||||
self.date.set(moment(self.data().getDue()));
|
||||
});
|
||||
}
|
||||
|
||||
classes() {
|
||||
let classes = 'due-date' + ' ';
|
||||
if (this.now.get().diff(this.date.get(), 'days') >= 2)
|
||||
const endAt = this.data().getEnd();
|
||||
const theDate = this.date.get();
|
||||
const now = this.now.get();
|
||||
// if the due date is after the end date, green - done early
|
||||
if ((endAt) && (theDate.isAfter(endAt)))
|
||||
classes += 'current';
|
||||
// if there is an end date, don't need to flag the due date
|
||||
else if (endAt)
|
||||
classes += '';
|
||||
else if (now.diff(theDate, 'days') >= 2)
|
||||
classes += 'long-overdue';
|
||||
else if (this.now.get().diff(this.date.get(), 'minute') >= 0)
|
||||
else if (now.diff(theDate, 'minute') >= 0)
|
||||
classes += 'due';
|
||||
else if (this.now.get().diff(this.date.get(), 'days') >= -1)
|
||||
else if (now.diff(theDate, 'days') >= -1)
|
||||
classes += 'almost-due';
|
||||
return classes;
|
||||
}
|
||||
@@ -305,17 +329,19 @@ class CardEndDate extends CardDate {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
self.autorun(() => {
|
||||
self.date.set(moment(self.data().endAt));
|
||||
self.date.set(moment(self.data().getEnd()));
|
||||
});
|
||||
}
|
||||
|
||||
classes() {
|
||||
let classes = 'end-date' + ' ';
|
||||
if (this.data.dueAt.diff(this.date.get(), 'days') >= 2)
|
||||
const dueAt = this.data().getDue();
|
||||
const theDate = this.date.get();
|
||||
if (theDate.diff(dueAt, 'days') >= 2)
|
||||
classes += 'long-overdue';
|
||||
else if (this.data.dueAt.diff(this.date.get(), 'days') >= 0)
|
||||
else if (theDate.diff(dueAt, 'days') >= 0)
|
||||
classes += 'due';
|
||||
else if (this.data.dueAt.diff(this.date.get(), 'days') >= -2)
|
||||
else if (theDate.diff(dueAt, 'days') >= -2)
|
||||
classes += 'almost-due';
|
||||
return classes;
|
||||
}
|
||||
@@ -355,4 +381,3 @@ CardEndDate.register('cardEndDate');
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}).register('minicardEndDate');
|
||||
|
||||
@@ -1,22 +1,3 @@
|
||||
.edit-card-date
|
||||
.fields
|
||||
.left
|
||||
width: 56%
|
||||
.right
|
||||
width: 38%
|
||||
.datepicker
|
||||
width: 100%
|
||||
table
|
||||
width: 100%
|
||||
border: none
|
||||
border-spacing: 0
|
||||
border-collapse: collapse
|
||||
thead
|
||||
background: none
|
||||
td, th
|
||||
box-sizing: border-box
|
||||
|
||||
|
||||
.card-date
|
||||
display: block
|
||||
border-radius: 4px
|
||||
@@ -62,12 +43,12 @@
|
||||
&.start-date
|
||||
time
|
||||
&::before
|
||||
content: "\f08b" // symbol: fa-sign-out
|
||||
content: "\f251" // symbol: fa-hourglass-start
|
||||
|
||||
&.received-date
|
||||
time
|
||||
&::before
|
||||
content: "\f251" // symbol: fa-hourglass-start
|
||||
content: "\f08b" // symbol: fa-sign-out
|
||||
|
||||
time
|
||||
&::before
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
template(name="cardDetails")
|
||||
section.card-details.js-card-details.js-perfect-scrollbar: .card-details-canvas
|
||||
.card-details-header
|
||||
.card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}')
|
||||
+inlinedForm(classNames="js-card-details-title")
|
||||
+editCardTitleForm
|
||||
else
|
||||
@@ -10,46 +10,65 @@ template(name="cardDetails")
|
||||
h2.card-details-title.js-card-title(
|
||||
class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
|
||||
+viewer
|
||||
= title
|
||||
= getTitle
|
||||
if isWatching
|
||||
i.fa.fa-eye.card-details-watch
|
||||
.card-details-path
|
||||
each parentList
|
||||
| >
|
||||
a.js-parent-card(href=linkForCard) {{title}}
|
||||
// else
|
||||
{{_ 'top-level-card'}}
|
||||
unless isSandstorm
|
||||
if isLinkedCard
|
||||
h3.linked-card-location
|
||||
+viewer
|
||||
| {{getBoardTitle}} > {{getTitle}}
|
||||
|
||||
if archived
|
||||
p.warning {{_ 'card-archived'}}
|
||||
if getArchived
|
||||
if isLinkedBoard
|
||||
unless isSandstorm
|
||||
p.warning {{_ 'board-archived'}}
|
||||
else
|
||||
p.warning {{_ 'card-archived'}}
|
||||
|
||||
.card-details-items
|
||||
.card-details-item.card-details-item-received
|
||||
h3.card-details-item-title {{_ 'card-received'}}
|
||||
if receivedAt
|
||||
if getReceived
|
||||
+cardReceivedDate
|
||||
else
|
||||
a.js-received-date {{_ 'add'}}
|
||||
if canModifyCard
|
||||
a.js-received-date {{_ 'add'}}
|
||||
|
||||
.card-details-item.card-details-item-start
|
||||
h3.card-details-item-title {{_ 'card-start'}}
|
||||
if startAt
|
||||
if getStart
|
||||
+cardStartDate
|
||||
else
|
||||
a.js-start-date {{_ 'add'}}
|
||||
if canModifyCard
|
||||
a.js-start-date {{_ 'add'}}
|
||||
|
||||
.card-details-item.card-details-item-due
|
||||
h3.card-details-item-title {{_ 'card-due'}}
|
||||
if dueAt
|
||||
if getDue
|
||||
+cardDueDate
|
||||
else
|
||||
a.js-due-date {{_ 'add'}}
|
||||
if canModifyCard
|
||||
a.js-due-date {{_ 'add'}}
|
||||
|
||||
.card-details-item.card-details-item-end
|
||||
h3.card-details-item-title {{_ 'card-end'}}
|
||||
if endAt
|
||||
if getEnd
|
||||
+cardEndDate
|
||||
else
|
||||
a.js-end-date {{_ 'add'}}
|
||||
if canModifyCard
|
||||
a.js-end-date {{_ 'add'}}
|
||||
|
||||
.card-details-items
|
||||
.card-details-item.card-details-item-members
|
||||
h3.card-details-item-title {{_ 'members'}}
|
||||
each members
|
||||
each getMembers
|
||||
+userAvatar(userId=this cardId=../_id)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
if canModifyCard
|
||||
@@ -66,9 +85,16 @@ template(name="cardDetails")
|
||||
i.fa.fa-plus
|
||||
|
||||
.card-details-items
|
||||
if spentTime
|
||||
each customFieldsWD
|
||||
.card-details-item.card-details-item-customfield
|
||||
h3.card-details-item-title
|
||||
= definition.name
|
||||
+cardCustomField
|
||||
|
||||
.card-details-items
|
||||
if getSpentTime
|
||||
.card-details-item.card-details-item-spent
|
||||
if isOvertime
|
||||
if getIsOvertime
|
||||
h3.card-details-item-title {{_ 'overtime-hours'}}
|
||||
else
|
||||
h3.card-details-item-title {{_ 'spent-time-hours'}}
|
||||
@@ -79,15 +105,15 @@ template(name="cardDetails")
|
||||
h3.card-details-item-title {{_ 'description'}}
|
||||
+inlinedCardDescription(classNames="card-description js-card-description")
|
||||
+editor(autofocus=true)
|
||||
| {{getUnsavedValue 'cardDescription' _id description}}
|
||||
| {{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 description
|
||||
if getDescription
|
||||
+viewer
|
||||
= description
|
||||
= getDescription
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
if (hasUnsavedValue 'cardDescription' _id)
|
||||
@@ -96,14 +122,51 @@ template(name="cardDetails")
|
||||
a.js-open-inlined-form {{_ 'view-it'}}
|
||||
= ' - '
|
||||
a.js-close-inlined-form {{_ 'discard'}}
|
||||
else if description
|
||||
else if getDescription
|
||||
h3.card-details-item-title {{_ 'description'}}
|
||||
+viewer
|
||||
= description
|
||||
= getDescription
|
||||
|
||||
.card-details-items
|
||||
.card-details-item.card-details-item-name
|
||||
h3.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'}}
|
||||
else if getRequestedBy
|
||||
+viewer
|
||||
= getRequestedBy
|
||||
|
||||
.card-details-item.card-details-item-name
|
||||
h3.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'}}
|
||||
else if getRequestedBy
|
||||
+viewer
|
||||
= getAssignedBy
|
||||
|
||||
hr
|
||||
+checklists(cardId = _id)
|
||||
|
||||
if currentBoard.allowsSubtasks
|
||||
hr
|
||||
+subtasks(cardId = _id)
|
||||
|
||||
hr
|
||||
h3
|
||||
i.fa.fa-paperclip
|
||||
@@ -112,28 +175,50 @@ template(name="cardDetails")
|
||||
+attachmentsGalery
|
||||
|
||||
hr
|
||||
.activity-title
|
||||
h3 {{ _ 'activity'}}
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch
|
||||
span.toggle-switch-title {{_ 'hide-system-messages'}}
|
||||
if hiddenSystemMessages
|
||||
input.toggle-switch(type="checkbox" id="toggleButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleButton")
|
||||
label.toggle-label(for="toggleButton")
|
||||
unless currentUser.isNoComments
|
||||
.activity-title
|
||||
h3 {{ _ 'activity'}}
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch
|
||||
span.toggle-switch-title {{_ 'hide-system-messages'}}
|
||||
if hiddenSystemMessages
|
||||
input.toggle-switch(type="checkbox" id="toggleButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleButton")
|
||||
label.toggle-label(for="toggleButton")
|
||||
if currentUser.isBoardMember
|
||||
+commentForm
|
||||
if isLoaded.get
|
||||
+activities(card=this mode="card")
|
||||
unless currentUser.isNoComments
|
||||
+commentForm
|
||||
unless currentUser.isNoComments
|
||||
if isLoaded.get
|
||||
if isLinkedCard
|
||||
unless isSandstorm
|
||||
+activities(card=this mode="linkedcard")
|
||||
else if isLinkedBoard
|
||||
unless isSandstorm
|
||||
+activities(card=this mode="linkedboard")
|
||||
else
|
||||
+activities(card=this mode="card")
|
||||
|
||||
template(name="editCardTitleForm")
|
||||
textarea.js-edit-card-title(rows='1' autofocus)
|
||||
= title
|
||||
= getTitle
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-title-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editCardRequesterForm")
|
||||
input.js-edit-card-requester(type='text' autofocus value=getRequestedBy)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-requester-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editCardAssignerForm")
|
||||
input.js-edit-card-assigner(type='text' autofocus value=getAssignedBy)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-assigner-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="cardDetailsActionsPopup")
|
||||
ul.pop-over-list
|
||||
li: a.js-toggle-watch-card {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}}
|
||||
@@ -143,11 +228,13 @@ template(name="cardDetailsActionsPopup")
|
||||
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 {{_ 'setCardColor-title'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-move-card-to-top {{_ 'moveCardToTop-title'}}
|
||||
@@ -162,33 +249,23 @@ template(name="cardDetailsActionsPopup")
|
||||
li: a.js-more {{_ 'cardMorePopup-title'}}
|
||||
|
||||
template(name="moveCardPopup")
|
||||
if isSandstorm
|
||||
+boardLists
|
||||
else
|
||||
+boardsAndLists
|
||||
+boardsAndLists
|
||||
|
||||
template(name="copyCardPopup")
|
||||
label(for='copy-card-title') {{_ 'title'}}:
|
||||
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
|
||||
= title
|
||||
if isSandstorm
|
||||
+boardLists
|
||||
else
|
||||
+boardsAndLists
|
||||
|
||||
= getTitle
|
||||
+boardsAndLists
|
||||
|
||||
template(name="copyChecklistToManyCardsPopup")
|
||||
label(for='copy-checklist-cards-title') {{_ 'copyChecklistToManyCardsPopup-instructions'}}:
|
||||
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
|
||||
| {{_ 'copyChecklistToManyCardsPopup-format'}}
|
||||
if isSandstorm
|
||||
+boardLists
|
||||
else
|
||||
+boardsAndLists
|
||||
+boardsAndLists
|
||||
|
||||
template(name="boardsAndLists")
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards
|
||||
select.js-select-boards(autofocus)
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
@@ -226,14 +303,51 @@ template(name="cardMorePopup")
|
||||
span {{_ 'link-card'}}
|
||||
= ' '
|
||||
i.fa.colorful(class="{{#if board.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
|
||||
input.inline-input(type="text" id="cardURL" readonly value="{{ absoluteUrl }}")
|
||||
input.inline-input(type="text" id="cardURL" readonly value="{{ absoluteUrl }}" autofocus="autofocus")
|
||||
button.js-copy-card-link-to-clipboard(class="btn") {{_ 'copy-card-link-to-clipboard'}}
|
||||
span.clearfix
|
||||
br
|
||||
h2 {{_ 'change-card-parent'}}
|
||||
label {{_ 'source-board'}}:
|
||||
select.js-field-parent-board
|
||||
each boards
|
||||
if isParentBoard
|
||||
option(value="{{_id}}" selected) {{title}}
|
||||
else
|
||||
option(value="{{_id}}") {{title}}
|
||||
if isTopLevel
|
||||
option(value="none" selected) {{_ 'custom-field-dropdown-none'}}
|
||||
else
|
||||
option(value="none") {{_ 'custom-field-dropdown-none'}}
|
||||
|
||||
label {{_ 'parent-card'}}:
|
||||
select.js-field-parent-card
|
||||
if isTopLevel
|
||||
option(value="none" selected) {{_ 'custom-field-dropdown-none'}}
|
||||
else
|
||||
each cards
|
||||
if isParentCard
|
||||
option(value="{{_id}}" selected) {{title}}
|
||||
else
|
||||
option(value="{{_id}}") {{title}}
|
||||
option(value="none") {{_ 'custom-field-dropdown-none'}}
|
||||
br
|
||||
| {{_ 'added'}}
|
||||
span.date(title=card.createdAt) {{ moment createdAt 'LLL' }}
|
||||
a.js-delete(title="{{_ 'card-delete-notice'}}") {{_ 'delete'}}
|
||||
|
||||
template(name="setCardColorPopup")
|
||||
p.quiet
|
||||
span.clearfix
|
||||
label {{_ "select-color"}}
|
||||
form.edit-label
|
||||
.palette-colors: each colors
|
||||
span.card-label.palette-color.js-palette-color(class="card-details-{{color}}")
|
||||
if(isSelected color)
|
||||
i.fa.fa-check
|
||||
button.primary.confirm.js-submit {{_ 'save'}}
|
||||
button.js-remove-color.negate.wide.right {{_ 'unset-color'}}
|
||||
|
||||
template(name="cardDeletePopup")
|
||||
p {{_ "card-delete-pop"}}
|
||||
unless archived
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndexData } = Utils;
|
||||
const { calculateIndexData, enableClickOnTouch } = Utils;
|
||||
|
||||
let cardColors;
|
||||
Meteor.startup(() => {
|
||||
cardColors = Cards.simpleSchema()._schema.color.allowedValues;
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
mixins() {
|
||||
@@ -20,9 +25,15 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
this.currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
this.isLoaded = new ReactiveVar(false);
|
||||
this.parentComponent().parentComponent().showOverlay.set(true);
|
||||
this.parentComponent().parentComponent().mouseHasEnterCardDetails = false;
|
||||
this.currentColor = new ReactiveVar(this.data().color);
|
||||
const boardBody = this.parentComponent().parentComponent();
|
||||
//in Miniview parent is Board, not BoardBody.
|
||||
if (boardBody !== null) {
|
||||
boardBody.showOverlay.set(true);
|
||||
boardBody.mouseHasEnterCardDetails = false;
|
||||
}
|
||||
this.calculateNextPeak();
|
||||
|
||||
Meteor.subscribe('unsaved-edits');
|
||||
@@ -44,7 +55,8 @@ BlazeComponent.extendComponent({
|
||||
scrollParentContainer() {
|
||||
const cardPanelWidth = 510;
|
||||
const bodyBoardComponent = this.parentComponent().parentComponent();
|
||||
|
||||
//On Mobile View Parent is Board, Not Board Body. I cant see how this funciton should work then.
|
||||
if (bodyBoardComponent === null) return;
|
||||
const $cardView = this.$(this.firstNode());
|
||||
const $cardContainer = bodyBoardComponent.$('.js-swimlanes');
|
||||
const cardContainerScroll = $cardContainer.scrollLeft();
|
||||
@@ -63,10 +75,53 @@ BlazeComponent.extendComponent({
|
||||
if (offset) {
|
||||
bodyBoardComponent.scrollLeft(cardContainerScroll + offset);
|
||||
}
|
||||
|
||||
//Scroll top
|
||||
const cardViewStartTop = $cardView.offset().top;
|
||||
const cardContainerScrollTop = $cardContainer.scrollTop();
|
||||
let topOffset = false;
|
||||
if(cardViewStartTop < 0){
|
||||
topOffset = 0;
|
||||
} else if(cardViewStartTop - cardContainerScrollTop > 100) {
|
||||
topOffset = cardViewStartTop - cardContainerScrollTop - 100;
|
||||
}
|
||||
if(topOffset !== false) {
|
||||
bodyBoardComponent.scrollTop(topOffset);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
presentParentTask() {
|
||||
let result = this.currentBoard.presentParentTask;
|
||||
if ((result === null) || (result === undefined)) {
|
||||
result = 'no-parent';
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
linkForCard() {
|
||||
const card = this.currentData();
|
||||
let result = '#';
|
||||
if (card) {
|
||||
const board = Boards.findOne(card.boardId);
|
||||
if (board) {
|
||||
result = FlowRouter.url('card', {
|
||||
boardId: card.boardId,
|
||||
slug: board.slug,
|
||||
cardId: card._id,
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
if (!Utils.isMiniScreen()) this.scrollParentContainer();
|
||||
if (!Utils.isMiniScreen()) {
|
||||
Meteor.setTimeout(() => {
|
||||
$('.card-details').mCustomScrollbar({theme:'minimal-dark', setWidth: false, setLeft: 0, scrollbarPosition: 'outside', mouseWheel: true });
|
||||
this.scrollParentContainer();
|
||||
}, 500);
|
||||
}
|
||||
const $checklistsDom = this.$('.card-checklist-items');
|
||||
|
||||
$checklistsDom.sortable({
|
||||
@@ -102,6 +157,47 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.card-checklist-items .js-checklist');
|
||||
|
||||
const $subtasksDom = this.$('.card-subtasks-items');
|
||||
|
||||
$subtasksDom.sortable({
|
||||
tolerance: 'pointer',
|
||||
helper: 'clone',
|
||||
handle: '.subtask-title',
|
||||
items: '.js-subtasks',
|
||||
placeholder: 'subtasks placeholder',
|
||||
distance: 7,
|
||||
start(evt, ui) {
|
||||
ui.placeholder.height(ui.helper.height());
|
||||
EscapeActions.executeUpTo('popup-close');
|
||||
},
|
||||
stop(evt, ui) {
|
||||
let prevChecklist = ui.item.prev('.js-subtasks').get(0);
|
||||
if (prevChecklist) {
|
||||
prevChecklist = Blaze.getData(prevChecklist).subtask;
|
||||
}
|
||||
let nextChecklist = ui.item.next('.js-subtasks').get(0);
|
||||
if (nextChecklist) {
|
||||
nextChecklist = Blaze.getData(nextChecklist).subtask;
|
||||
}
|
||||
const sortIndex = calculateIndexData(prevChecklist, nextChecklist, 1);
|
||||
|
||||
$subtasksDom.sortable('cancel');
|
||||
const subtask = Blaze.getData(ui.item.get(0)).subtask;
|
||||
|
||||
Subtasks.update(subtask._id, {
|
||||
$set: {
|
||||
subtaskSort: sortIndex.base,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.card-subtasks-items .js-subtasks');
|
||||
|
||||
function userIsMember() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember();
|
||||
}
|
||||
@@ -111,11 +207,17 @@ BlazeComponent.extendComponent({
|
||||
if ($checklistsDom.data('sortable')) {
|
||||
$checklistsDom.sortable('option', 'disabled', !userIsMember());
|
||||
}
|
||||
if ($subtasksDom.data('sortable')) {
|
||||
$subtasksDom.sortable('option', 'disabled', !userIsMember());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onDestroyed() {
|
||||
this.parentComponent().parentComponent().showOverlay.set(false);
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not board body.
|
||||
if (parentComponent === null) return;
|
||||
parentComponent.showOverlay.set(false);
|
||||
},
|
||||
|
||||
events() {
|
||||
@@ -146,6 +248,20 @@ BlazeComponent.extendComponent({
|
||||
this.data().setTitle(title);
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-assigner'(evt) {
|
||||
evt.preventDefault();
|
||||
const assigner = this.currentComponent().getValue().trim();
|
||||
if (assigner) {
|
||||
this.data().setAssignedBy(assigner);
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-requester'(evt) {
|
||||
evt.preventDefault();
|
||||
const requester = this.currentComponent().getValue().trim();
|
||||
if (requester) {
|
||||
this.data().setRequestedBy(requester);
|
||||
}
|
||||
},
|
||||
'click .js-member': Popup.open('cardMember'),
|
||||
'click .js-add-members': Popup.open('cardMembers'),
|
||||
'click .js-add-labels': Popup.open('cardLabels'),
|
||||
@@ -154,8 +270,11 @@ BlazeComponent.extendComponent({
|
||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||
'click .js-end-date': Popup.open('editCardEndDate'),
|
||||
'mouseenter .js-card-details' () {
|
||||
this.parentComponent().parentComponent().showOverlay.set(true);
|
||||
this.parentComponent().parentComponent().mouseHasEnterCardDetails = true;
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not BoardBody.
|
||||
if (parentComponent === null) return;
|
||||
parentComponent.showOverlay.set(true);
|
||||
parentComponent.mouseHasEnterCardDetails = true;
|
||||
},
|
||||
'click #toggleButton'() {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
@@ -180,7 +299,7 @@ BlazeComponent.extendComponent({
|
||||
close(isReset = false) {
|
||||
if (this.isOpen.get() && !isReset) {
|
||||
const draft = this.getValue().trim();
|
||||
if (draft !== Cards.findOne(Session.get('currentCard')).description) {
|
||||
if (draft !== Cards.findOne(Session.get('currentCard')).getDescription()) {
|
||||
UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue());
|
||||
}
|
||||
}
|
||||
@@ -215,6 +334,7 @@ Template.cardDetailsActionsPopup.events({
|
||||
'click .js-members': Popup.open('cardMembers'),
|
||||
'click .js-labels': Popup.open('cardLabels'),
|
||||
'click .js-attachments': Popup.open('cardAttachments'),
|
||||
'click .js-custom-fields': Popup.open('cardCustomFields'),
|
||||
'click .js-received-date': Popup.open('editCardReceivedDate'),
|
||||
'click .js-start-date': Popup.open('editCardStartDate'),
|
||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||
@@ -223,6 +343,7 @@ Template.cardDetailsActionsPopup.events({
|
||||
'click .js-move-card': Popup.open('moveCard'),
|
||||
'click .js-copy-card': Popup.open('copyCard'),
|
||||
'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
|
||||
'click .js-set-card-color': Popup.open('setCardColor'),
|
||||
'click .js-move-card-to-top' (evt) {
|
||||
evt.preventDefault();
|
||||
const minOrder = _.min(this.list().cards(this.swimlaneId).map((c) => c.sort));
|
||||
@@ -262,6 +383,32 @@ Template.editCardTitleForm.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardRequesterForm.onRendered(function() {
|
||||
autosize(this.$('.js-edit-card-requester'));
|
||||
});
|
||||
|
||||
Template.editCardRequesterForm.events({
|
||||
'keydown .js-edit-card-requester'(evt) {
|
||||
// If enter key was pressed, submit the data
|
||||
if (evt.keyCode === 13) {
|
||||
$('.js-submit-edit-card-requester-form').click();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.onRendered(function() {
|
||||
autosize(this.$('.js-edit-card-assigner'));
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.events({
|
||||
'keydown .js-edit-card-assigner'(evt) {
|
||||
// If enter key was pressed, submit the data
|
||||
if (evt.keyCode === 13) {
|
||||
$('.js-submit-edit-card-assigner-form').click();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.moveCardPopup.events({
|
||||
'click .js-done' () {
|
||||
// XXX We should *not* get the currentCard from the global state, but
|
||||
@@ -275,7 +422,6 @@ Template.moveCardPopup.events({
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
subManager.subscribe('board', Session.get('currentBoard'));
|
||||
@@ -312,6 +458,21 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('boardsAndLists');
|
||||
|
||||
|
||||
function cloneCheckList(_id, checklist) {
|
||||
'use strict';
|
||||
const checklistId = checklist._id;
|
||||
checklist.cardId = _id;
|
||||
checklist._id = null;
|
||||
const newChecklistId = Checklists.insert(checklist);
|
||||
ChecklistItems.find({checklistId}).forEach(function(item) {
|
||||
item._id = null;
|
||||
item.checklistId = newChecklistId;
|
||||
item.cardId = _id;
|
||||
ChecklistItems.insert(item);
|
||||
});
|
||||
}
|
||||
|
||||
Template.copyCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
@@ -340,12 +501,18 @@ Template.copyCardPopup.events({
|
||||
|
||||
// copy checklists
|
||||
let cursor = Checklists.find({cardId: oldId});
|
||||
cursor.forEach(function() {
|
||||
cloneCheckList(_id, arguments[0]);
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
cursor = Cards.find({parentId: oldId});
|
||||
cursor.forEach(function() {
|
||||
'use strict';
|
||||
const checklist = arguments[0];
|
||||
checklist.cardId = _id;
|
||||
checklist._id = null;
|
||||
Checklists.insert(checklist);
|
||||
const subtask = arguments[0];
|
||||
subtask.parentId = _id;
|
||||
subtask._id = null;
|
||||
/* const newSubtaskId = */ Cards.insert(subtask);
|
||||
});
|
||||
|
||||
// copy card comments
|
||||
@@ -363,17 +530,20 @@ Template.copyCardPopup.events({
|
||||
});
|
||||
|
||||
Template.copyChecklistToManyCardsPopup.events({
|
||||
'click .js-select-list' (evt) {
|
||||
'click .js-done' () {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const oldId = card._id;
|
||||
card._id = null;
|
||||
card.listId = this._id;
|
||||
const list = Lists.findOne(card.listId);
|
||||
card.boardId = list.boardId;
|
||||
const textarea = $(evt.currentTarget).parents('.content').find('textarea');
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
card.listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
card.swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
card.boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
const textarea = $('#copy-card-title');
|
||||
const titleEntry = textarea.val().trim();
|
||||
// insert new card to the bottom of new list
|
||||
card.sort = Lists.findOne(this._id).cards().count();
|
||||
card.sort = Lists.findOne(card.listId).cards().count();
|
||||
|
||||
if (titleEntry) {
|
||||
const titleList = JSON.parse(titleEntry);
|
||||
@@ -391,12 +561,18 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
|
||||
// copy checklists
|
||||
let cursor = Checklists.find({cardId: oldId});
|
||||
cursor.forEach(function() {
|
||||
cloneCheckList(_id, arguments[0]);
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
cursor = Cards.find({parentId: oldId});
|
||||
cursor.forEach(function() {
|
||||
'use strict';
|
||||
const checklist = arguments[0];
|
||||
checklist.cardId = _id;
|
||||
checklist._id = null;
|
||||
Checklists.insert(checklist);
|
||||
const subtask = arguments[0];
|
||||
subtask.parentId = _id;
|
||||
subtask._id = null;
|
||||
/* const newSubtaskId = */ Cards.insert(subtask);
|
||||
});
|
||||
|
||||
// copy card comments
|
||||
@@ -414,36 +590,150 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentCard = this.currentData();
|
||||
this.currentColor = new ReactiveVar(this.currentCard.color);
|
||||
},
|
||||
|
||||
Template.cardMorePopup.events({
|
||||
'click .js-copy-card-link-to-clipboard' () {
|
||||
// Clipboard code from:
|
||||
// https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
|
||||
const StringToCopyElement = document.getElementById('cardURL');
|
||||
StringToCopyElement.select();
|
||||
if (document.execCommand('copy')) {
|
||||
StringToCopyElement.blur();
|
||||
colors() {
|
||||
return cardColors.map((color) => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
return this.currentColor.get() === color;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
},
|
||||
'click .js-submit' () {
|
||||
this.currentCard.setColor(this.currentColor.get());
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-remove-color'() {
|
||||
this.currentCard.setColor(null);
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('setCardColorPopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentCard = this.currentData();
|
||||
this.parentCard = this.currentCard.parentCard();
|
||||
if (this.parentCard) {
|
||||
this.parentBoard = this.parentCard.board();
|
||||
} else {
|
||||
document.getElementById('cardURL').selectionStart = 0;
|
||||
document.getElementById('cardURL').selectionEnd = 999;
|
||||
document.execCommand('copy');
|
||||
if (window.getSelection) {
|
||||
if (window.getSelection().empty) { // Chrome
|
||||
window.getSelection().empty();
|
||||
} else if (window.getSelection().removeAllRanges) { // Firefox
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
} else if (document.selection) { // IE?
|
||||
document.selection.empty();
|
||||
}
|
||||
this.parentBoard = null;
|
||||
}
|
||||
},
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function () {
|
||||
Popup.close();
|
||||
Cards.remove(this._id);
|
||||
Utils.goBoardId(this.boardId);
|
||||
}),
|
||||
});
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return boards;
|
||||
},
|
||||
|
||||
cards() {
|
||||
if (this.parentBoard) {
|
||||
return this.parentBoard.cards();
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
isParentBoard() {
|
||||
const board = this.currentData();
|
||||
if (this.parentBoard) {
|
||||
return board._id === this.parentBoard;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
isParentCard() {
|
||||
const card = this.currentData();
|
||||
if (this.parentCard) {
|
||||
return card._id === this.parentCard;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
setParentCardId(cardId) {
|
||||
if (cardId === 'null') {
|
||||
cardId = null;
|
||||
this.parentCard = null;
|
||||
} else {
|
||||
this.parentCard = Cards.findOne(cardId);
|
||||
}
|
||||
this.currentCard.setParentId(cardId);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-copy-card-link-to-clipboard' () {
|
||||
// Clipboard code from:
|
||||
// https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
|
||||
const StringToCopyElement = document.getElementById('cardURL');
|
||||
StringToCopyElement.select();
|
||||
if (document.execCommand('copy')) {
|
||||
StringToCopyElement.blur();
|
||||
} else {
|
||||
document.getElementById('cardURL').selectionStart = 0;
|
||||
document.getElementById('cardURL').selectionEnd = 999;
|
||||
document.execCommand('copy');
|
||||
if (window.getSelection) {
|
||||
if (window.getSelection().empty) { // Chrome
|
||||
window.getSelection().empty();
|
||||
} else if (window.getSelection().removeAllRanges) { // Firefox
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
} else if (document.selection) { // IE?
|
||||
document.selection.empty();
|
||||
}
|
||||
}
|
||||
},
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function () {
|
||||
Popup.close();
|
||||
Cards.remove(this._id);
|
||||
Utils.goBoardId(this.boardId);
|
||||
}),
|
||||
'change .js-field-parent-board'(evt) {
|
||||
const selection = $(evt.currentTarget).val();
|
||||
const list = $('.js-field-parent-card');
|
||||
list.empty();
|
||||
if (selection === 'none') {
|
||||
this.parentBoard = null;
|
||||
list.prop('disabled', true);
|
||||
} else {
|
||||
this.parentBoard = Boards.findOne(selection);
|
||||
this.parentBoard.cards().forEach(function(card) {
|
||||
list.append(
|
||||
$('<option></option>').val(card._id).html(card.title)
|
||||
);
|
||||
});
|
||||
list.prop('disabled', false);
|
||||
}
|
||||
list.append(
|
||||
`<option value='none' selected='selected'>${TAPi18n.__('custom-field-dropdown-none')}</option>`
|
||||
);
|
||||
this.setParentCardId('null');
|
||||
},
|
||||
'change .js-field-parent-card'(evt) {
|
||||
const selection = $(evt.currentTarget).val();
|
||||
this.setParentCardId(selection);
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('cardMorePopup');
|
||||
|
||||
|
||||
// Close the card details pane by pressing escape
|
||||
EscapeActions.register('detailsPane',
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
@import 'nib'
|
||||
|
||||
.card-details
|
||||
padding: 0 20px
|
||||
padding: 0
|
||||
flex-shrink: 0
|
||||
flex-basis: 470px
|
||||
flex-basis: 510px
|
||||
will-change: flex-basis
|
||||
overflow: hidden
|
||||
overflow-y: scroll
|
||||
overflow-x: hidden
|
||||
background: darken(white, 3%)
|
||||
border-radius: bottom 3px
|
||||
z-index: 20 !important
|
||||
@@ -13,8 +14,16 @@
|
||||
box-shadow: 0 0 7px 0 darken(white, 30%)
|
||||
transition: flex-basis 0.1s
|
||||
|
||||
.mCustomScrollBox
|
||||
padding-left: 0
|
||||
|
||||
.ps-scrollbar-y-rail
|
||||
pointer-event: all
|
||||
position: absolute;
|
||||
|
||||
.card-details-canvas
|
||||
width: 470px
|
||||
padding-left: 20px;
|
||||
|
||||
.card-details-header
|
||||
margin: 0 -20px 5px
|
||||
@@ -46,6 +55,12 @@
|
||||
margin: 7px 0 0
|
||||
padding: 0
|
||||
|
||||
.linked-card-location
|
||||
font-style: italic
|
||||
font-size: 1em
|
||||
margin-bottom: 0
|
||||
& p
|
||||
margin-bottom: 0
|
||||
|
||||
form.inlined-form
|
||||
margin-top: 5px
|
||||
@@ -69,6 +84,7 @@
|
||||
|
||||
.card-details-items
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
margin: 15px 0
|
||||
|
||||
.card-details-item
|
||||
@@ -80,13 +96,15 @@
|
||||
&.card-details-item-received,
|
||||
&.card-details-item-start,
|
||||
&.card-details-item-due,
|
||||
&.card-details-item-end
|
||||
width: 50%
|
||||
flex-shrink: 1
|
||||
&.card-details-item-end,
|
||||
&.card-details-item-customfield,
|
||||
&.card-details-item-name
|
||||
max-width: 50%
|
||||
flex-grow: 1
|
||||
|
||||
.card-details-item-title
|
||||
font-size: 14px
|
||||
color: darken(white, 45%)
|
||||
font-size: 16px
|
||||
color: #000
|
||||
|
||||
.card-label
|
||||
padding-top: 5px
|
||||
@@ -122,3 +140,80 @@ input[type="submit"].attachment-add-link-submit
|
||||
|
||||
.card-details-menu
|
||||
margin-right: 10px
|
||||
|
||||
card-details-color(background, color...)
|
||||
background: background !important
|
||||
if color
|
||||
color: color //overwrite text for better visibility
|
||||
|
||||
.card-details-green
|
||||
card-details-color(#3cb500, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-yellow
|
||||
card-details-color(#fad900)
|
||||
|
||||
.card-details-orange
|
||||
card-details-color(#ff9f19)
|
||||
|
||||
.card-details-red
|
||||
card-details-color(#eb4646, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-purple
|
||||
card-details-color(#a632db, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-blue
|
||||
card-details-color(#0079bf, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-pink
|
||||
card-details-color(#ff78cb)
|
||||
|
||||
.card-details-sky
|
||||
card-details-color(#00c2e0, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-black
|
||||
card-details-color(#4d4d4d, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-lime
|
||||
card-details-color(#51e898)
|
||||
|
||||
.card-details-silver
|
||||
card-details-color(#c0c0c0)
|
||||
|
||||
.card-details-peachpuff
|
||||
card-details-color(#ffdab9)
|
||||
|
||||
.card-details-crimson
|
||||
card-details-color(#dc143c, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-plum
|
||||
card-details-color(#dda0dd)
|
||||
|
||||
.card-details-darkgreen
|
||||
card-details-color(#006400, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-slateblue
|
||||
card-details-color(#6a5acd, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-magenta
|
||||
card-details-color(#ff00ff, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-gold
|
||||
card-details-color(#ffd700)
|
||||
|
||||
.card-details-navy
|
||||
card-details-color(#000080, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-gray
|
||||
card-details-color(#808080, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-saddlebrown
|
||||
card-details-color(#8b4513, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-paleturquoise
|
||||
card-details-color(#afeeee)
|
||||
|
||||
.card-details-mistyrose
|
||||
card-details-color(#ffe4e1)
|
||||
|
||||
.card-details-indigo
|
||||
card-details-color(#4b0082, #ffffff) //White text for better visibility
|
||||
@@ -3,10 +3,10 @@ template(name="editCardSpentTime")
|
||||
form.edit-time
|
||||
.fields
|
||||
label(for="time") {{_ 'time'}}
|
||||
input.js-time-field#time(type="number" step="0.01" name="time" value="{{card.spentTime}}" placeholder=timeFormat autofocus)
|
||||
input.js-time-field#time(type="number" step="0.01" name="time" value="{{card.getSpentTime}}" placeholder=timeFormat autofocus)
|
||||
label(for="overtime") {{_ 'overtime'}}
|
||||
a.js-toggle-overtime
|
||||
.materialCheckBox#overtime(class="{{#if card.isOvertime}}is-checked{{/if}}" name="overtime")
|
||||
.materialCheckBox#overtime(class="{{#if getIsOvertime}}is-checked{{/if}}" name="overtime")
|
||||
|
||||
if error.get
|
||||
.warning {{_ error.get}}
|
||||
@@ -15,8 +15,8 @@ template(name="editCardSpentTime")
|
||||
|
||||
template(name="timeBadge")
|
||||
if canModifyCard
|
||||
a.js-edit-time.card-time(title="{{showTitle}}" class="{{#if isOvertime}}card-label-red{{else}}card-label-green{{/if}}")
|
||||
a.js-edit-time.card-time(title="{{showTitle}}" class="{{#if getIsOvertime}}card-label-red{{else}}card-label-green{{/if}}")
|
||||
| {{showTime}}
|
||||
else
|
||||
a.card-time(title="{{showTitle}}" class="{{#if isOvertime}}card-label-red{{else}}card-label-green{{/if}}")
|
||||
a.card-time(title="{{showTitle}}" class="{{#if getIsOvertime}}card-label-red{{else}}card-label-green{{/if}}")
|
||||
| {{showTime}}
|
||||
@@ -7,17 +7,17 @@ BlazeComponent.extendComponent({
|
||||
this.card = this.data();
|
||||
},
|
||||
toggleOvertime() {
|
||||
this.card.isOvertime = !this.card.isOvertime;
|
||||
this.card.setIsOvertime(!this.card.getIsOvertime());
|
||||
$('#overtime .materialCheckBox').toggleClass('is-checked');
|
||||
|
||||
$('#overtime').toggleClass('is-checked');
|
||||
},
|
||||
storeTime(spentTime, isOvertime) {
|
||||
this.card.setSpentTime(spentTime);
|
||||
this.card.setOvertime(isOvertime);
|
||||
this.card.setIsOvertime(isOvertime);
|
||||
},
|
||||
deleteTime() {
|
||||
this.card.unsetSpentTime();
|
||||
this.card.setSpentTime(null);
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
@@ -26,7 +26,7 @@ BlazeComponent.extendComponent({
|
||||
evt.preventDefault();
|
||||
|
||||
const spentTime = parseFloat(evt.target.time.value);
|
||||
const isOvertime = this.card.isOvertime;
|
||||
const isOvertime = this.card.getIsOvertime();
|
||||
|
||||
if (spentTime >= 0) {
|
||||
this.storeTime(spentTime, isOvertime);
|
||||
@@ -55,17 +55,14 @@ BlazeComponent.extendComponent({
|
||||
self.time = ReactiveVar();
|
||||
},
|
||||
showTitle() {
|
||||
if (this.data().isOvertime) {
|
||||
return `${TAPi18n.__('overtime')} ${this.data().spentTime} ${TAPi18n.__('hours')}`;
|
||||
if (this.data().getIsOvertime()) {
|
||||
return `${TAPi18n.__('overtime')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
} else {
|
||||
return `${TAPi18n.__('card-spent')} ${this.data().spentTime} ${TAPi18n.__('hours')}`;
|
||||
return `${TAPi18n.__('card-spent')} ${this.data().getSpentTime()} ${TAPi18n.__('hours')}`;
|
||||
}
|
||||
},
|
||||
showTime() {
|
||||
return this.data().spentTime;
|
||||
},
|
||||
isOvertime() {
|
||||
return this.data().isOvertime;
|
||||
return this.data().getSpentTime();
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
|
||||
@@ -23,11 +23,10 @@ template(name="checklistDetail")
|
||||
+editChecklistItemForm(checklist = checklist)
|
||||
else
|
||||
.checklist-title
|
||||
.checkbox.fa.fa-check-square-o
|
||||
span
|
||||
if canModifyCard
|
||||
a.js-delete-checklist.toggle-delete-checklist-dialog {{_ "delete"}}...
|
||||
|
||||
span.checklist-stat(class="{{#if checklist.isFinished}}is-finished{{/if}}") {{checklist.finishedCount}}/{{checklist.itemCount}}
|
||||
|
||||
if canModifyCard
|
||||
h2.title.js-open-inlined-form.is-editable
|
||||
+viewer
|
||||
@@ -75,7 +74,7 @@ template(name="checklistItems")
|
||||
+inlinedForm(classNames="js-edit-checklist-item" item = item checklist = checklist)
|
||||
+editChecklistItemForm(type = 'item' item = item checklist = checklist)
|
||||
else
|
||||
+itemDetail(item = item checklist = checklist)
|
||||
+checklistItemDetail(item = item checklist = checklist)
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist)
|
||||
+addChecklistItemForm
|
||||
@@ -84,7 +83,7 @@ template(name="checklistItems")
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-checklist-item'}}...
|
||||
|
||||
template(name='itemDetail')
|
||||
template(name='checklistItemDetail')
|
||||
.js-checklist-item.checklist-item
|
||||
if canModifyCard
|
||||
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { calculateIndexData } = Utils;
|
||||
const { calculateIndexData, enableClickOnTouch } = Utils;
|
||||
|
||||
function initSorting(items) {
|
||||
items.sortable({
|
||||
@@ -36,6 +36,9 @@ function initSorting(items) {
|
||||
checklistItem.move(checklistId, sortIndex.base);
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch('.js-checklist-item:not(.placeholder)');
|
||||
}
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -71,8 +74,10 @@ BlazeComponent.extendComponent({
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-add-checklist-item');
|
||||
const title = textarea.value.trim();
|
||||
const cardId = this.currentData().cardId;
|
||||
let cardId = this.currentData().cardId;
|
||||
const card = Cards.findOne(cardId);
|
||||
if (card.isLinked())
|
||||
cardId = card.linkedId;
|
||||
|
||||
if (title) {
|
||||
Checklists.insert({
|
||||
@@ -204,7 +209,7 @@ Template.checklistDeleteDialog.onDestroyed(() => {
|
||||
$cardDetails.animate( { scrollTop: this.scrollState.position });
|
||||
});
|
||||
|
||||
Template.itemDetail.helpers({
|
||||
Template.checklistItemDetail.helpers({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
},
|
||||
@@ -223,4 +228,4 @@ BlazeComponent.extendComponent({
|
||||
'click .js-checklist-item .check-box': this.toggleItem,
|
||||
}];
|
||||
},
|
||||
}).register('itemDetail');
|
||||
}).register('checklistItemDetail');
|
||||
@@ -26,7 +26,7 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
|
||||
.title
|
||||
font-size: 18px
|
||||
line-height: 30px
|
||||
line-height: 25px
|
||||
|
||||
.checklist-stat
|
||||
margin: 0 0.5em
|
||||
@@ -93,8 +93,8 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
|
||||
|
||||
.checklist-item
|
||||
margin: 0 0 0.5em 1.33em
|
||||
line-height: 25px
|
||||
margin: 0 0 0 0.1em
|
||||
line-height: 18px
|
||||
font-size: 1.1em
|
||||
margin-top: 3px
|
||||
display: flex
|
||||
@@ -114,7 +114,7 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
background-color: darken(white, 8%)
|
||||
|
||||
.check-box
|
||||
margin-top: 5px
|
||||
margin: 0.1em 0 0 0;
|
||||
&.is-checked
|
||||
border-bottom: 2px solid #3cb500
|
||||
border-right: 2px solid #3cb500
|
||||
@@ -125,6 +125,9 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
&.is-checked
|
||||
color: #8c8c8c
|
||||
font-style: italic
|
||||
& .viewer
|
||||
p
|
||||
margin-bottom: 2px
|
||||
|
||||
.js-delete-checklist-item
|
||||
margin: 0 0 0.5em 1.33em
|
||||
@@ -132,6 +135,5 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
padding: 12px 0 0 0
|
||||
|
||||
.add-checklist-item
|
||||
margin: 0 0 0.5em 1.33em
|
||||
padding-top: 0.5em
|
||||
margin: 0.2em 0 0.5em 1.33em
|
||||
display: inline-block
|
||||
@@ -3,7 +3,7 @@
|
||||
// XXX Use .board-widget-labels as a flexbox container
|
||||
.card-label
|
||||
border-radius: 4px
|
||||
color: white
|
||||
color: white //Default white text, in select cases, changed to black to improve contrast between label colour and text
|
||||
display: inline-block
|
||||
font-weight: 700
|
||||
font-size: 13px
|
||||
@@ -48,9 +48,11 @@
|
||||
|
||||
.card-label-yellow
|
||||
background-color: #fad900
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-orange
|
||||
background-color: #ff9f19
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-red
|
||||
background-color: #eb4646
|
||||
@@ -63,6 +65,7 @@
|
||||
|
||||
.card-label-pink
|
||||
background-color: #ff78cb
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-sky
|
||||
background-color: #00c2e0
|
||||
@@ -72,6 +75,55 @@
|
||||
|
||||
.card-label-lime
|
||||
background-color: #51e898
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-silver
|
||||
background-color: #c0c0c0
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-peachpuff
|
||||
background-color: #ffdab9
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-crimson
|
||||
background-color: #dc143c
|
||||
|
||||
.card-label-plum
|
||||
background-color: #dda0dd
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-darkgreen
|
||||
background-color: #006400
|
||||
|
||||
.card-label-slateblue
|
||||
background-color: #6a5acd
|
||||
|
||||
.card-label-magenta
|
||||
background-color: #ff00ff
|
||||
|
||||
.card-label-gold
|
||||
background-color: #ffd700
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-navy
|
||||
background-color: #000080
|
||||
|
||||
.card-label-gray
|
||||
background-color: #808080
|
||||
|
||||
.card-label-saddlebrown
|
||||
background-color: #8b4513
|
||||
|
||||
.card-label-paleturquoise
|
||||
background-color: #afeeee
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-mistyrose
|
||||
background-color: #ffe4e1
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-indigo
|
||||
background-color: #4b0082
|
||||
|
||||
.edit-label,
|
||||
.create-label
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
template(name="minicard")
|
||||
.minicard
|
||||
.minicard(
|
||||
class="{{#if isLinkedCard}}linked-card{{/if}}"
|
||||
class="{{#if isLinkedBoard}}linked-board{{/if}}"
|
||||
class="minicard-{{colorClass}}")
|
||||
if cover
|
||||
.minicard-cover(style="background-image: url('{{cover.url}}');")
|
||||
if labels
|
||||
@@ -7,30 +10,72 @@ template(name="minicard")
|
||||
each labels
|
||||
.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 ' > ' }}
|
||||
if $eq 'prefix-with-parent' currentBoard.presentParentTask
|
||||
.parent-prefix
|
||||
| {{ parentCardName }}
|
||||
if isLinkedBoard
|
||||
a.js-linked-link
|
||||
span.linked-icon.fa.fa-folder
|
||||
else if isLinkedCard
|
||||
a.js-linked-link
|
||||
span.linked-icon.fa.fa-id-card
|
||||
if getArchived
|
||||
span.linked-icon.linked-archived.fa.fa-archive
|
||||
+viewer
|
||||
= title
|
||||
= getTitle
|
||||
if $eq 'subtext-with-full-path' currentBoard.presentParentTask
|
||||
.parent-subtext
|
||||
| {{ parentString ' > ' }}
|
||||
if $eq 'subtext-with-parent' currentBoard.presentParentTask
|
||||
.parent-subtext
|
||||
| {{ parentCardName }}
|
||||
|
||||
.dates
|
||||
if startAt
|
||||
if getReceived
|
||||
unless getStart
|
||||
unless getDue
|
||||
unless getEnd
|
||||
.date
|
||||
+minicardReceivedDate
|
||||
if getStart
|
||||
.date
|
||||
+minicardStartDate
|
||||
if dueAt
|
||||
if getDue
|
||||
.date
|
||||
+minicardDueDate
|
||||
if spentTime
|
||||
if getSpentTime
|
||||
.date
|
||||
+cardSpentTime
|
||||
|
||||
if members
|
||||
.minicard-custom-fields
|
||||
each customFieldsWD
|
||||
if definition.showOnCard
|
||||
.minicard-custom-field
|
||||
if definition.showLabelOnMiniCard
|
||||
.minicard-custom-field-item
|
||||
= definition.name
|
||||
.minicard-custom-field-item
|
||||
+viewer
|
||||
= trueValue
|
||||
|
||||
if getMembers
|
||||
.minicard-members.js-minicard-members
|
||||
each members
|
||||
each getMembers
|
||||
+userAvatar(userId=this)
|
||||
|
||||
.badges
|
||||
if comments.count
|
||||
.badge(title="{{_ 'card-comments-title' comments.count }}")
|
||||
span.badge-icon.fa.fa-comment-o.badge-comment
|
||||
span.badge-text= comments.count
|
||||
if description
|
||||
.badge.badge-state-image-only(title=description)
|
||||
unless currentUser.isNoComments
|
||||
if comments.count
|
||||
.badge(title="{{_ 'card-comments-title' comments.count }}")
|
||||
span.badge-icon.fa.fa-comment-o.badge-comment
|
||||
span.badge-text= comments.count
|
||||
if getDescription
|
||||
.badge.badge-state-image-only(title=getDescription)
|
||||
span.badge-icon.fa.fa-align-left
|
||||
if attachments.count
|
||||
.badge
|
||||
|
||||
@@ -3,7 +3,22 @@
|
||||
// });
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentColor = new ReactiveVar(this.data().color);
|
||||
},
|
||||
|
||||
template() {
|
||||
return 'minicard';
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-linked-link' () {
|
||||
if (this.data().isLinkedCard())
|
||||
Utils.goCardId(this.data().linkedId);
|
||||
else if (this.data().isLinkedBoard())
|
||||
Utils.goBoardId(this.data().linkedId);
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('minicard');
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
&.placeholder
|
||||
background: darken(white, 20%)
|
||||
border-radius: 2px
|
||||
border-radius: 9px
|
||||
|
||||
&.ui-sortable-helper
|
||||
cursor: grabbing
|
||||
@@ -44,6 +44,16 @@
|
||||
transition: transform 0.2s,
|
||||
border-radius 0.2s
|
||||
|
||||
&.linked-board
|
||||
&.linked-card
|
||||
.linked-icon
|
||||
display: inline-block
|
||||
margin-right: 11px
|
||||
vertical-align: baseline
|
||||
font-size: 0.9em
|
||||
.linked-archived
|
||||
color: #937760
|
||||
|
||||
.is-selected &
|
||||
transform: translateX(11px)
|
||||
border-bottom-right-radius: 0
|
||||
@@ -77,9 +87,31 @@
|
||||
height: @width
|
||||
border-radius: 2px
|
||||
margin-left: 3px
|
||||
.minicard-custom-fields
|
||||
display:block;
|
||||
.minicard-custom-field
|
||||
display:flex;
|
||||
.minicard-custom-field-item
|
||||
max-width:50%;
|
||||
flex-grow:1;
|
||||
.handle
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
display:none;
|
||||
@media only screen and (max-width: 1199px) {
|
||||
display:block;
|
||||
}
|
||||
.fa-arrows
|
||||
font-size:20px;
|
||||
color: #ccc;
|
||||
.minicard-title
|
||||
p:last-child
|
||||
margin-bottom: 0
|
||||
.viewer
|
||||
display: inline-block
|
||||
.dates
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -155,6 +187,13 @@
|
||||
margin-bottom: 20px
|
||||
overflow-y: auto
|
||||
|
||||
.parent-prefix
|
||||
color: darken(white, 30%)
|
||||
font-size: 0.9em
|
||||
.parent-subtext
|
||||
color: darken(white, 30%)
|
||||
font-size: 0.9em
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.minicard
|
||||
.is-selected &
|
||||
@@ -163,3 +202,86 @@
|
||||
border-top-right-radius: 0
|
||||
z-index: 15
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.15)
|
||||
|
||||
minicard-color(background, color...)
|
||||
background-color: background
|
||||
if color
|
||||
color: color //overwrite text for better visibility
|
||||
&:hover:not(.minicard-composer),
|
||||
.is-selected &,
|
||||
.draggable-hover-card &
|
||||
background: darken(background, 3%)
|
||||
.draggable-hover-card &
|
||||
background: darken(background, 7%)
|
||||
|
||||
.minicard-green
|
||||
minicard-color(#3cb500, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-yellow
|
||||
minicard-color(#fad900)
|
||||
|
||||
.minicard-orange
|
||||
minicard-color(#ff9f19)
|
||||
|
||||
.minicard-red
|
||||
minicard-color(#eb4646, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-purple
|
||||
minicard-color(#a632db, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-blue
|
||||
minicard-color(#0079bf, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-pink
|
||||
minicard-color(#ff78cb)
|
||||
|
||||
.minicard-sky
|
||||
minicard-color(#00c2e0, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-black
|
||||
minicard-color(#4d4d4d, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-lime
|
||||
minicard-color(#51e898)
|
||||
|
||||
.minicard-silver
|
||||
minicard-color(#c0c0c0)
|
||||
|
||||
.minicard-peachpuff
|
||||
minicard-color(#ffdab9)
|
||||
|
||||
.minicard-crimson
|
||||
minicard-color(#dc143c, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-plum
|
||||
minicard-color(#dda0dd)
|
||||
|
||||
.minicard-darkgreen
|
||||
minicard-color(#006400, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-slateblue
|
||||
minicard-color(#6a5acd, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-magenta
|
||||
minicard-color(#ff00ff, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-gold
|
||||
minicard-color(#ffd700)
|
||||
|
||||
.minicard-navy
|
||||
minicard-color(#000080, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-gray
|
||||
minicard-color(#808080, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-saddlebrown
|
||||
minicard-color(#8b4513, #ffffff) //White text for better visibility
|
||||
|
||||
.minicard-paleturquoise
|
||||
minicard-color(#afeeee)
|
||||
|
||||
.minicard-mistyrose
|
||||
minicard-color(#ffe4e1)
|
||||
|
||||
.minicard-indigo
|
||||
minicard-color(#4b0082, #ffffff) //White text for better visibility
|
||||
@@ -0,0 +1,97 @@
|
||||
template(name="subtasks")
|
||||
h3 {{_ 'subtasks'}}
|
||||
if toggleDeleteDialog.get
|
||||
.board-overlay#card-details-overlay
|
||||
+subtaskDeleteDialog(subtask = subtaskToDelete)
|
||||
|
||||
|
||||
.card-subtasks-items
|
||||
each subtask in currentCard.subtasks
|
||||
+subtaskDetail(subtask = subtask)
|
||||
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-subtask" cardId = cardId)
|
||||
+addSubtaskItemForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-subtask'}}...
|
||||
|
||||
template(name="subtaskDetail")
|
||||
.js-subtasks.subtask
|
||||
+inlinedForm(classNames="js-edit-subtask-title" subtask = subtask)
|
||||
+editSubtaskItemForm(subtask = subtask)
|
||||
else
|
||||
.subtask-title
|
||||
span
|
||||
a.js-view-subtask(title="{{ subtask.title }}") {{_ "view-it"}}
|
||||
if canModifyCard
|
||||
a.js-delete-subtask.toggle-delete-subtask-dialog {{_ "delete"}}...
|
||||
|
||||
if canModifyCard
|
||||
h2.title.js-open-inlined-form.is-editable
|
||||
+viewer
|
||||
= subtask.title
|
||||
else
|
||||
h2.title
|
||||
+viewer
|
||||
= subtask.title
|
||||
|
||||
template(name="subtaskDeleteDialog")
|
||||
.js-confirm-subtask-delete
|
||||
p
|
||||
i(class="fa fa-exclamation-triangle" aria-hidden="true")
|
||||
p
|
||||
| {{_ 'confirm-subtask-delete-dialog'}}
|
||||
span {{subtask.title}}
|
||||
| ?
|
||||
.js-subtask-delete-buttons
|
||||
button.confirm-subtask-delete(type="button") {{_ 'delete'}}
|
||||
button.toggle-delete-subtask-dialog(type="button") {{_ 'cancel'}}
|
||||
|
||||
template(name="addSubtaskItemForm")
|
||||
textarea.js-add-subtask-item(rows='1' autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-add-subtask-item-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editSubtaskItemForm")
|
||||
textarea.js-edit-subtask-item(rows='1' autofocus)
|
||||
if $eq type 'item'
|
||||
= item.title
|
||||
else
|
||||
= subtask.title
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-subtask-item-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
span(title=createdAt) {{ moment createdAt }}
|
||||
if canModifyCard
|
||||
a.js-delete-subtask-item {{_ "delete"}}...
|
||||
|
||||
template(name="subtasksItems")
|
||||
.subtasks-items.js-subtasks-items
|
||||
each item in subtasks.items
|
||||
+inlinedForm(classNames="js-edit-subtask-item" item = item subtasks = subtasks)
|
||||
+editSubtaskItemForm(type = 'item' item = item subtasks = subtasks)
|
||||
else
|
||||
+subtaskItemDetail(item = item subtasks = subtasks)
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-subtask-item" subtasks = subtasks)
|
||||
+addSubtaskItemForm
|
||||
else
|
||||
a.add-subtask-item.js-open-inlined-form
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-subtask-item'}}...
|
||||
|
||||
template(name='subtaskItemDetail')
|
||||
.js-subtasks-item.subtasks-item
|
||||
if canModifyCard
|
||||
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
.item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
+viewer
|
||||
= item.title
|
||||
else
|
||||
.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
.item-title(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
+viewer
|
||||
= item.title
|
||||
@@ -0,0 +1,146 @@
|
||||
BlazeComponent.extendComponent({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
},
|
||||
}).register('subtaskDetail');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
||||
addSubtask(event) {
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-add-subtask-item');
|
||||
const title = textarea.value.trim();
|
||||
const cardId = this.currentData().cardId;
|
||||
const card = Cards.findOne(cardId);
|
||||
const sortIndex = -1;
|
||||
const crtBoard = Boards.findOne(card.boardId);
|
||||
const targetBoard = crtBoard.getDefaultSubtasksBoard();
|
||||
const listId = targetBoard.getDefaultSubtasksListId();
|
||||
const swimlaneId = targetBoard.getDefaultSwimline()._id;
|
||||
|
||||
if (title) {
|
||||
const _id = Cards.insert({
|
||||
title,
|
||||
parentId: cardId,
|
||||
members: [],
|
||||
labelIds: [],
|
||||
customFields: [],
|
||||
listId,
|
||||
boardId: targetBoard._id,
|
||||
sort: sortIndex,
|
||||
swimlaneId,
|
||||
type: 'cardType-card',
|
||||
});
|
||||
|
||||
// In case the filter is active we need to add the newly inserted card in
|
||||
// the list of exceptions -- cards that are not filtered. Otherwise the
|
||||
// card will disappear instantly.
|
||||
// See https://github.com/wekan/wekan/issues/80
|
||||
Filter.addException(_id);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
this.$('.add-subtask-item').last().click();
|
||||
}, 100);
|
||||
}
|
||||
textarea.value = '';
|
||||
textarea.focus();
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
},
|
||||
|
||||
deleteSubtask() {
|
||||
const subtask = this.currentData().subtask;
|
||||
if (subtask && subtask._id) {
|
||||
subtask.archive();
|
||||
this.toggleDeleteDialog.set(false);
|
||||
}
|
||||
},
|
||||
|
||||
editSubtask(event) {
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-edit-subtask-item');
|
||||
const title = textarea.value.trim();
|
||||
const subtask = this.currentData().subtask;
|
||||
subtask.setTitle(title);
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
this.toggleDeleteDialog = new ReactiveVar(false);
|
||||
this.subtaskToDelete = null; //Store data context to pass to subtaskDeleteDialog template
|
||||
},
|
||||
|
||||
pressKey(event) {
|
||||
//If user press enter key inside a form, submit it
|
||||
//Unless the user is also holding down the 'shift' key
|
||||
if (event.keyCode === 13 && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
const $form = $(event.currentTarget).closest('form');
|
||||
$form.find('button[type=submit]').click();
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
const events = {
|
||||
'click .toggle-delete-subtask-dialog'(event) {
|
||||
if($(event.target).hasClass('js-delete-subtask')){
|
||||
this.subtaskToDelete = this.currentData().subtask; //Store data context
|
||||
}
|
||||
this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
|
||||
},
|
||||
'click .js-view-subtask'(event) {
|
||||
if($(event.target).hasClass('js-view-subtask')){
|
||||
const subtask = this.currentData().subtask;
|
||||
const board = subtask.board();
|
||||
FlowRouter.go('card', {
|
||||
boardId: board._id,
|
||||
slug: board.slug,
|
||||
cardId: subtask._id,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return [{
|
||||
...events,
|
||||
'submit .js-add-subtask': this.addSubtask,
|
||||
'submit .js-edit-subtask-title': this.editSubtask,
|
||||
'click .confirm-subtask-delete': this.deleteSubtask,
|
||||
keydown: this.pressKey,
|
||||
}];
|
||||
},
|
||||
}).register('subtasks');
|
||||
|
||||
Template.subtaskDeleteDialog.onCreated(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
this.scrollState = { position: $cardDetails.scrollTop(), //save current scroll position
|
||||
top: false, //required for smooth scroll animation
|
||||
};
|
||||
//Callback's purpose is to only prevent scrolling after animation is complete
|
||||
$cardDetails.animate({ scrollTop: 0 }, 500, () => { this.scrollState.top = true; });
|
||||
|
||||
//Prevent scrolling while dialog is open
|
||||
$cardDetails.on('scroll', () => {
|
||||
if(this.scrollState.top) { //If it's already in position, keep it there. Otherwise let animation scroll
|
||||
$cardDetails.scrollTop(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Template.subtaskDeleteDialog.onDestroyed(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
$cardDetails.off('scroll'); //Reactivate scrolling
|
||||
$cardDetails.animate( { scrollTop: this.scrollState.position });
|
||||
});
|
||||
|
||||
Template.subtaskItemDetail.helpers({
|
||||
canModifyCard() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
// ...
|
||||
}).register('subtaskItemDetail');
|
||||
@@ -0,0 +1,142 @@
|
||||
.js-add-subtask
|
||||
color: #8c8c8c
|
||||
|
||||
textarea.js-add-subtask-item, textarea.js-edit-subtask-item
|
||||
overflow: hidden
|
||||
word-wrap: break-word
|
||||
resize: none
|
||||
height: 34px
|
||||
|
||||
.delete-text
|
||||
color: #8c8c8c
|
||||
text-decoration: underline
|
||||
word-wrap: break-word
|
||||
float: right
|
||||
padding-top: 6px
|
||||
&:hover
|
||||
color: inherit
|
||||
|
||||
.subtask-title
|
||||
.checkbox
|
||||
float: left
|
||||
width: 30px
|
||||
height 30px
|
||||
font-size: 18px
|
||||
line-height: 30px
|
||||
|
||||
.title
|
||||
font-size: 18px
|
||||
line-height: 25px
|
||||
|
||||
.subtasks-stat
|
||||
margin: 0 0.5em
|
||||
float: right
|
||||
padding-top: 6px
|
||||
&.is-finished
|
||||
color: #3cb500
|
||||
|
||||
.js-delete-subtask
|
||||
@extends .delete-text
|
||||
margin: 0 0.5em
|
||||
|
||||
.js-view-subtask
|
||||
@extends .delete-text
|
||||
|
||||
.js-confirm-subtask-delete
|
||||
background-color: darken(white, 3%)
|
||||
position: absolute
|
||||
float: left;
|
||||
width: 60%
|
||||
margin-top: 0
|
||||
margin-left: 13%
|
||||
padding-bottom: 2%
|
||||
padding-left: 3%
|
||||
padding-right: 3%
|
||||
z-index: 17
|
||||
border-radius: 3px
|
||||
|
||||
p
|
||||
position: relative
|
||||
margin-top: 3%
|
||||
width: 100%
|
||||
text-align: center
|
||||
span
|
||||
font-weight: bold
|
||||
|
||||
i
|
||||
font-size: 2em
|
||||
|
||||
.js-subtask-delete-buttons
|
||||
position: relative
|
||||
padding: left 2% right 2%
|
||||
.confirm-subtask-delete
|
||||
margin-left: 12%
|
||||
float: left
|
||||
.toggle-delete-subtask-dialog
|
||||
margin-right: 12%
|
||||
float: right
|
||||
|
||||
#card-details-overlay
|
||||
top: 0
|
||||
bottom: -600px
|
||||
right: 0
|
||||
|
||||
.subtasks
|
||||
background: darken(white, 3%)
|
||||
|
||||
&.placeholder
|
||||
background: darken(white, 20%)
|
||||
border-radius: 2px
|
||||
|
||||
&.ui-sortable-helper
|
||||
box-shadow: -2px 2px 8px rgba(0, 0, 0, .3),
|
||||
0 0 1px rgba(0, 0, 0, .5)
|
||||
transform: rotate(4deg)
|
||||
cursor: grabbing
|
||||
|
||||
|
||||
.subtasks-item
|
||||
margin: 0 0 0 0.1em
|
||||
line-height: 18px
|
||||
font-size: 1.1em
|
||||
margin-top: 3px
|
||||
display: flex
|
||||
background: darken(white, 3%)
|
||||
|
||||
&.placeholder
|
||||
background: darken(white, 20%)
|
||||
border-radius: 2px
|
||||
|
||||
&.ui-sortable-helper
|
||||
box-shadow: -2px 2px 8px rgba(0, 0, 0, .3),
|
||||
0 0 1px rgba(0, 0, 0, .5)
|
||||
transform: rotate(4deg)
|
||||
cursor: grabbing
|
||||
|
||||
&:hover
|
||||
background-color: darken(white, 8%)
|
||||
|
||||
.check-box
|
||||
margin: 0.1em 0 0 0;
|
||||
&.is-checked
|
||||
border-bottom: 2px solid #3cb500
|
||||
border-right: 2px solid #3cb500
|
||||
|
||||
.item-title
|
||||
flex: 1
|
||||
padding-left: 10px;
|
||||
&.is-checked
|
||||
color: #8c8c8c
|
||||
font-style: italic
|
||||
& .viewer
|
||||
p
|
||||
margin-bottom: 2px
|
||||
|
||||
.js-delete-subtask-item
|
||||
margin: 0 0 0.5em 1.33em
|
||||
@extends .delete-text
|
||||
padding: 12px 0 0 0
|
||||
|
||||
.add-subtask-item
|
||||
margin: 0.2em 0 0.5em 1.33em
|
||||
display: inline-block
|
||||
@@ -0,0 +1,15 @@
|
||||
template(name="datepicker")
|
||||
.datepicker-container
|
||||
form.edit-date
|
||||
.fields
|
||||
.left
|
||||
label(for="date") {{_ 'date'}}
|
||||
input.js-date-field#date(type="text" name="date" value=showDate placeholder=dateFormat autofocus)
|
||||
.right
|
||||
label(for="time") {{_ 'time'}}
|
||||
input.js-time-field#time(type="text" name="time" value=showTime placeholder=timeFormat)
|
||||
.js-datepicker
|
||||
if error.get
|
||||
.warning {{_ error.get}}
|
||||
button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}}
|
||||
button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}}
|
||||
@@ -0,0 +1,17 @@
|
||||
.datepicker-container
|
||||
.fields
|
||||
.left
|
||||
width: 56%
|
||||
.right
|
||||
width: 38%
|
||||
.datepicker
|
||||
width: 100%
|
||||
table
|
||||
width: 100%
|
||||
border: none
|
||||
border-spacing: 0
|
||||
border-collapse: collapse
|
||||
thead
|
||||
background: none
|
||||
td, th
|
||||
box-sizing: border-box
|
||||
@@ -1,10 +1,10 @@
|
||||
@import 'nib'
|
||||
|
||||
select,
|
||||
textarea,
|
||||
input:not([type=file]),
|
||||
button
|
||||
box-sizing: border-box
|
||||
-webkit-appearance: none
|
||||
background-color: #ebebeb
|
||||
border: 1px solid #ccc
|
||||
border-radius: 3px
|
||||
@@ -85,6 +85,9 @@ select
|
||||
width: 256px
|
||||
margin-bottom: 8px
|
||||
|
||||
&.inline
|
||||
width: 100%
|
||||
|
||||
option[disabled]
|
||||
color: #8c8c8c
|
||||
|
||||
@@ -222,9 +225,12 @@ textarea
|
||||
|
||||
.edit-controls,
|
||||
.add-controls
|
||||
display: flex
|
||||
align-items: baseline
|
||||
margin-top: 0
|
||||
|
||||
button[type=submit]
|
||||
input[type=button]
|
||||
float: left
|
||||
height: 32px
|
||||
margin-top: -2px
|
||||
|
||||
@@ -12,11 +12,11 @@ template(name="import")
|
||||
|
||||
template(name="importTextarea")
|
||||
form
|
||||
p: label(for='import-textarea') {{_ instruction}}
|
||||
p: label(for='import-textarea') {{_ instruction}} {{_ 'import-board-instruction-about-errors'}}
|
||||
textarea.js-import-json(placeholder="{{_ 'import-json-placeholder'}}" autofocus)
|
||||
| {{jsonText}}
|
||||
if isSandstorm
|
||||
h1.warning DANGER !!! THIS DESTROYS YOUR IMPORTED DATA, CAUSES BOARD NOT FOUND ERROR WHEN YOU OPEN THIS GRAIN AGAIN https://github.com/wekan/wekan/issues/1430
|
||||
h1.warning {{_ 'import-sandstorm-backup-warning'}}
|
||||
p.warning {{_ 'import-sandstorm-warning'}}
|
||||
input.primary.wide(type="submit" value="{{_ 'import'}}")
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { calculateIndex } = Utils;
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
// Proxy
|
||||
@@ -26,6 +26,13 @@ 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',
|
||||
@@ -47,6 +54,7 @@ BlazeComponent.extendComponent({
|
||||
items: itemsSelector,
|
||||
placeholder: 'minicard-wrapper placeholder',
|
||||
start(evt, ui) {
|
||||
ui.helper.css('z-index', 1000);
|
||||
ui.placeholder.height(ui.helper.height());
|
||||
EscapeActions.executeUpTo('popup-close');
|
||||
boardComponent.setIsDragging(true);
|
||||
@@ -83,6 +91,9 @@ 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(() => {
|
||||
$cards.sortable('option', 'disabled', !userIsMember());
|
||||
|
||||
@@ -187,3 +187,14 @@
|
||||
padding: 7px
|
||||
top: -@padding
|
||||
right: 17px
|
||||
|
||||
.link-board-wrapper
|
||||
display: flex
|
||||
align-items: baseline
|
||||
|
||||
.js-link-board
|
||||
margin-left: 15px
|
||||
|
||||
.search-card-results
|
||||
max-height: 250px
|
||||
overflow: hidden
|
||||
@@ -34,8 +34,62 @@ template(name="addCardForm")
|
||||
|
||||
.add-controls.clearfix
|
||||
button.primary.confirm(type="submit") {{_ 'add'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
unless isSandstorm
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
a.js-link {{_ 'link'}}
|
||||
span.quiet
|
||||
|
|
||||
| /
|
||||
a.js-search {{_ 'search'}}
|
||||
|
||||
template(name="autocompleteLabelLine")
|
||||
.minicard-label(class="card-label-{{colorName}}" title=labelName)
|
||||
span(class="{{#if hasNoName}}quiet{{/if}}")= labelName
|
||||
|
||||
template(name="linkCardPopup")
|
||||
label {{_ 'boards'}}:
|
||||
.link-board-wrapper
|
||||
select.js-select-boards
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
option(value="{{_id}}") {{title}}
|
||||
input.primary.confirm.js-link-board(type="button" value="{{_ 'link'}}")
|
||||
|
||||
label {{_ 'swimlanes'}}:
|
||||
select.js-select-swimlanes
|
||||
each swimlanes
|
||||
option(value="{{_id}}") {{title}}
|
||||
|
||||
label {{_ 'lists'}}:
|
||||
select.js-select-lists
|
||||
each lists
|
||||
option(value="{{_id}}") {{title}}
|
||||
|
||||
label {{_ 'cards'}}:
|
||||
select.js-select-cards
|
||||
each cards
|
||||
option(value="{{getId}}") {{getTitle}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
unless isSandstorm
|
||||
input.primary.confirm.js-done(type="button" value="{{_ 'link'}}")
|
||||
|
||||
template(name="searchCardPopup")
|
||||
label {{_ 'boards'}}:
|
||||
.link-board-wrapper
|
||||
select.js-select-boards
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
option(value="{{_id}}") {{title}}
|
||||
form.js-search-term-form
|
||||
input(type="text" name="searchTerm" placeholder="{{_ 'search-example'}}" autofocus)
|
||||
.list-body.js-perfect-scrollbar.search-card-results
|
||||
.minicards.clearfix.js-minicards
|
||||
each results
|
||||
a.minicard-wrapper.js-minicard
|
||||
+minicard(this)
|
||||
@@ -1,3 +1,5 @@
|
||||
const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
mixins() {
|
||||
return [Mixins.PerfectScrollbar];
|
||||
@@ -35,25 +37,30 @@ BlazeComponent.extendComponent({
|
||||
|
||||
const members = formComponent.members.get();
|
||||
const labelIds = formComponent.labels.get();
|
||||
const customFields = formComponent.customFields.get();
|
||||
|
||||
const boardId = this.data().board()._id;
|
||||
const board = Boards.findOne(boardId);
|
||||
const boardId = this.data().board();
|
||||
let swimlaneId = '';
|
||||
if (board.view === 'board-view-swimlanes')
|
||||
const boardView = Meteor.user().profile.boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id;
|
||||
else
|
||||
swimlaneId = Swimlanes.findOne({boardId})._id;
|
||||
else if ((boardView === 'board-view-lists') || (boardView === 'board-view-cal'))
|
||||
swimlaneId = boardId.getDefaultSwimline()._id;
|
||||
|
||||
if (title) {
|
||||
const _id = Cards.insert({
|
||||
title,
|
||||
members,
|
||||
labelIds,
|
||||
customFields,
|
||||
listId: this.data()._id,
|
||||
boardId: this.data().board()._id,
|
||||
boardId: boardId._id,
|
||||
sort: sortIndex,
|
||||
swimlaneId,
|
||||
type: 'cardType-card',
|
||||
});
|
||||
|
||||
|
||||
// In case the filter is active we need to add the newly inserted card in
|
||||
// the list of exceptions -- cards that are not filtered. Otherwise the
|
||||
// card will disappear instantly.
|
||||
@@ -106,8 +113,8 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
idOrNull(swimlaneId) {
|
||||
const board = Boards.findOne(Session.get('currentBoard'));
|
||||
if (board.view === 'board-view-swimlanes')
|
||||
const currentUser = Meteor.user();
|
||||
if (currentUser.profile.boardView === 'board-view-swimlanes')
|
||||
return swimlaneId;
|
||||
return undefined;
|
||||
},
|
||||
@@ -146,11 +153,21 @@ BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.labels = new ReactiveVar([]);
|
||||
this.members = new ReactiveVar([]);
|
||||
this.customFields = new ReactiveVar([]);
|
||||
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
arr = [];
|
||||
_.forEach(Boards.findOne(currentBoardId).customFields().fetch(), function(field){
|
||||
if(field.automaticallyOnCard)
|
||||
arr.push({_id: field._id, value: null});
|
||||
});
|
||||
this.customFields.set(arr);
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.labels.set([]);
|
||||
this.members.set([]);
|
||||
this.customFields.set([]);
|
||||
},
|
||||
|
||||
getLabels() {
|
||||
@@ -193,6 +210,8 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [{
|
||||
keydown: this.pressKey,
|
||||
'click .js-link': Popup.open('linkCard'),
|
||||
'click .js-search': Popup.open('searchCard'),
|
||||
}];
|
||||
},
|
||||
|
||||
@@ -264,3 +283,226 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
},
|
||||
}).register('addCardForm');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
// Prefetch first non-current board id
|
||||
const boardId = Boards.findOne({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
}, {
|
||||
sort: ['title'],
|
||||
})._id;
|
||||
// Subscribe to this board
|
||||
subManager.subscribe('board', boardId);
|
||||
this.selectedBoardId = new ReactiveVar(boardId);
|
||||
this.selectedSwimlaneId = new ReactiveVar('');
|
||||
this.selectedListId = new ReactiveVar('');
|
||||
|
||||
this.boardId = Session.get('currentBoard');
|
||||
// In order to get current board info
|
||||
subManager.subscribe('board', this.boardId);
|
||||
this.board = Boards.findOne(this.boardId);
|
||||
// List where to insert card
|
||||
const list = $(Popup._getTopStack().openerElement).closest('.js-list');
|
||||
this.listId = Blaze.getData(list[0])._id;
|
||||
// Swimlane where to insert card
|
||||
const swimlane = $(Popup._getTopStack().openerElement).closest('.js-swimlane');
|
||||
this.swimlaneId = '';
|
||||
const boardView = Meteor.user().profile.boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else if (boardView === 'board-view-lists')
|
||||
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return boards;
|
||||
},
|
||||
|
||||
swimlanes() {
|
||||
if (!this.selectedBoardId) {
|
||||
return [];
|
||||
}
|
||||
const swimlanes = Swimlanes.find({boardId: this.selectedBoardId.get()});
|
||||
if (swimlanes.count())
|
||||
this.selectedSwimlaneId.set(swimlanes.fetch()[0]._id);
|
||||
return swimlanes;
|
||||
},
|
||||
|
||||
lists() {
|
||||
if (!this.selectedBoardId) {
|
||||
return [];
|
||||
}
|
||||
const lists = Lists.find({boardId: this.selectedBoardId.get()});
|
||||
if (lists.count())
|
||||
this.selectedListId.set(lists.fetch()[0]._id);
|
||||
return lists;
|
||||
},
|
||||
|
||||
cards() {
|
||||
if (!this.board) {
|
||||
return [];
|
||||
}
|
||||
const ownCardsIds = this.board.cards().map((card) => { return card.linkedId || card._id; });
|
||||
return Cards.find({
|
||||
boardId: this.selectedBoardId.get(),
|
||||
swimlaneId: this.selectedSwimlaneId.get(),
|
||||
listId: this.selectedListId.get(),
|
||||
archived: false,
|
||||
linkedId: {$nin: ownCardsIds},
|
||||
_id: {$nin: ownCardsIds},
|
||||
});
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'change .js-select-boards'(evt) {
|
||||
subManager.subscribe('board', $(evt.currentTarget).val());
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'change .js-select-swimlanes'(evt) {
|
||||
this.selectedSwimlaneId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'change .js-select-lists'(evt) {
|
||||
this.selectedListId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'click .js-done' (evt) {
|
||||
// LINK CARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const linkedId = $('.js-select-cards option:selected').val();
|
||||
if (!linkedId) {
|
||||
Popup.close();
|
||||
return;
|
||||
}
|
||||
const _id = Cards.insert({
|
||||
title: $('.js-select-cards option:selected').text(), //dummy
|
||||
listId: this.listId,
|
||||
swimlaneId: this.swimlaneId,
|
||||
boardId: this.boardId,
|
||||
sort: Lists.findOne(this.listId).cards().count(),
|
||||
type: 'cardType-linkedCard',
|
||||
linkedId,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-link-board' (evt) {
|
||||
//LINK BOARD
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
const impBoardId = $('.js-select-boards option:selected').val();
|
||||
if (!impBoardId || Cards.findOne({linkedId: impBoardId, archived: false})) {
|
||||
Popup.close();
|
||||
return;
|
||||
}
|
||||
const _id = Cards.insert({
|
||||
title: $('.js-select-boards option:selected').text(), //dummy
|
||||
listId: this.listId,
|
||||
swimlaneId: this.swimlaneId,
|
||||
boardId: this.boardId,
|
||||
sort: Lists.findOne(this.listId).cards().count(),
|
||||
type: 'cardType-linkedBoard',
|
||||
linkedId: impBoardId,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('linkCardPopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
mixins() {
|
||||
return [Mixins.PerfectScrollbar];
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
// Prefetch first non-current board id
|
||||
let board = Boards.findOne({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
});
|
||||
if (!board) {
|
||||
Popup.close();
|
||||
return;
|
||||
}
|
||||
const boardId = board._id;
|
||||
// Subscribe to this board
|
||||
subManager.subscribe('board', boardId);
|
||||
this.selectedBoardId = new ReactiveVar(boardId);
|
||||
|
||||
this.boardId = Session.get('currentBoard');
|
||||
// In order to get current board info
|
||||
subManager.subscribe('board', this.boardId);
|
||||
board = Boards.findOne(this.boardId);
|
||||
// List where to insert card
|
||||
const list = $(Popup._getTopStack().openerElement).closest('.js-list');
|
||||
this.listId = Blaze.getData(list[0])._id;
|
||||
// Swimlane where to insert card
|
||||
const swimlane = $(Popup._getTopStack().openerElement).closest('.js-swimlane');
|
||||
this.swimlaneId = '';
|
||||
if (board.view === 'board-view-swimlanes')
|
||||
this.swimlaneId = Blaze.getData(swimlane[0])._id;
|
||||
else
|
||||
this.swimlaneId = Swimlanes.findOne({boardId: this.boardId})._id;
|
||||
this.term = new ReactiveVar('');
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {$ne: Session.get('currentBoard')},
|
||||
}, {
|
||||
sort: ['title'],
|
||||
});
|
||||
return boards;
|
||||
},
|
||||
|
||||
results() {
|
||||
if (!this.selectedBoardId) {
|
||||
return [];
|
||||
}
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
return board.searchCards(this.term.get(), false);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'change .js-select-boards'(evt) {
|
||||
subManager.subscribe('board', $(evt.currentTarget).val());
|
||||
this.selectedBoardId.set($(evt.currentTarget).val());
|
||||
},
|
||||
'submit .js-search-term-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.term.set(evt.target.searchTerm.value);
|
||||
},
|
||||
'click .js-minicard'(evt) {
|
||||
// LINK CARD
|
||||
const card = Blaze.getData(evt.currentTarget);
|
||||
const _id = Cards.insert({
|
||||
title: card.title, //dummy
|
||||
listId: this.listId,
|
||||
swimlaneId: this.swimlaneId,
|
||||
boardId: this.boardId,
|
||||
sort: Lists.findOne(this.listId).cards().count(),
|
||||
type: 'cardType-linkedCard',
|
||||
linkedId: card.linkedId || card._id,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
Popup.close();
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('searchCardPopup');
|
||||
@@ -15,9 +15,8 @@ template(name="listHeader")
|
||||
|/#{wipLimit.value})
|
||||
|
||||
if showCardsCountForList cards.count
|
||||
= cards.count
|
||||
span.lowercase
|
||||
| {{_ 'cards'}}
|
||||
|
|
||||
p.quiet.small {{cardsCount}} {{_ 'cards-count'}}
|
||||
if isMiniScreen
|
||||
if currentList
|
||||
if isWatching
|
||||
|
||||
@@ -22,20 +22,31 @@ BlazeComponent.extendComponent({
|
||||
return Meteor.user().getLimitToShowCardsCount();
|
||||
},
|
||||
|
||||
cardsCount() {
|
||||
const list = Template.currentData();
|
||||
let swimlaneId = '';
|
||||
const boardView = Meteor.user().profile.boardView;
|
||||
if (boardView === 'board-view-swimlanes')
|
||||
swimlaneId = this.parentComponent().parentComponent().data()._id;
|
||||
|
||||
return list.cards(swimlaneId).count();
|
||||
},
|
||||
|
||||
reachedWipLimit() {
|
||||
const list = Template.currentData();
|
||||
return list.getWipLimit('enabled') && list.getWipLimit('value') <= list.cards().count();
|
||||
},
|
||||
|
||||
showCardsCountForList(count) {
|
||||
return count > this.limitToShowCardsCount();
|
||||
const limit = this.limitToShowCardsCount();
|
||||
return limit > 0 && count > limit;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-open-list-menu': Popup.open('listAction'),
|
||||
'click .js-add-card' () {
|
||||
const listDom = document.getElementById(`js-list-${this.currentData()._id}`);
|
||||
'click .js-add-card' (evt) {
|
||||
const listDom = $(evt.target).parents(`#js-list-${this.currentData()._id}`)[0];
|
||||
const listComponent = BlazeComponent.getComponentForElement(listDom);
|
||||
listComponent.openForm({
|
||||
position: 'top',
|
||||
|
||||
@@ -38,7 +38,10 @@ Blaze.Template.registerHelper('mentions', new Template('mentions', function() {
|
||||
const view = this;
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const knowedUsers = currentBoard.members.map((member) => {
|
||||
member.username = Users.findOne(member.userId).username;
|
||||
const u = Users.findOne(member.userId);
|
||||
if(u){
|
||||
member.username = u.username;
|
||||
}
|
||||
return member;
|
||||
});
|
||||
const mentionRegex = /\B@([\w.]*)/gi;
|
||||
|
||||
@@ -47,6 +47,7 @@ template(name="header")
|
||||
+Template.dynamic(template=headerBar)
|
||||
|
||||
unless hideLogo
|
||||
|
||||
//-
|
||||
On sandstorm, the logo shouldn't be clickable, because we only have one
|
||||
page/document on it, and we don't want to see the home page containing
|
||||
@@ -55,8 +56,9 @@ template(name="header")
|
||||
.wekan-logo
|
||||
img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan")
|
||||
else
|
||||
a.wekan-logo(href="{{pathFor 'home'}}" title="{{_ 'header-logo-title'}}")
|
||||
img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan")
|
||||
unless currentSetting.hideLogo
|
||||
a.wekan-logo(href="{{pathFor 'home'}}" title="{{_ 'header-logo-title'}}")
|
||||
img(src="{{pathFor '/wekan-logo-header.png'}}" alt="Wekan")
|
||||
|
||||
if appIsOffline
|
||||
+offlineWarning
|
||||
@@ -66,7 +68,8 @@ template(name="header")
|
||||
.announcement
|
||||
p
|
||||
i.fa.fa-bullhorn
|
||||
| #{announcement}
|
||||
+viewer
|
||||
| #{announcement}
|
||||
i.fa.fa-times-circle.js-close-announcement
|
||||
|
||||
template(name="offlineWarning")
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
Meteor.subscribe('user-admin');
|
||||
Meteor.subscribe('boards');
|
||||
Meteor.subscribe('setting');
|
||||
|
||||
Template.header.helpers({
|
||||
wrappedHeader() {
|
||||
return !Session.get('currentBoard');
|
||||
},
|
||||
|
||||
currentSetting() {
|
||||
return Settings.findOne();
|
||||
},
|
||||
|
||||
hideLogo() {
|
||||
return Utils.isMiniScreen() && Session.get('currentBoard');
|
||||
},
|
||||
|
||||
@@ -29,13 +29,12 @@
|
||||
font-size: 0.9em
|
||||
margin-right: 10px
|
||||
|
||||
|
||||
.wekan-logo
|
||||
display: block
|
||||
margin: 3px auto 0
|
||||
margin: 3px auto auto
|
||||
width: 97px
|
||||
opacity: 0.6
|
||||
transition: opacity 0.15s
|
||||
float: right
|
||||
|
||||
&:hover
|
||||
opacity: 0.9
|
||||
@@ -189,8 +188,6 @@
|
||||
width: 100%
|
||||
padding: 10px 0px
|
||||
z-index: 30
|
||||
position: absolute
|
||||
bottom: 0px
|
||||
|
||||
ul
|
||||
width: calc(100% - 60px)
|
||||
@@ -219,7 +216,7 @@
|
||||
position: absolute
|
||||
right: 0px
|
||||
padding: 10px
|
||||
margin: -10px
|
||||
margin: -10px 0 -10px -10px
|
||||
|
||||
.announcement,
|
||||
.offline-warning
|
||||
|
||||
@@ -7,16 +7,26 @@ head
|
||||
where the application is deployed with a path prefix, but it seems to be
|
||||
difficult to do that cleanly with Blaze -- at least without adding extra
|
||||
packages.
|
||||
link(rel="shortcut icon" href="/wekan-favicon.png")
|
||||
link(rel="apple-touch-icon" href="/wekan-favicon.png")
|
||||
link(rel="manifest" href="/wekan-manifest.json")
|
||||
link(rel="shortcut icon" href="/public/wekan-favicon.png")
|
||||
link(rel="apple-touch-icon" href="/public/wekan-favicon.png")
|
||||
link(rel="mask-icon" href="/public/wekan-150.svg")
|
||||
link(rel="manifest" href="/public/wekan-manifest.json")
|
||||
|
||||
template(name="userFormsLayout")
|
||||
section.auth-layout
|
||||
h1.at-form-landing-logo
|
||||
img(src="{{pathFor '/wekan-logo.png'}}" alt="Wekan")
|
||||
unless currentSetting.hideLogo
|
||||
h1.at-form-landing-logo
|
||||
img(src="{{pathFor '/wekan-logo.png'}}" alt="Wekan")
|
||||
if currentSetting.hideLogo
|
||||
h1
|
||||
br
|
||||
br
|
||||
section.auth-dialog
|
||||
+Template.dynamic(template=content)
|
||||
+connectionMethod
|
||||
if isCas
|
||||
.at-form
|
||||
button#cas(class='at-btn submit' type='submit') {{casSignInLabel}}
|
||||
div.at-form-lang
|
||||
select.select-lang.js-userform-set-language
|
||||
each languages
|
||||
@@ -28,15 +38,24 @@ template(name="userFormsLayout")
|
||||
template(name="defaultLayout")
|
||||
+header
|
||||
#content
|
||||
| {{{afterBodyStart}}}
|
||||
+Template.dynamic(template=content)
|
||||
| {{{beforeBodyEnd}}}
|
||||
if (Modal.isOpen)
|
||||
#modal
|
||||
.overlay
|
||||
.modal-content
|
||||
a.modal-close-btn.js-close-modal
|
||||
i.fa.fa-times-thin
|
||||
+Template.dynamic(template=Modal.getHeaderName)
|
||||
+Template.dynamic(template=Modal.getTemplateName)
|
||||
if (Modal.isWide)
|
||||
.modal-content-wide.modal-container
|
||||
a.modal-close-btn.js-close-modal
|
||||
i.fa.fa-times-thin
|
||||
+Template.dynamic(template=Modal.getHeaderName)
|
||||
+Template.dynamic(template=Modal.getTemplateName)
|
||||
else
|
||||
.modal-content.modal-container
|
||||
a.modal-close-btn.js-close-modal
|
||||
i.fa.fa-times-thin
|
||||
+Template.dynamic(template=Modal.getHeaderName)
|
||||
+Template.dynamic(template=Modal.getTemplateName)
|
||||
|
||||
template(name="notFound")
|
||||
+message(label='page-not-found')
|
||||
|
||||
@@ -6,7 +6,29 @@ const i18nTagToT9n = (i18nTag) => {
|
||||
return i18nTag;
|
||||
};
|
||||
|
||||
const validator = {
|
||||
set(obj, prop, value) {
|
||||
if (prop === 'state' && value !== 'signIn') {
|
||||
$('.at-form-authentication').hide();
|
||||
} else if (prop === 'state' && value === 'signIn') {
|
||||
$('.at-form-authentication').show();
|
||||
}
|
||||
// The default behavior to store the value
|
||||
obj[prop] = value;
|
||||
// Indicate success
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
Template.userFormsLayout.onCreated(() => {
|
||||
Meteor.subscribe('setting');
|
||||
|
||||
});
|
||||
|
||||
Template.userFormsLayout.onRendered(() => {
|
||||
|
||||
AccountsTemplates.state.form.keys = new Proxy(AccountsTemplates.state.form.keys, validator);
|
||||
|
||||
const i18nTag = navigator.language;
|
||||
if (i18nTag) {
|
||||
T9n.setLanguage(i18nTagToT9n(i18nTag));
|
||||
@@ -15,6 +37,20 @@ Template.userFormsLayout.onRendered(() => {
|
||||
});
|
||||
|
||||
Template.userFormsLayout.helpers({
|
||||
|
||||
currentSetting() {
|
||||
return Settings.findOne();
|
||||
},
|
||||
|
||||
|
||||
afterBodyStart() {
|
||||
return currentSetting.customHTMLafterBodyStart;
|
||||
},
|
||||
|
||||
beforeBodyEnd() {
|
||||
return currentSetting.customHTMLbeforeBodyEnd;
|
||||
},
|
||||
|
||||
languages() {
|
||||
return _.map(TAPi18n.getLanguages(), (lang, code) => {
|
||||
const tag = code;
|
||||
@@ -39,6 +75,17 @@ Template.userFormsLayout.helpers({
|
||||
const curLang = T9n.getLanguage() || 'en';
|
||||
return t9nTag === curLang;
|
||||
},
|
||||
/*
|
||||
isCas() {
|
||||
return Meteor.settings.public &&
|
||||
Meteor.settings.public.cas &&
|
||||
Meteor.settings.public.cas.loginUrl;
|
||||
},
|
||||
|
||||
casSignInLabel() {
|
||||
return TAPi18n.__('casSignIn', {}, T9n.getLanguage() || 'en');
|
||||
},
|
||||
*/
|
||||
});
|
||||
|
||||
Template.userFormsLayout.events({
|
||||
@@ -47,6 +94,51 @@ Template.userFormsLayout.events({
|
||||
T9n.setLanguage(i18nTagToT9n(i18nTag));
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click button#cas'() {
|
||||
Meteor.loginWithCas(function() {
|
||||
if (FlowRouter.getRouteName() === 'atSignIn') {
|
||||
FlowRouter.go('/');
|
||||
}
|
||||
});
|
||||
},
|
||||
'click #at-btn'(event) {
|
||||
/* All authentication method can be managed/called here.
|
||||
!! DON'T FORGET to correctly fill the fields of the user during its creation if necessary authenticationMethod : String !!
|
||||
*/
|
||||
const authenticationMethodSelected = $('.select-authentication').val();
|
||||
// Local account
|
||||
if (authenticationMethodSelected === 'password') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop submit #at-pwd-form
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
const email = $('#at-field-username_and_email').val();
|
||||
const password = $('#at-field-password').val();
|
||||
|
||||
// Ldap account
|
||||
if (authenticationMethodSelected === 'ldap') {
|
||||
// Check if the user can use the ldap connection
|
||||
Meteor.subscribe('user-authenticationMethod', email, {
|
||||
onReady() {
|
||||
const user = Users.findOne();
|
||||
if (user === undefined || user.authenticationMethod === 'ldap') {
|
||||
// Use the ldap connection package
|
||||
Meteor.loginWithLDAP(email, password, function(error) {
|
||||
if (!error) {
|
||||
// Connection
|
||||
return FlowRouter.go('/');
|
||||
}
|
||||
return error;
|
||||
});
|
||||
}
|
||||
return this.stop();
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.defaultLayout.events({
|
||||
|
||||
@@ -61,6 +61,23 @@ body
|
||||
display: block
|
||||
float: right
|
||||
font-size: 24px
|
||||
|
||||
.modal-content-wide
|
||||
width: 800px
|
||||
min-height: 0px
|
||||
margin: 42px auto
|
||||
padding: 12px
|
||||
border-radius: 4px
|
||||
background: darken(white, 13%)
|
||||
z-index: 110
|
||||
|
||||
h2
|
||||
margin-bottom: 25px
|
||||
|
||||
.modal-close-btn
|
||||
display: block
|
||||
float: right
|
||||
font-size: 24px
|
||||
|
||||
h1
|
||||
font-size: 22px
|
||||
@@ -368,8 +385,8 @@ a
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
#content
|
||||
margin: 1px 0px 49px 0px
|
||||
height: calc(100% - 96px)
|
||||
margin: 1px 0px 0px 0px
|
||||
height: calc(100% - 0px)
|
||||
|
||||
> .wrapper
|
||||
margin-top: 0px
|
||||
|
||||
@@ -33,6 +33,9 @@ $popupWidth = 300px
|
||||
textarea
|
||||
height: 72px
|
||||
|
||||
form a span
|
||||
padding: 0 0.5rem
|
||||
|
||||
.header
|
||||
height: 36px
|
||||
position: relative
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
const { isTouchDevice } = Utils;
|
||||
|
||||
Mixins.PerfectScrollbar = BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
const component = this.mixinParent();
|
||||
const domElement = component.find('.js-perfect-scrollbar');
|
||||
Ps.initialize(domElement);
|
||||
if (!isTouchDevice()) {
|
||||
const component = this.mixinParent();
|
||||
const domElement = component.find('.js-perfect-scrollbar');
|
||||
Ps.initialize(domElement);
|
||||
|
||||
// XXX We should create an event map to be consistent with other components
|
||||
// but since BlazeComponent doesn't merge Mixins events transparently I
|
||||
// prefered to use a jQuery event (which is what an event map ends up doing)
|
||||
component.$(domElement).on('mouseenter', () => Ps.update(domElement));
|
||||
// XXX We should create an event map to be consistent with other components
|
||||
// but since BlazeComponent doesn't merge Mixins events transparently I
|
||||
// prefered to use a jQuery event (which is what an event map ends up doing)
|
||||
component.$(domElement).on('mouseenter', () => Ps.update(domElement));
|
||||
}
|
||||
},
|
||||
});
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,72 @@
|
||||
template(name="boardActions")
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-move-card-to'}}
|
||||
div.trigger-dropdown
|
||||
select(id="move-gen-action")
|
||||
option(value="top") {{_'r-top-of'}}
|
||||
option(value="bottom") {{_'r-bottom-of'}}
|
||||
div.trigger-text
|
||||
| {{_'r-its-list'}}
|
||||
div.trigger-button.js-add-gen-move-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-move-card-to'}}
|
||||
div.trigger-dropdown
|
||||
select(id="move-spec-action")
|
||||
option(value="top") {{_'r-top-of'}}
|
||||
option(value="bottom") {{_'r-bottom-of'}}
|
||||
div.trigger-text
|
||||
| {{_'r-list'}}
|
||||
div.trigger-dropdown
|
||||
input(id="listName",type=text,placeholder="{{_'r-name'}}")
|
||||
div.trigger-button.js-add-spec-move-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-dropdown
|
||||
select(id="arch-action")
|
||||
option(value="archive") {{_'r-archive'}}
|
||||
option(value="unarchive") {{_'r-unarchive'}}
|
||||
div.trigger-text
|
||||
| {{_'r-card'}}
|
||||
div.trigger-button.js-add-arch-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-add-swimlane'}}
|
||||
div.trigger-dropdown
|
||||
input(id="swimlane-name",type=text,placeholder="{{_'r-name'}}")
|
||||
div.trigger-button.js-add-swimlane-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-create-card'}}
|
||||
div.trigger-dropdown
|
||||
input(id="card-name",type=text,placeholder="{{_'r-name'}}")
|
||||
div.trigger-text
|
||||
| {{_'r-in-list'}}
|
||||
div.trigger-dropdown
|
||||
input(id="list-name",type=text,placeholder="{{_'r-name'}}")
|
||||
div.trigger-text
|
||||
| {{_'r-in-swimlane'}}
|
||||
div.trigger-dropdown
|
||||
input(id="swimlane-name2",type=text,placeholder="{{_'r-name'}}")
|
||||
div.trigger-button.js-create-card-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-create-card-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const cardName = this.find('#card-name').value;
|
||||
const listName = this.find('#list-name').value;
|
||||
const swimlaneName = this.find('#swimlane-name2').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'createCard',
|
||||
swimlaneName,
|
||||
cardName,
|
||||
listName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
|
||||
},
|
||||
'click .js-add-swimlane-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const swimlaneName = this.find('#swimlane-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addSwimlane',
|
||||
swimlaneName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
|
||||
},
|
||||
'click .js-add-spec-move-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#move-spec-action').value;
|
||||
const listTitle = this.find('#listName').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'top') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToTop',
|
||||
listTitle,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'bottom') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToBottom',
|
||||
listTitle,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-move-action' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#move-gen-action').value;
|
||||
if (actionSelected === 'top') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToTop',
|
||||
'listTitle': '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'bottom') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'moveCardToBottom',
|
||||
'listTitle': '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-arch-action' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#arch-action').value;
|
||||
if (actionSelected === 'archive') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'archive',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unarchive') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'unarchive',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
},
|
||||
|
||||
}).register('boardActions');
|
||||
/* eslint-no-undef */
|
||||
@@ -0,0 +1,70 @@
|
||||
template(name="cardActions")
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-dropdown
|
||||
select(id="label-action")
|
||||
option(value="add") {{{_'r-add'}}}
|
||||
option(value="remove") {{{_'r-remove'}}}
|
||||
div.trigger-text
|
||||
| {{{_'r-label'}}}
|
||||
div.trigger-dropdown
|
||||
select(id="label-id")
|
||||
each labels
|
||||
option(value="#{_id}")
|
||||
= name
|
||||
div.trigger-button.js-add-label-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-dropdown
|
||||
select(id="member-action")
|
||||
option(value="add") {{{_'r-add'}}}
|
||||
option(value="remove") {{{_'r-remove'}}}
|
||||
div.trigger-text
|
||||
| {{{_'r-member'}}}
|
||||
div.trigger-dropdown
|
||||
input(id="member-name",type=text,placeholder="{{{_'r-name'}}}")
|
||||
div.trigger-button.js-add-member-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{{_'r-remove-all'}}}
|
||||
div.trigger-button.js-add-removeall-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{{_'r-set-color'}}}
|
||||
div.trigger-dropdown
|
||||
select(id="color-action")
|
||||
option(value="white") {{{_'color-white'}}}
|
||||
option(value="green") {{{_'color-green'}}}
|
||||
option(value="yellow") {{{_'color-yellow'}}}
|
||||
option(value="orange") {{{_'color-orange'}}}
|
||||
option(value="red") {{{_'color-red'}}}
|
||||
option(value="purple") {{{_'color-purple'}}}
|
||||
option(value="blue") {{{_'color-blue'}}}
|
||||
option(value="sky") {{{_'color-sky'}}}
|
||||
option(value="lime") {{{_'color-lime'}}}
|
||||
option(value="pink") {{{_'color-pink'}}}
|
||||
option(value="black") {{{_'color-black'}}}
|
||||
option(value="silver") {{{_'color-silver'}}}
|
||||
option(value="peachpuff") {{{_'color-peachpuff'}}}
|
||||
option(value="crimson") {{{_'color-crimson'}}}
|
||||
option(value="plum") {{{_'color-plum'}}}
|
||||
option(value="darkgreen") {{{_'color-darkgreen'}}}
|
||||
option(value="slateblue") {{{_'color-slateblue'}}}
|
||||
option(value="magenta") {{{_'color-magenta'}}}
|
||||
option(value="gold") {{{_'color-gold'}}}
|
||||
option(value="navy") {{{_'color-navy'}}}
|
||||
option(value="gray") {{{_'color-gray'}}}
|
||||
option(value="saddlebrown") {{{_'color-saddlebrown'}}}
|
||||
option(value="paleturquoise") {{{_'color-paleturquoise'}}}
|
||||
option(value="mistyrose") {{{_'color-mistyrose'}}}
|
||||
option(value="indigo") {{{_'color-indigo'}}}
|
||||
div.trigger-button.js-set-color-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
@@ -0,0 +1,138 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.subscribe('allRules');
|
||||
},
|
||||
|
||||
labels() {
|
||||
const labels = Boards.findOne(Session.get('currentBoard')).labels;
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
if (labels[i].name === '' || labels[i].name === undefined) {
|
||||
labels[i].name = labels[i].color.toUpperCase();
|
||||
}
|
||||
}
|
||||
return labels;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-label-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#label-action').value;
|
||||
const labelId = this.find('#label-id').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'add') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addLabel',
|
||||
labelId,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'remove') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeLabel',
|
||||
labelId,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
'click .js-add-member-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#member-action').value;
|
||||
const username = this.find('#member-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'add') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addMember',
|
||||
username,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'remove') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeMember',
|
||||
username,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-removeall-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeMember',
|
||||
'username': '*',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
'click .js-set-color-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const selectedColor = this.find('#color-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'setColor',
|
||||
selectedColor,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
}];
|
||||
},
|
||||
|
||||
}).register('cardActions');
|
||||
@@ -0,0 +1,70 @@
|
||||
template(name="checklistActions")
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-dropdown
|
||||
select(id="check-action")
|
||||
option(value="add") {{{_'r-add'}}}
|
||||
option(value="remove") {{{_'r-remove'}}}
|
||||
div.trigger-text
|
||||
| {{{_'r-checklist'}}}
|
||||
div.trigger-dropdown
|
||||
input(id="checklist-name",type=text,placeholder="{{{_'r-name'}}}")
|
||||
div.trigger-button.js-add-checklist-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-dropdown
|
||||
select(id="checkall-action")
|
||||
option(value="check") {{{_'r-check-all'}}}
|
||||
option(value="uncheck") {{{_'r-uncheck-all'}}}
|
||||
div.trigger-text
|
||||
| {{{_'r-items-check'}}}
|
||||
div.trigger-dropdown
|
||||
input(id="checklist-name2",type=text,placeholder="{{{_'r-name'}}}")
|
||||
div.trigger-button.js-add-checkall-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-dropdown
|
||||
select(id="check-item-action")
|
||||
option(value="check") {{{_'r-check'}}}
|
||||
option(value="uncheck") {{{_'r-uncheck'}}}
|
||||
div.trigger-text
|
||||
| {{{_'r-item'}}}
|
||||
div.trigger-dropdown
|
||||
input(id="checkitem-name",type=text,placeholder="{{{_'r-name'}}}")
|
||||
div.trigger-text
|
||||
| {{{_'r-of-checklist'}}}
|
||||
div.trigger-dropdown
|
||||
input(id="checklist-name3",type=text,placeholder="{{{_'r-name'}}}")
|
||||
div.trigger-button.js-add-check-item-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{{_'r-add-checklist'}}}
|
||||
div.trigger-dropdown
|
||||
input(id="checklist-name-3",type=text,placeholder="{{{_'r-name'}}}")
|
||||
div.trigger-text
|
||||
| {{{_'r-with-items'}}}
|
||||
div.trigger-dropdown
|
||||
input(id="checklist-items",type=text,placeholder="{{{_'r-items-list'}}}")
|
||||
div.trigger-button.js-add-checklist-items-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{{_'r-checklist-note'}}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.subscribe('allRules');
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-checklist-items-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const checklistName = this.find('#checklist-name-3').value;
|
||||
const checklistItems = this.find('#checklist-items').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addChecklistWithItems',
|
||||
checklistName,
|
||||
checklistItems,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
|
||||
},
|
||||
'click .js-add-checklist-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#check-action').value;
|
||||
const checklistName = this.find('#checklist-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'add') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'addChecklist',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'remove') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'removeChecklist',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
'click .js-add-checkall-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const actionSelected = this.find('#checkall-action').value;
|
||||
const checklistName = this.find('#checklist-name2').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'check') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'checkAll',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncheck') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'uncheckAll',
|
||||
checklistName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-check-item-action' (event) {
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const checkItemName = this.find('#checkitem-name');
|
||||
const checklistName = this.find('#checklist-name3');
|
||||
const actionSelected = this.find('#check-item-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
if (actionSelected === 'check') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'checkItem',
|
||||
checklistName,
|
||||
checkItemName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncheck') {
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'uncheckItem',
|
||||
checklistName,
|
||||
checkItemName,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
},
|
||||
|
||||
}).register('checklistActions');
|
||||
@@ -0,0 +1,11 @@
|
||||
template(name="mailActions")
|
||||
div.trigger-item.trigger-item-mail
|
||||
div.trigger-content.trigger-content-mail
|
||||
div.trigger-text.trigger-text-email
|
||||
| {{_'r-send-email'}}
|
||||
div.trigger-dropdown-mail
|
||||
input(id="email-to",type=text,placeholder="{{_'r-to'}}")
|
||||
input(id="email-subject",type=text,placeholder="{{_'r-subject'}}")
|
||||
textarea(id="email-msg")
|
||||
div.trigger-button.trigger-button-email.js-mail-action.js-goto-rules
|
||||
i.fa.fa-plus
|
||||
@@ -0,0 +1,35 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-mail-action' (event) {
|
||||
const emailTo = this.find('#email-to').value;
|
||||
const emailSubject = this.find('#email-subject').value;
|
||||
const emailMsg = this.find('#email-msg').value;
|
||||
const trigger = this.data().triggerVar.get();
|
||||
const ruleName = this.data().ruleName.get();
|
||||
const triggerId = Triggers.insert(trigger);
|
||||
const boardId = Session.get('currentBoard');
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const actionId = Actions.insert({
|
||||
actionType: 'sendEmail',
|
||||
emailTo,
|
||||
emailSubject,
|
||||
emailMsg,
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
Rules.insert({
|
||||
title: ruleName,
|
||||
triggerId,
|
||||
actionId,
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
}];
|
||||
},
|
||||
|
||||
}).register('mailActions');
|
||||
@@ -0,0 +1,20 @@
|
||||
template(name="ruleDetails")
|
||||
.rules
|
||||
h2
|
||||
i.fa.fa-magic
|
||||
| {{{_ 'r-rule-details' }}}
|
||||
.triggers-content
|
||||
.triggers-body
|
||||
.triggers-main-body
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
= trigger
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
= action
|
||||
div.rules-back
|
||||
button.js-goback
|
||||
i.fa.fa-chevron-left
|
||||
| {{{_ 'back'}}}
|
||||
@@ -0,0 +1,38 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.subscribe('allRules');
|
||||
this.subscribe('allTriggers');
|
||||
this.subscribe('allActions');
|
||||
|
||||
},
|
||||
|
||||
trigger() {
|
||||
const ruleId = this.data().ruleId;
|
||||
const rule = Rules.findOne({
|
||||
_id: ruleId.get(),
|
||||
});
|
||||
const trigger = Triggers.findOne({
|
||||
_id: rule.triggerId,
|
||||
});
|
||||
const desc = trigger.description();
|
||||
const upperdesc = desc.charAt(0).toUpperCase() + desc.substr(1);
|
||||
return upperdesc;
|
||||
},
|
||||
action() {
|
||||
const ruleId = this.data().ruleId;
|
||||
const rule = Rules.findOne({
|
||||
_id: ruleId.get(),
|
||||
});
|
||||
const action = Actions.findOne({
|
||||
_id: rule.actionId,
|
||||
});
|
||||
const desc = action.description();
|
||||
const upperdesc = desc.charAt(0).toUpperCase() + desc.substr(1);
|
||||
return upperdesc;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{}];
|
||||
},
|
||||
|
||||
}).register('ruleDetails');
|
||||
@@ -0,0 +1,181 @@
|
||||
.rules-list
|
||||
overflow:hidden
|
||||
overflow-y:scroll
|
||||
max-height: 400px
|
||||
.rules-lists-item
|
||||
display: block
|
||||
position: relative
|
||||
overflow: auto
|
||||
p
|
||||
display: inline-block
|
||||
float: left
|
||||
margin: revert
|
||||
.hide-element
|
||||
display:none !important
|
||||
.user-details
|
||||
display:inline-block
|
||||
.rules-btns-group
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
button
|
||||
margin: auto
|
||||
.rules-add
|
||||
display: block
|
||||
overflow: auto
|
||||
margin-top: 15px
|
||||
margin-bottom: 5px
|
||||
input
|
||||
display: inline-block
|
||||
float: right
|
||||
margin: auto
|
||||
margin-right: 10px
|
||||
button
|
||||
display: inline-block
|
||||
float: right
|
||||
margin: auto
|
||||
.rules-back
|
||||
display: block
|
||||
overflow: auto
|
||||
margin-top: 15px
|
||||
margin-bottom: 5px
|
||||
button
|
||||
display: inline-block
|
||||
float: right
|
||||
margin: auto
|
||||
margin-right:14px
|
||||
|
||||
.flex
|
||||
display: -webkit-box
|
||||
display: -moz-box
|
||||
display: -webkit-flex
|
||||
display: -moz-flex
|
||||
display: -ms-flexbox
|
||||
display: flex
|
||||
|
||||
|
||||
|
||||
.triggers-content
|
||||
color: #727479
|
||||
background: #dedede
|
||||
.triggers-body
|
||||
display flex
|
||||
padding-top 15px
|
||||
height 100%
|
||||
|
||||
.triggers-side-menu
|
||||
background-color: #f7f7f7
|
||||
border: 1px solid #f0f0f0
|
||||
border-radius: 4px
|
||||
height: intrinsic
|
||||
box-shadow: inset -1px -1px 3px rgba(0,0,0,.05)
|
||||
|
||||
ul
|
||||
|
||||
li
|
||||
margin: 0.1rem 0.2rem;
|
||||
width:50px
|
||||
height:50px
|
||||
text-align:center
|
||||
font-size: 25px
|
||||
position: relative
|
||||
|
||||
i
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
box-shadow: none
|
||||
transform: translate(-50%,-50%);
|
||||
|
||||
|
||||
&.active
|
||||
background #fff
|
||||
box-shadow 0 1px 2px rgba(0,0,0,0.15);
|
||||
|
||||
&: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
|
||||
.triggers-main-body
|
||||
padding: 0.1em 1em
|
||||
width:100%
|
||||
.trigger-item
|
||||
overflow:auto
|
||||
padding:10px
|
||||
height:40px
|
||||
margin-bottom:5px
|
||||
border-radius: 3px
|
||||
position: relative
|
||||
background-color: white
|
||||
.trigger-content
|
||||
position:absolute
|
||||
top:50%
|
||||
transform: translateY(-50%)
|
||||
left:10px
|
||||
.trigger-text
|
||||
font-size: 16px
|
||||
display:inline-block
|
||||
.trigger-inline-button
|
||||
font-size: 16px
|
||||
display: inline;
|
||||
padding: 6px;
|
||||
border: 1px solid #eee
|
||||
border-radius: 4px
|
||||
box-shadow: inset -1px -1px 3px rgba(0,0,0,.05)
|
||||
&:hover, &.is-active
|
||||
box-shadow: 0 0 0 2px darken(white, 60%) inset
|
||||
.trigger-text.trigger-text-email
|
||||
margin-left: 5px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
.trigger-dropdown
|
||||
display:inline-block
|
||||
select
|
||||
width:auto
|
||||
height:30px
|
||||
margin:0px
|
||||
margin-left:5px
|
||||
input
|
||||
display: inline-block
|
||||
width: 80px;
|
||||
margin: 0;
|
||||
.trigger-content-mail
|
||||
left:20px
|
||||
right:100px
|
||||
.trigger-button
|
||||
position:absolute
|
||||
top:50%
|
||||
transform: translateY(-50%)
|
||||
width:30px
|
||||
height:30px
|
||||
border: 1px solid #eee
|
||||
border-radius: 4px
|
||||
box-shadow: inset -1px -1px 3px rgba(0,0,0,.05)
|
||||
text-align:center
|
||||
font-size: 20px
|
||||
right:10px
|
||||
i
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
box-shadow: none
|
||||
transform: translate(-50%,-50%)
|
||||
&:hover, &.is-active
|
||||
box-shadow: 0 0 0 2px darken(white, 60%) inset
|
||||
.trigger-button.trigger-button-email
|
||||
top:30px
|
||||
.trigger-button.trigger-button-person
|
||||
right:-40px
|
||||
.trigger-item.trigger-item-mail
|
||||
height:300px
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
template(name="rulesActions")
|
||||
h2
|
||||
i.fa.fa-magic
|
||||
| {{{_ 'r-rule' }}} "#{data.ruleName.get}" - {{{_ 'r-add-action'}}}
|
||||
.triggers-content
|
||||
.triggers-body
|
||||
.triggers-side-menu
|
||||
ul
|
||||
li.active.js-set-board-actions
|
||||
i.fa.fa-columns
|
||||
li.js-set-card-actions
|
||||
i.fa.fa-sticky-note
|
||||
li.js-set-checklist-actions
|
||||
i.fa.fa-check
|
||||
li.js-set-mail-actions
|
||||
i.fa.fa-at
|
||||
.triggers-main-body
|
||||
if ($eq currentActions.get 'board')
|
||||
+boardActions(ruleName=data.ruleName triggerVar=data.triggerVar)
|
||||
else if ($eq currentActions.get 'card')
|
||||
+cardActions(ruleName=data.ruleName triggerVar=data.triggerVar)
|
||||
else if ($eq currentActions.get 'checklist')
|
||||
+checklistActions(ruleName=data.ruleName triggerVar=data.triggerVar)
|
||||
else if ($eq currentActions.get 'mail')
|
||||
+mailActions(ruleName=data.ruleName triggerVar=data.triggerVar)
|
||||
div.rules-back
|
||||
button.js-goback
|
||||
i.fa.fa-chevron-left
|
||||
| {{{_ 'back'}}}
|
||||
@@ -0,0 +1,58 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentActions = new ReactiveVar('board');
|
||||
},
|
||||
|
||||
setBoardActions() {
|
||||
this.currentActions.set('board');
|
||||
$('.js-set-card-actions').removeClass('active');
|
||||
$('.js-set-board-actions').addClass('active');
|
||||
$('.js-set-checklist-actions').removeClass('active');
|
||||
$('.js-set-mail-actions').removeClass('active');
|
||||
},
|
||||
setCardActions() {
|
||||
this.currentActions.set('card');
|
||||
$('.js-set-card-actions').addClass('active');
|
||||
$('.js-set-board-actions').removeClass('active');
|
||||
$('.js-set-checklist-actions').removeClass('active');
|
||||
$('.js-set-mail-actions').removeClass('active');
|
||||
},
|
||||
setChecklistActions() {
|
||||
this.currentActions.set('checklist');
|
||||
$('.js-set-card-actions').removeClass('active');
|
||||
$('.js-set-board-actions').removeClass('active');
|
||||
$('.js-set-checklist-actions').addClass('active');
|
||||
$('.js-set-mail-actions').removeClass('active');
|
||||
},
|
||||
setMailActions() {
|
||||
this.currentActions.set('mail');
|
||||
$('.js-set-card-actions').removeClass('active');
|
||||
$('.js-set-board-actions').removeClass('active');
|
||||
$('.js-set-checklist-actions').removeClass('active');
|
||||
$('.js-set-mail-actions').addClass('active');
|
||||
},
|
||||
|
||||
rules() {
|
||||
return Rules.find({});
|
||||
},
|
||||
|
||||
name() {
|
||||
// console.log(this.data());
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-set-board-actions'(){
|
||||
this.setBoardActions();
|
||||
},
|
||||
'click .js-set-card-actions'() {
|
||||
this.setCardActions();
|
||||
},
|
||||
'click .js-set-mail-actions'() {
|
||||
this.setMailActions();
|
||||
},
|
||||
'click .js-set-checklist-actions'() {
|
||||
this.setChecklistActions();
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('rulesActions');
|
||||
@@ -0,0 +1,27 @@
|
||||
template(name="rulesList")
|
||||
.rules
|
||||
h2
|
||||
i.fa.fa-magic
|
||||
| {{{_ 'r-board-rules' }}}
|
||||
|
||||
ul.rules-list
|
||||
each rules
|
||||
li.rules-lists-item
|
||||
p
|
||||
= title
|
||||
div.rules-btns-group
|
||||
button.js-goto-details
|
||||
i.fa.fa-eye
|
||||
| {{{_ 'r-view-rule'}}}
|
||||
if currentUser.isAdmin
|
||||
button.js-delete-rule
|
||||
i.fa.fa-trash-o
|
||||
| {{{_ 'r-delete-rule'}}}
|
||||
else
|
||||
li.no-items-message {{{_ 'r-no-rules' }}}
|
||||
if currentUser.isAdmin
|
||||
div.rules-add
|
||||
button.js-goto-trigger
|
||||
i.fa.fa-plus
|
||||
| {{{_ 'r-add-rule'}}}
|
||||
input(type=text,placeholder="{{{_ 'r-new-rule-name' }}}",id="ruleTitle")
|
||||
@@ -0,0 +1,15 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.subscribe('allRules');
|
||||
},
|
||||
|
||||
rules() {
|
||||
const boardId = Session.get('currentBoard');
|
||||
return Rules.find({
|
||||
boardId,
|
||||
});
|
||||
},
|
||||
events() {
|
||||
return [{}];
|
||||
},
|
||||
}).register('rulesList');
|
||||
@@ -0,0 +1,9 @@
|
||||
template(name="rulesMain")
|
||||
if($eq rulesCurrentTab.get 'rulesList')
|
||||
+rulesList
|
||||
if($eq rulesCurrentTab.get 'trigger')
|
||||
+rulesTriggers(ruleName=ruleName triggerVar=triggerVar)
|
||||
if($eq rulesCurrentTab.get 'action')
|
||||
+rulesActions(ruleName=ruleName triggerVar=triggerVar)
|
||||
if($eq rulesCurrentTab.get 'ruleDetails')
|
||||
+ruleDetails(ruleId=ruleId)
|
||||
@@ -0,0 +1,97 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.rulesCurrentTab = new ReactiveVar('rulesList');
|
||||
this.ruleName = new ReactiveVar('');
|
||||
this.triggerVar = new ReactiveVar();
|
||||
this.ruleId = new ReactiveVar();
|
||||
},
|
||||
|
||||
setTrigger() {
|
||||
this.rulesCurrentTab.set('trigger');
|
||||
},
|
||||
sanitizeObject(obj){
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if(obj[key] === '' || obj[key] === undefined){
|
||||
obj[key] = '*';
|
||||
}}
|
||||
);
|
||||
},
|
||||
setRulesList() {
|
||||
this.rulesCurrentTab.set('rulesList');
|
||||
},
|
||||
|
||||
setAction() {
|
||||
this.rulesCurrentTab.set('action');
|
||||
},
|
||||
|
||||
setRuleDetails() {
|
||||
this.rulesCurrentTab.set('ruleDetails');
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-delete-rule' () {
|
||||
const rule = this.currentData();
|
||||
Rules.remove(rule._id);
|
||||
Actions.remove(rule.actionId);
|
||||
Triggers.remove(rule.triggerId);
|
||||
|
||||
},
|
||||
'click .js-goto-trigger' (event) {
|
||||
event.preventDefault();
|
||||
const ruleTitle = this.find('#ruleTitle').value;
|
||||
if(ruleTitle !== undefined && ruleTitle !== ''){
|
||||
this.find('#ruleTitle').value = '';
|
||||
this.ruleName.set(ruleTitle);
|
||||
this.setTrigger();
|
||||
}
|
||||
},
|
||||
'click .js-goto-action' (event) {
|
||||
event.preventDefault();
|
||||
// Add user to the trigger
|
||||
const username = $(event.currentTarget.offsetParent).find('.user-name').val();
|
||||
let trigger = this.triggerVar.get();
|
||||
trigger.userId = '*';
|
||||
if(username !== undefined ){
|
||||
const userFound = Users.findOne({username});
|
||||
if(userFound !== undefined){
|
||||
trigger.userId = userFound._id;
|
||||
this.triggerVar.set(trigger);
|
||||
}
|
||||
}
|
||||
// Sanitize trigger
|
||||
trigger = this.triggerVar.get();
|
||||
this.sanitizeObject(trigger);
|
||||
this.triggerVar.set(trigger);
|
||||
this.setAction();
|
||||
},
|
||||
'click .js-show-user-field' (event) {
|
||||
event.preventDefault();
|
||||
$(event.currentTarget.offsetParent).find('.user-details').removeClass('hide-element');
|
||||
},
|
||||
'click .js-goto-rules' (event) {
|
||||
event.preventDefault();
|
||||
this.setRulesList();
|
||||
},
|
||||
'click .js-goback' (event) {
|
||||
event.preventDefault();
|
||||
if(this.rulesCurrentTab.get() === 'trigger' || this.rulesCurrentTab.get() === 'ruleDetails' ){
|
||||
this.setRulesList();
|
||||
}
|
||||
if(this.rulesCurrentTab.get() === 'action'){
|
||||
this.setTrigger();
|
||||
}
|
||||
},
|
||||
'click .js-goto-details' (event) {
|
||||
event.preventDefault();
|
||||
const rule = this.currentData();
|
||||
this.ruleId.set(rule._id);
|
||||
this.setRuleDetails();
|
||||
},
|
||||
|
||||
}];
|
||||
},
|
||||
|
||||
}).register('rulesMain');
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
template(name="rulesTriggers")
|
||||
h2
|
||||
i.fa.fa-magic
|
||||
| {{{_ 'r-rule' }}} "#{data.ruleName.get}" - {{{_ 'r-add-trigger'}}}
|
||||
.triggers-content
|
||||
.triggers-body
|
||||
.triggers-side-menu
|
||||
ul
|
||||
li.active.js-set-board-triggers
|
||||
i.fa.fa-columns
|
||||
li.js-set-card-triggers
|
||||
i.fa.fa-sticky-note
|
||||
li.js-set-checklist-triggers
|
||||
i.fa.fa-check
|
||||
.triggers-main-body
|
||||
if showBoardTrigger.get
|
||||
+boardTriggers
|
||||
else if showCardTrigger.get
|
||||
+cardTriggers
|
||||
else if showChecklistTrigger.get
|
||||
+checklistTriggers
|
||||
div.rules-back
|
||||
button.js-goback
|
||||
i.fa.fa-chevron-left
|
||||
| {{{_ 'back'}}}
|
||||
@@ -0,0 +1,53 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.showBoardTrigger = new ReactiveVar(true);
|
||||
this.showCardTrigger = new ReactiveVar(false);
|
||||
this.showChecklistTrigger = new ReactiveVar(false);
|
||||
},
|
||||
|
||||
setBoardTriggers() {
|
||||
this.showBoardTrigger.set(true);
|
||||
this.showCardTrigger.set(false);
|
||||
this.showChecklistTrigger.set(false);
|
||||
$('.js-set-card-triggers').removeClass('active');
|
||||
$('.js-set-board-triggers').addClass('active');
|
||||
$('.js-set-checklist-triggers').removeClass('active');
|
||||
},
|
||||
setCardTriggers() {
|
||||
this.showBoardTrigger.set(false);
|
||||
this.showCardTrigger.set(true);
|
||||
this.showChecklistTrigger.set(false);
|
||||
$('.js-set-card-triggers').addClass('active');
|
||||
$('.js-set-board-triggers').removeClass('active');
|
||||
$('.js-set-checklist-triggers').removeClass('active');
|
||||
},
|
||||
setChecklistTriggers() {
|
||||
this.showBoardTrigger.set(false);
|
||||
this.showCardTrigger.set(false);
|
||||
this.showChecklistTrigger.set(true);
|
||||
$('.js-set-card-triggers').removeClass('active');
|
||||
$('.js-set-board-triggers').removeClass('active');
|
||||
$('.js-set-checklist-triggers').addClass('active');
|
||||
},
|
||||
|
||||
rules() {
|
||||
return Rules.find({});
|
||||
},
|
||||
|
||||
name() {
|
||||
// console.log(this.data());
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-set-board-triggers' () {
|
||||
this.setBoardTriggers();
|
||||
},
|
||||
'click .js-set-card-triggers' () {
|
||||
this.setCardTriggers();
|
||||
},
|
||||
'click .js-set-checklist-triggers' () {
|
||||
this.setChecklistTriggers();
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('rulesTriggers');
|
||||
@@ -0,0 +1,117 @@
|
||||
template(name="boardTriggers")
|
||||
div.trigger-item#trigger-two
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-card'}}
|
||||
div.trigger-inline-button.js-open-card-title-popup
|
||||
i.fa.fa-filter
|
||||
div.trigger-text
|
||||
| {{_'r-is'}}
|
||||
div.trigger-text
|
||||
| {{_'r-added-to'}}
|
||||
div.trigger-text
|
||||
| {{_'r-list'}}
|
||||
div.trigger-dropdown
|
||||
input(id="create-list-name",type=text,placeholder="{{_'r-list-name'}}")
|
||||
div.trigger-text
|
||||
| {{_'r-in-swimlane'}}
|
||||
div.trigger-dropdown
|
||||
input(id="create-swimlane-name",type=text,placeholder="{{_'r-swimlane-name'}}")
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-create-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item#trigger-three
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-card'}}
|
||||
div.trigger-inline-button.js-open-card-title-popup
|
||||
i.fa.fa-filter
|
||||
div.trigger-text
|
||||
| {{_'r-is-moved'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-gen-moved-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item#trigger-four
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-card'}}
|
||||
div.trigger-inline-button.js-open-card-title-popup
|
||||
i.fa.fa-filter
|
||||
div.trigger-text
|
||||
| {{_'r-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="move-action")
|
||||
option(value="moved-to") {{_'r-moved-to'}}
|
||||
option(value="moved-from") {{_'r-moved-from'}}
|
||||
div.trigger-text
|
||||
| {{_'r-list'}}
|
||||
div.trigger-dropdown
|
||||
input(id="move-list-name",type=text,placeholder="{{_'r-list-name'}}")
|
||||
div.trigger-text
|
||||
| {{_'r-in-swimlane'}}
|
||||
div.trigger-dropdown
|
||||
input(id="create-swimlane-name",type=text,placeholder="{{_'r-swimlane-name'}}")
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-moved-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item#trigger-five
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-card'}}
|
||||
div.trigger-inline-button.js-open-card-title-popup
|
||||
i.fa.fa-filter
|
||||
div.trigger-text
|
||||
| {{_'r-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="arch-action")
|
||||
option(value="archived") {{_'r-archived'}}
|
||||
option(value="unarchived") {{_'r-unarchived'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-arch-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{{_'r-board-note'}}}
|
||||
|
||||
template(name="boardCardTitlePopup")
|
||||
form
|
||||
label
|
||||
| Card Title Filter
|
||||
input.js-card-filter-name(type="text" value=title autofocus)
|
||||
input.js-card-filter-button.primary.wide(type="submit" value="{{_ 'set-filter'}}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.provaVar = new ReactiveVar('');
|
||||
this.currentPopupTriggerId = 'def';
|
||||
this.cardTitleFilters = {};
|
||||
},
|
||||
setNameFilter(name){
|
||||
this.cardTitleFilters[this.currentPopupTriggerId] = name;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{
|
||||
'click .js-open-card-title-popup'(event){
|
||||
const funct = Popup.open('boardCardTitle');
|
||||
const divId = $(event.currentTarget.parentNode.parentNode).attr('id');
|
||||
//console.log('current popup');
|
||||
//console.log(this.currentPopupTriggerId);
|
||||
this.currentPopupTriggerId = divId;
|
||||
funct.call(this, event);
|
||||
},
|
||||
'click .js-add-create-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const listName = this.find('#create-list-name').value;
|
||||
const swimlaneName = this.find('#create-swimlane-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
const divId = $(event.currentTarget.parentNode).attr('id');
|
||||
const cardTitle = this.cardTitleFilters[divId];
|
||||
// move to generic funciont
|
||||
datas.triggerVar.set({
|
||||
activityType: 'createCard',
|
||||
boardId,
|
||||
cardTitle,
|
||||
swimlaneName,
|
||||
listName,
|
||||
desc,
|
||||
});
|
||||
},
|
||||
'click .js-add-moved-trigger' (event) {
|
||||
const datas = this.data();
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const swimlaneName = this.find('#create-swimlane-name').value;
|
||||
const actionSelected = this.find('#move-action').value;
|
||||
const listName = this.find('#move-list-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'moved-to') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'moveCard',
|
||||
boardId,
|
||||
listName,
|
||||
swimlaneName,
|
||||
'oldListName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'moved-from') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'moveCard',
|
||||
boardId,
|
||||
swimlaneName,
|
||||
'listName': '*',
|
||||
'oldListName': listName,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-moved-trigger' (event){
|
||||
const datas = this.data();
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const boardId = Session.get('currentBoard');
|
||||
|
||||
datas.triggerVar.set({
|
||||
'activityType': 'moveCard',
|
||||
boardId,
|
||||
'swimlaneName': '*',
|
||||
'listName':'*',
|
||||
'oldListName': '*',
|
||||
desc,
|
||||
});
|
||||
},
|
||||
'click .js-add-arc-trigger' (event) {
|
||||
const datas = this.data();
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const actionSelected = this.find('#arch-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'archived') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'archivedCard',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unarchived') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'restoredCard',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
}];
|
||||
},
|
||||
|
||||
}).register('boardTriggers');
|
||||
|
||||
|
||||
Template.boardCardTitlePopup.events({
|
||||
submit(evt, tpl) {
|
||||
const title = tpl.$('.js-card-filter-name').val().trim();
|
||||
Popup.getOpenerComponent().setNameFilter(title);
|
||||
evt.preventDefault();
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,114 @@
|
||||
template(name="cardTriggers")
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-label-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="label-action")
|
||||
option(value="added") {{_'r-added-to'}}
|
||||
option(value="removed") {{_'r-removed-from'}}
|
||||
div.trigger-text
|
||||
| {{_'r-a-card'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-gen-label-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-the-label-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="spec-label")
|
||||
each labels
|
||||
option(value="#{_id}" style="background-color: #{name}")
|
||||
= translatedname
|
||||
div.trigger-text
|
||||
| {{_'r-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="spec-label-action")
|
||||
option(value="added") {{_'r-added-to'}}
|
||||
option(value="removed") {{_'r-removed-from'}}
|
||||
div.trigger-text
|
||||
| {{_'r-a-card'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-spec-label-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-member'}}
|
||||
div.trigger-dropdown
|
||||
select(id="gen-member-action")
|
||||
option(value="added") {{_'r-added-to'}}
|
||||
option(value="removed") {{_'r-removed-from'}}
|
||||
div.trigger-text
|
||||
| {{_'r-a-card'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-gen-member-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-the-member'}}
|
||||
div.trigger-dropdown
|
||||
input(id="spec-member",type=text,placeholder="{{_'r-name'}}")
|
||||
div.trigger-text
|
||||
| {{_'r-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="spec-member-action")
|
||||
option(value="added") {{_'r-added-to'}}
|
||||
option(value="removed") {{_'r-removed-from'}}
|
||||
div.trigger-text
|
||||
| {{_'r-a-card'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-spec-member-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-attach'}}
|
||||
div.trigger-text
|
||||
| {{_'r-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="attach-action")
|
||||
option(value="added") {{_'r-added-to'}}
|
||||
option(value="removed") {{_'r-removed-from'}}
|
||||
div.trigger-text
|
||||
| {{_'r-a-card'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-attachment-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
@@ -0,0 +1,131 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.subscribe('allRules');
|
||||
},
|
||||
labels() {
|
||||
const labels = Boards.findOne(Session.get('currentBoard')).labels;
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
if (labels[i].name === '' || labels[i].name === undefined) {
|
||||
labels[i].name = labels[i].color;
|
||||
labels[i].translatedname = `${TAPi18n.__(`color-${ labels[i].color}`)}`;
|
||||
} else {
|
||||
labels[i].translatedname = labels[i].name;
|
||||
}
|
||||
}
|
||||
return labels;
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-gen-label-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#label-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addedLabel',
|
||||
boardId,
|
||||
'labelId': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removedLabel',
|
||||
boardId,
|
||||
'labelId': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-label-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-label-action').value;
|
||||
const labelId = this.find('#spec-label').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addedLabel',
|
||||
boardId,
|
||||
labelId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removedLabel',
|
||||
boardId,
|
||||
labelId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-member-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-member-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'joinMember',
|
||||
boardId,
|
||||
'username': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'unjoinMember',
|
||||
boardId,
|
||||
'username': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-member-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-member-action').value;
|
||||
const username = this.find('#spec-member').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'joinMember',
|
||||
boardId,
|
||||
username,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'unjoinMember',
|
||||
boardId,
|
||||
username,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-attachment-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#attach-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'added') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addAttachment',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'deleteAttachment',
|
||||
boardId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
},
|
||||
}).register('cardTriggers');
|
||||
@@ -0,0 +1,125 @@
|
||||
template(name="checklistTriggers")
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-checklist'}}
|
||||
div.trigger-dropdown
|
||||
select(id="gen-check-action")
|
||||
option(value="created") {{_'r-added-to'}}
|
||||
option(value="removed") {{_'r-removed-from'}}
|
||||
div.trigger-text
|
||||
| {{_'r-a-card'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-gen-check-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-the-checklist'}}
|
||||
div.trigger-dropdown
|
||||
input(id="check-name",type=text,placeholder="{{_'r-name'}}")
|
||||
div.trigger-text
|
||||
| {{_'r-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="spec-check-action")
|
||||
option(value="created") {{_'r-added-to'}}
|
||||
option(value="removed") {{_'r-removed-from'}}
|
||||
div.trigger-text
|
||||
| {{_'r-a-card'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-spec-check-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-checklist'}}
|
||||
div.trigger-dropdown
|
||||
select(id="gen-comp-check-action")
|
||||
option(value="completed") {{_'r-completed'}}
|
||||
option(value="uncompleted") {{_'r-made-incomplete'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-gen-comp-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-the-checklist'}}
|
||||
div.trigger-dropdown
|
||||
input(id="spec-comp-check-name",type=text,placeholder="{{_'r-name'}}")
|
||||
div.trigger-text
|
||||
| {{_'r-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="spec-comp-check-action")
|
||||
option(value="completed") {{_'r-completed'}}
|
||||
option(value="uncompleted") {{_'r-made-incomplete'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-spec-comp-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-a-item'}}
|
||||
div.trigger-dropdown
|
||||
select(id="check-item-gen-action")
|
||||
option(value="checked") {{_'r-checked'}}
|
||||
option(value="unchecked") {{_'r-unchecked'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-gen-check-item-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
|
||||
div.trigger-item
|
||||
div.trigger-content
|
||||
div.trigger-text
|
||||
| {{_'r-when-the-item'}}
|
||||
div.trigger-dropdown
|
||||
input(id="check-item-name",type=text,placeholder="{{_'r-name'}}")
|
||||
div.trigger-text
|
||||
| {{_'r-is'}}
|
||||
div.trigger-dropdown
|
||||
select(id="check-item-spec-action")
|
||||
option(value="checked") {{_'r-checked'}}
|
||||
option(value="unchecked") {{_'r-unchecked'}}
|
||||
div.trigger-button.trigger-button-person.js-show-user-field
|
||||
i.fa.fa-user
|
||||
div.user-details.hide-element
|
||||
div.trigger-text
|
||||
| {{_'r-by'}}
|
||||
div.trigger-dropdown
|
||||
input(class="user-name",type=text,placeholder="{{_'username'}}")
|
||||
div.trigger-button.js-add-spec-check-item-trigger.js-goto-action
|
||||
i.fa.fa-plus
|
||||
@@ -0,0 +1,146 @@
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.subscribe('allRules');
|
||||
},
|
||||
events() {
|
||||
return [{
|
||||
'click .js-add-gen-check-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-check-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-check-action').value;
|
||||
const checklistId = this.find('#check-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'created') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'addChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'removed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'removeChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-comp-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#gen-comp-check-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'completed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'completeChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncompleted') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncompleteChecklist',
|
||||
boardId,
|
||||
'checklistName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-comp-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#spec-comp-check-action').value;
|
||||
const checklistId = this.find('#spec-comp-check-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'completed') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'completeChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'uncompleted') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncompleteChecklist',
|
||||
boardId,
|
||||
'checklistName': checklistId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-gen-check-item-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#check-item-gen-action').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'checked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'checkedItem',
|
||||
boardId,
|
||||
'checklistItemName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unchecked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncheckedItem',
|
||||
boardId,
|
||||
'checklistItemName': '*',
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-add-spec-check-item-trigger' (event) {
|
||||
const desc = Utils.getTriggerActionDesc(event, this);
|
||||
const datas = this.data();
|
||||
const actionSelected = this.find('#check-item-spec-action').value;
|
||||
const checklistItemId = this.find('#check-item-name').value;
|
||||
const boardId = Session.get('currentBoard');
|
||||
if (actionSelected === 'checked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'checkedItem',
|
||||
boardId,
|
||||
'checklistItemName': checklistItemId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
if (actionSelected === 'unchecked') {
|
||||
datas.triggerVar.set({
|
||||
activityType: 'uncheckedItem',
|
||||
boardId,
|
||||
'checklistItemName': checklistItemId,
|
||||
desc,
|
||||
});
|
||||
}
|
||||
},
|
||||
}];
|
||||
},
|
||||
|
||||
}).register('checklistTriggers');
|
||||
@@ -0,0 +1,6 @@
|
||||
template(name='connectionMethod')
|
||||
div.at-form-authentication
|
||||
label {{_ 'authentication-method'}}
|
||||
select.select-authentication
|
||||
each authentications
|
||||
option(value="{{value}}") {{_ value}}
|
||||
@@ -0,0 +1,34 @@
|
||||
Template.connectionMethod.onCreated(function() {
|
||||
this.authenticationMethods = new ReactiveVar([]);
|
||||
|
||||
Meteor.call('getAuthenticationsEnabled', (_, result) => {
|
||||
if (result) {
|
||||
// TODO : add a management of different languages
|
||||
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
|
||||
this.authenticationMethods.set([
|
||||
{value: 'password'},
|
||||
// Gets only the authentication methods availables
|
||||
...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
|
||||
]);
|
||||
}
|
||||
|
||||
// If only the default authentication available, hides the select boxe
|
||||
const content = $('.at-form-authentication');
|
||||
if (!(this.authenticationMethods.get().length > 1)) {
|
||||
content.hide();
|
||||
} else {
|
||||
content.show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Template.connectionMethod.onRendered(() => {
|
||||
// Moves the select boxe in the first place of the at-pwd-form div
|
||||
$('.at-form-authentication').detach().prependTo('.at-pwd-form');
|
||||
});
|
||||
|
||||
Template.connectionMethod.helpers({
|
||||
authentications() {
|
||||
return Template.instance().authenticationMethods.get();
|
||||
},
|
||||
});
|
||||
@@ -17,7 +17,7 @@ template(name='statistics')
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
th {{_ 'Wekan_version'}}
|
||||
th Wekan {{_ 'info'}}
|
||||
td {{statistics.version}}
|
||||
tr
|
||||
th {{_ 'Node_version'}}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
Template.invitationCode.onRendered(() => {
|
||||
const setting = Settings.findOne();
|
||||
if (setting || setting.disableRegistration) {
|
||||
$('#invitationcode').hide();
|
||||
}
|
||||
Template.invitationCode.onRendered(function() {
|
||||
Meteor.subscribe('setting', {
|
||||
onReady() {
|
||||
const setting = Settings.findOne();
|
||||
|
||||
if (!setting || !setting.disableRegistration) {
|
||||
$('#invitationcode').hide();
|
||||
}
|
||||
|
||||
return this.stop();
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -27,6 +27,7 @@ template(name="peopleGeneral")
|
||||
th {{_ 'verified'}}
|
||||
th {{_ 'createdAt'}}
|
||||
th {{_ 'active'}}
|
||||
th {{_ 'authentication-method'}}
|
||||
th
|
||||
each user in peopleList
|
||||
+peopleRow(userId=user._id)
|
||||
@@ -52,6 +53,7 @@ template(name="peopleRow")
|
||||
| {{_ 'no'}}
|
||||
else
|
||||
| {{_ 'yes'}}
|
||||
td {{_ userData.authenticationMethod }}
|
||||
td
|
||||
a.edit-user
|
||||
| {{_ 'edit'}}
|
||||
@@ -66,12 +68,18 @@ template(name="editUserPopup")
|
||||
| {{_ 'username'}}
|
||||
span.error.hide.username-taken
|
||||
| {{_ 'error-username-taken'}}
|
||||
input.js-profile-username(type="text" value=user.username)
|
||||
if isLdap
|
||||
input.js-profile-username(type="text" value=user.username readonly)
|
||||
else
|
||||
input.js-profile-username(type="text" value=user.username)
|
||||
label
|
||||
| {{_ 'email'}}
|
||||
span.error.hide.email-taken
|
||||
| {{_ 'error-email-taken'}}
|
||||
input.js-profile-email(type="email" value="{{user.emails.[0].address}}")
|
||||
if isLdap
|
||||
input.js-profile-email(type="email" value="{{user.emails.[0].address}}" readonly)
|
||||
else
|
||||
input.js-profile-email(type="email" value="{{user.emails.[0].address}}")
|
||||
label
|
||||
| {{_ 'admin'}}
|
||||
select.select-role.js-profile-isadmin
|
||||
@@ -82,9 +90,17 @@ template(name="editUserPopup")
|
||||
select.select-active.js-profile-isactive
|
||||
option(value="false") {{_ 'yes'}}
|
||||
option(value="true" selected="{{user.loginDisabled}}") {{_ 'no'}}
|
||||
label
|
||||
| {{_ 'authentication-type'}}
|
||||
select.select-authenticationMethod.js-authenticationMethod
|
||||
each authentications
|
||||
if isSelected value
|
||||
option(value="{{value}}" selected) {{_ value}}
|
||||
else
|
||||
option(value="{{value}}") {{_ value}}
|
||||
hr
|
||||
label
|
||||
| {{_ 'password'}}
|
||||
input.js-profile-password(type="password")
|
||||
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
@@ -62,10 +62,39 @@ Template.peopleRow.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editUserPopup.onCreated(function() {
|
||||
this.authenticationMethods = new ReactiveVar([]);
|
||||
|
||||
Meteor.call('getAuthenticationsEnabled', (_, result) => {
|
||||
if (result) {
|
||||
// TODO : add a management of different languages
|
||||
// (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
|
||||
this.authenticationMethods.set([
|
||||
{value: 'password'},
|
||||
// Gets only the authentication methods availables
|
||||
...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Template.editUserPopup.helpers({
|
||||
user() {
|
||||
return Users.findOne(this.userId);
|
||||
},
|
||||
authentications() {
|
||||
return Template.instance().authenticationMethods.get();
|
||||
},
|
||||
isSelected(match) {
|
||||
const userId = Template.instance().data.userId;
|
||||
const selected = Users.findOne(userId).authenticationMethod;
|
||||
return selected === match;
|
||||
},
|
||||
isLdap() {
|
||||
const userId = Template.instance().data.userId;
|
||||
const selected = Users.findOne(userId).authenticationMethod;
|
||||
return selected === 'ldap';
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -91,6 +120,7 @@ Template.editUserPopup.events({
|
||||
const isAdmin = tpl.find('.js-profile-isadmin').value.trim();
|
||||
const isActive = tpl.find('.js-profile-isactive').value.trim();
|
||||
const email = tpl.find('.js-profile-email').value.trim();
|
||||
const authentication = tpl.find('.js-authenticationMethod').value.trim();
|
||||
|
||||
const isChangePassword = password.length > 0;
|
||||
const isChangeUserName = username !== user.username;
|
||||
@@ -101,6 +131,7 @@ Template.editUserPopup.events({
|
||||
'profile.fullname': fullname,
|
||||
'isAdmin': isAdmin === 'true',
|
||||
'loginDisabled': isActive === 'true',
|
||||
'authenticationMethod': authentication,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
overflow: scroll;
|
||||
|
||||
table
|
||||
font-family: arial, sans-serif;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
color: #000;
|
||||
|
||||
td, th
|
||||
border: 1px solid #d2d0d0;
|
||||
|
||||
Loaded 100 of 270 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user