Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab823795cf | ||
|
|
d90ab03bbe | ||
|
|
bf053e0018 | ||
|
|
46c911bb2a | ||
|
|
2bc8df9188 | ||
|
|
6367b18f4f | ||
|
|
a307bfa1ec | ||
|
|
cfb3caf504 | ||
|
|
daf524238b | ||
|
|
5435604448 | ||
|
|
64500f1f73 | ||
|
|
d5ee0b567a | ||
|
|
5ec4988efe | ||
|
|
e41072dcdf | ||
|
|
126ddda45d | ||
|
|
bec460f77e | ||
|
|
fd56d4c63c | ||
|
|
d9cefb84b8 | ||
|
|
3013114c1f | ||
|
|
095dfdb24f | ||
|
|
a032586aaf | ||
|
|
d16c141821 | ||
|
|
dcf75f0bcd | ||
|
|
282a9e3097 | ||
|
|
0e4b121209 | ||
|
|
3e469c5a44 | ||
|
|
e760a4140b | ||
|
|
2048975e92 | ||
|
|
cf5312ed2e | ||
|
|
8f33ab3487 | ||
|
|
d4d6a5f96d | ||
|
|
a55992276d | ||
|
|
6405c35bc0 | ||
|
|
bd3d7ec9c9 | ||
|
|
cd51ad7508 | ||
|
|
bd5bb2a8a9 | ||
|
|
9bc6366993 | ||
|
|
b6c7737f73 | ||
|
|
6a9d738348 | ||
|
|
6e2f84673e | ||
|
|
63201e992c | ||
|
|
f512047ac6 | ||
|
|
c4293ecd95 | ||
|
|
f047c6da29 | ||
|
|
59874d16b9 | ||
|
|
026236edc9 | ||
|
|
e91e68c48c | ||
|
|
d840cb3be7 | ||
|
|
2b1d2222cc | ||
|
|
b6fdbbd2bb | ||
|
|
0c54c1540c | ||
|
|
fae5f6d64a | ||
|
|
17c4c44426 | ||
|
|
67835e3421 | ||
|
|
832bd6187c | ||
|
|
6507d2be71 | ||
|
|
21785a9322 | ||
|
|
fc387d0ddf | ||
|
|
27e749abb8 | ||
|
|
83143ee570 | ||
|
|
d4252f3056 | ||
|
|
1dd97ebd49 | ||
|
|
bfa15d4719 | ||
|
|
4b22f29826 | ||
|
|
0f14bd3358 | ||
|
|
f7d5d65c0c | ||
|
|
86fbfd3c50 | ||
|
|
cfde9d5a61 | ||
|
|
a9932823c4 | ||
|
|
67b99a88fc | ||
|
|
455ec8c3d8 | ||
|
|
42ece21fa1 | ||
|
|
663ecab51e | ||
|
|
dbae718f4d | ||
|
|
52e76c5496 | ||
|
|
71b6cd630a | ||
|
|
2c36fe3d45 | ||
|
|
28b14777c2 | ||
|
|
4e97a5351a | ||
|
|
2ab5009de7 | ||
|
|
65668e91fe | ||
|
|
d6098dbedf | ||
|
|
fe6b94e539 | ||
|
|
00e60ff855 | ||
|
|
c4882707e9 | ||
|
|
402528d97e | ||
|
|
4e25e335a9 | ||
|
|
972981a579 | ||
|
|
ce5abf8299 | ||
|
|
8bb31861a7 | ||
|
|
7d2e40ee47 | ||
|
|
f0de48a3c4 | ||
|
|
799e66c078 | ||
|
|
55903472aa | ||
|
|
22c7d3b07f | ||
|
|
485737f6b0 | ||
|
|
b756150f76 | ||
|
|
40108d63bb | ||
|
|
8f3103e329 | ||
|
|
9bf1478587 | ||
|
|
91cf377948 | ||
|
|
ff4c8a5d23 | ||
|
|
439f76e48a | ||
|
|
720d4223e2 | ||
|
|
4518e2d23c | ||
|
|
c9a24c38d4 | ||
|
|
b54d17467c | ||
|
|
5c93de8911 | ||
|
|
8323649cf0 | ||
|
|
75fbf00eb7 | ||
|
|
820908ab05 | ||
|
|
4fe168b03b | ||
|
|
4825bcec8d | ||
|
|
0f99f22fa5 | ||
|
|
051d1670b7 | ||
|
|
ef0e1f9089 | ||
|
|
974c177237 | ||
|
|
75de7b119d | ||
|
|
b088763272 | ||
|
|
c45bf4e368 | ||
|
|
af5054e7fb | ||
|
|
9529d71732 | ||
|
|
74ea237c2a | ||
|
|
c8f1d99fe6 | ||
|
|
a22631915b | ||
|
|
05df419a46 | ||
|
|
0f1b57fee4 | ||
|
|
e69a9f17b8 | ||
|
|
de3467a6d5 | ||
|
|
7214f6d49a | ||
|
|
c1bc6d4733 | ||
|
|
3d4f97364c | ||
|
|
2c74240bcb | ||
|
|
90da40fde0 | ||
|
|
fe985e219a | ||
|
|
f5355422ee | ||
|
|
aae7960f25 | ||
|
|
b99f878c98 | ||
|
|
4b61f38cba | ||
|
|
4085a98bcd | ||
|
|
524e3b098f | ||
|
|
dc8595ad68 | ||
|
|
a76067abfa | ||
|
|
6ef10a54bd | ||
|
|
c9e80eaa03 | ||
|
|
e47dcf734a | ||
|
|
4dfcc31506 | ||
|
|
14e5c96f73 | ||
|
|
53e569d285 | ||
|
|
71381dce95 | ||
|
|
0cc63b810c | ||
|
|
7724b0d5bb | ||
|
|
6206eca725 | ||
|
|
069e2c69b2 | ||
|
|
0e902d73a8 | ||
|
|
be72274ca5 | ||
|
|
ab0dacc65a | ||
|
|
cfa0a063ce | ||
|
|
26abb2d99d | ||
|
|
396ca6967e | ||
|
|
52ce6da32c | ||
|
|
e64942a909 | ||
|
|
b38e2fa35f | ||
|
|
16991498fb | ||
|
|
7ebdd1b68b | ||
|
|
79e61f6d64 | ||
|
|
a130e9b52b | ||
|
|
e103bf46dd | ||
|
|
92375c3c2b | ||
|
|
eacb310e59 | ||
|
|
4dbbb29573 | ||
|
|
73e690a5fe | ||
|
|
f2acf67cb6 | ||
|
|
637442563e | ||
|
|
8ae70ed0c0 | ||
|
|
240e05ba16 | ||
|
|
028dd9f0be | ||
|
|
33c2b4b74a | ||
|
|
8cba42efbe | ||
|
|
be2e002720 | ||
|
|
59c0b378a1 | ||
|
|
b68493b009 | ||
|
|
c777bf39d1 | ||
|
|
fa32010a65 | ||
|
|
a29e40359a | ||
|
|
549982b5e1 | ||
|
|
110030ac60 | ||
|
|
676591c317 | ||
|
|
22469ba18b | ||
|
|
6425e5bf04 | ||
|
|
eb62c7a0e4 | ||
|
|
2c20f7c6db | ||
|
|
44e0f897c9 | ||
|
|
3a6f99f47c | ||
|
|
31f8c54571 | ||
|
|
f516f12b13 | ||
|
|
8a37cabec4 | ||
|
|
d0a04522d0 | ||
|
|
7ada41e741 | ||
|
|
ea39cfbf5a | ||
|
|
7eea0207e4 | ||
|
|
68d2b470a7 | ||
|
|
47478ee2b4 | ||
|
|
3b49f10897 | ||
|
|
1f778a5d89 | ||
|
|
522ab40f43 | ||
|
|
e431ce8405 | ||
|
|
b40654cdfe | ||
|
|
b64715f70a | ||
|
|
c27a00a4a8 | ||
|
|
f5eccc1605 | ||
|
|
05c6e101ff | ||
|
|
1ed057daf3 | ||
|
|
ab944e51c7 | ||
|
|
d11082385f | ||
|
|
22d98fec38 | ||
|
|
870fd3d526 | ||
|
|
73a25775e1 | ||
|
|
9a9dbd26f9 | ||
|
|
09bdc666c6 | ||
|
|
cd8aa91aa8 | ||
|
|
fcb9e99528 | ||
|
|
c6a8b4bcbc | ||
|
|
6b88ae2ba2 | ||
|
|
e1478f2f45 | ||
|
|
a45b101193 | ||
|
|
8610f7356c | ||
|
|
3aedc24c2b | ||
|
|
7d1ab0a388 | ||
|
|
56a1617c7e | ||
|
|
7baa95fcab | ||
|
|
8f70ae1cd0 | ||
|
|
32fb38e539 | ||
|
|
dfe05cc924 | ||
|
|
3e77e27d7b | ||
|
|
c138212592 | ||
|
|
473e2c28c6 | ||
|
|
8d9df72114 | ||
|
|
ffdeed099e | ||
|
|
f56ac1d45e | ||
|
|
c898a3f5ea | ||
|
|
941fccd10a | ||
|
|
d971428f49 | ||
|
|
688de96615 | ||
|
|
ecfce447be | ||
|
|
4933bf8203 | ||
|
|
adaa3dcc58 | ||
|
|
e3a85df053 | ||
|
|
d72b436542 | ||
|
|
7f9aa75093 | ||
|
|
451f860b5d | ||
|
|
ee4a3b5b35 | ||
|
|
e1e2461e21 | ||
|
|
74b98a5ee4 | ||
|
|
e67f5b3967 | ||
|
|
6fdd3d6c53 | ||
|
|
089913ff4c | ||
|
|
55f084dc7d | ||
|
|
36cf471e69 | ||
|
|
ed03a9ccaf | ||
|
|
b9f74c5add | ||
|
|
4a45d32d4f | ||
|
|
b818f0e5cd | ||
|
|
0be4eff3f4 | ||
|
|
18087f756e | ||
|
|
30f50fc705 | ||
|
|
47468a4750 | ||
|
|
968a32164b | ||
|
|
3b936ff6e7 | ||
|
|
75d524f615 | ||
|
|
fac3ff9c37 | ||
|
|
f4e315cf52 | ||
|
|
021db14a43 | ||
|
|
6215e3b584 | ||
|
|
a781c0e7dc | ||
|
|
922452fe41 | ||
|
|
c22e71e3a2 | ||
|
|
e01fd2b158 | ||
|
|
2d652df176 | ||
|
|
777c8c1ae1 | ||
|
|
edcc937a6b | ||
|
|
873d8f16d8 | ||
|
|
9a505c7ccc | ||
|
|
65a231a040 | ||
|
|
daa57fd05e | ||
|
|
72fa2972f0 | ||
|
|
00826660b4 | ||
|
|
8b12018e26 | ||
|
|
86814a0fd8 | ||
|
|
1a35db62b1 | ||
|
|
894ce65a79 | ||
|
|
e60eddda04 | ||
|
|
d9c26c7c49 | ||
|
|
e5ddee13c1 | ||
|
|
a415becbf7 | ||
|
|
80312d3052 | ||
|
|
1f216f5bff | ||
|
|
2c99d1cecb | ||
|
|
427eb8ebd1 | ||
|
|
ac67a25891 | ||
|
|
a5cb55935b | ||
|
|
2a190fdc19 | ||
|
|
66fa59d715 | ||
|
|
7f91055d8c | ||
|
|
29f153d71d | ||
|
|
7c80a34cf2 | ||
|
|
41cdfa4496 | ||
|
|
e83945c1a6 | ||
|
|
a15c54833f | ||
|
|
cb2a683f0a | ||
|
|
db0c91fee5 | ||
|
|
7d754a137c | ||
|
|
8ea9780140 | ||
|
|
925099a1ee | ||
|
|
16a2cc6ba9 | ||
|
|
94bc6d8e45 | ||
|
|
da28af516d | ||
|
|
73b5bbfc9e | ||
|
|
e2dde19db2 | ||
|
|
f421d135a6 | ||
|
|
ae4232465b | ||
|
|
112b628a99 | ||
|
|
bf313ca842 | ||
|
|
a0da05c660 | ||
|
|
394149b2e0 | ||
|
|
9ce51d8db2 | ||
|
|
76591632c5 | ||
|
|
b03ea37944 | ||
|
|
6e490b0f8d | ||
|
|
32e50b869d | ||
|
|
6a38b9fb0f | ||
|
|
1f059898ca | ||
|
|
cb4ce5cddf | ||
|
|
7dfb1eb6ce | ||
|
|
9857d4a5bf | ||
|
|
73f943f89a | ||
|
|
d5308d2d04 | ||
|
|
b62221504d | ||
|
|
9d5af24e7b | ||
|
|
fe20a5b54e | ||
|
|
b068d07ce5 | ||
|
|
a494c768f2 | ||
|
|
7b94188f64 | ||
|
|
249d3b0739 | ||
|
|
e181f3a385 | ||
|
|
90797066e7 | ||
|
|
13711f02b6 | ||
|
|
f8adc3605a | ||
|
|
f75fd82339 | ||
|
|
45edeccef9 | ||
|
|
6ffde36d84 | ||
|
|
17aa702860 | ||
|
|
4c7f1090fd | ||
|
|
baf1ddc8e5 | ||
|
|
38b1301258 | ||
|
|
f295f42363 | ||
|
|
0716eba84e | ||
|
|
c47d5ca64e | ||
|
|
7d48fe4c23 | ||
|
|
f4a6ce9f31 | ||
|
|
c769bed7dd | ||
|
|
b1525d4221 | ||
|
|
4153fe7d0d | ||
|
|
c570e426a1 | ||
|
|
271877565b | ||
|
|
d6b960f79a | ||
|
|
be938ebd4b | ||
|
|
558d406148 | ||
|
|
f61b9239de | ||
|
|
409bd50d67 | ||
|
|
48dcd11af0 | ||
|
|
08afc05c38 | ||
|
|
28e97808f5 | ||
|
|
8a1c2e8860 | ||
|
|
4b22174183 | ||
|
|
9bf3b960a1 | ||
|
|
fa58d0ec3b | ||
|
|
03f7fd6340 | ||
|
|
d48068f63c | ||
|
|
998f3fe8a7 | ||
|
|
81ece7cec2 | ||
|
|
30073ce1d6 | ||
|
|
fc9fb9132c | ||
|
|
2f8c01eccf | ||
|
|
6d1705af34 | ||
|
|
fa6db6118d | ||
|
|
f555acee6f | ||
|
|
4bc5799b4f | ||
|
|
b2b8a99899 | ||
|
|
9641ac7ee8 | ||
|
|
f9823f67bd | ||
|
|
af14ff6e89 | ||
|
|
323f1b1d87 | ||
|
|
3e23a5ddce | ||
|
|
037bba83d6 | ||
|
|
a60acac51a | ||
|
|
eea0b1afc4 | ||
|
|
9a6d0c4aad | ||
|
|
1c530632ac | ||
|
|
18c49695b1 | ||
|
|
c8a743bb8e | ||
|
|
7e92068955 | ||
|
|
8c84254392 | ||
|
|
6b1403984f | ||
|
|
c965cc9b76 | ||
|
|
ab40737211 | ||
|
|
950b477d9d | ||
|
|
529a43fa0e | ||
|
|
eb16cd19bb | ||
|
|
fbd64a6a9e | ||
|
|
7536c7b532 | ||
|
|
406766a82b | ||
|
|
5bb794e3bb | ||
|
|
ee73e04004 | ||
|
|
672be413f7 | ||
|
|
b0ba57a165 | ||
|
|
79c2301ad5 | ||
|
|
b2c7be244e | ||
|
|
8f8c045930 | ||
|
|
21ffff3977 | ||
|
|
7d1bca3702 | ||
|
|
d93a58d892 | ||
|
|
99d9291a28 | ||
|
|
bda166ba50 | ||
|
|
f190a102f9 | ||
|
|
d0ba89d913 | ||
|
|
cb4f3c8895 | ||
|
|
fd003f4402 | ||
|
|
3a22d4efdc | ||
|
|
48c763dbe3 | ||
|
|
10a9aef974 | ||
|
|
eecb143977 | ||
|
|
767bbc5195 | ||
|
|
b107fb0017 | ||
|
|
a94cc5351f | ||
|
|
7865368153 | ||
|
|
1be3a5c918 | ||
|
|
10d90046b9 | ||
|
|
16eb499806 | ||
|
|
ef39dfc051 | ||
|
|
9f6e6bdc81 | ||
|
|
a60102f6c6 | ||
|
|
f0620ce880 | ||
|
|
c40b27d9ad | ||
|
|
0161f19ba7 | ||
|
|
4885934477 | ||
|
|
f7b34bc85b | ||
|
|
f9ed7e1baf | ||
|
|
aca6ba5270 | ||
|
|
31636eb0c0 | ||
|
|
a773abaf2f | ||
|
|
7001f8edaf | ||
|
|
7b607b2aaf | ||
|
|
f3845549fe | ||
|
|
d3a87beef1 | ||
|
|
62c2d59f46 | ||
|
|
b73e65d63e | ||
|
|
0b1f906576 | ||
|
|
e5423989a1 | ||
|
|
a8197a990e | ||
|
|
e4ae57bf7f | ||
|
|
6ad5ede317 | ||
|
|
1271409989 | ||
|
|
d17d12e719 | ||
|
|
a5f918c670 | ||
|
|
5d6367f404 | ||
|
|
8dc7aa490a | ||
|
|
bf0187f2c4 | ||
|
|
6c34b75061 | ||
|
|
c4fdc5987b | ||
|
|
70c2f0f89f | ||
|
|
bcdad79621 | ||
|
|
d9a4d7a521 | ||
|
|
a50cc9fb8c | ||
|
|
ab19bbce8e | ||
|
|
25c8f07c62 | ||
|
|
db40c94f02 | ||
|
|
871f68058e | ||
|
|
3f91dd23bc | ||
|
|
7baf7d2256 | ||
|
|
76b9ce4fe3 | ||
|
|
a2f479f4bd | ||
|
|
fafd8a077f | ||
|
|
caa8d08738 | ||
|
|
13979389ca | ||
|
|
350a52935a | ||
|
|
945e57bb43 | ||
|
|
3c374a2891 | ||
|
|
c9074bd8d9 | ||
|
|
bc685c59e0 | ||
|
|
aa53ad38fc | ||
|
|
875442f1ce | ||
|
|
a8c071f04c | ||
|
|
ef6c9c66af | ||
|
|
3df38447e9 | ||
|
|
338aabc02f | ||
|
|
912717ebf9 | ||
|
|
9f1a2ec625 | ||
|
|
93f0a69c53 | ||
|
|
2846522450 | ||
|
|
be8374d100 | ||
|
|
399f63cef6 | ||
|
|
adf8f7b69a | ||
|
|
8e9206bdee | ||
|
|
1946ba150d | ||
|
|
60cb5fb017 | ||
|
|
1892a17855 | ||
|
|
588e051f0f | ||
|
|
1c5857f064 | ||
|
|
1067542b94 | ||
|
|
538e197147 | ||
|
|
36db6c6e2d | ||
|
|
e53b63541e | ||
|
|
d6ca13a61d | ||
|
|
7caf817c81 | ||
|
|
286617e7be | ||
|
|
a423f7883e | ||
|
|
6d949ccdb4 | ||
|
|
0e0ac36788 | ||
|
|
99ab6d13d4 | ||
|
|
dcf7232958 | ||
|
|
a4408b55ed | ||
|
|
06f0ceebd1 | ||
|
|
5fe78a477c | ||
|
|
7ef8de50bd | ||
|
|
6646b8638e | ||
|
|
46840c9373 | ||
|
|
76bd3bfe1d | ||
|
|
2a2ed1eb2c | ||
|
|
a533605608 | ||
|
|
7749c0bd9a | ||
|
|
a37caf459e | ||
|
|
1c0a1e021a | ||
|
|
f483b162ce | ||
|
|
42fad8fb98 | ||
|
|
316acd0366 | ||
|
|
f80ecded0a | ||
|
|
40a5422e75 | ||
|
|
726fd5d60d | ||
|
|
211656e9da | ||
|
|
5eb4eb04c4 | ||
|
|
fb3b47e2bb | ||
|
|
14ac099e87 | ||
|
|
f83ee124d0 | ||
|
|
59ee616304 | ||
|
|
25989b9736 | ||
|
|
fef6433b75 | ||
|
|
ea72ce1fa2 | ||
|
|
26e326a204 | ||
|
|
ebb356cf2d | ||
|
|
d6142e4b75 | ||
|
|
c7b554afd9 | ||
|
|
30c1b5cca8 | ||
|
|
0c58bcf99c | ||
|
|
f3d58339ae | ||
|
|
67c6dd7bee | ||
|
|
f2d52b55c8 | ||
|
|
8c72531e6d | ||
|
|
b2f19ed714 | ||
|
|
e5359dc838 | ||
|
|
6fd3c0fa6b | ||
|
|
981a641bce | ||
|
|
47c70f03a2 | ||
|
|
030359aa0e | ||
|
|
81c9bb6899 | ||
|
|
4d192a0165 | ||
|
|
33a8de7bb5 | ||
|
|
9125ab440d | ||
|
|
1c966ed7b4 | ||
|
|
6a4b03324c | ||
|
|
4a8dcde8ee | ||
|
|
c2139662cd | ||
|
|
c0ecfb87b0 | ||
|
|
7673c77c57 | ||
|
|
66c2140911 | ||
|
|
ea3144492e | ||
|
|
10a9306abd | ||
|
|
76aae41637 | ||
|
|
49071ca5fd | ||
|
|
08e3ca3435 | ||
|
|
6b7b464acc | ||
|
|
767d8e66e7 | ||
|
|
e30edce73e | ||
|
|
f5796faa84 | ||
|
|
b48297df22 | ||
|
|
c262620993 | ||
|
|
41685536d7 | ||
|
|
92052458f5 | ||
|
|
6c3170360b | ||
|
|
cd3a27941c | ||
|
|
8a98f03f1a | ||
|
|
5552c17eba | ||
|
|
92733b24d1 | ||
|
|
806ad78637 | ||
|
|
9e557f6887 | ||
|
|
ad9daba60d | ||
|
|
2c92524cf4 | ||
|
|
06e374f0ff | ||
|
|
48ba81eb3d | ||
|
|
0767f50af8 | ||
|
|
68610e5066 | ||
|
|
ba9e2debe2 | ||
|
|
6c8a5cc580 | ||
|
|
3a7eece244 | ||
|
|
bf27ffb92e | ||
|
|
3fc9c6efe7 | ||
|
|
fe2015735a | ||
|
|
2ab34d3b87 | ||
|
|
79ea4d6d1e | ||
|
|
54f7361767 | ||
|
|
0a7ffe4cb0 | ||
|
|
0e714a90e0 | ||
|
|
5e3a9dc059 | ||
|
|
6e1ef3d94a | ||
|
|
bf48d4371c | ||
|
|
2f2aa7b08a | ||
|
|
20b0422024 | ||
|
|
dcb9fb7465 | ||
|
|
3a287a494b | ||
|
|
0926943670 | ||
|
|
06e26d8d89 | ||
|
|
94953a1c97 | ||
|
|
02e9795816 | ||
|
|
c29cff3822 | ||
|
|
29216599e4 | ||
|
|
3b65113d05 | ||
|
|
a182482cfb | ||
|
|
9022e9949f | ||
|
|
cecf69af02 | ||
|
|
b7dbeba7de | ||
|
|
c6ee58ef88 | ||
|
|
1b3135a79f | ||
|
|
131959e050 | ||
|
|
3f3b4746a9 | ||
|
|
30d154724a | ||
|
|
16130b3f73 | ||
|
|
47427f89b0 | ||
|
|
384d80a647 | ||
|
|
43d11af631 | ||
|
|
9bda4372a5 | ||
|
|
61223503e3 | ||
|
|
4d2ad079dc | ||
|
|
50c071682b | ||
|
|
015ccb7a28 | ||
|
|
b9aed314f3 | ||
|
|
452e2e2408 | ||
|
|
9370c1d472 | ||
|
|
8143dc9196 | ||
|
|
98ba18b052 | ||
|
|
9860f20be3 | ||
|
|
cfbb06ed1f | ||
|
|
e39760eb19 | ||
|
|
b3da55f85c | ||
|
|
bc4b637f06 | ||
|
|
c993c8f47e | ||
|
|
40470bedb3 | ||
|
|
912479baa4 | ||
|
|
cfbf3e320c | ||
|
|
4431ec5a27 | ||
|
|
d7784a8d38 | ||
|
|
2c4ed5f298 | ||
|
|
6a97aeb60c | ||
|
|
c7dc912dd4 | ||
|
|
0ee886692e | ||
|
|
34fef627a1 | ||
|
|
52d36d3780 | ||
|
|
8555c24eb2 | ||
|
|
c41c26e5a1 | ||
|
|
35cc9b3fcc | ||
|
|
dbfd219491 | ||
|
|
0da5f26668 | ||
|
|
8bf40e219a | ||
|
|
8f1f8c0fb9 | ||
|
|
386a65d6c6 | ||
|
|
a73ce72e46 | ||
|
|
d0616d8d67 | ||
|
|
6ad007a5f1 | ||
|
|
95d3e5cdc6 | ||
|
|
08a3506f12 | ||
|
|
520f295733 | ||
|
|
7da5121ddd | ||
|
|
3222625286 | ||
|
|
05d952b864 | ||
|
|
a3837ccbc7 | ||
|
|
ac8199177f | ||
|
|
140a134a11 | ||
|
|
c1b3e5367c | ||
|
|
b637a8e8ea | ||
|
|
999323526c | ||
|
|
2e0168283e | ||
|
|
13039c895a | ||
|
|
4148238af3 | ||
|
|
7a98445370 | ||
|
|
7ef3bba9f7 | ||
|
|
492306453c | ||
|
|
e12353bd7e | ||
|
|
a3622ddc1d | ||
|
|
ac95be9e41 | ||
|
|
4a3fc42d1f | ||
|
|
557f9709ac | ||
|
|
4468c71d98 | ||
|
|
c8491749e0 | ||
|
|
e5123f2d81 | ||
|
|
41664f16a3 | ||
|
|
8c9c0e9316 | ||
|
|
f21bd16cec | ||
|
|
edc5a5b6a2 | ||
|
|
7d4de21be0 | ||
|
|
5babe0358d | ||
|
|
62ef1fe7cc | ||
|
|
4be5727a18 | ||
|
|
e128a5775d | ||
|
|
ba9f9705d4 | ||
|
|
3aae07695c | ||
|
|
4393deb130 | ||
|
|
83ae1d4a19 | ||
|
|
94992fca44 | ||
|
|
a8af5343a7 | ||
|
|
070e8831b4 | ||
|
|
f177e416ee | ||
|
|
ffea7aff99 | ||
|
|
01b16566c8 | ||
|
|
549b15000b | ||
|
|
d05b84dff9 | ||
|
|
c6cad13f1e | ||
|
|
a8d1210655 | ||
|
|
45d33fa2e4 | ||
|
|
81ceb1ca0f | ||
|
|
923d26ad81 | ||
|
|
583f114c68 | ||
|
|
b29ec93c34 | ||
|
|
c7ee585a62 | ||
|
|
a7721b9048 | ||
|
|
184fd3abdd | ||
|
|
5fd2f1ee8a | ||
|
|
23218d3808 | ||
|
|
9b8de53bfe | ||
|
|
9130410f09 | ||
|
|
1f57a3f62e | ||
|
|
6938cec583 | ||
|
|
3c2cc351f4 | ||
|
|
ebeadeef60 | ||
|
|
7d59ae93f9 | ||
|
|
59e15d262f | ||
|
|
712417b379 | ||
|
|
cad55d57d7 | ||
|
|
cca7e59eb2 | ||
|
|
59a358903a | ||
|
|
2eadd3eb8b | ||
|
|
0261bb592e | ||
|
|
7862e280ec | ||
|
|
13fecfae27 | ||
|
|
b7819cad69 | ||
|
|
028fba9996 | ||
|
|
ce94433c46 | ||
|
|
442eefbe6b | ||
|
|
84c3b38052 | ||
|
|
8557d91468 | ||
|
|
949a3a1337 | ||
|
|
24288919e2 | ||
|
|
92c57fc91e | ||
|
|
9d1e7abd07 | ||
|
|
8dfdbe7a16 | ||
|
|
15fbe7ca50 | ||
|
|
9aa8bc8921 | ||
|
|
e90791fdbc | ||
|
|
b869e3efb6 | ||
|
|
7e8073d621 | ||
|
|
c10f7b6134 | ||
|
|
1a71abbc1d | ||
|
|
0a46d06c1f | ||
|
|
23595674cf | ||
|
|
6fead14c11 | ||
|
|
52d54f35a3 | ||
|
|
3c1eff6241 | ||
|
|
fb2972396a | ||
|
|
4d9f76cc68 | ||
|
|
924c579680 | ||
|
|
ca2ea2c13e | ||
|
|
b579dc598b | ||
|
|
04c5f60287 | ||
|
|
123789aff0 | ||
|
|
74b66fdcaf | ||
|
|
3f09b54e04 | ||
|
|
c39403c307 | ||
|
|
13ea7bf3d7 | ||
|
|
dc2835d341 | ||
|
|
94ff2d6a9e | ||
|
|
6479c6a5c5 | ||
|
|
4b04af9a9c | ||
|
|
26d2efdedb | ||
|
|
0ec9c4c37b | ||
|
|
f2e4a280d7 | ||
|
|
a1ddf8e6d2 | ||
|
|
102b39cd29 | ||
|
|
b7373a27b8 | ||
|
|
fb0b762293 | ||
|
|
5b799b4536 | ||
|
|
8050d7353d | ||
|
|
66b1736bb3 | ||
|
|
f33ea5baf0 | ||
|
|
5959fd1fd0 | ||
|
|
c8ec8d456b | ||
|
|
87b394949e | ||
|
|
d93481d840 | ||
|
|
20fa441cfe | ||
|
|
46281542c5 | ||
|
|
5de6d5954a | ||
|
|
7a3c340d65 | ||
|
|
5e8669be4e | ||
|
|
df40384200 | ||
|
|
515556a784 | ||
|
|
af63259f09 | ||
|
|
bb807d53ba | ||
|
|
38279e2a32 | ||
|
|
3e6b396d09 | ||
|
|
42fc182832 | ||
|
|
3f1bba4f1a | ||
|
|
b008986122 | ||
|
|
26b30c817c | ||
|
|
b1083607d4 | ||
|
|
78f9a3303e | ||
|
|
a5519983fe | ||
|
|
bc0a879ca1 | ||
|
|
4c016169c5 | ||
|
|
46c29a0b97 | ||
|
|
9ab0efe87a | ||
|
|
6638d8215f | ||
|
|
e1a00090ba | ||
|
|
44bfe89ea8 | ||
|
|
b75e16f104 | ||
|
|
695cdfcc4e | ||
|
|
285ef2e36d | ||
|
|
fb3aa6ef4f | ||
|
|
0eb5cd05f5 | ||
|
|
3d1c70b96b | ||
|
|
946c288925 | ||
|
|
04464dcdb3 | ||
|
|
8a4f25527e | ||
|
|
581fd9c9fb | ||
|
|
fbce80a4c8 | ||
|
|
1ee3215cf3 | ||
|
|
95efcbe717 | ||
|
|
594d8a8ba1 | ||
|
|
9933f3a583 | ||
|
|
23bcf88aa2 | ||
|
|
21009f5ffe | ||
|
|
2810520612 | ||
|
|
3e088b4eb1 | ||
|
|
8e98eebe03 | ||
|
|
1f11f8add5 | ||
|
|
7192bc944b | ||
|
|
04aaa6f30c | ||
|
|
4405693b1f | ||
|
|
47380244b8 | ||
|
|
4640effb16 | ||
|
|
01a1eb177e | ||
|
|
0afac8bbff | ||
|
|
0a59e9732e | ||
|
|
ed92db87bc | ||
|
|
e892a6104a | ||
|
|
d489b4bf23 | ||
|
|
8c6df7f20c | ||
|
|
49d0fb2eae | ||
|
|
edf08c8cd4 | ||
|
|
ebe6946233 | ||
|
|
54aad2d067 | ||
|
|
d06d22fd65 | ||
|
|
940fbda494 | ||
|
|
58cd17468c | ||
|
|
977c63eb2f | ||
|
|
b9cbab2577 | ||
|
|
2dafd52610 | ||
|
|
3b95728567 | ||
|
|
825742359e | ||
|
|
56e29dae33 | ||
|
|
4b005b4b39 | ||
|
|
8fb1a45cee | ||
|
|
41c1dde504 | ||
|
|
985ee3df91 | ||
|
|
467835192f | ||
|
|
7a929024f2 | ||
|
|
6698c34dbb | ||
|
|
3fbf9ab98f | ||
|
|
bd747cea24 | ||
|
|
be61e7c10c | ||
|
|
515275583e | ||
|
|
e386c16a7f | ||
|
|
3bba30cc1e | ||
|
|
85f02a1b7c | ||
|
|
528c5f2627 | ||
|
|
780e453cf5 | ||
|
|
f3ebc4a28e | ||
|
|
7df32ff4ac | ||
|
|
985882a4ad | ||
|
|
986bc27001 | ||
|
|
053f944691 | ||
|
|
85080519a1 | ||
|
|
7569cc54ab | ||
|
|
923f5d6bb1 | ||
|
|
c8b82576a3 | ||
|
|
aa8926e61c | ||
|
|
6d6b9c831a | ||
|
|
650ed70f92 | ||
|
|
a3d37e621c | ||
|
|
06799b19b0 | ||
|
|
e6a13d7220 | ||
|
|
45e2827516 | ||
|
|
0b395d03f6 | ||
|
|
2d8ac3bf80 | ||
|
|
301580d61a | ||
|
|
b0a2c590fe | ||
|
|
9bb56d3fc0 | ||
|
|
8162654d6f | ||
|
|
b33a3ef254 | ||
|
|
a89c52cfdb | ||
|
|
06aaf7cb8d | ||
|
|
f929cdae9c | ||
|
|
20eb6338de | ||
|
|
6c76c277c7 | ||
|
|
49ef80ab6c | ||
|
|
8b4193149b | ||
|
|
2b4dd3b5ce | ||
|
|
006560b96d | ||
|
|
32ec95ffa8 | ||
|
|
10c2512d74 | ||
|
|
28e5b0bd5b | ||
|
|
87fc4f936a | ||
|
|
71d3135bd2 | ||
|
|
6052b0d042 | ||
|
|
e83b0ac651 | ||
|
|
30ada60862 | ||
|
|
d136a5206c | ||
|
|
ea908adbf9 | ||
|
|
97d0eada57 | ||
|
|
352546f554 | ||
|
|
b583e67ae8 | ||
|
|
9de7040401 | ||
|
|
8951d0aa57 | ||
|
|
582bc3a4c4 | ||
|
|
5f21823c43 | ||
|
|
11162585ca | ||
|
|
dad5d2a905 | ||
|
|
2d683ea198 | ||
|
|
c082a48e67 | ||
|
|
ba387bcc84 | ||
|
|
c8d1a576b0 | ||
|
|
3e691f8b25 | ||
|
|
f700970cd4 | ||
|
|
2828027cc8 | ||
|
|
4a5d05a19f | ||
|
|
0867f728d1 | ||
|
|
0bd8b298d7 | ||
|
|
6d8cf57868 | ||
|
|
4790d31ff7 | ||
|
|
d7243bba77 | ||
|
|
0225eefa66 | ||
|
|
18240c9d07 | ||
|
|
4b4fcda66d | ||
|
|
97c6b65315 | ||
|
|
8f176421da | ||
|
|
0bd9f309a1 | ||
|
|
6fb9529518 | ||
|
|
e6c59fc9b0 | ||
|
|
a12d465881 | ||
|
|
a921bde6ac | ||
|
|
8fbfd5ad0a | ||
|
|
acaefab0f4 | ||
|
|
952affc6df | ||
|
|
521e840b54 | ||
|
|
752a558b30 | ||
|
|
40c077101a | ||
|
|
f7075f4ccb | ||
|
|
9fc5283856 | ||
|
|
e0d63db1ab | ||
|
|
d519429ea1 | ||
|
|
e4d3ae4c3f | ||
|
|
f7e45273a1 | ||
|
|
f57fc3cd6e | ||
|
|
033b8c5d48 | ||
|
|
ad26e57e78 | ||
|
|
e9e3e14695 | ||
|
|
e127c33bae | ||
|
|
5192f4a326 | ||
|
|
2260f214ed | ||
|
|
2b91557ad2 | ||
|
|
c1f0017867 | ||
|
|
20ea0dba95 | ||
|
|
712dbb8e73 | ||
|
|
f9b760bfc4 | ||
|
|
14d2209587 | ||
|
|
bcd43629cc | ||
|
|
8985799c20 | ||
|
|
3300bbe9e7 | ||
|
|
14bd59a101 | ||
|
|
c0bdde26cf | ||
|
|
01edbb2351 | ||
|
|
cbad4cf594 | ||
|
|
11b61b8fe2 | ||
|
|
1d0da28d9e | ||
|
|
23f6c107c6 | ||
|
|
95f8a05cbd | ||
|
|
167863d957 | ||
|
|
22fc643f25 | ||
|
|
4d9d4a9dfd | ||
|
|
93766fe6ed | ||
|
|
aef6967d4c | ||
|
|
0d4ec25593 | ||
|
|
2b13158fcd | ||
|
|
a1ff7b9673 | ||
|
|
aeb0f1e6d8 | ||
|
|
5034c957ba | ||
|
|
620f932dcd | ||
|
|
5f5c75bb0f | ||
|
|
7184f4c66e | ||
|
|
1d5c2c63b9 | ||
|
|
f660aa13d1 | ||
|
|
47ac33d6c2 | ||
|
|
ce35799506 | ||
|
|
418fe725e5 | ||
|
|
ba7818c51e | ||
|
|
9af03b0416 | ||
|
|
0defe76fe3 | ||
|
|
76ac070f9b | ||
|
|
f0a6fa68ea | ||
|
|
2da7b1d542 | ||
|
|
053e817393 | ||
|
|
f11650ece1 | ||
|
|
aff94a6770 | ||
|
|
0090734cd8 | ||
|
|
af816fe50f | ||
|
|
e7122a9b36 | ||
|
|
1db4161538 | ||
|
|
2295b218e3 | ||
|
|
0053ce2708 | ||
|
|
fb37f48a86 | ||
|
|
974ad26f06 | ||
|
|
5539653492 | ||
|
|
11e8cbf66f | ||
|
|
a45feacd3f | ||
|
|
9aeb5b4f05 | ||
|
|
ed0fa5df5c | ||
|
|
70de2d48be | ||
|
|
c0acc336cc | ||
|
|
c068dfe87b | ||
|
|
21f37db562 | ||
|
|
030faf918e | ||
|
|
8df318bced | ||
|
|
bbf1f3a43f | ||
|
|
f258d8d517 | ||
|
|
0985438df5 | ||
|
|
9d0956511d | ||
|
|
d6b8c76cc7 | ||
|
|
7d69bd5c46 | ||
|
|
cfd283e7e3 | ||
|
|
999c20f3fa | ||
|
|
85f9996b41 | ||
|
|
c4e07d6a53 | ||
|
|
2aba1e3bf9 | ||
|
|
5e5847c5e4 | ||
|
|
224c257c4f | ||
|
|
28a0a97ca4 | ||
|
|
662a4a5f00 | ||
|
|
df152e292d | ||
|
|
d92a97ff1e | ||
|
|
115cb016a0 | ||
|
|
7cda243b42 | ||
|
|
46b573462f | ||
|
|
5b3bd8ff2a | ||
|
|
6faae766a8 | ||
|
|
bf636725e8 | ||
|
|
bafba7aa6a | ||
|
|
e1984831bd | ||
|
|
c826438328 | ||
|
|
e39390ab92 | ||
|
|
120f57894f | ||
|
|
b89a01fddf | ||
|
|
21e4a64dc7 | ||
|
|
4aeab872de | ||
|
|
a0a2c9375e | ||
|
|
d1dd9ba955 | ||
|
|
e48db7d7ea | ||
|
|
239dea793a | ||
|
|
b0ce378629 | ||
|
|
7de50c0f4b | ||
|
|
07926386ea | ||
|
|
f1c962fbbc | ||
|
|
5d79c231ed | ||
|
|
972f0481e5 | ||
|
|
49781aae17 | ||
|
|
51f22f48dc | ||
|
|
a64381153f | ||
|
|
10280966ac | ||
|
|
16ad6bf9fc | ||
|
|
1aa5c23bba | ||
|
|
9216a69c05 | ||
|
|
7b2cff4c5e | ||
|
|
bf42958059 | ||
|
|
1deccf2f4b | ||
|
|
57755ef413 | ||
|
|
c2cf850179 | ||
|
|
67838ae29e | ||
|
|
4d22477477 | ||
|
|
96b5ed1df6 | ||
|
|
b9ec43c0f7 | ||
|
|
8a0899a819 | ||
|
|
e390852a67 | ||
|
|
c4083c23d6 | ||
|
|
bdc751ac24 | ||
|
|
0e23975964 | ||
|
|
ad53414c4d | ||
|
|
23e01130e9 | ||
|
|
f779c35d0e | ||
|
|
6e5d260fd2 | ||
|
|
691efb7843 | ||
|
|
67500abcd4 | ||
|
|
092b121c38 | ||
|
|
c27a2b22c6 | ||
|
|
9213451dca | ||
|
|
139a1c0f78 | ||
|
|
41d5ecd605 | ||
|
|
fd9478dc57 | ||
|
|
f1092c6f40 | ||
|
|
51b31df2c8 | ||
|
|
2f46f2a888 | ||
|
|
cae0b29500 | ||
|
|
aa0e1955b0 | ||
|
|
78bac6ca99 | ||
|
|
382168a5b4 | ||
|
|
e34cfad06b | ||
|
|
71f6697f55 | ||
|
|
ff993e7c91 | ||
|
|
b349ecbdf7 | ||
|
|
c3577aa434 | ||
|
|
ed5ab735b4 | ||
|
|
ed058914b5 | ||
|
|
c730dd1523 | ||
|
|
ba418cb0e7 | ||
|
|
dd6e7372b7 | ||
|
|
32bce898f4 | ||
|
|
aca50e7dda | ||
|
|
42550c2637 | ||
|
|
7cd4271881 | ||
|
|
b04f504adc | ||
|
|
f8bc186252 | ||
|
|
3083302e14 | ||
|
|
6f3e187dfb | ||
|
|
bc61e6c4f0 | ||
|
|
3bdbc80c30 | ||
|
|
d7554fc585 | ||
|
|
a909ddfca8 | ||
|
|
7ca4894789 | ||
|
|
e2b94c9bcb | ||
|
|
c16f7eaf22 | ||
|
|
d9a7e135d3 | ||
|
|
4bb63a147e | ||
|
|
10c1df7af4 | ||
|
|
fbd2b7e46b | ||
|
|
11151b2359 | ||
|
|
b5d35d4645 | ||
|
|
953a355e63 | ||
|
|
962fb70b16 | ||
|
|
27971e6a66 | ||
|
|
3c5ab1ad72 | ||
|
|
b7b8b05661 | ||
|
|
d8bbcd8e03 | ||
|
|
9946cb60fa | ||
|
|
a22319f3ab | ||
|
|
7a00f3628a | ||
|
|
de49900961 | ||
|
|
1733203f8e | ||
|
|
e3f4049943 | ||
|
|
2ae344e685 | ||
|
|
f049a44280 | ||
|
|
d8593c68e9 | ||
|
|
c1261baaee | ||
|
|
c78ed0828f | ||
|
|
9b31604c0c | ||
|
|
a9c66d1883 | ||
|
|
362fb66b84 | ||
|
|
a3e3e70a3a | ||
|
|
b4963d872b | ||
|
|
e108dbd02c | ||
|
|
2442e2a372 | ||
|
|
cfbbab6b86 | ||
|
|
d32d46d22d | ||
|
|
0043fe010f | ||
|
|
b13ebcea12 | ||
|
|
38af077a94 | ||
|
|
bed2669ff8 | ||
|
|
7d7c45c33f | ||
|
|
c7330a47a0 | ||
|
|
2d7639262f | ||
|
|
f93219c293 | ||
|
|
1124117f0c | ||
|
|
133a81b08c | ||
|
|
0609629d75 | ||
|
|
1362f0f58d | ||
|
|
beaa50551d | ||
|
|
de67f4fdc7 | ||
|
|
eda2b3b406 | ||
|
|
c38cbf920c | ||
|
|
434ce9f7c4 | ||
|
|
bd0b539183 | ||
|
|
1e73f9b909 | ||
|
|
e0aad13fef | ||
|
|
05139ed553 | ||
|
|
93845a00eb | ||
|
|
cd1750f368 | ||
|
|
617d4f9fbf | ||
|
|
cc90291192 | ||
|
|
8092f8be28 | ||
|
|
c817deef9e | ||
|
|
5379541733 | ||
|
|
83fb18b868 | ||
|
|
a7fb36bebb | ||
|
|
17a06ad1b1 | ||
|
|
733c0e1710 | ||
|
|
1511009c6f | ||
|
|
2baaf6bea5 | ||
|
|
e7d5879ef4 | ||
|
|
a0318e59c1 | ||
|
|
e5ad06d7f1 | ||
|
|
608059b1ce | ||
|
|
bc67b5c3bc | ||
|
|
38e8aad9cf | ||
|
|
4a5cd5b001 | ||
|
|
e2cbe751ec | ||
|
|
f7e2267498 | ||
|
|
4c814ce3fe | ||
|
|
ec51e4cf39 | ||
|
|
dda37688ad | ||
|
|
f2a92be01a | ||
|
|
5591319259 | ||
|
|
f20656909b | ||
|
|
d184779cc3 | ||
|
|
0ae8e4912b | ||
|
|
67742c8070 | ||
|
|
391607ec79 | ||
|
|
fc68b68069 | ||
|
|
f1ff6d1e1f | ||
|
|
8aaabb5a3a | ||
|
|
a8498e3990 | ||
|
|
52355b4cfe | ||
|
|
fb6f618917 | ||
|
|
028633b00a | ||
|
|
3d6e77dba9 | ||
|
|
246163a5d3 | ||
|
|
015b1428b0 | ||
|
|
62dc63c033 | ||
|
|
c1a9d62cf9 | ||
|
|
182e1d4bf4 | ||
|
|
d65f8cda1b | ||
|
|
3846b130d8 | ||
|
|
9a838e7990 | ||
|
|
b31eba7481 | ||
|
|
2b2bb5d6e2 | ||
|
|
161b20834f | ||
|
|
d6322100c9 | ||
|
|
f22fe5497b | ||
|
|
62242b4a96 | ||
|
|
3c6d66f86b | ||
|
|
a4f99a3a55 | ||
|
|
1914422181 | ||
|
|
d90e78e417 | ||
|
|
02fe918388 | ||
|
|
e0b1c0018c | ||
|
|
6e99a0cb25 | ||
|
|
ec0967b859 | ||
|
|
558093ff0c | ||
|
|
6d77172d54 | ||
|
|
209d946f94 | ||
|
|
160add6d73 | ||
|
|
71d35b0525 | ||
|
|
b4cc3a8756 | ||
|
|
21e2eabd60 | ||
|
|
0ad73ee0c0 | ||
|
|
f69834034f | ||
|
|
36716283f4 | ||
|
|
6e3f25c771 | ||
|
|
25f85db453 | ||
|
|
67c41afe9a | ||
|
|
b8dba93b52 | ||
|
|
abc5601942 | ||
|
|
ccbc7f6122 | ||
|
|
5104e74303 | ||
|
|
6b59c31e4f | ||
|
|
f6d7377977 | ||
|
|
cae4cc33f8 | ||
|
|
f9318f494a | ||
|
|
5f946707e5 | ||
|
|
25c9f7ee21 | ||
|
|
88604b634b | ||
|
|
abb4388e29 | ||
|
|
78e5778527 | ||
|
|
12810673a8 | ||
|
|
d1f519917b | ||
|
|
ac1568770f | ||
|
|
ed3a512518 | ||
|
|
ceea4a9b9f | ||
|
|
a561d1b633 | ||
|
|
b17b9ea7c5 | ||
|
|
c4d1ffc2e5 | ||
|
|
0dde88adb7 | ||
|
|
fe1e1983ad | ||
|
|
f044a8ba48 | ||
|
|
689e37e434 | ||
|
|
b3cf2ee419 | ||
|
|
49546b7e3b | ||
|
|
baa9651da3 | ||
|
|
2be0385c25 | ||
|
|
cb336b4ec2 | ||
|
|
f485ccea2f | ||
|
|
1165b21b1b | ||
|
|
b9ae5a19c7 | ||
|
|
1777f52bc1 | ||
|
|
d189212e9c | ||
|
|
92d87e76bc | ||
|
|
9caf63840b | ||
|
|
66fc0679ff | ||
|
|
26b52a1da8 | ||
|
|
1e84109ea6 | ||
|
|
1ee7a85e0f | ||
|
|
253d1aee46 | ||
|
|
49404203ab | ||
|
|
bd03669d73 | ||
|
|
351fbdffd7 | ||
|
|
fa6ef6e21c | ||
|
|
e8d483098e | ||
|
|
7936961264 | ||
|
|
80505c4078 | ||
|
|
5507db8950 | ||
|
|
2ad56af57b | ||
|
|
d268713fd7 | ||
|
|
252b2f6f87 | ||
|
|
530d64898b | ||
|
|
a3e70fbfdc | ||
|
|
743d9d2be8 | ||
|
|
84057381f4 | ||
|
|
38f7384fd7 | ||
|
|
5c0a389fb9 | ||
|
|
e7d1e2fc9c | ||
|
|
e62b19affe | ||
|
|
3735d21be2 | ||
|
|
57c09e15ac | ||
|
|
90ea11859d | ||
|
|
6a4e17e039 | ||
|
|
0849df2e88 | ||
|
|
3b59620ee3 | ||
|
|
73648ec1e4 | ||
|
|
c490d5b35a | ||
|
|
0ddabbaf75 | ||
|
|
85a51de703 | ||
|
|
f304c3ad52 | ||
|
|
60bc603399 | ||
|
|
c9466c688b | ||
|
|
047bd3e065 | ||
|
|
fbae3b0e55 | ||
|
|
087ad7d4e5 | ||
|
|
a3ecda64cf | ||
|
|
f841296714 | ||
|
|
20e3282edd | ||
|
|
686275d656 | ||
|
|
2ef75a810d | ||
|
|
474cb73329 | ||
|
|
b3039c74a5 | ||
|
|
d710168cfa | ||
|
|
e50ead1ea3 | ||
|
|
55bd94bbf4 | ||
|
|
5a022e25ac | ||
|
|
ca9d47c2aa | ||
|
|
9b82ce149f | ||
|
|
ba3fc4a9b5 | ||
|
|
6b993fafd3 | ||
|
|
7adba9fea3 | ||
|
|
65618ea8dc | ||
|
|
a52213b93a | ||
|
|
f6ebcf6084 | ||
|
|
f67ffc910c | ||
|
|
5d9b9fada4 | ||
|
|
631629b2a3 | ||
|
|
a9dfcf8520 | ||
|
|
5887b1c109 | ||
|
|
7e5e29cacb | ||
|
|
4390ece55c | ||
|
|
e214bc55dc | ||
|
|
2d16e35ac4 | ||
|
|
93fa56d039 | ||
|
|
916cc11bda | ||
|
|
d474ce64b8 | ||
|
|
9810986978 | ||
|
|
15bf69c319 | ||
|
|
8152597a03 | ||
|
|
2573d325e6 | ||
|
|
5b4ea83044 | ||
|
|
005c91af51 | ||
|
|
ab49545876 | ||
|
|
87828583e7 | ||
|
|
a42aff837c | ||
|
|
eb15751dae | ||
|
|
d736b920d5 | ||
|
|
7810e90524 | ||
|
|
aa9a3886de | ||
|
|
9b7bcee16f | ||
|
|
e06390362b | ||
|
|
88cb35e75c | ||
|
|
836bc6d125 | ||
|
|
4543fd73df | ||
|
|
4d47f6b80b | ||
|
|
5f8f5f2892 | ||
|
|
21ddac67bb | ||
|
|
0096c69595 | ||
|
|
aa26069e9f | ||
|
|
d1dfffb4b2 | ||
|
|
9f1c60dfc7 | ||
|
|
e5e2ba7340 | ||
|
|
12ebac7307 | ||
|
|
625a9f16f5 | ||
|
|
38e8c3b0c6 | ||
|
|
53e67ff57a | ||
|
|
ef64b0a654 | ||
|
|
bfb0c05e83 | ||
|
|
b20408cde0 | ||
|
|
68422b5a15 | ||
|
|
aeac6e605b | ||
|
|
ce5b21c156 | ||
|
|
1155cb8d68 | ||
|
|
2d15489618 | ||
|
|
fc28b6cbe2 | ||
|
|
7c0251cef5 | ||
|
|
f6dfd2ab67 | ||
|
|
923e48d64c | ||
|
|
b98888e128 | ||
|
|
62e16d33fb | ||
|
|
ffdb82d3c8 | ||
|
|
8edda06dca | ||
|
|
b70a6cb348 | ||
|
|
37cecfe27d | ||
|
|
7af0d85687 | ||
|
|
f8c3f66ee5 | ||
|
|
7c6b557186 | ||
|
|
a0d9103d3c | ||
|
|
41b97102ad | ||
|
|
96d0c9d5eb | ||
|
|
ccf4f59430 | ||
|
|
d191e0b6d3 | ||
|
|
2ca76fbf0d | ||
|
|
6fc55433ef | ||
|
|
21e6e4cb1a | ||
|
|
3be639dac2 | ||
|
|
a30276e3d8 | ||
|
|
e5f5e156a2 | ||
|
|
0e5f98fd96 | ||
|
|
19d980f9fc | ||
|
|
5ce0834529 | ||
|
|
97c5e0d6fe | ||
|
|
7661f45cfd | ||
|
|
34862810df | ||
|
|
7f6e12c0a0 | ||
|
|
551f57b03b | ||
|
|
e2c241f4a8 | ||
|
|
6e4f95df57 | ||
|
|
ad38a41628 | ||
|
|
84562aa47c | ||
|
|
e2d5a83d7c | ||
|
|
d2436ba001 | ||
|
|
d323c1e51e | ||
|
|
344fa07316 | ||
|
|
81e847a153 | ||
|
|
834408c740 | ||
|
|
389b07138c | ||
|
|
5215ef919b | ||
|
|
48020d6ff8 | ||
|
|
d65492b05c | ||
|
|
d1ad8c23ec | ||
|
|
610bd66122 | ||
|
|
39975395e1 | ||
|
|
eb52599e20 | ||
|
|
682639137e | ||
|
|
48b7ce8273 | ||
|
|
18d88035fb | ||
|
|
53bfa967c6 | ||
|
|
d7f451a7d9 | ||
|
|
2e996712f8 | ||
|
|
13992072f3 | ||
|
|
659263f97e | ||
|
|
c5f57e1522 | ||
|
|
9dff09fbcf | ||
|
|
c307aa6354 | ||
|
|
627d183015 | ||
|
|
5ae1c3de1e | ||
|
|
3e25c307ce | ||
|
|
3bb8b0d601 | ||
|
|
080cec07c0 | ||
|
|
9c8a631662 | ||
|
|
f875139d7c | ||
|
|
94b0d7783c | ||
|
|
26a52c424c | ||
|
|
207276e7ec | ||
|
|
f1fa1580b1 | ||
|
|
2754c29772 | ||
|
|
5a3fab4f65 | ||
|
|
0a3d2c2868 | ||
|
|
57166b891a | ||
|
|
a805eb09df | ||
|
|
6cb84673d4 | ||
|
|
aa2c3774a2 | ||
|
|
b485c7304f | ||
|
|
ab56f51dc0 | ||
|
|
bbc6d0955c | ||
|
|
d610d03423 | ||
|
|
58e1084ba5 | ||
|
|
cf7c051242 | ||
|
|
a92eafce80 | ||
|
|
b62bce388c | ||
|
|
e309b53ded | ||
|
|
1813400ecf | ||
|
|
5d09fca481 | ||
|
|
c99a86cdb0 | ||
|
|
072d239a85 | ||
|
|
9e44ccaf5d | ||
|
|
86d86b30e9 | ||
|
|
b2edacbd12 | ||
|
|
a6ec394730 | ||
|
|
4ae0d49431 | ||
|
|
eaea1936a0 | ||
|
|
315597dcff | ||
|
|
51ed0acc4f | ||
|
|
a06e971a53 | ||
|
|
33f39d047f | ||
|
|
98de6ae0db | ||
|
|
6b4613d3ed | ||
|
|
eace04db8e | ||
|
|
3772ce3aca | ||
|
|
663e2823fa | ||
|
|
77927a1ca4 | ||
|
|
6bf1fcbbc7 | ||
|
|
0f6d84d69f | ||
|
|
9d6cb7fbdb | ||
|
|
29572de2e7 | ||
|
|
3a0269640b | ||
|
|
9a15750e7c | ||
|
|
ebfabba284 | ||
|
|
05c34cefde | ||
|
|
4097301e60 | ||
|
|
04827fd8f0 | ||
|
|
1e4fba3ec8 | ||
|
|
ba1d04b99f | ||
|
|
ed72ef0e5e | ||
|
|
2287758fdc | ||
|
|
c6fa37640a | ||
|
|
4907bf5030 | ||
|
|
85edae924e | ||
|
|
e6561462e9 | ||
|
|
0d3b7ca041 | ||
|
|
8fc23429a0 | ||
|
|
bfffe58672 | ||
|
|
ca35970f3f | ||
|
|
f26f86dd3f | ||
|
|
63d000fbbb | ||
|
|
17f97f6acc | ||
|
|
33657a9ed1 | ||
|
|
43a7096753 | ||
|
|
fbe1f5f948 | ||
|
|
a21883e3a1 | ||
|
|
af2da93423 | ||
|
|
14f832f955 | ||
|
|
6ec81ccbc1 | ||
|
|
d470a74a9f | ||
|
|
cee1dd94ee | ||
|
|
c587d34158 | ||
|
|
68e4e6f049 | ||
|
|
5c81cfbd7b | ||
|
|
edc610d019 | ||
|
|
5ca84d2930 | ||
|
|
6f864c5022 | ||
|
|
3c3adef26a | ||
|
|
f533aa8d8e | ||
|
|
c1d449d3bd | ||
|
|
2c2f906c8b | ||
|
|
9e0d7ee00f | ||
|
|
11e467618d | ||
|
|
f315721fef | ||
|
|
8eb4ffea89 | ||
|
|
f20ca31a3f | ||
|
|
f4e11ff7c5 | ||
|
|
f6fbd0a5c2 | ||
|
|
25443926d3 | ||
|
|
03a75c9c05 | ||
|
|
6a20f6dc70 | ||
|
|
3d2fbf1397 | ||
|
|
d0f1382055 | ||
|
|
0de353508a | ||
|
|
c291b9ebb8 | ||
|
|
24566ce853 | ||
|
|
c64a221453 | ||
|
|
469d81f8a5 | ||
|
|
503f6128bd | ||
|
|
e1398d04da | ||
|
|
79add98aa7 | ||
|
|
7a6098774d | ||
|
|
ce1f8b674d | ||
|
|
11e666f229 | ||
|
|
c131b983b3 | ||
|
|
a9334b3bf4 | ||
|
|
c437c0d3f1 | ||
|
|
97109df673 | ||
|
|
d4338dd5cc | ||
|
|
15ce816ec6 | ||
|
|
3e921da314 | ||
|
|
dc210dc53b | ||
|
|
43135ffc8d | ||
|
|
1516615934 | ||
|
|
e37bf2b51d | ||
|
|
0553e36bfb | ||
|
|
03deeb6729 | ||
|
|
a06d1806df | ||
|
|
0a4e472601 | ||
|
|
f320069ec8 | ||
|
|
20c2679dc8 | ||
|
|
464bc2f87b | ||
|
|
3aba91885f | ||
|
|
110a83a736 | ||
|
|
af120f2e0b | ||
|
|
ea937810f2 | ||
|
|
a6b4a698af | ||
|
|
778084e934 | ||
|
|
c23f5dc858 | ||
|
|
2cd8cfc9b9 | ||
|
|
2bb1feee51 | ||
|
|
0e6a1a171e | ||
|
|
f833d1288f | ||
|
|
cfccee897e | ||
|
|
ea5025595a | ||
|
|
344aeb4051 | ||
|
|
db8e707049 | ||
|
|
f6f5c7ccf0 | ||
|
|
43f380c17a | ||
|
|
e6b705be26 | ||
|
|
b5d311c00c | ||
|
|
91b1713bf7 | ||
|
|
9274501939 | ||
|
|
7c619859fc | ||
|
|
c091fa5993 | ||
|
|
f9934eeec9 | ||
|
|
98b7953d7f | ||
|
|
feb6fb72f4 | ||
|
|
c63c84e2ea | ||
|
|
b334a4ee00 | ||
|
|
8566f32bbd | ||
|
|
af4bcb47e7 | ||
|
|
8560b36a5a | ||
|
|
6fbd817e83 | ||
|
|
a86ea22303 | ||
|
|
54e6e32ab2 | ||
|
|
dbcc8539bc | ||
|
|
65dee38150 | ||
|
|
ee3c5cbb6a | ||
|
|
f480f945c5 | ||
|
|
9824b90e69 | ||
|
|
e6c3995713 | ||
|
|
d7bb0b4cc0 | ||
|
|
3762768ed3 | ||
|
|
61d2fd0308 | ||
|
|
458b372b1b | ||
|
|
6eb1495c3b | ||
|
|
e9a251d6a8 | ||
|
|
b712380661 | ||
|
|
abf76b92d4 | ||
|
|
43360c103b | ||
|
|
ec4564d994 | ||
|
|
49a023733a | ||
|
|
339e044a30 | ||
|
|
01c938d6b2 | ||
|
|
cb42fa37b1 | ||
|
|
37f9de8b9b | ||
|
|
08e18200c6 | ||
|
|
3ddb97c8c9 | ||
|
|
4941fd183c | ||
|
|
2be017cf4a | ||
|
|
1b30485e68 | ||
|
|
0c224a0a7f | ||
|
|
d7292690bd | ||
|
|
1467f3de31 | ||
|
|
063ad08e9e | ||
|
|
ed3223fedc | ||
|
|
a551f2bcc7 | ||
|
|
0efc9aca15 | ||
|
|
73ea39466b | ||
|
|
ec2b008552 | ||
|
|
86d6d2d19a | ||
|
|
26f8df2995 | ||
|
|
b0758449bc | ||
|
|
b433fbdda8 | ||
|
|
2e1ca73379 | ||
|
|
14e5d08a19 | ||
|
|
aeb29a4f95 | ||
|
|
27198c4064 | ||
|
|
bba1f9e133 | ||
|
|
fd6f2132af | ||
|
|
a24c47901f | ||
|
|
d37adbb046 | ||
|
|
0f0fab28b0 | ||
|
|
69c3bdf553 | ||
|
|
6594795f39 | ||
|
|
36ae118afc | ||
|
|
b062eb8af4 | ||
|
|
404fbacc41 | ||
|
|
986b8e0463 | ||
|
|
6289dcf4c2 | ||
|
|
5b883f4ea6 | ||
|
|
58da4e4004 | ||
|
|
c2dc80c452 | ||
|
|
1b95f9f167 | ||
|
|
72c44e703f | ||
|
|
8e17177331 | ||
|
|
e825480b57 | ||
|
|
b2a555f996 | ||
|
|
b2fd576f36 | ||
|
|
1c4b54d1ad | ||
|
|
b36920e657 | ||
|
|
b3056626f3 | ||
|
|
760dde2c10 | ||
|
|
e7a2e8302c | ||
|
|
e81900178e | ||
|
|
648adb414e | ||
|
|
e3149289db | ||
|
|
b680cf4938 | ||
|
|
69d454dd03 | ||
|
|
703c849482 | ||
|
|
284f440136 | ||
|
|
b00169d24f | ||
|
|
267becdb9a | ||
|
|
054d420dc9 | ||
|
|
0fbc6efbcf | ||
|
|
63f5c88a15 | ||
|
|
24598eaf00 | ||
|
|
aa067b777a | ||
|
|
2e9fca93ec | ||
|
|
c2f5355f9a | ||
|
|
9fdf739695 | ||
|
|
17f8f8f14f | ||
|
|
9a68c96585 | ||
|
|
ed2a725a2c | ||
|
|
8914d4404e | ||
|
|
c5dd0b729d | ||
|
|
f4f9c501d1 | ||
|
|
239dd1a341 | ||
|
|
ee254cb9c8 | ||
|
|
05a98fa88b | ||
|
|
5df5c7f5d7 | ||
|
|
ccea223ef7 | ||
|
|
25f9963645 | ||
|
|
b1c41518df | ||
|
|
ae09f0f008 | ||
|
|
490ad65b6e | ||
|
|
cb78501b04 | ||
|
|
65a031f29d | ||
|
|
6c94b57cfc | ||
|
|
7ae15cae27 | ||
|
|
0596595284 | ||
|
|
724d1386f6 | ||
|
|
920b32735f | ||
|
|
86aa7ff353 | ||
|
|
20d87f2299 | ||
|
|
88f05e8c4e | ||
|
|
c3bfcb78e9 | ||
|
|
83a78c792a | ||
|
|
99b2fdbf99 | ||
|
|
7ca9e8f4d3 | ||
|
|
dac85c0a03 | ||
|
|
04e04ead92 | ||
|
|
008fc806e8 | ||
|
|
af7874b700 | ||
|
|
94e1fa79b3 | ||
|
|
6694088ac7 | ||
|
|
b4c74b8d4c | ||
|
|
28612612bd | ||
|
|
f2f834e810 | ||
|
|
3e5f8f458c | ||
|
|
cffb5baa68 | ||
|
|
01d0dd3b1d | ||
|
|
c8bb3c7353 | ||
|
|
709b17805b | ||
|
|
e9982e34ed | ||
|
|
1c2c3d9dea | ||
|
|
bd26ed7f7a | ||
|
|
c48299c0dd | ||
|
|
753c54a741 | ||
|
|
48a9fe3440 | ||
|
|
be1893e66a | ||
|
|
0d67a86f2c | ||
|
|
6e979beb01 | ||
|
|
12615fef5d | ||
|
|
403e2f4cbd | ||
|
|
6b4ffa69c6 | ||
|
|
0f65e06338 | ||
|
|
84d51393e4 | ||
|
|
51fb490c22 | ||
|
|
f9e25e028e | ||
|
|
937797356c | ||
|
|
21ae1b59d6 | ||
|
|
e25050316f | ||
|
|
2e06d466b9 | ||
|
|
5739b5d93c | ||
|
|
6b6bdddce6 | ||
|
|
dcff70c983 | ||
|
|
ad5167de55 | ||
|
|
947692693c | ||
|
|
0c8e812991 | ||
|
|
fb1742b149 | ||
|
|
054915031e | ||
|
|
ada6b8ee9c | ||
|
|
63e0597c68 | ||
|
|
c71cedf9f4 | ||
|
|
f6ee321d0f | ||
|
|
2a24720fad | ||
|
|
a7fd512124 | ||
|
|
b7f62cf5f2 | ||
|
|
dfea3d0e73 | ||
|
|
e4e1a8f4e6 | ||
|
|
43b2d71404 | ||
|
|
c9f8e111c5 | ||
|
|
67596602d7 | ||
|
|
5af4f54f4c | ||
|
|
0d7d38b358 | ||
|
|
680770a7df | ||
|
|
14c3432896 | ||
|
|
34fc724d95 | ||
|
|
1ed2c3b45a | ||
|
|
81f74f4db6 | ||
|
|
b5bc07d2e7 | ||
|
|
85fcaff678 | ||
|
|
d5247daf8a | ||
|
|
9ff87d189f | ||
|
|
2d36116823 | ||
|
|
9f0b9b29a5 | ||
|
|
1d7e08390c | ||
|
|
bbffa2eb6b | ||
|
|
52ea940265 | ||
|
|
355b358fe2 | ||
|
|
d74662bfd7 | ||
|
|
e151da7163 | ||
|
|
1c71baec81 | ||
|
|
9382c2cd04 | ||
|
|
25e50e3908 | ||
|
|
c6331830d4 | ||
|
|
4226a0bc37 | ||
|
|
693a49f852 | ||
|
|
0b797ceb7e | ||
|
|
2587e2f00a | ||
|
|
6ad83bcaeb | ||
|
|
f2815a9039 | ||
|
|
6d7e69cc9f | ||
|
|
fac1ba5952 | ||
|
|
3db14ad03a | ||
|
|
9ca26d22d0 | ||
|
|
7640c7c04d | ||
|
|
ea66eca60d | ||
|
|
0ae8de4c04 | ||
|
|
e50d69cfb7 | ||
|
|
d95436df51 | ||
|
|
3b419848d1 | ||
|
|
8efc8605a1 | ||
|
|
4f44c5bf87 | ||
|
|
4b12245537 | ||
|
|
adcc33ed7f | ||
|
|
f1482cfa94 | ||
|
|
148ef0eb65 | ||
|
|
6c8563d02f | ||
|
|
60ac208ec2 | ||
|
|
dde98c8870 | ||
|
|
990477e9c6 | ||
|
|
b4d46f3016 | ||
|
|
24bed7bdb2 | ||
|
|
ddca4b55a7 | ||
|
|
cf727a08df | ||
|
|
b6374dac3d | ||
|
|
11f8b17a05 | ||
|
|
26e1c1dc4a | ||
|
|
21c09c67e4 | ||
|
|
985c2cdbfd | ||
|
|
a2968d74d7 | ||
|
|
1dd7388783 | ||
|
|
8964a18e76 | ||
|
|
58da992a0c | ||
|
|
3ed1fc3e6b | ||
|
|
f4fe33fb05 | ||
|
|
55815aac71 | ||
|
|
77d72ae20a | ||
|
|
6af581422c | ||
|
|
27f8356b54 | ||
|
|
072778b9aa | ||
|
|
01a1a2cdce | ||
|
|
7e43a6f4fb | ||
|
|
907013228d | ||
|
|
00768b4392 | ||
|
|
a73a4c1e5b | ||
|
|
963a4711db | ||
|
|
2e354f9b1e | ||
|
|
bc8b8cbdbe | ||
|
|
4a78c718d3 | ||
|
|
bfef7844ba | ||
|
|
ca0dfe160f | ||
|
|
303cebef63 | ||
|
|
2856cf9baa | ||
|
|
1fc3ed407a | ||
|
|
28b478d14f | ||
|
|
38d26fa7e3 | ||
|
|
5ddf88bca1 | ||
|
|
255eac9f98 | ||
|
|
f260b7ba88 | ||
|
|
40aae378b6 | ||
|
|
0865f3597a | ||
|
|
b9d6fe8146 | ||
|
|
000e27a4a1 | ||
|
|
fbb0ed5ec1 | ||
|
|
58d760a615 | ||
|
|
b97c51abde | ||
|
|
8c8560cfc2 | ||
|
|
567be456be | ||
|
|
454fd6e95a | ||
|
|
8c6dd6a4a9 | ||
|
|
b5a8260086 | ||
|
|
7dd5fa4ac4 | ||
|
|
75525c2215 | ||
|
|
31449167be | ||
|
|
441b3e9c0a | ||
|
|
2e5ec0308f | ||
|
|
d1714abc6a | ||
|
|
3f250a17fd | ||
|
|
3fad014e91 | ||
|
|
86115887af | ||
|
|
7778dcc3fc | ||
|
|
65434876eb | ||
|
|
4c146141f9 | ||
|
|
1705b46f39 | ||
|
|
3aca5bc3e4 | ||
|
|
79a866e238 | ||
|
|
efc19ea88c | ||
|
|
79051e5779 | ||
|
|
456fa270cf | ||
|
|
d271678ef4 | ||
|
|
a9390a9897 | ||
|
|
a196a5ed63 | ||
|
|
3fb709ed70 | ||
|
|
50ce0ce442 | ||
|
|
fbb4f0c933 | ||
|
|
a34cbf95e8 | ||
|
|
68e8155805 | ||
|
|
9f723b5863 | ||
|
|
e726b9be7d | ||
|
|
0e6b31b636 | ||
|
|
7607090bdf | ||
|
|
5a3bc74daf | ||
|
|
d8702b2ca1 | ||
|
|
2dfbb48114 | ||
|
|
5a3778fd5a | ||
|
|
43d6900ecb | ||
|
|
2193c86766 | ||
|
|
8d395e6cb1 | ||
|
|
6b2209c8e1 | ||
|
|
8f43b74bbc | ||
|
|
566527dfad | ||
|
|
c858e0b796 | ||
|
|
f40b0d495d | ||
|
|
28317c7cf3 | ||
|
|
3bea047499 | ||
|
|
958d94e1d3 | ||
|
|
64afe811a7 | ||
|
|
e91e4b5329 | ||
|
|
b7ab79efef | ||
|
|
f1a52c99c5 | ||
|
|
d1efd1f493 | ||
|
|
bd8eda2351 | ||
|
|
5e567365f3 | ||
|
|
a167d8ff3f | ||
|
|
9367ef8c41 | ||
|
|
5c890e4cc3 | ||
|
|
c6212c7d62 | ||
|
|
b8d14abe0c | ||
|
|
a064e03fc7 | ||
|
|
9d587e76ab | ||
|
|
e75f423edd | ||
|
|
dace63d4dc | ||
|
|
cfb88baa7f | ||
|
|
72b8672e62 | ||
|
|
99c37bd521 | ||
|
|
9ef45a75af | ||
|
|
fe018225b4 | ||
|
|
927f155078 | ||
|
|
d63f1b740c | ||
|
|
883beba9ed | ||
|
|
dd0d5dbe9f | ||
|
|
0fcfd8e168 | ||
|
|
44fd652b05 | ||
|
|
536fb00d61 | ||
|
|
8c7ed1855e | ||
|
|
54cf71ce1b | ||
|
|
0e768ff363 | ||
|
|
42a8f04058 | ||
|
|
25f8dd5141 | ||
|
|
2a802ec986 | ||
|
|
41837aac74 | ||
|
|
74fc42a08c | ||
|
|
c79b766b58 | ||
|
|
42b05b263d | ||
|
|
d30e52d336 | ||
|
|
59531f785a | ||
|
|
c0979fffbd | ||
|
|
0c3ca14699 | ||
|
|
6b7cbd9d81 | ||
|
|
44dbca5e05 | ||
|
|
855be80572 | ||
|
|
1b83399236 | ||
|
|
2b579372c3 | ||
|
|
a4732bacce | ||
|
|
1cddd607ec | ||
|
|
80d0ec0898 | ||
|
|
06b812b590 | ||
|
|
2288c8ac15 | ||
|
|
2265d726ae | ||
|
|
09553b7672 | ||
|
|
3d6085157e | ||
|
|
efcca9739a | ||
|
|
1875551a93 | ||
|
|
72abf50b36 | ||
|
|
31d7fbe5fb | ||
|
|
71b82e0049 | ||
|
|
f847439e10 | ||
|
|
94bf192f12 | ||
|
|
bb780976e9 | ||
|
|
0d97b1314d | ||
|
|
e4e8da4718 | ||
|
|
5dcf7f55fd | ||
|
|
0f272b1e66 | ||
|
|
b05fa636ea | ||
|
|
74deeeaaa6 | ||
|
|
dcac00696e | ||
|
|
48cd554229 | ||
|
|
4929653367 | ||
|
|
4158334c2d | ||
|
|
5e131f4c76 | ||
|
|
50990b6e6a | ||
|
|
2cddcd60a1 | ||
|
|
c29393dd7e | ||
|
|
6f995a5bdd | ||
|
|
21a9ab72c2 | ||
|
|
3bfe5c284f | ||
|
|
ef712a700e | ||
|
|
fa9ed55d7d | ||
|
|
075a9016d4 | ||
|
|
2d53cd13b6 | ||
|
|
47397e5be3 | ||
|
|
5af78c6990 | ||
|
|
97356603aa | ||
|
|
6a9433593b | ||
|
|
3813554918 | ||
|
|
76ffdd70d0 | ||
|
|
f8d927c61a | ||
|
|
ccbc53cb9b | ||
|
|
b49aaf8e68 | ||
|
|
3f8a3f26d6 | ||
|
|
c1dba52720 | ||
|
|
1ae5bc482b | ||
|
|
96f8b5c9f4 | ||
|
|
85480a6cae | ||
|
|
6e1bf061f8 | ||
|
|
4277d89514 | ||
|
|
3071152978 | ||
|
|
010aed98a8 | ||
|
|
9294eac642 | ||
|
|
393be58a94 | ||
|
|
c8656afc1e | ||
|
|
6d0f0c3606 | ||
|
|
815e2db0ae | ||
|
|
3eb72c6c48 | ||
|
|
e3daa37c5a | ||
|
|
4e72dadc6f | ||
|
|
3c39953b42 | ||
|
|
c926475eb1 | ||
|
|
4bbe63365c | ||
|
|
c696c8eed0 | ||
|
|
2b9f555f31 | ||
|
|
eb745106d8 | ||
|
|
4ba2a23c72 | ||
|
|
60af7766bf | ||
|
|
ae700d64f7 | ||
|
|
95b39f6d47 | ||
|
|
147a3d99cb | ||
|
|
19c54720c6 | ||
|
|
77085daaa8 | ||
|
|
2b1675847c | ||
|
|
3ae777f2c9 | ||
|
|
89c521c20e | ||
|
|
be8b12457c | ||
|
|
95a4b4fd91 | ||
|
|
8dcdfce0ec | ||
|
|
6d31f493bd | ||
|
|
fb48c746ae | ||
|
|
e018dfe541 | ||
|
|
023deb6c95 | ||
|
|
5654c411fe | ||
|
|
ce55d84fd8 | ||
|
|
cb749ff614 | ||
|
|
3c9a4f6ffa | ||
|
|
45642911a9 | ||
|
|
36305f64c5 | ||
|
|
4677081725 | ||
|
|
359692a564 | ||
|
|
555d42d05e | ||
|
|
e7fa9598e3 | ||
|
|
9ed1105e7e | ||
|
|
ab077e7eb9 | ||
|
|
5f2820d7e0 | ||
|
|
b1ea3fef00 | ||
|
|
1360517af8 | ||
|
|
43d324d138 | ||
|
|
b81dcf1dc8 | ||
|
|
f87cb10d09 | ||
|
|
626a7f83fc | ||
|
|
a825b2fe81 | ||
|
|
12448ab473 | ||
|
|
f824eab198 | ||
|
|
4057bb6372 | ||
|
|
15e3c72430 | ||
|
|
8a39a52d5d | ||
|
|
939976ce88 | ||
|
|
8b2cf30754 | ||
|
|
5bcde1e9a7 | ||
|
|
534559c5a9 | ||
|
|
cb99b66023 | ||
|
|
12c9317e15 | ||
|
|
7d5ed14419 | ||
|
|
dfbad25986 | ||
|
|
00e21ae88e | ||
|
|
dbf034cd87 | ||
|
|
031d6a22df | ||
|
|
37fdde1835 | ||
|
|
f613a5875f | ||
|
|
6dc858d790 | ||
|
|
4ca7b0dad7 | ||
|
|
5b31e2f2fb | ||
|
|
49a996624f | ||
|
|
b7aaff5313 | ||
|
|
0e3d8e8607 | ||
|
|
5eca3de00f | ||
|
|
e7a11c5733 | ||
|
|
5744aac7bf | ||
|
|
0d9c37b006 | ||
|
|
7eb04897ab | ||
|
|
7c637e71f0 | ||
|
|
f8d86923b4 | ||
|
|
6b6fde834c | ||
|
|
bc61d1c205 | ||
|
|
66b733a11e | ||
|
|
536021107e | ||
|
|
a96d18454a | ||
|
|
0c5f9aa167 | ||
|
|
30dbab2577 | ||
|
|
8c68d631d4 | ||
|
|
c6cb6f270e | ||
|
|
4534177f47 | ||
|
|
2576127b10 | ||
|
|
db4421c7b8 | ||
|
|
51094a3c46 | ||
|
|
f2378fcff6 | ||
|
|
c18f531915 | ||
|
|
1a117d1941 | ||
|
|
bf449c5864 | ||
|
|
7b428fa8cb | ||
|
|
325899f7f5 | ||
|
|
3394f54fb4 | ||
|
|
a91e274785 | ||
|
|
4f970eee26 | ||
|
|
08f188c45f | ||
|
|
9df99435a0 | ||
|
|
266af19716 | ||
|
|
470645193e | ||
|
|
128a7ee777 | ||
|
|
ada770c9e6 | ||
|
|
a76eb9da03 | ||
|
|
2a362a2e85 | ||
|
|
35837a6b24 | ||
|
|
c99d069b0d | ||
|
|
10b8612ed9 | ||
|
|
0ecaf58515 | ||
|
|
25dc378c46 | ||
|
|
c8f4fe3da5 | ||
|
|
1e42aebd70 | ||
|
|
266d5dda15 | ||
|
|
74d468ea79 | ||
|
|
14aeef8d96 | ||
|
|
10555e1514 | ||
|
|
437b139490 | ||
|
|
9238b6620c | ||
|
|
d90ef2ebd8 | ||
|
|
5d3d5e4b2d | ||
|
|
c3faa99491 | ||
|
|
9ab2d5fab0 | ||
|
|
c359733270 | ||
|
|
747a3b17d5 | ||
|
|
b9670a50eb | ||
|
|
02e977f512 | ||
|
|
dd89afff4d | ||
|
|
2b655f4b67 | ||
|
|
f8b2237274 | ||
|
|
84d94079cc | ||
|
|
edb4db30bb | ||
|
|
9509269622 | ||
|
|
ffba683c84 | ||
|
|
0426e09cd7 | ||
|
|
5fc5500845 | ||
|
|
971391eae9 | ||
|
|
a19e1b3400 | ||
|
|
9be6312ce1 | ||
|
|
c397b5eda8 | ||
|
|
3251c8f419 | ||
|
|
f2f3b76eda | ||
|
|
402fe5528f | ||
|
|
e2f8989939 | ||
|
|
1da0786211 | ||
|
|
744e90d680 | ||
|
|
ca15e060bb | ||
|
|
1431e4fd48 | ||
|
|
e61e9bb508 | ||
|
|
1cb52a60c1 | ||
|
|
9099e20b80 | ||
|
|
0b6e6491f9 | ||
|
|
0fb00bb18f | ||
|
|
aa3f133f56 | ||
|
|
bf023e8218 | ||
|
|
60466612b6 | ||
|
|
376bcbb373 | ||
|
|
556507705a | ||
|
|
825bc4cf25 | ||
|
|
68c958a890 | ||
|
|
d2414670fa | ||
|
|
2f4e201d55 | ||
|
|
278a8e49b9 | ||
|
|
5514c357ca | ||
|
|
8280ed2d3e | ||
|
|
9fa5b59d7c | ||
|
|
f9b6978303 | ||
|
|
90be8ff936 | ||
|
|
221d90f34b | ||
|
|
850aef41a4 | ||
|
|
d18da20cdf | ||
|
|
62ea3cd4bf | ||
|
|
8a014dbfae | ||
|
|
01a91f3a36 | ||
|
|
9f807c59d7 | ||
|
|
ab0faf9b52 | ||
|
|
272aa34083 | ||
|
|
1f277af722 | ||
|
|
bfef9fc14d | ||
|
|
4603948442 | ||
|
|
27ea306c1c | ||
|
|
6d5651e53f | ||
|
|
ada7b3dd2d | ||
|
|
34c2c87307 | ||
|
|
9179e5ea71 | ||
|
|
f90875d8ef | ||
|
|
c4bf99a9b9 | ||
|
|
87ec6a1076 | ||
|
|
e450337b62 | ||
|
|
a45d1fa855 | ||
|
|
0712309266 | ||
|
|
f8b60de988 | ||
|
|
3887c6f412 | ||
|
|
a2beaa6e83 | ||
|
|
2d30b62b70 | ||
|
|
e1550e9172 | ||
|
|
0565f58165 | ||
|
|
bf6bfc8ed6 | ||
|
|
d52e0bcb6e | ||
|
|
263b405fc8 | ||
|
|
a0dbfa1f7e | ||
|
|
5b60efbe82 | ||
|
|
00f2b77468 | ||
|
|
a7e510412d | ||
|
|
dc6bc9ed54 | ||
|
|
82129139a2 | ||
|
|
e5cfcc84fa | ||
|
|
5d5605754c | ||
|
|
9b4179e163 | ||
|
|
ab33866d1a | ||
|
|
41ba67538b | ||
|
|
e0d8ddf003 | ||
|
|
1a7a41698c | ||
|
|
2b999d2836 | ||
|
|
dc99218e4e | ||
|
|
bec440360a | ||
|
|
cc0658a225 | ||
|
|
00b99cda73 | ||
|
|
b378bb55ac | ||
|
|
7d5eac6876 | ||
|
|
4d539f369a | ||
|
|
5dadb4f0a1 | ||
|
|
1629e6aae6 | ||
|
|
0775e2a3e5 | ||
|
|
454d0b270d | ||
|
|
3076547cee | ||
|
|
4bcddcc98a | ||
|
|
099085b416 | ||
|
|
9bd6879455 | ||
|
|
00efbf00d3 | ||
|
|
5c214c143d | ||
|
|
3418b97faa | ||
|
|
a484101abf | ||
|
|
2f79432e4e | ||
|
|
f71728ae16 | ||
|
|
e7e4296b02 | ||
|
|
b85db43c47 | ||
|
|
db6fc57515 | ||
|
|
6b404da9f8 | ||
|
|
4d31985eb9 | ||
|
|
0e9aa2eeed | ||
|
|
7d73f2bbba | ||
|
|
4929979221 | ||
|
|
90cc96302d | ||
|
|
4a074836fd | ||
|
|
0e0bea0139 | ||
|
|
d00596f88a | ||
|
|
9ca8d78514 | ||
|
|
11d8096b8d | ||
|
|
a49a918547 | ||
|
|
4ebd113bf0 | ||
|
|
14df71de5b | ||
|
|
9f459a37b8 | ||
|
|
5487c015db | ||
|
|
47ca6829eb | ||
|
|
368fe24956 | ||
|
|
086cb9c067 | ||
|
|
25a2ad1144 | ||
|
|
926b617f33 | ||
|
|
f94ae76609 | ||
|
|
f46173077b | ||
|
|
40265144af | ||
|
|
c373013134 | ||
|
|
12c5d2324d | ||
|
|
e6dc20f6c7 | ||
|
|
ea658842a2 | ||
|
|
12f0c85546 | ||
|
|
033a5d8186 | ||
|
|
b769a71af3 | ||
|
|
ad35e7b7bd | ||
|
|
a9e56f2b09 | ||
|
|
5f807b80fe | ||
|
|
4d89eb6083 | ||
|
|
bd2739e78d | ||
|
|
265ee9ab83 | ||
|
|
065485168a | ||
|
|
7d609ef0b1 | ||
|
|
dc7b97c178 | ||
|
|
a5b376e6b5 | ||
|
|
993e99330f | ||
|
|
e563212475 | ||
|
|
cc54867976 | ||
|
|
36250f83b9 | ||
|
|
e715106d65 | ||
|
|
f888b5cb23 | ||
|
|
59823a0661 | ||
|
|
6a9bc12664 | ||
|
|
e99130d3ea | ||
|
|
036fb3e794 | ||
|
|
a15ee068d1 | ||
|
|
1da242ae35 | ||
|
|
addf669d44 | ||
|
|
1c2dbe5149 | ||
|
|
77cb5d88de | ||
|
|
1b591dcda9 | ||
|
|
545fe96c30 | ||
|
|
d8d8a6a64d | ||
|
|
5a74d98795 | ||
|
|
0e8a02ee2c | ||
|
|
2c175f2be6 | ||
|
|
e080aea61d | ||
|
|
59a936ae90 | ||
|
|
c7500661b8 | ||
|
|
0ad7eb4753 | ||
|
|
0254282daf | ||
|
|
d0a27aaec2 | ||
|
|
be712541d5 | ||
|
|
9471eb327e | ||
|
|
c41335a967 | ||
|
|
9de430fdc1 | ||
|
|
13a6067081 | ||
|
|
e2f7b94cde | ||
|
|
ac7d16c40f | ||
|
|
3da88ed858 | ||
|
|
c85f710e74 | ||
|
|
7b64c8b235 | ||
|
|
f244125cd3 | ||
|
|
692d853eca | ||
|
|
eb747ef8e9 | ||
|
|
412b60f794 | ||
|
|
0f91eaa0a0 | ||
|
|
386828fe3e | ||
|
|
9b1ff4dd6d | ||
|
|
2dc6e15024 | ||
|
|
e702f17c7b | ||
|
|
16506e7a6a | ||
|
|
08cc308992 | ||
|
|
ae8d4c1bbd | ||
|
|
e2934b9b09 | ||
|
|
55acce9f0c | ||
|
|
627d7d9456 | ||
|
|
b62dd5782a | ||
|
|
0460a7d9d3 | ||
|
|
45d53dede4 | ||
|
|
ce3c87f767 | ||
|
|
ceed865e30 | ||
|
|
9c3043d40a | ||
|
|
a38a78d9a3 | ||
|
|
966541704c | ||
|
|
52a63c5423 | ||
|
|
18bd797ab9 | ||
|
|
d75fd69406 | ||
|
|
337470cbd0 | ||
|
|
a9bd5c7496 | ||
|
|
e1730c9912 | ||
|
|
8f1754be50 | ||
|
|
4ace1db9b0 | ||
|
|
eff636ddd6 | ||
|
|
9b7bf1ba28 | ||
|
|
7190057d0c | ||
|
|
bb5b723520 | ||
|
|
a2e6f01469 | ||
|
|
12ee7efc5f | ||
|
|
7c7640e63e | ||
|
|
e668fdc500 | ||
|
|
f02478d57c | ||
|
|
8fc117eb53 | ||
|
|
af598b13e8 | ||
|
|
f00f164078 | ||
|
|
70ac18a7e6 | ||
|
|
e461c4a018 | ||
|
|
186364f227 | ||
|
|
72f9d45bbe | ||
|
|
6392e06a36 | ||
|
|
c879e63fc2 | ||
|
|
290a6dfb6e | ||
|
|
94c82d121c | ||
|
|
3e92d6f6a3 | ||
|
|
3a2b50720e | ||
|
|
8f47e65684 | ||
|
|
95ebf3bda8 | ||
|
|
ea95357ae4 | ||
|
|
7501e255dd | ||
|
|
ff13571c71 | ||
|
|
6501173ce4 | ||
|
|
1d13040305 | ||
|
|
af1439b274 | ||
|
|
32af18b33d | ||
|
|
a48e537b2d | ||
|
|
424bc0e7c5 | ||
|
|
e98821fafc | ||
|
|
2d61fcdef4 | ||
|
|
045160c7bb | ||
|
|
f98b03bee5 | ||
|
|
703baf4ce9 | ||
|
|
1bf1ed79e8 | ||
|
|
a7888f0c66 | ||
|
|
7122f59c51 | ||
|
|
839266903a | ||
|
|
a8e3dc06fe | ||
|
|
7e0bc91a44 | ||
|
|
62efb67d06 | ||
|
|
00fd47105d | ||
|
|
1bee24fa68 | ||
|
|
9a539fb414 | ||
|
|
c0828f2d00 | ||
|
|
90c70aef96 |
No files matched your search
+48
-16
@@ -1,13 +1,17 @@
|
||||
FROM quay.io/wekan/ubuntu:groovy-20210115
|
||||
FROM ubuntu:21.10
|
||||
LABEL maintainer="sgr"
|
||||
|
||||
ENV BUILD_DEPS="gnupg gosu libarchive-tools wget curl bzip2 g++ build-essential python git ca-certificates iproute2"
|
||||
# 2022-04-25:
|
||||
# - gyp does not yet work with Ubuntu 22.04 ubuntu:rolling,
|
||||
# so changing to 21.10. https://github.com/wekan/wekan/issues/4488
|
||||
|
||||
ENV BUILD_DEPS="gnupg gosu libarchive-tools wget curl bzip2 g++ build-essential python3 git ca-certificates iproute2"
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.22.9 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
NODE_VERSION=v14.21.4 \
|
||||
METEOR_RELEASE=METEOR@2.13 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=latest \
|
||||
@@ -16,6 +20,7 @@ ENV \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
RESULTS_PER_PAGE="" \
|
||||
DEFAULT_BOARD_ID="" \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
|
||||
@@ -26,6 +31,12 @@ ENV \
|
||||
RICHER_CARD_COMMENT_EDITOR=false \
|
||||
CARD_OPENED_WEBHOOK_ENABLED=false \
|
||||
ATTACHMENTS_STORE_PATH="" \
|
||||
ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="" \
|
||||
ATTACHMENTS_UPLOAD_MIME_TYPES="" \
|
||||
ATTACHMENTS_UPLOAD_MAX_SIZE=0 \
|
||||
AVATARS_UPLOAD_EXTERNAL_PROGRAM="" \
|
||||
AVATARS_UPLOAD_MIME_TYPES="" \
|
||||
AVATARS_UPLOAD_MAX_SIZE=0 \
|
||||
MAX_IMAGE_PIXEL="" \
|
||||
IMAGE_COMPRESS_RATIO="" \
|
||||
NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE="" \
|
||||
@@ -132,7 +143,18 @@ ENV \
|
||||
SAML_IDENTIFIER_FORMAT="" \
|
||||
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
|
||||
SAML_ATTRIBUTES="" \
|
||||
DEFAULT_WAIT_SPINNER=""
|
||||
DEFAULT_WAIT_SPINNER="" \
|
||||
S3=""
|
||||
# \
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap:
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# Install OS
|
||||
RUN set -o xtrace \
|
||||
@@ -141,14 +163,18 @@ RUN set -o xtrace \
|
||||
&& apt-get install --assume-yes --no-install-recommends apt-utils apt-transport-https ca-certificates 2>&1 \
|
||||
&& apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
|
||||
|
||||
# OLD:
|
||||
# && curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-$ARCHITECTURE.tar.xz" \
|
||||
# && curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/SHASUMS256.txt.asc" \
|
||||
|
||||
# Install NodeJS
|
||||
RUN set -o xtrace \
|
||||
&& cd /tmp \
|
||||
&& curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-$ARCHITECTURE.tar.xz" \
|
||||
&& curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/SHASUMS256.txt.asc" \
|
||||
&& grep " node-$NODE_VERSION-$ARCHITECTURE.tar.xz\$" SHASUMS256.txt.asc | sha256sum -c - \
|
||||
&& curl -fsSLO --compressed "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.xz" \
|
||||
&& curl -fsSLO --compressed "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt" \
|
||||
&& grep " node-$NODE_VERSION-$ARCHITECTURE.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
|
||||
&& tar -xJf "node-$NODE_VERSION-$ARCHITECTURE.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
|
||||
&& rm "node-$NODE_VERSION-$ARCHITECTURE.tar.xz" SHASUMS256.txt.asc \
|
||||
&& rm "node-$NODE_VERSION-$ARCHITECTURE.tar.xz" SHASUMS256.txt \
|
||||
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
|
||||
&& mkdir -p /usr/local/lib/node_modules/fibers/.node-gyp /root/.node-gyp/${NODE_VERSION} /home/wekan/.config \
|
||||
&& npm install -g npm@${NPM_VERSION} \
|
||||
@@ -211,12 +237,6 @@ RUN set -o xtrace \
|
||||
|
||||
USER wekan
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \
|
||||
cd /home/wekan/.meteor && \
|
||||
/home/wekan/.meteor/meteor -- help;
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
# Build app
|
||||
@@ -229,7 +249,9 @@ RUN \
|
||||
set -o xtrace && \
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
chmod u+w package.json npm-shrinkwrap.json && \
|
||||
npm install
|
||||
npm install && \
|
||||
cd node_modules/fibers && \
|
||||
node build.js
|
||||
|
||||
USER root
|
||||
# Cleanup
|
||||
@@ -248,4 +270,14 @@ ENV PORT=3000
|
||||
EXPOSE $PORT
|
||||
WORKDIR /home/wekan/app
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap:
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------------
|
||||
#TODO:
|
||||
#CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 /build/main.js"]
|
||||
|
||||
CMD ["/home/wekan/.meteor/meteor", "run", "--verbose", "--settings", "settings.json"]
|
||||
@@ -9,4 +9,6 @@ cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/pa
|
||||
cd .build/bundle/programs/server
|
||||
rm -rf node_modules
|
||||
/home/wekan/.meteor/meteor npm install
|
||||
cd node_modules/fibers
|
||||
node build.js
|
||||
cd /home/wekan/app
|
||||
@@ -3,7 +3,7 @@ version: '3.7'
|
||||
services:
|
||||
|
||||
wekandb-dev:
|
||||
image: mongo:4.4
|
||||
image: mongo:6
|
||||
container_name: wekan-dev-db
|
||||
restart: unless-stopped
|
||||
command: mongod --oplogSize 128
|
||||
@@ -33,14 +33,16 @@ services:
|
||||
- WITH_API=true
|
||||
- RICHER_CARD_COMMENT_EDITOR=true
|
||||
- BROWSER_POLICY_ENABLED=true
|
||||
- WRITABLE_PATH=/data
|
||||
depends_on:
|
||||
- wekandb-dev
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./volumes/data:/data
|
||||
- ../client:/home/wekan/app/client
|
||||
- ../models:/home/wekan/app/models
|
||||
- ../config:/home/wekan/app/config
|
||||
- ../i18n:/home/wekan/app/i18n
|
||||
- ../imports:/home/wekan/app/imports
|
||||
- ../server:/home/wekan/app/server
|
||||
- ../public:/home/wekan/app/public
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.22.9
|
||||
node-engine: 14.21.3
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
name: wekan
|
||||
version: git
|
||||
summary: The open-source kanban
|
||||
description: |
|
||||
Wekan is an open-source and collaborative kanban board application.
|
||||
|
||||
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.
|
||||
Depending on target environment, some configuration settings might need to be adjusted.
|
||||
For full list of configuration options call:
|
||||
$ wekan.help
|
||||
|
||||
confinement: strict
|
||||
grade: stable
|
||||
base: core18
|
||||
|
||||
architectures:
|
||||
- amd64
|
||||
|
||||
plugs:
|
||||
mongodb-plug:
|
||||
interface: content
|
||||
target: $SNAP_DATA/shared
|
||||
|
||||
hooks:
|
||||
configure:
|
||||
plugs:
|
||||
- network
|
||||
- network-bind
|
||||
|
||||
slots:
|
||||
mongodb-slot:
|
||||
interface: content
|
||||
write:
|
||||
- $SNAP_DATA/share
|
||||
|
||||
apps:
|
||||
wekan:
|
||||
command: wekan-control
|
||||
daemon: simple
|
||||
plugs: [network, network-bind]
|
||||
|
||||
mongodb:
|
||||
command: mongodb-control
|
||||
daemon: simple
|
||||
plugs: [network, network-bind]
|
||||
|
||||
caddy:
|
||||
command: caddy-control
|
||||
daemon: simple
|
||||
plugs: [network, network-bind]
|
||||
|
||||
help:
|
||||
command: wekan-help
|
||||
|
||||
database-backup:
|
||||
command: mongodb-backup
|
||||
plugs: [network, network-bind]
|
||||
|
||||
database-list-backups:
|
||||
command: ls -al $SNAP_COMMON/db-backups/
|
||||
|
||||
database-restore:
|
||||
command: mongodb-restore
|
||||
plugs: [network, network-bind]
|
||||
|
||||
parts:
|
||||
mongodb:
|
||||
source: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.2/multiverse/binary-amd64/mongodb-org-server_4.2.2_amd64.deb
|
||||
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.14.tgz
|
||||
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
|
||||
plugin: dump
|
||||
stage-packages: [libssl1.0.0, libcurl3]
|
||||
filesets:
|
||||
mongo:
|
||||
- usr
|
||||
- bin
|
||||
- lib
|
||||
stage:
|
||||
- $mongo
|
||||
prime:
|
||||
- $mongo
|
||||
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 14.19.0
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
- fibers
|
||||
build-packages:
|
||||
- ca-certificates
|
||||
- apt-utils
|
||||
- build-essential
|
||||
- python
|
||||
- python3
|
||||
- g++
|
||||
- capnproto
|
||||
- curl
|
||||
- libcurl3
|
||||
- execstack
|
||||
- nodejs
|
||||
- npm
|
||||
stage-packages:
|
||||
- libfontconfig1
|
||||
override-build: |
|
||||
echo "Cleaning environment first"
|
||||
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
|
||||
rm -rf .build
|
||||
echo "Installing meteor"
|
||||
curl https://install.meteor.com/ -o install_meteor.sh
|
||||
chmod +x install_meteor.sh
|
||||
sh install_meteor.sh
|
||||
rm install_meteor.sh
|
||||
rm -rf .build
|
||||
meteor add standard-minifier-js --allow-superuser
|
||||
meteor npm install --allow-superuser
|
||||
meteor npm install --allow-superuser --save babel-runtime
|
||||
meteor build .build --directory --allow-superuser
|
||||
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
|
||||
cd .build/bundle/programs/server
|
||||
npm install
|
||||
npm install --allow-superuser --save babel-runtime
|
||||
# Change back to Wekan source directory
|
||||
cd ../../../..
|
||||
cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
|
||||
cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
|
||||
|
||||
organize:
|
||||
README: README.wekan
|
||||
prime:
|
||||
- -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
|
||||
|
||||
helpers:
|
||||
source: snap-src
|
||||
plugin: dump
|
||||
|
||||
caddy:
|
||||
plugin: dump
|
||||
source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
|
||||
source-type: tar
|
||||
organize:
|
||||
caddy: bin/caddy
|
||||
CHANGES.txt: CADDY_CHANGES.txt
|
||||
EULA.txt: CADDY_EULA.txt
|
||||
LICENSES.txt: CADDY_LICENSES.txt
|
||||
README.txt: CADDY_README.txt
|
||||
stage:
|
||||
- -init
|
||||
+93
-65
@@ -1,8 +1,9 @@
|
||||
name: wekan
|
||||
version: git
|
||||
summary: The open-source kanban
|
||||
version: '6.21'
|
||||
base: core20
|
||||
summary: Open Source kanban
|
||||
description: |
|
||||
Wekan is an open-source and collaborative kanban board application.
|
||||
WeKan ® is an Open Source and collaborative kanban board application.
|
||||
|
||||
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.
|
||||
Depending on target environment, some configuration settings might need to be adjusted.
|
||||
@@ -11,10 +12,19 @@ description: |
|
||||
|
||||
confinement: strict
|
||||
grade: stable
|
||||
base: core18
|
||||
|
||||
architectures:
|
||||
- amd64
|
||||
- build-on: amd64
|
||||
run-on: amd64
|
||||
|
||||
- build-on: arm64
|
||||
run-on: arm64
|
||||
|
||||
- build-on: ppc64el
|
||||
run-on: ppc64el
|
||||
|
||||
- build-on: s390x
|
||||
run-on: s390x
|
||||
|
||||
plugs:
|
||||
mongodb-plug:
|
||||
@@ -37,12 +47,14 @@ apps:
|
||||
wekan:
|
||||
command: wekan-control
|
||||
daemon: simple
|
||||
plugs: [network, network-bind]
|
||||
plugs: [network, network-bind, mount-observe, system-observe, bluetooth-control]
|
||||
restart-condition: on-failure
|
||||
|
||||
mongodb:
|
||||
command: mongodb-control
|
||||
daemon: simple
|
||||
plugs: [network, network-bind]
|
||||
plugs: [network, network-bind, mount-observe, system-observe, bluetooth-control]
|
||||
restart-condition: on-failure
|
||||
|
||||
caddy:
|
||||
command: caddy-control
|
||||
@@ -54,22 +66,27 @@ apps:
|
||||
|
||||
database-backup:
|
||||
command: mongodb-backup
|
||||
plugs: [network, network-bind]
|
||||
plugs: [network, network-bind, mount-observe, system-observe, bluetooth-control]
|
||||
|
||||
database-list-backups:
|
||||
command: ls -al $SNAP_COMMON/db-backups/
|
||||
|
||||
database-restore:
|
||||
command: mongodb-restore
|
||||
plugs: [network, network-bind]
|
||||
plugs: [network, network-bind, mount-observe, system-observe, bluetooth-control]
|
||||
|
||||
parts:
|
||||
mongodb:
|
||||
source: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.2/multiverse/binary-amd64/mongodb-org-server_4.2.2_amd64.deb
|
||||
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.14.tgz
|
||||
#https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
|
||||
plugin: dump
|
||||
stage-packages: [libssl1.0.0, libcurl3]
|
||||
source:
|
||||
- on amd64: https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/4.4/multiverse/binary-amd64/mongodb-org-server_4.4.13_amd64.deb
|
||||
- on arm64: https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/4.4/multiverse/binary-arm64/mongodb-org-server_4.4.13_arm64.deb
|
||||
- on ppc64el: https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/4.4/multiverse/binary-ppc64el/mongodb-org-server_4.4.13_ppc64el.deb
|
||||
- on s390x: https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/4.4/multiverse/binary-s390x/mongodb-org-server_4.4.13_s390x.deb
|
||||
stage-packages:
|
||||
- libssl1.1
|
||||
- libcurl3-dev
|
||||
- libcurl4-openssl-dev
|
||||
filesets:
|
||||
mongo:
|
||||
- usr
|
||||
@@ -81,61 +98,46 @@ parts:
|
||||
- $mongo
|
||||
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.22.9
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
- fibers
|
||||
build-packages:
|
||||
- ca-certificates
|
||||
- apt-utils
|
||||
- build-essential
|
||||
- python
|
||||
- python3
|
||||
- g++
|
||||
- capnproto
|
||||
- curl
|
||||
- libcurl3
|
||||
- execstack
|
||||
- nodejs
|
||||
- npm
|
||||
stage-packages:
|
||||
- libfontconfig1
|
||||
#plugin: npm
|
||||
plugin: dump
|
||||
source:
|
||||
# Fixed URLs to some allowed GitHub releases URL.
|
||||
# Non-GitHub build server file urls are not allowed at 2022-03-02 and later.
|
||||
- on amd64: https://github.com/wekan/wekan/releases/download/v6.20/wekan-6.20-amd64.zip
|
||||
- on arm64: https://github.com/wekan/wekan/releases/download/v6.20/wekan-6.20-arm64.zip
|
||||
- on ppc64el: https://github.com/wekan/wekan/releases/download/v6.20/wekan-6.20-ppc64el.zip
|
||||
- on s390x: https://github.com/wekan/wekan/releases/download/v6.20/wekan-6.20-s390x.zip
|
||||
# npm-node-version: 14.19.1
|
||||
# node-packages:
|
||||
# - node-gyp
|
||||
# - node-pre-gyp
|
||||
# - fibers
|
||||
# build-packages:
|
||||
# - npm
|
||||
# - build-essential
|
||||
# - ca-certificates
|
||||
# - apt-utils
|
||||
# - python
|
||||
# - python3
|
||||
# - g++
|
||||
# - capnproto
|
||||
# - curl
|
||||
# - execstack
|
||||
# - nodejs
|
||||
# - npm
|
||||
# - p7zip-full
|
||||
# stage-packages:
|
||||
# - libfontconfig1
|
||||
override-build: |
|
||||
echo "Cleaning environment first"
|
||||
rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
|
||||
rm -rf .build
|
||||
echo "Installing meteor"
|
||||
curl https://install.meteor.com/ -o install_meteor.sh
|
||||
chmod +x install_meteor.sh
|
||||
sh install_meteor.sh
|
||||
rm install_meteor.sh
|
||||
rm -rf .build
|
||||
meteor add standard-minifier-js --allow-superuser
|
||||
meteor npm install --allow-superuser
|
||||
meteor npm install --allow-superuser --save babel-runtime
|
||||
meteor build .build --directory --allow-superuser
|
||||
cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
|
||||
cd .build/bundle/programs/server
|
||||
npm install
|
||||
npm install --allow-superuser --save babel-runtime
|
||||
# Change back to Wekan source directory
|
||||
cd ../../../..
|
||||
cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
|
||||
cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
|
||||
|
||||
cp -r bundle/* $SNAPCRAFT_PART_INSTALL/
|
||||
cp bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
|
||||
rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
|
||||
snapcraftctl build
|
||||
organize:
|
||||
README: README.wekan
|
||||
prime:
|
||||
- -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
|
||||
- -lib/node_modules/weka*
|
||||
|
||||
helpers:
|
||||
source: snap-src
|
||||
@@ -143,8 +145,34 @@ parts:
|
||||
|
||||
caddy:
|
||||
plugin: dump
|
||||
source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
|
||||
source-type: tar
|
||||
## Caddy v1 is not developed anymore. TODO: Sometime migrate to Caddy v2.
|
||||
## https://caddy.community/t/caddyfile-v1-adapter/9129
|
||||
## https://github.com/caddyserver/caddy/tree/v1
|
||||
#source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
|
||||
#source-type: tar
|
||||
# Using last working binary that was downloaded from above URL to Wekan Snap,
|
||||
# and .txt files from https://github.com/caddyserver/caddy/tree/v1/dist
|
||||
source: https://wekan.github.io/caddy-v1-linux-amd64.7z
|
||||
source-type: 7z
|
||||
organize:
|
||||
caddy: bin/caddy
|
||||
CHANGES.txt: CADDY_CHANGES.txt
|
||||
EULA.txt: CADDY_EULA.txt
|
||||
LICENSES.txt: CADDY_LICENSES.txt
|
||||
README.txt: CADDY_README.txt
|
||||
stage:
|
||||
- -init
|
||||
|
||||
caddy2:
|
||||
plugin: dump
|
||||
source:
|
||||
# Fixed URLs to some allowed GitHub releases URL.
|
||||
# Non-GitHub build server file urls are not allowed at 2022-03-02 and later.
|
||||
- on amd64: https://github.com/wekan/wekan/releases/download/v6.20/caddy-v2-amd64.zip
|
||||
- on arm64: https://github.com/wekan/wekan/releases/download/v6.20/caddy-v2-arm64.zip
|
||||
- on ppc64el: https://github.com/wekan/wekan/releases/download/v6.20/caddy-v2-ppc64el.zip
|
||||
- on s390x: https://github.com/wekan/wekan/releases/download/v6.20/caddy-v2-s390x.zip
|
||||
source-type: zip
|
||||
organize:
|
||||
caddy: bin/caddy
|
||||
CHANGES.txt: CADDY_CHANGES.txt
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
## Issue
|
||||
<!--
|
||||
Please report these elsewhere:
|
||||
- SECURITY ISSUES, PGP EMAIL: https://github.com/wekan/wekan/blob/main/SECURITY.md
|
||||
- UCS: https://github.com/wekan/univention/issues
|
||||
|
||||
**[PLEASE UPGRADE](https://github.com/wekan/wekan/wiki/Backup)** to the newest WeKan ® before reporting an issue.
|
||||
Please search existing Open and Closed issues, most questions have already been answered.
|
||||
|
||||
If you can not login for any reason: https://github.com/wekan/wekan/wiki/Forgot-Password
|
||||
Email settings, only SMTP MAIL_URL and MAIL_FROM are in use: https://github.com/wekan/wekan/wiki/Troubleshooting-Mail
|
||||
|
||||
The following types of issues should be reported separately:
|
||||
- SECURITY ISSUES: https://github.com/wekan/wekan/blob/master/SECURITY.md
|
||||
- UCS: https://github.com/wekan/univention/issues
|
||||
-->
|
||||
|
||||
### Server Setup Information
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -1,62 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '0 16 * * 3'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Override automatic language detection by changing the below list
|
||||
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
||||
language: ['javascript', 'python']
|
||||
# Learn more...
|
||||
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -0,0 +1,14 @@
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v4
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v3
|
||||
@@ -9,11 +9,11 @@ on:
|
||||
schedule:
|
||||
- cron: '28 23 * * *'
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
# Publish semver tags as releases.
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
@@ -32,13 +32,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -48,14 +48,14 @@ jobs:
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
@@ -3,7 +3,10 @@ name: Docker Image CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -12,6 +15,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build the Docker image
|
||||
run: docker build . --file Dockerfile --tag wekan:$(date +%s)
|
||||
@@ -3,14 +3,19 @@ name: Release Charts
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release:
|
||||
permissions:
|
||||
contents: write # for helm/chart-releaser-action to push chart release and create a release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -20,6 +25,6 @@ jobs:
|
||||
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||
|
||||
- name: Run chart-releaser
|
||||
uses: helm/chart-releaser-action@v1.1.0
|
||||
uses: helm/chart-releaser-action@v1.6.0
|
||||
env:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@@ -3,9 +3,12 @@ name: Test suite
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
# the following are optional jobs and need to be configured according
|
||||
# to this project's settings:
|
||||
@@ -15,7 +18,7 @@ jobs:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: checkout
|
||||
# uses: actions/checkout@v2
|
||||
# uses: actions/checkout@v4
|
||||
#
|
||||
# - name: setup node
|
||||
# uses: actions/setup-node@v1
|
||||
@@ -39,7 +42,7 @@ jobs:
|
||||
# needs: [lintcode]
|
||||
# steps:
|
||||
# - name: checkout
|
||||
# uses: actions/checkout@v2
|
||||
# uses: actions/checkout@v4
|
||||
#
|
||||
# - name: setup node
|
||||
# uses: actions/setup-node@v1
|
||||
@@ -62,7 +65,7 @@ jobs:
|
||||
# needs: [lintcode,lintstyle]
|
||||
# steps:
|
||||
# - name: checkout
|
||||
# uses: actions/checkout@v2
|
||||
# uses: actions/checkout@v4
|
||||
#
|
||||
# - name: setup node
|
||||
# uses: actions/setup-node@v1
|
||||
@@ -87,12 +90,12 @@ jobs:
|
||||
|
||||
# CHECKOUTS
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# CACHING
|
||||
- name: Install Meteor
|
||||
id: cache-meteor-install
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.meteor
|
||||
key: v1-meteor-${{ hashFiles('.meteor/versions') }}
|
||||
@@ -101,7 +104,7 @@ jobs:
|
||||
|
||||
- name: Cache NPM dependencies
|
||||
id: cache-meteor-npm
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: v1-npm-${{ hashFiles('package-lock.json') }}
|
||||
@@ -110,7 +113,7 @@ jobs:
|
||||
|
||||
- name: Cache Meteor build
|
||||
id: cache-meteor-build
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
.meteor/local/resolver-result-cache.json
|
||||
@@ -118,7 +121,7 @@ jobs:
|
||||
.meteor/local/isopacks
|
||||
.meteor/local/bundler-cache/scanner
|
||||
key: v1-meteor_build_cache-${{ github.ref }}-${{ github.sha }}
|
||||
restore-key: |
|
||||
restore-keys: |
|
||||
v1-meteor_build_cache-
|
||||
|
||||
- name: Setup meteor
|
||||
@@ -133,7 +136,7 @@ jobs:
|
||||
run: sh ./test-wekan.sh -cv
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: coverage-folder
|
||||
path: .coverage/
|
||||
@@ -144,17 +147,17 @@ jobs:
|
||||
needs: [tests]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download coverage
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: coverage-folder
|
||||
path: .coverage/
|
||||
|
||||
|
||||
- name: Coverage Report
|
||||
uses: VeryGoodOpenSource/very_good_coverage@v1.1.1
|
||||
uses: VeryGoodOpenSource/very_good_coverage@v2.2.0
|
||||
with:
|
||||
path: ".coverage/lcov.info"
|
||||
min_coverage: 1 # TODO add tests and increase to 95!
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
*~
|
||||
*.swp
|
||||
*.sw*
|
||||
.meteor-spk
|
||||
*.sublime-workspace
|
||||
tmp/
|
||||
|
||||
+46
-98
@@ -3,12 +3,11 @@
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
meteor-base@1.4.0
|
||||
meteor-base@1.5.1
|
||||
|
||||
# Build system
|
||||
ecmascript@0.15.1
|
||||
standard-minifier-css@1.7.2
|
||||
standard-minifier-js@2.6.0
|
||||
ecmascript@0.16.8-beta2140.4
|
||||
standard-minifier-js@2.8.1
|
||||
mquandalle:jade
|
||||
coffeescript@2.4.1!
|
||||
|
||||
@@ -17,132 +16,81 @@ es5-shim@4.8.0
|
||||
|
||||
# Collections
|
||||
aldeed:collection2
|
||||
wekan-cfs-standard-packages
|
||||
cottz:publish-relations
|
||||
dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
matb33:collection-hooks
|
||||
matteodem:easy-search
|
||||
mongo@1.11.0
|
||||
easy:search
|
||||
mongo@1.16.8-beta2140.4
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
kenton:accounts-sandstorm
|
||||
service-configuration@1.0.11
|
||||
useraccounts:unstyled
|
||||
accounts-password@2.4.0-beta2140.4
|
||||
useraccounts:core
|
||||
useraccounts:flow-routing
|
||||
useraccounts:unstyled
|
||||
simple:rest-accounts-password
|
||||
wekan-ldap
|
||||
wekan-accounts-cas
|
||||
wekan-accounts-sandstorm
|
||||
wekan-accounts-lockout
|
||||
wekan-oidc
|
||||
wekan-accounts-oidc
|
||||
|
||||
# Utilities
|
||||
check@1.3.1
|
||||
jquery@1.11.10
|
||||
random@1.2.0
|
||||
reactive-dict@1.3.0
|
||||
session@1.2.0
|
||||
tracker@1.2.0
|
||||
underscore@1.0.10
|
||||
3stack:presence
|
||||
check@1.3.2
|
||||
jquery@3.0.0!
|
||||
random@1.2.1
|
||||
reactive-dict@1.3.1
|
||||
session@1.2.1
|
||||
tracker@1.3.2
|
||||
underscore@1.0.13
|
||||
arillo:flow-router-helpers
|
||||
audit-argument-checks@1.0.7
|
||||
kadira:blaze-layout
|
||||
kadira:dochead
|
||||
mquandalle:autofocus
|
||||
ongoworks:speakingurl
|
||||
raix:handlebar-helpers
|
||||
tap:i18n
|
||||
http@1.4.2
|
||||
http@2.0.0! # force new http package
|
||||
|
||||
# Datepicker
|
||||
wekan-bootstrap-datepicker
|
||||
|
||||
# UI components
|
||||
blaze
|
||||
reactive-var@1.0.11
|
||||
fortawesome:fontawesome
|
||||
ostrio:i18n
|
||||
reactive-var@1.0.12
|
||||
mousetrap:mousetrap
|
||||
mquandalle:jquery-textcomplete
|
||||
mquandalle:mousetrap-bindglobal
|
||||
peerlibrary:blaze-components@=0.15.1
|
||||
templates:tabs
|
||||
meteor-autosize
|
||||
simple:json-routes
|
||||
rajit:bootstrap3-datepicker
|
||||
shell-server@0.5.0
|
||||
simple:rest-accounts-password
|
||||
useraccounts:core
|
||||
email@2.0.0
|
||||
horka:swipebox
|
||||
dynamic-import@0.6.0
|
||||
|
||||
accounts-password@1.7.0
|
||||
wekan-cfs-gridfs
|
||||
rzymek:fullcalendar
|
||||
momentjs:moment@2.22.2
|
||||
browser-policy-framing@1.1.0
|
||||
mquandalle:moment
|
||||
email@2.2.5
|
||||
dynamic-import@0.7.3
|
||||
msavin:usercache
|
||||
# Keep stylus in 1.1.0, because building v2 takes extra 52 minutes.
|
||||
coagmano:stylus@1.1.0!
|
||||
meteorhacks:subs-manager
|
||||
meteorhacks:picker
|
||||
lamhieu:unblock
|
||||
meteorhacks:aggregate@1.3.0
|
||||
wekan-markdown
|
||||
konecty:mongo-counter
|
||||
percolate:synced-cron
|
||||
wekan-cfs-filesystem
|
||||
steffo:meteor-accounts-saml
|
||||
rajit:bootstrap3-datepicker-fi
|
||||
rajit:bootstrap3-datepicker-ar
|
||||
rajit:bootstrap3-datepicker-bg
|
||||
rajit:bootstrap3-datepicker-br
|
||||
rajit:bootstrap3-datepicker-ca
|
||||
rajit:bootstrap3-datepicker-cs
|
||||
rajit:bootstrap3-datepicker-da
|
||||
rajit:bootstrap3-datepicker-de
|
||||
rajit:bootstrap3-datepicker-el
|
||||
rajit:bootstrap3-datepicker-en-gb
|
||||
rajit:bootstrap3-datepicker-eo
|
||||
rajit:bootstrap3-datepicker-es
|
||||
rajit:bootstrap3-datepicker-eu
|
||||
rajit:bootstrap3-datepicker-fa
|
||||
rajit:bootstrap3-datepicker-fr
|
||||
rajit:bootstrap3-datepicker-gl
|
||||
rajit:bootstrap3-datepicker-he
|
||||
rajit:bootstrap3-datepicker-hi
|
||||
rajit:bootstrap3-datepicker-hu
|
||||
rajit:bootstrap3-datepicker-hy
|
||||
rajit:bootstrap3-datepicker-id
|
||||
rajit:bootstrap3-datepicker-it
|
||||
rajit:bootstrap3-datepicker-ja
|
||||
rajit:bootstrap3-datepicker-ka
|
||||
rajit:bootstrap3-datepicker-km
|
||||
rajit:bootstrap3-datepicker-ko
|
||||
rajit:bootstrap3-datepicker-lv
|
||||
rajit:bootstrap3-datepicker-mk
|
||||
rajit:bootstrap3-datepicker-mn
|
||||
rajit:bootstrap3-datepicker-nb
|
||||
rajit:bootstrap3-datepicker-nl
|
||||
rajit:bootstrap3-datepicker-oc
|
||||
rajit:bootstrap3-datepicker-pl
|
||||
rajit:bootstrap3-datepicker-pt-br
|
||||
rajit:bootstrap3-datepicker-pt
|
||||
rajit:bootstrap3-datepicker-ro
|
||||
rajit:bootstrap3-datepicker-ru
|
||||
rajit:bootstrap3-datepicker-sl
|
||||
rajit:bootstrap3-datepicker-sr
|
||||
rajit:bootstrap3-datepicker-sv
|
||||
rajit:bootstrap3-datepicker-sw
|
||||
rajit:bootstrap3-datepicker-ta
|
||||
rajit:bootstrap3-datepicker-th
|
||||
rajit:bootstrap3-datepicker-tr
|
||||
rajit:bootstrap3-datepicker-uk
|
||||
rajit:bootstrap3-datepicker-vi
|
||||
rajit:bootstrap3-datepicker-zh-cn
|
||||
rajit:bootstrap3-datepicker-zh-tw
|
||||
staringatlights:fast-render
|
||||
spacebars
|
||||
easylogic:summernote
|
||||
ostrio:cookies
|
||||
ostrio:files@2.3.0
|
||||
pascoual:pdfkit
|
||||
wekan-accounts-lockout
|
||||
lmieulet:meteor-coverage
|
||||
meteortesting:mocha
|
||||
meteortesting:mocha@2.0.3
|
||||
aldeed:simple-schema
|
||||
matb33:collection-hooks
|
||||
simple:json-routes
|
||||
kadira:flow-router
|
||||
spacebars
|
||||
service-configuration@1.3.2-beta2140.4
|
||||
communitypackages:picker
|
||||
minifier-css@1.6.4
|
||||
blaze
|
||||
kadira:blaze-layout
|
||||
peerlibrary:blaze-components
|
||||
ejson@1.1.3
|
||||
logging@1.3.3-beta2140.4
|
||||
wekan-fullcalendar
|
||||
momentjs:moment@2.29.3
|
||||
wekan-fontawesome
|
||||
+1
-1
@@ -1 +1 @@
|
||||
METEOR@2.2
|
||||
METEOR@2.14-beta.4
|
||||
+95
-178
@@ -1,103 +1,88 @@
|
||||
3stack:presence@1.1.2
|
||||
accounts-base@1.9.0
|
||||
accounts-oauth@1.2.0
|
||||
accounts-password@1.7.1
|
||||
accounts-base@2.2.9-beta2140.4
|
||||
accounts-oauth@1.4.3-beta2140.4
|
||||
accounts-password@2.4.0-beta2140.4
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
aldeed:schema-index@1.1.1
|
||||
aldeed:simple-schema@1.5.4
|
||||
allow-deny@1.1.0
|
||||
allow-deny@1.1.1
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
audit-argument-checks@1.0.7
|
||||
autoupdate@1.7.0
|
||||
babel-compiler@7.6.1
|
||||
babel-runtime@1.5.0
|
||||
autoupdate@1.8.0
|
||||
babel-compiler@7.10.5-beta2140.4
|
||||
babel-runtime@1.5.1
|
||||
base64@1.0.12
|
||||
binary-heap@1.0.11
|
||||
blaze@2.5.0
|
||||
blaze-tools@1.1.2
|
||||
boilerplate-generator@1.7.1
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
blaze@2.7.1
|
||||
blaze-tools@1.1.3
|
||||
boilerplate-generator@1.7.2-beta2140.4
|
||||
caching-compiler@1.2.2
|
||||
caching-html-compiler@1.2.0
|
||||
callback-hook@1.3.0
|
||||
cfs:http-methods@0.0.32
|
||||
check@1.3.1
|
||||
chuangbo:cookie@1.1.0
|
||||
coagmano:stylus@1.1.0
|
||||
coffeescript@2.4.1
|
||||
caching-html-compiler@1.2.1
|
||||
callback-hook@1.5.1
|
||||
check@1.3.2
|
||||
coffeescript@2.7.0
|
||||
coffeescript-compiler@2.4.1
|
||||
communitypackages:picker@1.1.1
|
||||
cottz:publish-relations@2.0.8
|
||||
dburles:collection-helpers@1.1.0
|
||||
ddp@1.4.0
|
||||
ddp-client@2.4.1
|
||||
ddp@1.4.1
|
||||
ddp-client@2.6.1
|
||||
ddp-common@1.4.0
|
||||
ddp-rate-limiter@1.0.9
|
||||
ddp-server@2.3.3
|
||||
ddp-rate-limiter@1.2.1-beta2140.4
|
||||
ddp-server@2.7.0-beta2140.4
|
||||
deps@1.0.12
|
||||
diff-sequence@1.1.1
|
||||
dynamic-import@0.6.0
|
||||
easylogic:summernote@0.8.8
|
||||
ecmascript@0.15.1
|
||||
ecmascript-runtime@0.7.0
|
||||
ecmascript-runtime-client@0.11.1
|
||||
ecmascript-runtime-server@0.10.1
|
||||
ejson@1.1.1
|
||||
email@2.0.0
|
||||
diff-sequence@1.1.2
|
||||
dynamic-import@0.7.3
|
||||
easy:search@2.2.1
|
||||
easysearch:components@2.2.2
|
||||
easysearch:core@2.2.2
|
||||
ecmascript@0.16.8-beta2140.4
|
||||
ecmascript-runtime@0.8.1
|
||||
ecmascript-runtime-client@0.12.1
|
||||
ecmascript-runtime-server@0.11.0
|
||||
ejson@1.1.3
|
||||
email@2.2.5
|
||||
es5-shim@4.8.0
|
||||
fastclick@1.0.13
|
||||
fetch@0.1.1
|
||||
fortawesome:fontawesome@4.7.0
|
||||
geojson-utils@1.0.10
|
||||
horka:swipebox@1.0.2
|
||||
fetch@0.1.4-beta2140.4
|
||||
geojson-utils@1.0.11
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.1.2
|
||||
html-tools@1.1.3
|
||||
htmljs@1.1.1
|
||||
http@1.4.4
|
||||
http@2.0.0
|
||||
id-map@1.1.1
|
||||
idmontie:migrations@1.0.3
|
||||
inter-process-messaging@0.1.1
|
||||
jquery@1.11.11
|
||||
jquery@3.0.0
|
||||
kadira:blaze-layout@2.3.0
|
||||
kadira:dochead@1.5.0
|
||||
kadira:flow-router@2.12.1
|
||||
kenton:accounts-sandstorm@0.7.0
|
||||
konecty:mongo-counter@0.0.5_3
|
||||
lamhieu:meteorx@2.1.1
|
||||
lamhieu:unblock@1.0.0
|
||||
launch-screen@1.2.1
|
||||
livedata@1.0.18
|
||||
lmieulet:meteor-coverage@3.2.0
|
||||
lmieulet:meteor-coverage@1.1.4
|
||||
localstorage@1.2.0
|
||||
logging@1.2.0
|
||||
matb33:collection-hooks@0.9.1
|
||||
matteodem:easy-search@1.6.4
|
||||
logging@1.3.3-beta2140.4
|
||||
matb33:collection-hooks@1.3.0
|
||||
mdg:validation-error@0.5.1
|
||||
meteor@1.9.3
|
||||
meteor@1.11.4-beta2140.4
|
||||
meteor-autosize@5.0.1
|
||||
meteor-base@1.4.0
|
||||
meteor-platform@1.2.6
|
||||
meteor-base@1.5.1
|
||||
meteorhacks:aggregate@1.3.0
|
||||
meteorhacks:collection-utils@1.2.0
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
meteorspark:util@0.2.0
|
||||
meteortesting:browser-tests@1.3.4
|
||||
meteortesting:mocha@2.0.1
|
||||
meteortesting:browser-tests@1.4.2
|
||||
meteortesting:mocha@2.1.0
|
||||
meteortesting:mocha-core@8.0.1
|
||||
minifier-css@1.5.4
|
||||
minifier-js@2.6.0
|
||||
minifier-css@1.6.4
|
||||
minifier-js@2.7.5
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.6.2
|
||||
mobile-status-bar@1.1.0
|
||||
modern-browsers@0.1.5
|
||||
modules@0.16.0
|
||||
modules-runtime@0.12.0
|
||||
momentjs:moment@2.29.1
|
||||
mongo@1.11.1
|
||||
mongo-decimal@0.1.2
|
||||
minimongo@1.9.3
|
||||
modern-browsers@0.1.10-beta2140.4
|
||||
modules@0.20.0-beta2140.4
|
||||
modules-runtime@0.13.1
|
||||
momentjs:moment@2.29.3
|
||||
mongo@1.16.8-beta2140.4
|
||||
mongo-decimal@0.1.3
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.8
|
||||
mongo-livedata@1.0.12
|
||||
@@ -107,143 +92,75 @@ mquandalle:collection-mutations@0.1.0
|
||||
mquandalle:jade@0.4.9
|
||||
mquandalle:jade-compiler@0.4.5
|
||||
mquandalle:jquery-textcomplete@0.8.0_1
|
||||
mquandalle:moment@1.0.1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
msavin:usercache@1.8.0
|
||||
npm-bcrypt@0.9.4
|
||||
npm-mongo@3.9.0
|
||||
oauth@1.3.2
|
||||
oauth2@1.3.0
|
||||
observe-sequence@1.0.16
|
||||
npm-mongo@4.17.0-beta2140.4
|
||||
oauth@2.2.1-beta2140.4
|
||||
oauth2@1.3.2
|
||||
observe-sequence@1.0.21
|
||||
ongoworks:speakingurl@1.1.0
|
||||
ordered-dict@1.1.0
|
||||
ostrio:cookies@2.7.2
|
||||
ostrio:cstorage@4.0.1
|
||||
ostrio:files@2.3.3
|
||||
ostrio:i18n@3.2.1
|
||||
pascoual:pdfkit@1.0.7
|
||||
peerlibrary:assert@0.3.0
|
||||
peerlibrary:base-component@0.16.0
|
||||
peerlibrary:blaze-components@0.15.1
|
||||
peerlibrary:base-component@0.17.1
|
||||
peerlibrary:blaze-components@0.23.0
|
||||
peerlibrary:computed-field@0.10.0
|
||||
peerlibrary:data-lookup@0.3.0
|
||||
peerlibrary:reactive-field@0.6.0
|
||||
percolate:synced-cron@1.3.2
|
||||
promise@0.11.2
|
||||
promise@0.12.2
|
||||
raix:eventemitter@0.1.3
|
||||
raix:handlebar-helpers@0.2.5
|
||||
rajit:bootstrap3-datepicker@1.7.1_1
|
||||
rajit:bootstrap3-datepicker-ar@1.7.1
|
||||
rajit:bootstrap3-datepicker-bg@1.7.1
|
||||
rajit:bootstrap3-datepicker-br@1.7.1
|
||||
rajit:bootstrap3-datepicker-ca@1.7.1
|
||||
rajit:bootstrap3-datepicker-cs@1.7.1
|
||||
rajit:bootstrap3-datepicker-da@1.7.1
|
||||
rajit:bootstrap3-datepicker-de@1.7.1
|
||||
rajit:bootstrap3-datepicker-el@1.7.1
|
||||
rajit:bootstrap3-datepicker-en-gb@1.7.1
|
||||
rajit:bootstrap3-datepicker-eo@1.7.1
|
||||
rajit:bootstrap3-datepicker-es@1.7.1
|
||||
rajit:bootstrap3-datepicker-eu@1.7.1
|
||||
rajit:bootstrap3-datepicker-fa@1.7.1
|
||||
rajit:bootstrap3-datepicker-fi@1.7.1
|
||||
rajit:bootstrap3-datepicker-fr@1.7.1
|
||||
rajit:bootstrap3-datepicker-gl@1.7.1
|
||||
rajit:bootstrap3-datepicker-he@1.7.1
|
||||
rajit:bootstrap3-datepicker-hi@1.7.1
|
||||
rajit:bootstrap3-datepicker-hu@1.7.1
|
||||
rajit:bootstrap3-datepicker-hy@1.7.1
|
||||
rajit:bootstrap3-datepicker-id@1.7.1
|
||||
rajit:bootstrap3-datepicker-it@1.7.1
|
||||
rajit:bootstrap3-datepicker-ja@1.7.1
|
||||
rajit:bootstrap3-datepicker-ka@1.7.1
|
||||
rajit:bootstrap3-datepicker-km@1.7.1
|
||||
rajit:bootstrap3-datepicker-ko@1.7.1
|
||||
rajit:bootstrap3-datepicker-lv@1.7.1
|
||||
rajit:bootstrap3-datepicker-mk@1.7.1
|
||||
rajit:bootstrap3-datepicker-mn@1.7.1
|
||||
rajit:bootstrap3-datepicker-nb@1.7.1
|
||||
rajit:bootstrap3-datepicker-nl@1.7.1
|
||||
rajit:bootstrap3-datepicker-oc@1.7.1
|
||||
rajit:bootstrap3-datepicker-pl@1.7.1
|
||||
rajit:bootstrap3-datepicker-pt@1.7.1
|
||||
rajit:bootstrap3-datepicker-pt-br@1.7.1
|
||||
rajit:bootstrap3-datepicker-ro@1.7.1
|
||||
rajit:bootstrap3-datepicker-ru@1.7.1
|
||||
rajit:bootstrap3-datepicker-sl@1.7.1
|
||||
rajit:bootstrap3-datepicker-sr@1.7.1
|
||||
rajit:bootstrap3-datepicker-sv@1.7.1
|
||||
rajit:bootstrap3-datepicker-sw@1.7.1
|
||||
rajit:bootstrap3-datepicker-ta@1.7.1
|
||||
rajit:bootstrap3-datepicker-th@1.7.1
|
||||
rajit:bootstrap3-datepicker-tr@1.7.1
|
||||
rajit:bootstrap3-datepicker-uk@1.7.1
|
||||
rajit:bootstrap3-datepicker-vi@1.7.1
|
||||
rajit:bootstrap3-datepicker-zh-cn@1.7.1
|
||||
rajit:bootstrap3-datepicker-zh-tw@1.7.1
|
||||
random@1.2.0
|
||||
rate-limit@1.0.9
|
||||
react-fast-refresh@0.1.1
|
||||
reactive-dict@1.3.0
|
||||
reactive-var@1.0.11
|
||||
random@1.2.1
|
||||
rate-limit@1.1.1
|
||||
react-fast-refresh@0.2.8-beta2140.4
|
||||
reactive-dict@1.3.1
|
||||
reactive-var@1.0.12
|
||||
reload@1.3.1
|
||||
retry@1.1.0
|
||||
routepolicy@1.1.0
|
||||
rzymek:fullcalendar@3.8.0
|
||||
server-render@0.3.1
|
||||
service-configuration@1.0.11
|
||||
session@1.2.0
|
||||
routepolicy@1.1.1
|
||||
service-configuration@1.3.2-beta2140.4
|
||||
session@1.2.1
|
||||
sha@1.0.9
|
||||
shell-server@0.5.0
|
||||
simple:authenticate-user-by-token@1.0.1
|
||||
simple:json-routes@2.1.0
|
||||
simple:rest-accounts-password@1.1.2
|
||||
simple:rest-bearer-token-parser@1.0.1
|
||||
simple:rest-json-error-handler@1.0.1
|
||||
socket-stream-client@0.3.3
|
||||
softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.2.0
|
||||
spacebars-compiler@1.2.1
|
||||
srp@1.1.0
|
||||
standard-minifier-css@1.7.2
|
||||
standard-minifier-js@2.6.0
|
||||
staringatlights:fast-render@3.3.0
|
||||
staringatlights:inject-data@2.3.0
|
||||
steffo:meteor-accounts-saml@0.0.18
|
||||
tap:i18n@1.8.2
|
||||
simple:authenticate-user-by-token@1.2.1
|
||||
simple:json-routes@2.3.1
|
||||
simple:rest-accounts-password@1.2.2
|
||||
simple:rest-bearer-token-parser@1.1.1
|
||||
simple:rest-json-error-handler@1.1.1
|
||||
socket-stream-client@0.5.2-beta2140.4
|
||||
spacebars@1.4.1
|
||||
spacebars-compiler@1.3.1
|
||||
standard-minifier-js@2.8.1
|
||||
templates:tabs@2.3.0
|
||||
templating@1.4.0
|
||||
templating@1.4.1
|
||||
templating-compiler@1.4.1
|
||||
templating-runtime@1.4.0
|
||||
templating-tools@1.2.0
|
||||
tracker@1.2.0
|
||||
twbs:bootstrap@3.3.6
|
||||
templating-runtime@1.5.0
|
||||
templating-tools@1.2.2
|
||||
tracker@1.3.2
|
||||
typescript@4.9.5-beta2140.4
|
||||
ui@1.0.13
|
||||
underscore@1.0.10
|
||||
underscore@1.0.13
|
||||
url@1.3.2
|
||||
useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:core@1.16.2
|
||||
useraccounts:flow-routing@1.15.0
|
||||
useraccounts:unstyled@1.14.2
|
||||
webapp@1.10.1
|
||||
webapp-hashing@1.1.0
|
||||
webapp@1.13.5
|
||||
webapp-hashing@1.1.1
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-lockout@1.0.0
|
||||
wekan-accounts-oidc@1.0.10
|
||||
wekan-cfs-access-point@0.1.50
|
||||
wekan-cfs-base-package@0.0.30
|
||||
wekan-cfs-collection@0.5.5
|
||||
wekan-cfs-collection-filters@0.2.4
|
||||
wekan-cfs-data-man@0.0.6
|
||||
wekan-cfs-file@0.1.17
|
||||
wekan-cfs-filesystem@0.1.2
|
||||
wekan-cfs-gridfs@0.0.34
|
||||
wekan-cfs-http-methods@0.0.32
|
||||
wekan-cfs-http-publish@0.0.13
|
||||
wekan-cfs-power-queue@0.9.11
|
||||
wekan-cfs-reactive-list@0.0.9
|
||||
wekan-cfs-reactive-property@0.0.4
|
||||
wekan-cfs-standard-packages@0.5.10
|
||||
wekan-cfs-storage-adapter@0.2.4
|
||||
wekan-cfs-tempstore@0.1.6
|
||||
wekan-cfs-upload-http@0.0.21
|
||||
wekan-cfs-worker@0.1.5
|
||||
wekan-accounts-sandstorm@0.8.0
|
||||
wekan-bootstrap-datepicker@1.10.0
|
||||
wekan-fontawesome@6.4.2
|
||||
wekan-fullcalendar@3.10.5
|
||||
wekan-ldap@0.0.2
|
||||
wekan-markdown@1.0.9
|
||||
wekan-oidc@1.0.12
|
||||
yasaricli:slugify@0.0.7
|
||||
zimme:active-route@2.3.2
|
||||
zodern:types@1.0.10
|
||||
+1
-1
@@ -3,7 +3,7 @@ sudo: required
|
||||
|
||||
env:
|
||||
TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
|
||||
TRAVIS_NODE_VERSION: 12.22.9
|
||||
TRAVIS_NODE_VERSION: 14.21.3
|
||||
TRAVIS_NPM_VERSION: latest
|
||||
|
||||
before_install:
|
||||
|
||||
+6
-52
@@ -1,55 +1,9 @@
|
||||
# This is the configuration of the Transifex tool that we use to manage the
|
||||
# translations on Wekan. Documentation at: http://docs.transifex.com/client.
|
||||
#
|
||||
# Push
|
||||
# ====
|
||||
#
|
||||
# It is recommended that contributors use the Transifex web UI to create and
|
||||
# edit translated strings. However in case a contributor has directly jumped
|
||||
# into the code and made its translations in the corresponding i18n.json file
|
||||
# we can push it using
|
||||
#
|
||||
# > tx push -t -l ar
|
||||
#
|
||||
# Where `ar` is the language identifier. In addition, the project maintainer
|
||||
# should push the English source file to Transifex at least before each release
|
||||
# candidate using:
|
||||
#
|
||||
# > tx push -s
|
||||
#
|
||||
# Pull
|
||||
# ====
|
||||
#
|
||||
# The set of accepted language is directly managed in Transifex, the only
|
||||
# restriction we define to bundle a new language in the application, is that its
|
||||
# completion is at least at 75%.
|
||||
#
|
||||
# We use:
|
||||
#
|
||||
# > tx pull
|
||||
#
|
||||
# to download new versions of existing translations, and
|
||||
#
|
||||
# > tx pull -a --minimum-perc=75
|
||||
#
|
||||
# to download new sufficiently advanced translations.
|
||||
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
# tap:i18n requires us to use `-` separator in the language identifiers whereas
|
||||
# Transifex uses a `_` separator, without an option to customize it on one side
|
||||
# or the other, so we need to do a Manual mapping.
|
||||
lang_map = ar_EG:ar-EG, bg_BG:bg, de_AT:de-AT, de_CH:de-CH, en_DE:en-DE, en_IT:en-IT, en_GB:en-GB, et_EE:et-EE, es_AR:es-AR, es_CL:es-CL, es_419:es-LA, es_PE:es-PE, es_MX:es-MX, es_TX:es-TX, es_PY:es-PY, el_GR:el-GR, fa_IR:fa-IR, fi_FI:fi, fr_FR:fr-FR, fr_CH:fr-CH, gu_IN:gu-IN, hi_IN:hi-IN, hu_HU:hu, id_ID:id, mn_MN:mn, ms_MY:ms-MY, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, ru_UA:ru-UA, sl_SI:sl, uk_UA:uk-UA, zh_CN:zh-CN, zh_TW:zh-TW, zh_Hans:zh-Hans, zh_HK:zh-HK
|
||||
host = https://www.transifex.com
|
||||
lang_map = es_AR: es-AR, es_419: es-LA, es_TX: es-TX, he_IL: he-IL, zh_CN: zh-CN, ar_EG: ar-EG, cs_CZ: cs-CZ, fa_IR: fa-IR, ms_MY: ms-MY, nl_NL: nl-NL, de_CH: de-CH, en_IT: en-IT, uz_UZ: uz-UZ, fr_CH: fr-CH, hi_IN: hi-IN, et_EE: et-EE, es_PE: es-PE, es_MX: es-MX, gl_ES: gl-ES, mn_MN: mn, sl_SI: sl, zh_TW: zh-TW, ast_ES: ast-ES, es_CL: es-CL, ja_JP: ja, lv_LV: lv, ro_RO: ro-RO, az_AZ: az-AZ, cy_GB: cy-GB, gu_IN: gu-IN, pl_PL: pl-PL, vep: ve-PP, en_BR: en-BR, en@ysv: en-YS, hu_HU: hu, ko_KR: ko-KR, pt_BR: pt-BR, zh_HK: zh-HK, zu_ZA: zu-ZA, en_MY: en-MY, ja-Hira: ja-HI, fi_FI: fi, vec: ve-CC, vi_VN: vi-VN, fr_FR: fr-FR, id_ID: id, zh_Hans: zh-Hans, en_DE: en-DE, en_GB: en-GB, el_GR: el-GR, uk_UA: uk-UA, az@latin: az-LA, de_AT: de-AT, uz@Latn: uz-LA, vls: vl-SS, ar_DZ: ar-DZ, bg_BG: bg, es_PY: es-PY, fy_NL: fy-NL, uz@Arab: uz-AR, ru_UA: ru-UA, war: wa-RR, zh_CN.GB2312: zh-GB
|
||||
|
||||
[wekan.application]
|
||||
file_filter = i18n/<lang>.i18n.json
|
||||
[o:wekan:p:wekan:r:application]
|
||||
file_filter = imports/i18n/data/<lang>.i18n.json
|
||||
source_file = imports/i18n/data/en.i18n.json
|
||||
source_lang = en
|
||||
type = KEYVALUEJSON
|
||||
|
||||
# We might have a dedicated second resource later to translate the “Welcome
|
||||
# Board” data.
|
||||
#
|
||||
# [wekan.welcomeBoard]
|
||||
# file_filter = private/welcomeBoard/<lang>.json
|
||||
# source_lang = en
|
||||
# type = KEYVALUEJSON
|
||||
type = KEYVALUEJSON
|
||||
Vendored
+17
@@ -17,6 +17,23 @@
|
||||
"outputCapture": "std",
|
||||
"port": 9229,
|
||||
"timeout": 60000
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Test: Node",
|
||||
"runtimeExecutable": "meteor",
|
||||
"runtimeArgs": [
|
||||
"test",
|
||||
"--inspect-brk=9229",
|
||||
"--port=4040",
|
||||
"--exclude-archs=web.browser.legacy,web.cordova",
|
||||
"--driver-package=meteortesting:mocha",
|
||||
"--settings=settings.json"
|
||||
],
|
||||
"outputCapture": "std",
|
||||
"port": 9229,
|
||||
"timeout": 60000
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
|
||||
+2718
-11
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,22 @@
|
||||
# Code of Conduct
|
||||
|
||||
For all code at WeKan GitHub Organization https://github.com/wekan
|
||||
|
||||
- All code in pull requests need to have permission already to add it to WeKan with MIT license, and will become MIT license.
|
||||
- All code xet7 add is MIT license.
|
||||
- For any dependencies, permissive licenses like https://copyfree.org are preferred
|
||||
- For anything currently that is non-permissive (like GPL, AGPL, SSPL), those will be replaced with permissive-licensed alternatives
|
||||
|
||||
# Reporting about violations or something else
|
||||
|
||||
## Private reports
|
||||
|
||||
- Email support (at) wekan.team using [this PGP public key](support-at-wekan.team_pgp-publickey.asc) if possible
|
||||
- Security issues: [SECURITY.md](SECURITY.md)
|
||||
- License violations
|
||||
- Anything private, sensitive or negative
|
||||
|
||||
## Public
|
||||
|
||||
- Feature Requests and Bug Reports https://github.com/wekan/wekan/issues
|
||||
- Anything happy, positive, encouraging, helping, at friendly WeKan Global FOSS Community
|
||||
+89
-2
@@ -1,4 +1,91 @@
|
||||
To get started, [please sign the Contributor License Agreement](https://www.clahub.com/agreements/wekan/wekan).
|
||||
## About money
|
||||
|
||||
[Then, please read documentation at wiki](https://github.com/wekan/wekan/wiki).
|
||||
Not paid:
|
||||
|
||||
- Money is not paid for these, everyone uses their own time at their own cost:
|
||||
- Security reports, see [SECURITY.md](SECURITY.md)
|
||||
- Pull requests
|
||||
- xet7 checking pull requests
|
||||
- Public Community Support
|
||||
- https://github.com/wekan/wekan/issues
|
||||
|
||||
Paid by customers of WeKan Team:
|
||||
|
||||
- Commercial Support at https://wekan.team/commercial-support/
|
||||
- Support
|
||||
- Private Chat
|
||||
- Features
|
||||
- Fixes
|
||||
- Hosting
|
||||
|
||||
## Contributing Security related
|
||||
|
||||
For responsible security disclosure, please follow this process:
|
||||
https://github.com/wekan/wekan/blob/main/SECURITY.md
|
||||
|
||||
CVE Hall of Fame is at https://wekan.github.io/hall-of-fame/
|
||||
|
||||
## Contributing to Documentation Wiki
|
||||
|
||||
Please clone wiki:
|
||||
```
|
||||
git clone https://github.com/wekan/wekan.wiki
|
||||
```
|
||||
Edit .md files, and add changed files in .zip attachment
|
||||
directly to comment of new issue at
|
||||
https://github.com/wekan/wekan/issues
|
||||
|
||||
## Contributing code
|
||||
|
||||
[Building WeKan and sending PR](https://github.com/wekan/wekan/wiki/Emoji).
|
||||
|
||||
WeKan code contributors Hall of Fame is at ChangeLog, where
|
||||
GitHub usernames are mentioned with changes added:
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/CHANGELOG.md
|
||||
|
||||
Changes can be like typo fixes, bugfixes, features, or anything else
|
||||
like for example at open GitHub issues https://github.com/wekan/wekan/issues .
|
||||
Closed issues are already fixed or implemented.
|
||||
|
||||
Also see other docs at wiki, for example:
|
||||
|
||||
https://github.com/wekan/wekan/wiki/Developer-Documentation
|
||||
|
||||
Do not use code formatting or linting like eslist or prettier.
|
||||
|
||||
Only send minimal changed code lines, that are related to feature or fix.
|
||||
|
||||
WeKan code has MIT license.
|
||||
|
||||
About 300 persons have contributed to WeKan, stats at:
|
||||
|
||||
https://www.openhub.net/p/wekan
|
||||
|
||||
WeKan maintainer xet7 reviews PR for typos etc before accepting to WeKan,
|
||||
so that WeKan code will still work OK.
|
||||
|
||||
## Contributing translations
|
||||
|
||||
Non-English translations are contributed only at
|
||||
https://transifex.com/wekan/wekan
|
||||
|
||||
When adding new features, in your PR to
|
||||
https://github.com/wekan/wekan/pulls
|
||||
only add new English source language strings
|
||||
to https://github.com/wekan/wekan/blob/main/imports/i18n/data/en.i18n.json
|
||||
|
||||
Maintainer of WeKan xet7 downloads all newest
|
||||
translations from Transifex and adds
|
||||
them to WeKan repo before making
|
||||
new release.
|
||||
|
||||
## About WeKan Organization https://github.com/wekan
|
||||
|
||||
Only xet7 has write access to WeKan Organization.
|
||||
|
||||
xet7 reviews all PRs before merging.
|
||||
|
||||
There has been over 300 contributors to WeKan, newest stats at:
|
||||
|
||||
https://www.openhub.net/p/wekan
|
||||
+69
-112
@@ -1,5 +1,17 @@
|
||||
FROM ubuntu:rolling
|
||||
LABEL maintainer="wekan"
|
||||
FROM --platform=linux/amd64 ubuntu:23.10 as wekan
|
||||
LABEL maintainer="wekan" \
|
||||
org.opencontainers.image.ref.name="ubuntu" \
|
||||
org.opencontainers.image.version="23.10" \
|
||||
org.opencontainers.image.source="https://github.com/wekan/wekan"
|
||||
|
||||
# 2022-09-04:
|
||||
# - above "--platform=linux/amd64 ubuntu:22.04 as wekan" is needed to build Dockerfile
|
||||
# correctly on Mac M1 etc, to not get this error:
|
||||
# https://stackoverflow.com/questions/71040681/qemu-x86-64-could-not-open-lib64-ld-linux-x86-64-so-2-no-such-file-or-direc
|
||||
|
||||
# 2022-04-25:
|
||||
# - gyp does not yet work with Ubuntu 22.04 ubuntu:rolling,
|
||||
# so changing to 21.10. https://github.com/wekan/wekan/issues/4488
|
||||
|
||||
# 2021-09-18:
|
||||
# - Above Ubuntu base image copied from Docker Hub ubuntu:hirsute-20210825
|
||||
@@ -12,16 +24,17 @@ ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.22.9 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
NODE_VERSION=v14.21.4 \
|
||||
METEOR_RELEASE=METEOR@2.13.3 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=latest \
|
||||
NPM_VERSION=9.8.1 \
|
||||
FIBERS_VERSION=4.0.1 \
|
||||
ARCHITECTURE=linux-x64 \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
RESULTS_PER_PAGE="" \
|
||||
DEFAULT_BOARD_ID="" \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
|
||||
@@ -29,9 +42,14 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \
|
||||
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \
|
||||
ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS=90 \
|
||||
ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="" \
|
||||
ATTACHMENTS_UPLOAD_MIME_TYPES="" \
|
||||
ATTACHMENTS_UPLOAD_MAX_SIZE=0 \
|
||||
AVATARS_UPLOAD_EXTERNAL_PROGRAM="" \
|
||||
AVATARS_UPLOAD_MIME_TYPES="" \
|
||||
AVATARS_UPLOAD_MAX_SIZE=72000 \
|
||||
RICHER_CARD_COMMENT_EDITOR=false \
|
||||
CARD_OPENED_WEBHOOK_ENABLED=false \
|
||||
ATTACHMENTS_STORE_PATH="" \
|
||||
MAX_IMAGE_PIXEL="" \
|
||||
IMAGE_COMPRESS_RATIO="" \
|
||||
NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE="" \
|
||||
@@ -43,10 +61,12 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
MATOMO_SITE_ID="" \
|
||||
MATOMO_DO_NOT_TRACK=true \
|
||||
MATOMO_WITH_USERNAME=false \
|
||||
METRICS_ALLOWED_IP_ADDRESSES="" \
|
||||
BROWSER_POLICY_ENABLED=true \
|
||||
TRUSTED_URL="" \
|
||||
WEBHOOKS_ATTRIBUTES="" \
|
||||
OAUTH2_ENABLED=false \
|
||||
OIDC_REDIRECTION_ENABLED=false \
|
||||
OAUTH2_CA_CERT="" \
|
||||
OAUTH2_ADFS_ENABLED=false \
|
||||
OAUTH2_LOGIN_STYLE=redirect \
|
||||
@@ -140,7 +160,17 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
|
||||
SAML_ATTRIBUTES="" \
|
||||
ORACLE_OIM_ENABLED=false \
|
||||
WAIT_SPINNER=""
|
||||
WAIT_SPINNER="" \
|
||||
WRITABLE_PATH=/data \
|
||||
S3=""
|
||||
|
||||
# NODE_OPTIONS="--max_old_space_size=4096" \
|
||||
|
||||
#---------------------------------------------
|
||||
# == at docker-compose.yml: AUTOLOGIN WITH OIDC/OAUTH2 ====
|
||||
# https://github.com/wekan/wekan/wiki/autologin
|
||||
#- OIDC_REDIRECTION_ENABLED=true
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# Copy the app to the image
|
||||
COPY ${SRC_PATH} /home/wekan/app
|
||||
@@ -152,7 +182,6 @@ RUN \
|
||||
\
|
||||
# OS dependencies
|
||||
apt-get update -y && apt-get install -y --no-install-recommends ${BUILD_DEPS} && \
|
||||
#pip3 install -U pip setuptools wheel && \
|
||||
\
|
||||
# Meteor installer doesn't work with the default tar binary, so using bsdtar while installing.
|
||||
# https://github.com/coreos/bugs/issues/1095#issuecomment-350574389
|
||||
@@ -160,47 +189,17 @@ RUN \
|
||||
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 && \
|
||||
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt && \
|
||||
#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)" && \
|
||||
#\
|
||||
# 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 && \
|
||||
# Ignore socket files then delete files then delete directories
|
||||
#find "$GNUPGHOME" -type f | xargs rm -f && \
|
||||
#find "$GNUPGHOME" -type d | xargs rm -fR && \
|
||||
rm -f SHASUMS256.txt.asc && \
|
||||
grep node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz SHASUMS256.txt | shasum -a 256 -c - && \
|
||||
rm -f SHASUMS256.txt && \
|
||||
#grep ${NODE_VERSION}-${ARCHITECTURE}.tar.gz SHASUMS256.txt.asc | shasum -a 256 -c - && \
|
||||
#rm -f SHASUMS256.txt.asc && \
|
||||
\
|
||||
# Install Node
|
||||
tar xvzf node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
@@ -208,97 +207,40 @@ RUN \
|
||||
mv node-${NODE_VERSION}-${ARCHITECTURE} /opt/nodejs && \
|
||||
ln -s /opt/nodejs/bin/node /usr/bin/node && \
|
||||
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
|
||||
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/8.16.1 /home/wekan/.config && \
|
||||
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/${NODE_VERSION} /home/wekan/.config && \
|
||||
chown wekan --recursive /home/wekan/.config && \
|
||||
\
|
||||
#DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
#paxctl -mC `which node` && \
|
||||
\
|
||||
# Install Node dependencies. Python path for node-gyp.
|
||||
npm install -g npm@${NPM_VERSION} && \
|
||||
#npm config set python python2.7 && \
|
||||
#npm install -g node-gyp && \
|
||||
#npm install -g fibers@${FIBERS_VERSION} && \
|
||||
#npm install -g npm@${NPM_VERSION} && \
|
||||
\
|
||||
# Change user to wekan and install meteor
|
||||
cd /home/wekan/ && \
|
||||
chown wekan --recursive /home/wekan && \
|
||||
#curl "https://install.meteor.com" -o /home/wekan/install_meteor.sh && \
|
||||
#curl "https://install.meteor.com/?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" && \
|
||||
gosu wekan:wekan curl https://install.meteor.com/ | /bin/sh && \
|
||||
mv /root/.meteor /home/wekan/ && \
|
||||
chown wekan --recursive /home/wekan/.meteor && \
|
||||
\
|
||||
# Check if opting for a release candidate instead of major release
|
||||
#if [ "$USE_EDGE" = false ]; then \
|
||||
#gosu wekan:wekan sh /home/wekan/install_meteor.sh; \
|
||||
# gosu wekan:wekan curl https://install.meteor.com/ | sh; \
|
||||
#else \
|
||||
# gosu wekan:wekan git clone --recursive --depth 1 -b release/METEOR@${METEOR_EDGE} https://github.com/meteor/meteor.git /home/wekan/.meteor; \
|
||||
#fi; \
|
||||
#\
|
||||
# Get additional packages
|
||||
#mkdir -p /home/wekan/app/packages && \
|
||||
#chown wekan:wekan --recursive /home/wekan && \
|
||||
# REPOS BELOW ARE INCLUDED TO WEKAN REPO
|
||||
#cd /home/wekan/app/packages && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/flow-router.git kadira-flow-router && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/meteor-useraccounts/core.git meteor-useraccounts-core && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-cas.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/wekan-ldap.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/wekan-scrollbar.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-oidc.git && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git && \
|
||||
#gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_accounts-oidc wekan-accounts-oidc && \
|
||||
#gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_oidc wekan-oidc && \
|
||||
#gosu wekan:wekan rm -rf meteor-accounts-oidc && \
|
||||
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \
|
||||
cd /home/wekan/.meteor && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor -- help; \
|
||||
\
|
||||
# extract the OpenAPI specification
|
||||
#npm install -g api2html@0.3.3 && \
|
||||
#mkdir -p /home/wekan/python && \
|
||||
#chown wekan --recursive /home/wekan/python && \
|
||||
#cd /home/wekan/python && \
|
||||
#gosu wekan:wekan git clone --depth 1 -b master https://github.com/Kronuz/esprima-python && \
|
||||
#cd /home/wekan/python/esprima-python && \
|
||||
#python3 setup.py install --record files.txt && \
|
||||
#cd /home/wekan/app && \
|
||||
#mkdir -p /home/wekan/app/public/api && \
|
||||
#chown wekan --recursive /home/wekan/app && \
|
||||
#gosu wekan:wekan python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml && \
|
||||
#gosu wekan:wekan /opt/nodejs/bin/api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml; \
|
||||
# Build app
|
||||
cd /home/wekan/app && \
|
||||
mkdir -p /home/wekan/.npm && \
|
||||
chown wekan --recursive /home/wekan/.npm /home/wekan/.config /home/wekan/.meteor && \
|
||||
#gosu wekan:wekan /home/wekan/.meteor/meteor add standard-minifier-js && \
|
||||
chmod u+w *.json && \
|
||||
gosu wekan:wekan npm install && \
|
||||
gosu wekan:wekan meteor npm install && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build && \
|
||||
#cp /home/wekan/app/fix-download-unicode/cfs_access-point.txt /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
|
||||
#rm /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs && \
|
||||
#chown wekan /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \
|
||||
#Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
|
||||
#https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
|
||||
#https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
|
||||
#cd /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/npm-bcrypt && \
|
||||
#gosu wekan:wekan rm -rf node_modules/bcrypt && \
|
||||
#gosu wekan:wekan npm install bcrypt && \
|
||||
#
|
||||
# Delete phantomjs
|
||||
#cd /home/wekan/app_build/bundle && \
|
||||
#find . -name "*phantomjs*" | xargs rm -rf && \
|
||||
#
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
chmod u+w *.json && \
|
||||
gosu wekan:wekan npm install && \
|
||||
#gosu wekan:wekan npm install bcrypt && \
|
||||
gosu wekan:wekan meteor npm install && \
|
||||
cd node_modules/fibers && \
|
||||
node build.js && \
|
||||
cd ../.. && \
|
||||
# Remove legacy webbroser bundle, so that Wekan works also at Android Firefox, iOS Safari, etc.
|
||||
rm -rf /home/wekan/app_build/bundle/programs/web.browser.legacy && \
|
||||
mv /home/wekan/app_build/bundle /build && \
|
||||
@@ -314,7 +256,9 @@ RUN \
|
||||
rm -R /var/lib/apt/lists/* && \
|
||||
rm -R /home/wekan/.meteor && \
|
||||
rm -R /home/wekan/app && \
|
||||
rm -R /home/wekan/app_build
|
||||
rm -R /home/wekan/app_build && \
|
||||
mkdir /data && \
|
||||
chown wekan --recursive /data
|
||||
#cat /home/wekan/python/esprima-python/files.txt | xargs rm -R && \
|
||||
#rm -R /home/wekan/python
|
||||
#rm /home/wekan/install_meteor.sh
|
||||
@@ -323,4 +267,17 @@ ENV PORT=8080
|
||||
EXPOSE $PORT
|
||||
USER wekan
|
||||
|
||||
CMD ["node", "/build/main.js"]
|
||||
STOPSIGNAL SIGKILL
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap:
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------------
|
||||
#
|
||||
# CMD ["node", "/build/main.js"]
|
||||
|
||||
#CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 /build/main.js"]
|
||||
CMD ["bash", "-c", "ulimit -s 65500; exec node /build/main.js"]
|
||||
+29
-14
@@ -1,15 +1,18 @@
|
||||
FROM amd64/alpine:3.7 AS builder
|
||||
FROM arm64v8/ubuntu:23.04 AS builder
|
||||
#FROM amd64/alpine:latest AS builder
|
||||
|
||||
# Set the environment variables for builder
|
||||
ENV QEMU_VERSION=v4.2.0-6 \
|
||||
ENV QEMU_VERSION=v7.2.0-1 \
|
||||
QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.22.9 \
|
||||
NODE_VERSION=v14.21.4 \
|
||||
WEKAN_VERSION=latest \
|
||||
WEKAN_ARCHITECTURE=arm64
|
||||
|
||||
# Install dependencies
|
||||
RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
#RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
|
||||
RUN apt update && apt install ca-certificates wget unzip -y && \
|
||||
\
|
||||
# Download qemu static for our architecture
|
||||
wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
|
||||
@@ -24,23 +27,27 @@ RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
unzip wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
|
||||
\
|
||||
# Download node and shasums
|
||||
wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
|
||||
wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
|
||||
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
|
||||
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
|
||||
\
|
||||
# Verify nodejs authenticity
|
||||
grep node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz SHASUMS256.txt.asc | sha256sum -c - && \
|
||||
grep node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz SHASUMS256.txt | sha256sum -c - && \
|
||||
\
|
||||
# Extract node and remove tar.gz
|
||||
tar xvzf node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz
|
||||
|
||||
# Build wekan dockerfile
|
||||
FROM arm64v8/ubuntu:19.10
|
||||
FROM --platform=linux/arm64 arm64v8/ubuntu:23.04
|
||||
LABEL maintainer="wekan"
|
||||
|
||||
# Set the environment variables (defaults where required)
|
||||
ENV QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.22.9 \
|
||||
NODE_VERSION=v14.21.4 \
|
||||
NODE_ENV=production \
|
||||
NPM_VERSION=latest \
|
||||
WITH_API=true \
|
||||
@@ -66,12 +73,20 @@ RUN \
|
||||
ln -s /opt/nodejs/bin/node /usr/bin/node && \
|
||||
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
|
||||
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/8.16.1 /home/wekan/.config && \
|
||||
chown wekan --recursive /home/wekan/.config && \
|
||||
\
|
||||
# Install Node dependencies
|
||||
npm install -g npm@${NPM_VERSION}
|
||||
chown wekan --recursive /home/wekan/.config
|
||||
|
||||
# \
|
||||
# # Install Node dependencies
|
||||
# #npm install -g npm@${NPM_VERSION} && \
|
||||
# \
|
||||
# # Install Health Check dependencies
|
||||
# #apk add curl
|
||||
#
|
||||
#HEALTHCHECK --start-period=30s --interval=30s --timeout=10s --retries=3 \
|
||||
# CMD curl --fail "http://localhost:$PORT" || exit 1
|
||||
|
||||
EXPOSE $PORT
|
||||
USER wekan
|
||||
|
||||
CMD ["node", "/home/wekan/bundle/main.js"]
|
||||
CMD ["bash", "-c", "ulimit -s 65500; exec node /home/wekan/bundle/main.js"]
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
FROM arm64v8/ubuntu:23.04 AS builder
|
||||
#FROM --platform=linux/amd64 amd64/ubuntu:23.04 AS builder
|
||||
#FROM --platform=linux/amd64 ghcr.io/wekan/wekan:main AS builder
|
||||
#FROM arm64v8/ubuntu:23.04 AS builder
|
||||
#FROM amd64/alpine:latest AS builder
|
||||
|
||||
# Set the environment variables for builder
|
||||
ENV QEMU_VERSION=v7.2.0-1 \
|
||||
QEMU_ARCHITECTURE=s390x \
|
||||
NODE_ARCHITECTURE=linux-s390x \
|
||||
NODE_VERSION=v14.21.4 \
|
||||
WEKAN_VERSION=latest \
|
||||
WEKAN_ARCHITECTURE=s390x
|
||||
|
||||
# Install dependencies
|
||||
#RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
|
||||
RUN apt update && apt install ca-certificates wget unzip -y && \
|
||||
\
|
||||
# Download qemu static for our architecture
|
||||
wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
|
||||
\
|
||||
# Download wekan and shasum
|
||||
wget https://releases.wekan.team/${WEKAN_ARCHITECTURE}/wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
|
||||
wget https://releases.wekan.team/${WEKAN_ARCHITECTURE}/SHA256SUMS.txt && \
|
||||
# Verify wekan
|
||||
grep wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip SHA256SUMS.txt | sha256sum -c - && \
|
||||
\
|
||||
# Unzip wekan
|
||||
unzip wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
|
||||
\
|
||||
# Download node and shasums
|
||||
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
|
||||
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
|
||||
\
|
||||
# Verify nodejs authenticity
|
||||
grep node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz SHASUMS256.txt | sha256sum -c - && \
|
||||
\
|
||||
# Extract node and remove tar.gz
|
||||
tar xvzf node-${NODE_VERSION}-${NODE_ARCHITECTURE}.tar.gz
|
||||
|
||||
# Build wekan dockerfile
|
||||
FROM --platform=linux/s390x s390x/ubuntu:23.04
|
||||
LABEL maintainer="wekan"
|
||||
|
||||
# Set the environment variables (defaults where required)
|
||||
ENV QEMU_ARCHITECTURE=s390x \
|
||||
NODE_ARCHITECTURE=linux-s390x \
|
||||
NODE_VERSION=v14.21.4 \
|
||||
NODE_ENV=production \
|
||||
NPM_VERSION=latest \
|
||||
WITH_API=true \
|
||||
PORT=8080 \
|
||||
ROOT_URL=http://localhost \
|
||||
MONGO_URL=mongodb://127.0.0.1:27017/wekan
|
||||
|
||||
# Copy qemu-static to image
|
||||
COPY --from=builder qemu-${QEMU_ARCHITECTURE}-static /usr/bin
|
||||
|
||||
# Copy the app to the image
|
||||
COPY --from=builder bundle /home/wekan/bundle
|
||||
|
||||
# Copy
|
||||
COPY --from=builder node-${NODE_VERSION}-${NODE_ARCHITECTURE} /opt/nodejs
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
# Add non-root user wekan
|
||||
useradd --user-group --system --home-dir /home/wekan wekan && \
|
||||
\
|
||||
# Install Node
|
||||
ln -s /opt/nodejs/bin/node /usr/bin/node && \
|
||||
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
|
||||
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/8.16.1 /home/wekan/.config && \
|
||||
chown wekan --recursive /home/wekan/.config
|
||||
|
||||
# \
|
||||
# # Install Node dependencies
|
||||
# #npm install -g npm@${NPM_VERSION} && \
|
||||
# \
|
||||
# # Install Health Check dependencies
|
||||
# #apk add curl
|
||||
#
|
||||
#HEALTHCHECK --start-period=30s --interval=30s --timeout=10s --retries=3 \
|
||||
# CMD curl --fail "http://localhost:$PORT" || exit 1
|
||||
|
||||
EXPOSE $PORT
|
||||
USER wekan
|
||||
|
||||
CMD ["bash", "-c", "ulimit -s 65500; exec node /home/wekan/bundle/main.js"]
|
||||
@@ -0,0 +1,40 @@
|
||||
# Future
|
||||
|
||||
## Moved Import/Export/Sync issues to Big Picture Roadmap wiki page
|
||||
|
||||
This change is limited to only Import/Export/Sync issues, while those are In Progress of being fixed.
|
||||
|
||||
2023-11-21 xet7 closed 261 issues that are linked at https://github.com/wekan/wekan/wiki/Sync ,
|
||||
that is Roadmap of Import/Export/Sync in WeKan. It means, that those issues progress will be
|
||||
updated at that wiki page, when xet7 and other WeKan contributors fix those.
|
||||
Many of those issues are In Progress of being fixed and added.
|
||||
|
||||
## Platform Updates
|
||||
|
||||
Issues related to platforms are being closed, because only list of working platforms is mentioned now
|
||||
at WeKan website https://wekan.github.io Install section and at [ChangeLog](https://github.com/wekan/wekan/blob/main/CHANGELOG.md)
|
||||
where is this new text:
|
||||
|
||||
> Newest WeKan at amd64 platforms: Linux bundle, Snap Candidate, Docker, Kubernetes. Fixing other platforms In Progress.
|
||||
|
||||
Platform support changes often, because:
|
||||
|
||||
- There are many dependencies, that update or break or change often
|
||||
- Node.js segfaults at some CPU/OS
|
||||
- Some platforms have build errors
|
||||
|
||||
Roadmap is to update all existing platforms, and add more platforms.
|
||||
|
||||
Upcoming platform upgrades:
|
||||
|
||||
- Fix migrations, so that newest WeKan can be released to Snap Stable. (Currently newest is at Snap Candidate).
|
||||
|
||||
## WeKan features
|
||||
|
||||
Most Meteor WeKan features are listed here:
|
||||
|
||||
https://github.com/wekan/wekan/wiki/Deep-Dive-Into-WeKan
|
||||
|
||||
Remaining features and all changes are listed here:
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/CHANGELOG.md
|
||||
@@ -0,0 +1,10 @@
|
||||
# Governance
|
||||
|
||||
Anyone can send pull request to https://github.com/wekan/wekan/wiki/pulls ,
|
||||
if there is permission to add code to WeKan with MIT license.
|
||||
|
||||
As maintainer, xet7 checks all pull requests and merges them.
|
||||
|
||||
Only xet7 has write access to repo https://github.com/wekan/wekan
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2019 The Wekan Team
|
||||
Copyright (c) 2014-2023 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,25 +1,38 @@
|
||||
[](https://gitpod.io/#https://github.com/wekan/wekan)
|
||||
[Gitpod Ready-to-Code](https://gitpod.io/#https://github.com/wekan/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)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwekan%2Fwekan?ref=badge_shield)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/4619)
|
||||
## Downloads
|
||||
|
||||
## [Translate WeKan ® at Transifex](https://transifex.com/wekan/wekan)
|
||||
https://wekan.github.io / Install WeKan ® Server
|
||||
|
||||
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 .
|
||||
## Docker Containers
|
||||
|
||||
## [Wekan feature requests and bugs](https://github.com/wekan/wekan/issues)
|
||||
- [GitHub](https://github.com/wekan/wekan/pkgs/container/wekan)
|
||||
- [Quay](https://quay.io/repository/wekan/wekan)
|
||||
- [Docker Hub](https://hub.docker.com/r/wekanteam/wekan)
|
||||
|
||||
docker-compose.yml at https://github.com/wekan/wekan
|
||||
|
||||
## Standards
|
||||
|
||||
- [WeKan and Standard for Public Code](https://wekan.github.io/standard-for-public-code/) assessment was made at 2023-11.
|
||||
Currently Wekan meets 8 out of 16 criteria out of the box.
|
||||
Some others could be met with small changes.
|
||||
|
||||
## Code stats
|
||||
|
||||
- [CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4619)
|
||||
- [Code Climate](https://codeclimate.com/github/wekan/wekan)
|
||||
- [Open Hub](https://www.openhub.net/p/wekan)
|
||||
- [OSS Insight](https://ossinsight.io/analyze/wekan/wekan)
|
||||
|
||||
## [Translate WeKan ® at Transifex](https://app.transifex.com/wekan/)
|
||||
|
||||
Translations to non-English languages are accepted only at [Transifex](https://app.transifex.com/wekan/) using webbrowser.
|
||||
New English strings of new features can be added as PRs to master branch file wekan/imports/i18n/data/en.i18n.json .
|
||||
|
||||
## [WeKan ® feature requests and bugs](https://github.com/wekan/wekan/issues)
|
||||
|
||||
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.
|
||||
@@ -28,12 +41,12 @@ It's better than at chat where details get lost when chat scrolls up.
|
||||
|
||||
[Discussions][discussions] - WeKan Community GitHub Discussions, that are not [Feature Requests and Bugs](https://github.com/wekan/wekan/issues).
|
||||
|
||||
[Wekan IRC FAQ](https://github.com/wekan/wekan/wiki/IRC-FAQ)
|
||||
[WeKan IRC FAQ](https://github.com/wekan/wekan/wiki/IRC-FAQ)
|
||||
|
||||
## Docker: Please only use Docker release tags
|
||||
## Docker: Latest tag has newest release
|
||||
|
||||
Note: With Docker, please don't use latest tag. Only use release tags.
|
||||
See https://github.com/wekan/wekan/issues/3874
|
||||
You can use latest tag to get newest release tag.
|
||||
See bottom of https://github.com/wekan/wekan/issues/3874
|
||||
|
||||
## FAQ
|
||||
|
||||
@@ -56,8 +69,8 @@ install Wekan on your own computer or server. In fact we encourage you to do
|
||||
that by providing one-click installation on various platforms.
|
||||
|
||||
- WeKan ® is used in [most countries of the world](https://snapcraft.io/wekan).
|
||||
- Wekan largest user has 13k users using Wekan in their company.
|
||||
- Wekan has been [translated](https://transifex.com/wekan/wekan) to about 70 languages.
|
||||
- WeKan ® largest user has 30k users using WeKan ® in their company.
|
||||
- WeKan ® has been [translated](https://app.transifex.com/wekan/) to about 105 languages.
|
||||
- [Features][features]: WeKan ® has real-time user interface.
|
||||
- [Platforms][platforms]: WeKan ® supports many platforms.
|
||||
WeKan ® is critical part of new platforms Wekan is currently being integrated to.
|
||||
@@ -68,14 +81,14 @@ that by providing one-click installation on various platforms.
|
||||
[Mac](https://github.com/wekan/wekan/wiki/Mac) / [Windows](https://github.com/wekan/wekan/wiki/Install-Wekan-from-source-on-Windows).
|
||||
[More Platforms](https://github.com/wekan/wekan/wiki/Platforms), bundle for RasPi3 ARM and other CPUs where Node.js and MongoDB exists.
|
||||
- 1 GB RAM minimum free for WeKan ®. Production server should have minimum total 4 GB RAM.
|
||||
For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/master/docker-compose.yml): 3 frontend servers,
|
||||
For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/main/docker-compose.yml): 3 frontend servers,
|
||||
each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.
|
||||
- Enough disk space and alerts about low disk space. If you run out disk space, MongoDB database gets corrupted.
|
||||
- SECURITY: Updating to newest WeKan ® version very often. Please check you do not have automatic updates of Sandstorm or Snap turned off.
|
||||
Old versions have security issues because of old versions Node.js etc. Only newest WeKan ® is supported.
|
||||
WeKan ® on Sandstorm is not usually affected by any Standalone WeKan ® (Snap/Docker/Source) security issues.
|
||||
- [Reporting all new bugs immediately](https://github.com/wekan/wekan/issues).
|
||||
New features and fixes are added to WeKan ® [many times a day](https://github.com/wekan/wekan/blob/master/CHANGELOG.md).
|
||||
New features and fixes are added to WeKan ® [many times a day](https://github.com/wekan/wekan/blob/main/CHANGELOG.md).
|
||||
- [Backups](https://github.com/wekan/wekan/wiki/Backup) of WeKan ® database once a day miminum.
|
||||
Bugs, updates, users deleting list or card, harddrive full, harddrive crash etc can eat your data. There is no undo yet.
|
||||
Some bug can cause WeKan ® board to not load at all, requiring manual fixing of database content.
|
||||
@@ -87,7 +100,7 @@ that by providing one-click installation on various platforms.
|
||||
[Developer Documentation][dev_docs]
|
||||
|
||||
- There is many companies and individuals contributing code to WeKan ®, to add features and bugfixes
|
||||
[many times a day](https://github.com/wekan/wekan/blob/master/CHANGELOG.md).
|
||||
[many times a day](https://github.com/wekan/wekan/blob/main/CHANGELOG.md).
|
||||
- [Please add Add new Feature Requests and Bug Reports immediately](https://github.com/wekan/wekan/issues).
|
||||
- [Commercial Support](https://wekan.team/commercial-support/).
|
||||
|
||||
@@ -107,18 +120,14 @@ with [Meteor](https://www.meteor.com).
|
||||
|
||||
[platforms]: https://github.com/wekan/wekan/wiki/Platforms
|
||||
[dev_docs]: https://github.com/wekan/wekan/wiki/Developer-Documentation
|
||||
[screenshot_wekan]: https://wekan.github.io/wekan-markdown.png
|
||||
[screenshot_wekan]: https://wekan.github.io/wekan-dark-mode.png
|
||||
[features]: https://github.com/wekan/wekan/wiki/Features
|
||||
[roadmap_wekan]: https://boards.wekan.team/b/D2SzJKZDS4Z48yeQH/wekan-open-source-kanban-board-with-mit-license
|
||||
[wekan_issues]: https://github.com/wekan/wekan/issues
|
||||
[wekan_issues]: https://github.com/wekan/wekan/issues
|
||||
[docker_image]: https://hub.docker.com/r/wekanteam/wekan/
|
||||
[travis_badge]: https://travis-ci.org/wekan/wekan.svg?branch=devel
|
||||
[travis_status]: https://travis-ci.org/wekan/wekan
|
||||
[wekan_wiki]: https://github.com/wekan/wekan/wiki
|
||||
[translate_wekan]: https://www.transifex.com/wekan/wekan/
|
||||
[translate_wekan]: https://app.transifex.com/wekan/
|
||||
[open_source]: https://en.wikipedia.org/wiki/Open-source_software
|
||||
[free_software]: https://en.wikipedia.org/wiki/Free_software
|
||||
[discussions]: https://github.com/wekan/wekan/discussions
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwekan%2Fwekan?ref=badge_large)
|
||||
+125
-28
@@ -1,3 +1,5 @@
|
||||
About money, see [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
Security is very important to us. If you discover any issue regarding security, please disclose
|
||||
the information responsibly by sending an email to support (at) wekan.team using
|
||||
[this PGP public key](support-at-wekan.team_pgp-publickey.asc) and not by
|
||||
@@ -29,7 +31,7 @@ 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
|
||||
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.
|
||||
@@ -48,31 +50,132 @@ 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 is used at most countries of the world https://snapcraft.io/wekan
|
||||
and by by companies that have 30k users.
|
||||
|
||||
Wekan uses xss package for input fields like cards, as you can see from
|
||||
[package.json](https://github.com/wekan/wekan/blob/master/package.json). Other used versions can be seen from
|
||||
[Meteor versions file](https://github.com/wekan/wekan/blob/master/.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 private board attachments are not accessible without logging in.
|
||||
- There is feature to set board public, so that board is visible without logging in in readonly mode, with realtime updates.
|
||||
- Admin Panel has feature to disable all public boards, so all boards are private.
|
||||
|
||||
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.
|
||||
## SSL/TLS
|
||||
|
||||
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/master/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/).
|
||||
- SSL/TLS encrypts traffic between webbrowser and webserver.
|
||||
- If you are thinking about TLS MITM, look at Caddy 2 webserver MITM detections.
|
||||
- Let's Encrypt TLS requires publicly accessible webserver, that Let's Encrypt TLS validation servers check.
|
||||
- If firewall limits to only allowed IP addresses, you may need non-Let's Encrypt TLS cert.
|
||||
- For On Premise:
|
||||
- https://caddyserver.com/docs/automatic-https#local-https
|
||||
- https://github.com/wekan/wekan/wiki/Caddy-Webserver-Config
|
||||
- https://github.com/wekan/wekan/wiki/Azure
|
||||
- https://github.com/wekan/wekan/wiki/Traefik-and-self-signed-SSL-certs
|
||||
|
||||
[All Wekan Platforms](https://github.com/wekan/wekan/wiki/Platforms)
|
||||
## XSS
|
||||
|
||||
- Dompurify https://www.npmjs.com/package/dompurify
|
||||
- WeKan uses dompurify npm package to filter for XSS at fields like cards, as you can see from
|
||||
[package.json](https://github.com/wekan/wekan/blob/main/package.json). Other used versions can be seen from
|
||||
[Meteor versions file](https://github.com/wekan/wekan/blob/main/.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.
|
||||
- Dompurify cleans up viewed code, so Javascript in input fields does not execute
|
||||
- https://wekan.github.io/hall-of-fame/fieldbleed/
|
||||
- Reaction in comment is now checked, that it does not have extra added code
|
||||
- https://wekan.github.io/hall-of-fame/reactionbleed/
|
||||
- https://github.com/wekan/wekan/blob/main/packages/markdown/src/template-integration.js#L76
|
||||
|
||||
## QA about PubSub
|
||||
|
||||
Q:
|
||||
|
||||
Hello,
|
||||
I have just seen the Meteor DevTools Evolved extension and was wondering if anyone had asked themselves the question of security.
|
||||
Insofar as all data is shown in the minimongo tab in plain text.
|
||||
How can data be hidden from this extension?
|
||||
|
||||
A:
|
||||
|
||||
## PubSub
|
||||
|
||||
- It is not security issue to show some text or image, that user has permission to see. It is a security issue, if browserside is some text or image that user should not see.
|
||||
- Meteor has browserside minimongo database, made with Javascript, updated with Publish/Subscribe, PubSub.
|
||||
- Publish/Subscribe means, that realtime web framework reads database changes stream, and then immediately updates webpage,
|
||||
like like dashboards, chat, kanban. That is the point in any realtime web framework in any programming language.
|
||||
- Yes, you should check with Meteor DevTools Evolved Chromium/Firefox extension that at minimongo is only text that user has permission to see.
|
||||
- Do checking as logged in user, and logged out user.
|
||||
- Check permissions and sanitize before allowing some change, because someone could modify content of input field,
|
||||
PubSub/websocket data (for example with Burp Suite Community Edition), etc.
|
||||
- If you have REST API, also check that only those that have login token, and have permission, can view or edit text
|
||||
- You should not include any data user is not allowed to see. Not to webpage text, not to websockets/PubSub, etc.
|
||||
- Minimongo should not have password hashes PubSub https://wekan.github.io/hall-of-fame/userbleed/
|
||||
- PubSub uses Websockets, so you need those to be enabled at webserver like Caddy/Nginx/Apache etc, examples of settings
|
||||
at right menu of https://github.com/wekan/wekan/wiki
|
||||
- Clientside https://github.com/wekan/wekan/tree/main/client/components subscribes to
|
||||
PubSub https://github.com/wekan/wekan/tree/main/server/publications or calls meteor methods at https://github.com/wekan/wekan/tree/main/models
|
||||
- For Admin:
|
||||
- You can have input field for password https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
|
||||
- You can save password to database https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
|
||||
- Check that only current user or Admin can change password https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
|
||||
- Note that currentUser uses code like Meteor.user() in .js file
|
||||
- Do not have password hashes in PubSub https://github.com/wekan/wekan/blob/main/server/publications/users.js
|
||||
- Only show Admin Panel to Admin https://github.com/wekan/wekan/blob/main/client/components/settings/settingBody.jade#L3
|
||||
- If there is a lot of data, use pagination https://github.com/wekan/wekan/blob/main/client/components/settings/peopleBody.js
|
||||
- Only have limited amount of data published in PubSub. Limit in MongoDB query in publications how much is published. Too much could make browser too slow.
|
||||
- Use Environment variables for any email etc passwords.
|
||||
- But what if you would like to remove minimongo? And only use Meteor methods for saving? In that case, you don't have realtime updates,
|
||||
and you need to write much more code to load and save data yourself, handle any multi user data saving conflicts yourself,
|
||||
and many Meteor Atmospherejs.com PubSub using packages would not work anymore https://github.com/wekan/we
|
||||
|
||||
## PubSub: Fix that user can not change to Admin
|
||||
|
||||
- With PubSub, there is checking, that someone modifying Websockets content, like permission isAdmin, can not change to Admin.
|
||||
- https://github.com/wekan/wekan/commit/cbad4cf5943d47b916f64b4582f8ca76a9dfd743
|
||||
- https://wekan.github.io/hall-of-fame/adminbleed/
|
||||
|
||||
## Permissions and Roles
|
||||
|
||||
- For any user permissions, it's best to use Meteor package package https://github.com/Meteor-Community-Packages/meteor-roles .
|
||||
- Currently WeKan has custom hardcoded permissions, WeKan does not yet use that meteor-roles package.
|
||||
- Using permissions at WeKan sidebar https://github.com/wekan/wekan/blob/main/client/components/sidebar/sidebar.js#L1854-L1875
|
||||
- List of roles https://github.com/wekan/wekan/wiki/REST-API-Role . Change at board or Admin Panel. Also Organizations/Teams.
|
||||
- Worker role: https://github.com/wekan/wekan/issues/2788
|
||||
- Not implemented yet: Granular Roles https://github.com/wekan/wekan/issues/3022
|
||||
- Check is user logged in, with `if (Meteor.user()) {`
|
||||
- Check is code running at server `if (Meteor.isServer()) {` or client `if Meteor.isClient()) {` .
|
||||
- Here is some authentication code https://github.com/wekan/wekan/blob/main/server/authentication.js
|
||||
|
||||
## Environment variables
|
||||
|
||||
- For any passwords, use environment variables, those are serverside
|
||||
- Do not copy environment variable to public variable that is visible browserside https://github.com/wekan/wekan/blob/main/server/max-size.js
|
||||
|
||||
```
|
||||
Meteor.startup(() => {
|
||||
if (process.env.HEADER_LOGIN_ID) {
|
||||
Meteor.settings.public.attachmentsUploadMaxSize = process.env.ATTACHMENTS_UPLOAD_MAX_SIZE;
|
||||
Meteor.settings.public.attachmentsUploadMimeTypes = process.env.ATTACHMENTS_UPLOAD_MIME_TYPES;
|
||||
Meteor.settings.public.avatarsUploadMaxSize = process.env.AVATARS_UPLOAD_MAX_SIZE;
|
||||
```
|
||||
|
||||
- For serverside, you can set Meteor.settings.variablename, without text public
|
||||
- For WeKan kanban, there is feature for setting board public, it can be viewed by anyone, there is realtime updates. But
|
||||
- Some of those permissions are checked at users.js models at https://github.com/wekan/wekan/tree/main/models
|
||||
- Environment variables are used for email server passwords, etc, at all platforms https://github.com/wekan/wekan/commit/a781c0e7dcfdbe34c1483ee83cec12455b7026f7
|
||||
|
||||
## Escape HTML comment tags so that HTML comments are visible
|
||||
|
||||
- Someone reported, that it is problem that content of HTML comments in edit mode, are not visible at at view mode, so this makes HTML comments visible.
|
||||
- https://github.com/wekan/wekan/commit/167863d95711249e69bb3511175d73b34acbbdb3
|
||||
- https://wekan.github.io/hall-of-fame/invisiblebleed/
|
||||
|
||||
## Attachments: XSS in filename is sanitized
|
||||
|
||||
- https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
|
||||
- https://wekan.github.io/hall-of-fame/filebleed/
|
||||
|
||||
## Brute force login protection
|
||||
|
||||
- https://github.com/wekan/wekan/commit/23e5e1e3bd081699ce39ce5887db7e612616014d
|
||||
- https://github.com/wekan/wekan/tree/main/packages/wekan-accounts-lockout
|
||||
|
||||
### Sandstorm Wekan Security
|
||||
|
||||
@@ -105,12 +208,6 @@ a security issue, we'd like to know about it, and also how to fix it:
|
||||
|
||||
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/master/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
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v5.97.0"
|
||||
appVersion: "v7.20.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -21,31 +21,40 @@ import sys
|
||||
|
||||
arguments = len(sys.argv) - 1
|
||||
|
||||
syntax = """=== Wekan API Python CLI: Shows IDs for addcard ===
|
||||
# AUTHORID is USERID that writes card or custom field.
|
||||
If *nix: chmod +x api.py => ./api.py users
|
||||
Syntax:
|
||||
User API:
|
||||
python3 api.py user # Current user and list of current user boards
|
||||
python3 api.py boards USERID # Boards of USERID
|
||||
python3 api.py swimlanes BOARDID # Swimlanes of BOARDID
|
||||
python3 api.py lists BOARDID # Lists of BOARDID
|
||||
python3 api.py list BOARDID LISTID # Info of LISTID
|
||||
python3 api.py createlist BOARDID LISTTITLE # Create list
|
||||
python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION
|
||||
python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION
|
||||
python3 api.py customfields BOARDID # Custom Fields of BOARDID
|
||||
python3 api.py customfield BOARDID CUSTOMFIELDID # Info of CUSTOMFIELDID
|
||||
python3 api.py addcustomfieldtoboard AUTHORID BOARDID NAME TYPE SETTINGS SHOWONCARD AUTOMATICALLYONCARD SHOWLABELONMINICARD SHOWSUMATTOPOFLIST # Add Custom Field to Board
|
||||
python3 api.py editcustomfield BOARDID LISTID CARDID CUSTOMFIELDID NEWCUSTOMFIELDVALUE
|
||||
python3 api.py listattachments BOARDID # List attachments
|
||||
|
||||
Admin API:
|
||||
python3 api.py users # All users
|
||||
python3 api.py boards # All Public Boards
|
||||
python3 api.py newuser USERNAME EMAIL PASSWORD
|
||||
"""
|
||||
|
||||
if arguments == 0:
|
||||
print("=== Wekan API Python CLI: Shows IDs for addcard ===")
|
||||
print("AUTHORID is USERID that writes card.")
|
||||
print("If *nix: chmod +x api.py => ./api.py users")
|
||||
print("Syntax:")
|
||||
print(" python3 api.py users # All users")
|
||||
print(" python3 api.py boards # All Public Boards")
|
||||
print(" python3 api.py boards USERID # Boards of USERID")
|
||||
print(" python3 api.py board BOARDID # Info of BOARDID")
|
||||
print(" python3 api.py customfields BOARDID # Custom Fields of BOARDID")
|
||||
print(" python3 api.py customfield BOARDID CUSTOMFIELDID # Info of CUSTOMFIELDID")
|
||||
print(" python3 api.py addcustomfieldtoboard AUTHORID BOARDID NAME TYPE SETTINGS SHOWONCARD AUTOMATICALLYONCARD SHOWLABELONMINICARD SHOWSUMATTOPOFLIST # Add Custom Field to Board")
|
||||
print(" python3 api.py swimlanes BOARDID # Swimlanes of BOARDID")
|
||||
print(" python3 api.py lists BOARDID # Lists of BOARDID")
|
||||
print(" python3 api.py list BOARDID LISTID # Info of LISTID")
|
||||
print(" python3 api.py createlist BOARDID LISTTITLE # Create list")
|
||||
print(" python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION")
|
||||
print(" python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION")
|
||||
print(" python3 api.py listattachments BOARDID # List attachments")
|
||||
print(syntax)
|
||||
exit
|
||||
|
||||
# TODO:
|
||||
# print(" python3 api.py attachmentjson BOARDID ATTACHMENTID # One attachment as JSON base64")
|
||||
# print(" python3 api.py attachmentbinary BOARDID ATTACHMENTID # One attachment as binary file")
|
||||
# print(" python3 api.py attachmentdownload BOARDID ATTACHMENTID # One attachment as file")
|
||||
# print(" python3 api.py attachmentsdownload BOARDID # All attachments as files")
|
||||
exit
|
||||
|
||||
# ------- SETTINGS START -------------
|
||||
|
||||
@@ -61,33 +70,6 @@ wekanurl = 'http://localhost:4000/'
|
||||
# ------- SETTINGS END -------------
|
||||
|
||||
"""
|
||||
EXAMPLE:
|
||||
|
||||
python3 api.py
|
||||
|
||||
OR:
|
||||
chmod +x api.py
|
||||
./api.py
|
||||
|
||||
=== Wekan API Python CLI: Shows IDs for addcard ===
|
||||
AUTHORID is USERID that writes card.
|
||||
Syntax:
|
||||
python3 api.py users # All users
|
||||
python3 api.py boards USERID # Boards of USERID
|
||||
python3 api.py board BOARDID # Info of BOARDID
|
||||
python3 api.py customfields BOARDID # Custom Fields of BOARDID
|
||||
python3 api.py customfield BOARDID CUSTOMFIELDID # Info of CUSTOMFIELDID
|
||||
python3 api.py addcustomfieldtoboard AUTHORID BOARDID NAME TYPE SETTINGS SHOWONCARD AUTOMATICALLYONCARD SHOWLABELONMINICARD SHOWSUMATTOPOFLIST # Add Custom Field to Board
|
||||
python3 api.py swimlanes BOARDID # Swimlanes of BOARDID
|
||||
python3 api.py lists BOARDID # Lists of BOARDID
|
||||
python3 api.py list BOARDID LISTID # Info of LISTID
|
||||
python3 api.py createlist BOARDID LISTTITLE # Create list
|
||||
python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION
|
||||
python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION
|
||||
python3 api.py listattachments BOARDID # List attachments
|
||||
python3 api.py attachmentjson BOARDID ATTACHMENTID # One attachment as JSON base64
|
||||
python3 api.py attachmentbinary BOARDID ATTACHMENTID # One attachment as binary file
|
||||
|
||||
=== ADD CUSTOM FIELD TO BOARD ===
|
||||
|
||||
Type: text, number, date, dropdown, checkbox, currency, stringtemplate.
|
||||
@@ -144,6 +126,8 @@ wekanloginurl = wekanurl + loginurl
|
||||
apiboards = 'api/boards/'
|
||||
apiattachments = 'api/attachments/'
|
||||
apiusers = 'api/users'
|
||||
apiuser = 'api/user'
|
||||
apiallusers = 'api/allusers'
|
||||
e = 'export'
|
||||
s = '/'
|
||||
l = 'lists'
|
||||
@@ -152,17 +136,20 @@ sws = 'swimlanes'
|
||||
cs = 'cards'
|
||||
cf = 'custom-fields'
|
||||
bs = 'boards'
|
||||
apbs = 'allpublicboards'
|
||||
atl = 'attachmentslist'
|
||||
at = 'attachment'
|
||||
ats = 'attachments'
|
||||
users = wekanurl + apiusers
|
||||
user = wekanurl + apiuser
|
||||
allusers = wekanurl + apiallusers
|
||||
|
||||
# ------- API URL GENERATION END -----------
|
||||
|
||||
# ------- LOGIN TOKEN START -----------
|
||||
|
||||
data = {"username": username, "password": password}
|
||||
body = requests.post(wekanloginurl, data=data)
|
||||
body = requests.post(wekanloginurl, json=data)
|
||||
d = body.json()
|
||||
apikey = d['token']
|
||||
|
||||
@@ -231,6 +218,40 @@ if arguments == 6:
|
||||
print(data2)
|
||||
# ------- EDIT CARD END -----------
|
||||
|
||||
if sys.argv[1] == 'editcustomfield':
|
||||
|
||||
# ------- EDIT CUSTOMFIELD START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
cardid = sys.argv[4]
|
||||
customfieldid = sys.argv[5]
|
||||
newcustomfieldvalue = sys.argv[6]
|
||||
edfield = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + cardid + s + 'customFields' + s + customfieldid
|
||||
#print(edfield)
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
post_data = {'_id': '{}'.format(customfieldid), 'value': '{}'.format(newcustomfieldvalue)}
|
||||
#print(post_data)
|
||||
body = requests.post(edfield, data=post_data, headers=headers)
|
||||
print("=== EDIT CUSTOMFIELD ===\n")
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- EDIT CUSTOMFIELD END -----------
|
||||
|
||||
if arguments == 4:
|
||||
|
||||
if sys.argv[1] == 'newuser':
|
||||
|
||||
# ------- CREATE NEW USER START -----------
|
||||
username = sys.argv[2]
|
||||
email = sys.argv[3]
|
||||
password = sys.argv[4]
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
post_data = {'username': '{}'.format(username),'email': '{}'.format(email),'password': '{}'.format(password)}
|
||||
body = requests.post(users, data=post_data, headers=headers)
|
||||
print("=== CREATE NEW USER ===\n")
|
||||
print(body.text)
|
||||
# ------- CREATE NEW USER END -----------
|
||||
|
||||
if arguments == 3:
|
||||
|
||||
if sys.argv[1] == 'createlist':
|
||||
@@ -356,6 +377,16 @@ if arguments == 1:
|
||||
print(data2)
|
||||
# ------- LIST OF USERS END -----------
|
||||
|
||||
if sys.argv[1] == 'user':
|
||||
# ------- LIST OF ALL USERS START -----------
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
print(user)
|
||||
print("=== USER ===\n")
|
||||
body = requests.get(user, headers=headers)
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- LIST OF ALL USERS END -----------
|
||||
|
||||
if sys.argv[1] == 'boards':
|
||||
|
||||
# ------- LIST OF PUBLIC BOARDS START -----------
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# v1.0 2022-03-22 Perl Asana export to WeKan ® release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Added Perl scripts for Asana export to WeKan ®](https://github.com/wekan/wekan/commit/376bcbb373d16317060adc2b1154cc20496775cc).
|
||||
Thanks to GeekRuthie.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2019 The Wekan Team
|
||||
Copyright (c) 2022 WeKan ® Team and GeekRuthie
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -0,0 +1,178 @@
|
||||
#!/usr/local/perl-cbt/bin/perl
|
||||
use Modern::Perl;
|
||||
use Carp;
|
||||
use Data::Dumper;
|
||||
use HTTP::Request;
|
||||
use JSON;
|
||||
use LWP::UserAgent;
|
||||
use MIME::Base64 qw/encode_base64/;
|
||||
|
||||
my $BASE_URL = 'https://app.asana.com/api/1.0';
|
||||
my $ASANA_API_KEY = 'ASANA_PERSONAL_TOKEN';
|
||||
my $ua = LWP::UserAgent->new();
|
||||
|
||||
open my $input_wekan, '<', 'template.json';
|
||||
my $wekan_json = readline($input_wekan);
|
||||
close $input_wekan;
|
||||
my $wekan_board = decode_json($wekan_json);
|
||||
my %users;
|
||||
my %users_by_gid;
|
||||
# get user IDs from template board
|
||||
foreach my $user ( @{ $wekan_board->{users} } ) {
|
||||
$users{ $user->{profile}->{fullname} } = $user->{_id};
|
||||
}
|
||||
# get list IDs from template (we ended up not using these)
|
||||
my %lists;
|
||||
foreach my $list ( @{ $wekan_board->{lists} } ) {
|
||||
$lists{ $list->{title} } = $list->{_id};
|
||||
}
|
||||
|
||||
my @headers;
|
||||
push @headers, ( 'Accept', 'application/json' );
|
||||
push @headers, ( 'Authorization', "Bearer $ASANA_API_KEY" );
|
||||
my $projects_req = HTTP::Request->new( "GET", "$BASE_URL/projects", \@headers, );
|
||||
my $projects_res = $ua->request($projects_req);
|
||||
my $projects = decode_json( $projects_res->content )->{data};
|
||||
foreach my $project (@$projects) {
|
||||
say "Project: ".$project->{name};
|
||||
my $tasks_url =
|
||||
'/tasks?project='
|
||||
. $project->{gid}
|
||||
. '&opt_fields=completed,name,notes,assignee,created_by,memberships.project.name, memberships.section.name,due_on,created_at,custom_fields';
|
||||
my $tasks_req = HTTP::Request->new( 'GET', "$BASE_URL$tasks_url", \@headers );
|
||||
my $tasks_res = $ua->request($tasks_req);
|
||||
my @output_tasks;
|
||||
my $tasks = decode_json( $tasks_res->content )->{data};
|
||||
foreach my $task (@$tasks) {
|
||||
next if $task->{completed};
|
||||
say ' - '.$task->{name};
|
||||
my $git_branch;
|
||||
my $prio;
|
||||
foreach my $custom ( @{ $task->{custom_fields} } ) {
|
||||
if ( $custom->{name} eq 'git branch' ) {
|
||||
$git_branch = $custom->{text_value};
|
||||
next;
|
||||
}
|
||||
# We ended up not importing these.
|
||||
if ( $custom->{name} eq 'Priority' && defined $custom->{display_value} ) {
|
||||
$prio =
|
||||
$custom->{display_value} eq 'High' ? 'fwccC9'
|
||||
: $custom->{display_value} eq 'Med' ? 'yPnaFa'
|
||||
: $custom->{display_value} eq 'Low' ? 'W4vMvm'
|
||||
: 'ML5drH';
|
||||
next;
|
||||
}
|
||||
}
|
||||
if ( !defined $users_by_gid{ $task->{created_by}->{gid} } ) {
|
||||
my $user_req =
|
||||
HTTP::Request->new( 'GET', "$BASE_URL/users/" . $task->{created_by}->{gid},
|
||||
\@headers );
|
||||
my $user_res = $ua->request($user_req);
|
||||
my $user = decode_json( $user_res->content )->{data};
|
||||
if ( defined $users{ $user->{name} } ) {
|
||||
$users_by_gid{ $task->{created_by}->{gid} } = $users{ $user->{name} };
|
||||
}
|
||||
}
|
||||
my $creator = $users_by_gid{ $task->{created_by}->{gid} } // undef;
|
||||
if ( defined $task->{assignee} && !defined $users_by_gid{ $task->{assignee}->{gid} } ) {
|
||||
my $user_req =
|
||||
HTTP::Request->new( 'GET', "$BASE_URL/users/" . $task->{assignee}->{gid},
|
||||
\@headers );
|
||||
my $user_res = $ua->request($user_req);
|
||||
my $user = decode_json( $user_res->content )->{data};
|
||||
if ( defined $users{ $user->{name} } ) {
|
||||
$users_by_gid{ $task->{assignee}->{gid} } = $users{ $user->{name} };
|
||||
}
|
||||
}
|
||||
my $assignee = defined $task->{assignee} ? $users_by_gid{ $task->{assignee}->{gid} } : undef;
|
||||
my $list;
|
||||
foreach my $membership ( @{ $task->{memberships} } ) {
|
||||
next if $membership->{project}->{name} ne $project->{name};
|
||||
$list = $membership->{section}->{name};
|
||||
}
|
||||
|
||||
# I was trying to create JSON that I could use on the import screen in Wekan,
|
||||
# but for bigger boards, it was just *too* hefty, so I took that JSON and used
|
||||
# APIs to import.
|
||||
my %output_task = (
|
||||
swimlaneId => 'As4SNerx4Y4mMnJ8n', # 'Bugs'
|
||||
sort => 0,
|
||||
type => 'cardType-card',
|
||||
archived => JSON::false,
|
||||
title => $task->{name},
|
||||
description => $task->{notes},
|
||||
createdAt => $task->{created_at},
|
||||
dueAt => defined $task->{due_on} ? $task->{due_on} . 'T22:00:00.000Z' : undef,
|
||||
customFields => [
|
||||
{
|
||||
_id => 'rL8BpFHp5xxSFbDdr',
|
||||
value => $git_branch,
|
||||
},
|
||||
],
|
||||
labelIds => [$prio],
|
||||
listId => $list,
|
||||
userId => $creator,
|
||||
assignees => [$assignee],
|
||||
);
|
||||
my @final_comments;
|
||||
my $comments_req =
|
||||
HTTP::Request->new( 'GET', "$BASE_URL/tasks/" . $task->{gid} . '/stories',
|
||||
\@headers );
|
||||
my $comments_res = $ua->request($comments_req);
|
||||
my $comments = decode_json( $comments_res->content )->{data};
|
||||
foreach my $comment (@$comments) {
|
||||
next if $comment->{type} ne 'comment';
|
||||
if ( !defined $users_by_gid{ $comment->{created_by}->{gid} } ) {
|
||||
my $user_req =
|
||||
HTTP::Request->new( 'GET', "$BASE_URL/users/" . $comment->{created_by}->{gid},
|
||||
\@headers );
|
||||
my $user_res = $ua->request($user_req);
|
||||
my $user = decode_json( $user_res->content )->{data};
|
||||
if ( defined $users{ $user->{name} } ) {
|
||||
$users_by_gid{ $comment->{created_bye}->{gid} } = $users{ $user->{name} };
|
||||
}
|
||||
}
|
||||
my $commentor = $users_by_gid{ $comment->{created_by}->{gid} };
|
||||
my %this_comment = (
|
||||
text => $comment->{text},
|
||||
createdAt => $comment->{created_at},
|
||||
userId => $commentor,
|
||||
);
|
||||
push @final_comments, \%this_comment;
|
||||
}
|
||||
$output_task{comments} = \@final_comments;
|
||||
|
||||
|
||||
my @final_attachments;
|
||||
my $attachments_req =
|
||||
HTTP::Request->new( 'GET', "$BASE_URL/tasks/" . $task->{gid} . '/attachments',
|
||||
\@headers );
|
||||
my $attachments_res = $ua->request($attachments_req);
|
||||
my $attachments = decode_json( $attachments_res->content )->{data};
|
||||
foreach my $attachment (@$attachments) {
|
||||
my $att_req =
|
||||
HTTP::Request->new( 'GET', "$BASE_URL/attachments/" . $attachment->{gid},
|
||||
\@headers );
|
||||
my $att_res = $ua->request($att_req);
|
||||
my $att = decode_json( $att_res->content )->{data};
|
||||
my $file_req=HTTP::Request->new('GET',$att->{download_url});
|
||||
my $file_res=$ua->request($file_req);
|
||||
my $file=encode_base64($file_res->content);
|
||||
|
||||
my %this_attachment = (
|
||||
file => $file,
|
||||
name => $att->{name},
|
||||
createdAt => $att->{created_at},
|
||||
);
|
||||
push @final_attachments, \%this_attachment;
|
||||
|
||||
}
|
||||
$output_task{attachments} = \@final_attachments;
|
||||
push @output_tasks, \%output_task;
|
||||
}
|
||||
my $file_name = $project->{name};
|
||||
$file_name =~ s/\//_/g;
|
||||
open my $output_file, '>',$file_name.'_exported.json';
|
||||
print $output_file encode_json(\@output_tasks);
|
||||
close $output_file;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#!/usr/local/perl-cbt/bin/perl
|
||||
use Modern::Perl;
|
||||
use Carp;
|
||||
use Data::Dumper;
|
||||
use HTTP::Request;
|
||||
use JSON;
|
||||
use LWP::UserAgent;
|
||||
use MIME::Base64 qw/decode_base64/;
|
||||
use Try::Tiny;
|
||||
|
||||
my $BASE_URL = 'https://taskboard.example.com/api';
|
||||
my $TOKEN = 'MY_TOKEN';
|
||||
my $me = 'MY_USER_ID';
|
||||
my $ua = LWP::UserAgent->new();
|
||||
|
||||
my @headers;
|
||||
push @headers, ( 'Accept', 'application/json' );
|
||||
push @headers, ( 'Authorization', "Bearer $TOKEN" );
|
||||
|
||||
my @form_headers;
|
||||
push @form_headers, ( 'Content-Type', 'application/json' );
|
||||
push @form_headers, ( 'Accept', 'application/json' );
|
||||
push @form_headers, ( 'Authorization', "Bearer $TOKEN" );
|
||||
|
||||
# Prior to running this, I built all the boards, with labels and lists and swimlanes.
|
||||
# Grabbed the IDs for the boards for each project, and put them here to match up with
|
||||
# filenames from the Asana export script.
|
||||
|
||||
my %board_to_use = (
|
||||
Project_1 => 'F5ZiCXnf4d7qNBRjp',
|
||||
Project_2 => 'Shw3tyfC2JWCutBLj',
|
||||
);
|
||||
|
||||
opendir my $files_dir, '.'
|
||||
or croak "Cannot open input directory: $!";
|
||||
my @files = readdir $files_dir;
|
||||
closedir $files_dir;
|
||||
|
||||
foreach my $tasks_file (@files) {
|
||||
next if $tasks_file !~ /_exported.json/;
|
||||
my $project = $tasks_file;
|
||||
$project =~ s/_exported.json//;
|
||||
say "Project - $project";
|
||||
my $board;
|
||||
if ( $board_to_use{$project} ) {
|
||||
$board = $board_to_use{$project};
|
||||
}
|
||||
say ' No board!' if !$board;
|
||||
next if !$board;
|
||||
my $labels_req = HTTP::Request->new( 'GET', "$BASE_URL/boards/$board", \@headers );
|
||||
my $labels_res = $ua->request($labels_req);
|
||||
my $board_data = decode_json( $labels_res->content);
|
||||
my $labels = $board_data->{labels};
|
||||
my $label_to_use;
|
||||
# We're merging several Asana boards onto one Wekan board, with labels per project.
|
||||
foreach my $label (@$labels) {
|
||||
$label_to_use = $label->{_id} if $label->{name} eq $project;
|
||||
}
|
||||
|
||||
my $lanes_req = HTTP::Request->new( 'GET', "$BASE_URL/boards/$board/swimlanes", \@headers );
|
||||
my $lanes_res = $ua->request($lanes_req);
|
||||
my $lanes = decode_json( $lanes_res->content );
|
||||
my $lane_to_use;
|
||||
foreach my $lane (@$lanes) {
|
||||
# Our Asana didn't use swimlanes; all of our Wekan boards have a "Bugs" lane, so use that.
|
||||
$lane_to_use = $lane->{_id} if $lane->{title} eq 'Bugs';
|
||||
}
|
||||
|
||||
my $lists_req = HTTP::Request->new( 'GET', "$BASE_URL/boards/$board/lists", \@headers );
|
||||
my $lists_res = $ua->request($lists_req);
|
||||
my $lists = decode_json( $lists_res->content );
|
||||
my %list_to_use;
|
||||
foreach my $list (@$lists) {
|
||||
$list_to_use{ $list->{title} } = $list->{_id};
|
||||
}
|
||||
|
||||
open my $task_export_file, '<', $tasks_file;
|
||||
my $tasks_json = readline($task_export_file);
|
||||
close $task_export_file;
|
||||
my $tasks = decode_json($tasks_json);
|
||||
foreach my $task (@$tasks) {
|
||||
say ' - ' . $task->{title};
|
||||
my %body_info = (
|
||||
swimlaneId => $lane_to_use,
|
||||
authorId => $task->{userId},
|
||||
assignees => $task->{assignees},
|
||||
title => $task->{title},
|
||||
description => $task->{description},
|
||||
);
|
||||
my $body = encode_json( \%body_info );
|
||||
my $list = $list_to_use{ $task->{listId} } // $list_to_use{'Backlog'};
|
||||
my $task_req = HTTP::Request->new( 'POST', "$BASE_URL/boards/$board/lists/$list/cards",
|
||||
\@form_headers, $body );
|
||||
my $task_res = $ua->request($task_req);
|
||||
my $res;
|
||||
try {
|
||||
$res = decode_json( $task_res->content );
|
||||
} catch {
|
||||
# Did these manually afterward.
|
||||
say "--->UNABLE TO LOAD TASK";
|
||||
next;
|
||||
};
|
||||
my $card = $res->{_id};
|
||||
|
||||
if ($label_to_use) {
|
||||
my $card_edit_body = encode_json( { labelIds => [ $label_to_use ]});
|
||||
my $card_edit_req = HTTP::Request->new( 'PUT', "$BASE_URL/boards/$board/lists/$list/cards/$card",
|
||||
\@form_headers, $card_edit_body );
|
||||
my $card_edit_res = $ua->request($card_edit_req);
|
||||
}
|
||||
|
||||
foreach my $comment ( @{ $task->{comments} } ) {
|
||||
my $comment_body =
|
||||
encode_json( { authorId => $comment->{userId}, comment => $comment->{text} } );
|
||||
my $comment_req =
|
||||
HTTP::Request->new( 'POST', "$BASE_URL/boards/$board/cards/$card/comments",
|
||||
\@form_headers, $comment_body );
|
||||
my $comment_res = $ua->request($comment_req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
.activity-title {
|
||||
margin: 0 0.5em 0.8em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.reactions-popup .add-comment-reaction {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
font-size: 22px;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
width: 40px;
|
||||
}
|
||||
.reactions-popup .add-comment-reaction:hover {
|
||||
background-color: #b0c4de;
|
||||
}
|
||||
.activities {
|
||||
clear: both;
|
||||
}
|
||||
.activities .activity {
|
||||
margin: 0.5px 0;
|
||||
padding: 6px 0;
|
||||
display: flex;
|
||||
}
|
||||
.activities .activity .member {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.activities .activity .activity-member {
|
||||
font-weight: 700;
|
||||
}
|
||||
.activities .activity .activity-desc {
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
margin: 0;
|
||||
margin-left: 3px;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
}
|
||||
.activities .activity .activity-desc .activity-comment {
|
||||
display: block;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
text-decoration: none;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions {
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
gap: 5px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .open-comment-reaction-popup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
height: 24px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .open-comment-reaction-popup i.fa.fa-smile-o {
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .open-comment-reaction-popup i.fa.fa-plus {
|
||||
font-size: 8px;
|
||||
margin-top: -7px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .reaction {
|
||||
cursor: pointer;
|
||||
border: 1px solid #808080;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .reaction.selected {
|
||||
background-color: #b0c4de;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .reaction:hover {
|
||||
background-color: #b0c4de;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .reaction .reaction-count {
|
||||
font-size: 12px;
|
||||
}
|
||||
.activities .activity .activity-desc .activity-checklist {
|
||||
display: block;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
text-decoration: none;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
.activities .activity .activity-desc .activity-meta {
|
||||
font-size: 0.8em;
|
||||
color: #999;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ template(name="addReactionPopup")
|
||||
span.add-comment-reaction(data-codepoint="#{codepoint}") !{codepoint}
|
||||
|
||||
template(name="activity")
|
||||
.activity
|
||||
.activity(data-id=activity._id)
|
||||
+userAvatar(userId=activity.user._id)
|
||||
p.activity-desc
|
||||
span.activity-member
|
||||
@@ -59,18 +59,17 @@ template(name="activity")
|
||||
img.attachment-image-preview(src=activity.attachment.url)
|
||||
|
||||
//- board activity ------------------------------------------------------
|
||||
if($eq mode 'board')
|
||||
if($eq activity.activityType 'createBoard')
|
||||
| {{{_ 'activity-created' boardLabelLink}}}.
|
||||
if($eq activity.activityType 'createBoard')
|
||||
| {{{_ 'activity-created' boardLabelLink}}}.
|
||||
|
||||
if($eq activity.activityType 'importBoard')
|
||||
| {{{_ 'activity-imported-board' boardLabelLink sourceLink}}}.
|
||||
if($eq activity.activityType 'importBoard')
|
||||
| {{{_ 'activity-imported-board' boardLabelLink sourceLink}}}.
|
||||
|
||||
if($eq activity.activityType 'addBoardMember')
|
||||
| {{{_ 'activity-added' memberLink boardLabelLink}}}.
|
||||
if($eq activity.activityType 'addBoardMember')
|
||||
| {{{_ 'activity-added' memberLink boardLabelLink}}}.
|
||||
|
||||
if($eq activity.activityType 'removeBoardMember')
|
||||
| {{{_ 'activity-excluded' memberLink boardLabelLink}}}.
|
||||
if($eq activity.activityType 'removeBoardMember')
|
||||
| {{{_ 'activity-excluded' memberLink boardLabelLink}}}.
|
||||
|
||||
//- card activity -------------------------------------------------------
|
||||
if($eq activity.activityType 'createCard')
|
||||
@@ -153,10 +152,10 @@ template(name="activity")
|
||||
+editOrDeleteComment
|
||||
|
||||
if($eq activity.activityType 'deleteComment')
|
||||
| {{{_ 'activity-deleteComment' currentData.commentId}}}.
|
||||
| {{{_ 'activity-deleteComment' activity.commentId}}}.
|
||||
|
||||
if($eq activity.activityType 'editComment')
|
||||
| {{{_ 'activity-editComment' currentData.commentId}}}.
|
||||
| {{{_ 'activity-editComment' activity.commentId}}}.
|
||||
else
|
||||
//- if we are not in card mode we only display a summary of the comment
|
||||
if($eq activity.activityType 'addComment')
|
||||
@@ -166,42 +165,27 @@ template(name="activity")
|
||||
= activity.comment.text
|
||||
|
||||
//- date activity ------------------------------------------------
|
||||
if($eq mode 'card')
|
||||
if($eq activity.activityType 'a-receivedAt')
|
||||
| {{{_ 'activity-receivedDate' (sanitize receivedDate) cardLink}}}.
|
||||
if($eq activity.activityType 'a-receivedAt')
|
||||
| {{{_ 'activity-receivedDate' (sanitize receivedDate) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'a-startAt')
|
||||
| {{{_ 'activity-startDate' (sanitize startDate) cardLink}}}.
|
||||
if($eq activity.activityType 'a-startAt')
|
||||
| {{{_ 'activity-startDate' (sanitize startDate) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'a-dueAt')
|
||||
| {{{_ 'activity-dueDate' (sanitize dueDate) cardLink}}}.
|
||||
if($eq activity.activityType 'a-dueAt')
|
||||
| {{{_ 'activity-dueDate' (sanitize dueDate) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'a-endAt')
|
||||
| {{{_ 'activity-endDate' (sanitize endDate) cardLink}}}.
|
||||
|
||||
if($eq mode 'board')
|
||||
if($eq activity.activityType 'a-receivedAt')
|
||||
| {{{_ 'activity-receivedDate' (sanitize receivedDate) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'a-startAt')
|
||||
| {{{_ 'activity-startDate' (sanitize startDate) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'a-dueAt')
|
||||
| {{{_ 'activity-dueDate' (sanitize dueDate) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'a-endAt')
|
||||
| {{{_ 'activity-endDate' (sanitize endDate) cardLink}}}.
|
||||
if($eq activity.activityType 'a-endAt')
|
||||
| {{{_ 'activity-endDate' (sanitize endDate) cardLink}}}.
|
||||
|
||||
//- customField activity ------------------------------------------------
|
||||
if($eq mode 'board')
|
||||
if($eq activity.activityType 'createCustomField')
|
||||
| {{_ 'activity-customfield-created' customField}}.
|
||||
if($eq activity.activityType 'createCustomField')
|
||||
| {{_ 'activity-customfield-created' customField}}.
|
||||
|
||||
if($eq activity.activityType 'setCustomField')
|
||||
| {{{_ 'activity-set-customfield' (sanitize lastCustomField) (sanitize lastCustomFieldValue) cardLink}}}.
|
||||
if($eq activity.activityType 'setCustomField')
|
||||
| {{{_ 'activity-set-customfield' (sanitize lastCustomField) (sanitize lastCustomFieldValue) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'unsetCustomField')
|
||||
| {{{_ 'activity-unset-customfield' (sanitize lastCustomField) cardLink}}}.
|
||||
if($eq activity.activityType 'unsetCustomField')
|
||||
| {{{_ 'activity-unset-customfield' (sanitize lastCustomField) cardLink}}}.
|
||||
|
||||
//- label activity ------------------------------------------------------
|
||||
if($eq activity.activityType 'addedLabel')
|
||||
@@ -238,12 +222,11 @@ template(name="activity")
|
||||
| {{{_ 'activity-removed' memberLink cardLink}}}.
|
||||
|
||||
//- swimlane activity --------------------------------------------------
|
||||
if($neq mode 'card')
|
||||
if($eq activity.activityType 'createSwimlane')
|
||||
| {{_ 'activity-added' (sanitize activity.swimlane.title) boardLabelLink}}.
|
||||
if($eq activity.activityType 'createSwimlane')
|
||||
| {{{_ 'activity-added' (sanitize activity.swimlane.title) boardLabelLink}}}.
|
||||
|
||||
if($eq activity.activityType 'archivedSwimlane')
|
||||
| {{_ 'activity-archived' (sanitize activity.swimlane.title)}}.
|
||||
if($eq activity.activityType 'archivedSwimlane')
|
||||
| {{{_ 'activity-archived' (sanitize activity.swimlane.title)}}}.
|
||||
|
||||
|
||||
//- I don't understand this part ----------------------------------------
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
|
||||
const activitiesPerPage = 500;
|
||||
|
||||
@@ -23,7 +25,7 @@ BlazeComponent.extendComponent({
|
||||
searchId = Session.get(`current${capitalizedMode}`);
|
||||
}
|
||||
const limit = this.page.get() * activitiesPerPage;
|
||||
const user = Meteor.user();
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
const hideSystem = user ? user.hasHiddenSystemMessages() : false;
|
||||
if (searchId === null) return;
|
||||
|
||||
@@ -64,28 +66,57 @@ Template.activities.helpers({
|
||||
BlazeComponent.extendComponent({
|
||||
checkItem() {
|
||||
const checkItemId = this.currentData().activity.checklistItemId;
|
||||
const checkItem = ChecklistItems.findOne({ _id: checkItemId });
|
||||
const checkItem = ReactiveCache.getChecklistItem(checkItemId);
|
||||
return checkItem && checkItem.title;
|
||||
},
|
||||
|
||||
boardLabelLink() {
|
||||
const data = this.currentData();
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (data.mode !== 'board') {
|
||||
return createBoardLink(data.activity.board(), data.activity.listName);
|
||||
// data.mode: card, linkedcard, linkedboard
|
||||
return createBoardLink(data.activity.board(), data.activity.listName ? data.activity.listName : null);
|
||||
}
|
||||
else if (currentBoardId != data.activity.boardId) {
|
||||
// data.mode: board
|
||||
// current activitie is linked
|
||||
return createBoardLink(data.activity.board(), data.activity.listName ? data.activity.listName : null);
|
||||
}
|
||||
return TAPi18n.__('this-board');
|
||||
},
|
||||
|
||||
cardLabelLink() {
|
||||
const data = this.currentData();
|
||||
if (data.mode !== 'card') {
|
||||
return createCardLink(data.activity.card());
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (data.mode == 'card') {
|
||||
// data.mode: card
|
||||
return TAPi18n.__('this-card');
|
||||
}
|
||||
return TAPi18n.__('this-card');
|
||||
else if (data.mode !== 'board') {
|
||||
// data.mode: linkedcard, linkedboard
|
||||
return createCardLink(data.activity.card(), null);
|
||||
}
|
||||
else if (currentBoardId != data.activity.boardId) {
|
||||
// data.mode: board
|
||||
// current activitie is linked
|
||||
return createCardLink(data.activity.card(), data.activity.board().title);
|
||||
}
|
||||
return createCardLink(this.currentData().activity.card(), null);
|
||||
},
|
||||
|
||||
cardLink() {
|
||||
return createCardLink(this.currentData().activity.card());
|
||||
const data = this.currentData();
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (data.mode !== 'board') {
|
||||
// data.mode: card, linkedcard, linkedboard
|
||||
return createCardLink(data.activity.card(), null);
|
||||
}
|
||||
else if (currentBoardId != data.activity.boardId) {
|
||||
// data.mode: board
|
||||
// current activitie is linked
|
||||
return createCardLink(data.activity.card(), data.activity.board().title);
|
||||
}
|
||||
return createCardLink(this.currentData().activity.card(), null);
|
||||
},
|
||||
|
||||
receivedDate() {
|
||||
@@ -115,7 +146,7 @@ BlazeComponent.extendComponent({
|
||||
lastLabel() {
|
||||
const lastLabelId = this.currentData().activity.labelId;
|
||||
if (!lastLabelId) return null;
|
||||
const lastLabel = Boards.findOne(
|
||||
const lastLabel = ReactiveCache.getBoard(
|
||||
this.currentData().activity.boardId,
|
||||
).getLabelById(lastLabelId);
|
||||
if (lastLabel && (lastLabel.name === undefined || lastLabel.name === '')) {
|
||||
@@ -128,7 +159,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
lastCustomField() {
|
||||
const lastCustomField = CustomFields.findOne(
|
||||
const lastCustomField = ReactiveCache.getCustomField(
|
||||
this.currentData().activity.customFieldId,
|
||||
);
|
||||
if (!lastCustomField) return null;
|
||||
@@ -136,7 +167,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
lastCustomFieldValue() {
|
||||
const lastCustomField = CustomFields.findOne(
|
||||
const lastCustomField = ReactiveCache.getCustomField(
|
||||
this.currentData().activity.customFieldId,
|
||||
);
|
||||
if (!lastCustomField) return null;
|
||||
@@ -196,14 +227,14 @@ BlazeComponent.extendComponent({
|
||||
// trying to display url before file is stored generates js errors
|
||||
return (
|
||||
(attachment &&
|
||||
attachment.url({ download: true }) &&
|
||||
attachment.path &&
|
||||
Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: attachment.url({ download: true }),
|
||||
href: `${attachment.link()}?download=true`,
|
||||
target: '_blank',
|
||||
},
|
||||
DOMPurify.sanitize(attachment.name()),
|
||||
DOMPurify.sanitize(attachment.name),
|
||||
),
|
||||
)) ||
|
||||
DOMPurify.sanitize(this.currentData().activity.attachmentName)
|
||||
@@ -252,10 +283,10 @@ Template.activity.helpers({
|
||||
|
||||
Template.commentReactions.events({
|
||||
'click .reaction'(event) {
|
||||
if (Meteor.user().isBoardMember()) {
|
||||
if (ReactiveCache.getCurrentUser().isBoardMember()) {
|
||||
const codepoint = event.currentTarget.dataset['codepoint'];
|
||||
const commentId = Template.instance().data.commentId;
|
||||
const cardComment = CardComments.findOne({_id: commentId});
|
||||
const cardComment = ReactiveCache.getCardComment(commentId);
|
||||
cardComment.toggleReaction(codepoint);
|
||||
}
|
||||
},
|
||||
@@ -264,10 +295,10 @@ Template.commentReactions.events({
|
||||
|
||||
Template.addReactionPopup.events({
|
||||
'click .add-comment-reaction'(event) {
|
||||
if (Meteor.user().isBoardMember()) {
|
||||
if (ReactiveCache.getCurrentUser().isBoardMember()) {
|
||||
const codepoint = event.currentTarget.dataset['codepoint'];
|
||||
const commentId = Template.instance().data.commentId;
|
||||
const cardComment = CardComments.findOne({_id: commentId});
|
||||
const cardComment = ReactiveCache.getCardComment(commentId);
|
||||
cardComment.toggleReaction(codepoint);
|
||||
}
|
||||
Popup.back();
|
||||
@@ -295,17 +326,20 @@ Template.addReactionPopup.helpers({
|
||||
|
||||
Template.commentReactions.helpers({
|
||||
isSelected(userIds) {
|
||||
return userIds.includes(Meteor.user()._id);
|
||||
return Meteor.userId() && userIds.includes(Meteor.userId());
|
||||
},
|
||||
userNames(userIds) {
|
||||
return Users.find({_id: {$in: userIds}})
|
||||
.map(user => user.profile.fullname)
|
||||
.join(', ');
|
||||
const ret = ReactiveCache.getUsers({_id: {$in: userIds}})
|
||||
.map(user => user.profile.fullname)
|
||||
.join(', ');
|
||||
return ret;
|
||||
}
|
||||
})
|
||||
|
||||
function createCardLink(card) {
|
||||
function createCardLink(card, board) {
|
||||
if (!card) return '';
|
||||
let text = card.title;
|
||||
if (board) text = `${board} > ` + text;
|
||||
return (
|
||||
card &&
|
||||
Blaze.toHTML(
|
||||
@@ -314,7 +348,7 @@ function createCardLink(card) {
|
||||
href: card.originRelativeUrl(),
|
||||
class: 'action-card',
|
||||
},
|
||||
DOMPurify.sanitize(card.title, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
DOMPurify.sanitize(text, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
.activity-title
|
||||
margin: 0 0.5em 0.8em
|
||||
display: flex
|
||||
justify-content:space-between
|
||||
|
||||
.reactions-popup
|
||||
.add-comment-reaction
|
||||
display: inline-block
|
||||
cursor: pointer
|
||||
border-radius: 5px
|
||||
font-size: 22px
|
||||
text-align: center
|
||||
line-height: 30px
|
||||
width: 40px
|
||||
|
||||
&:hover {
|
||||
background-color: #b0c4de
|
||||
}
|
||||
|
||||
.activities
|
||||
clear: both
|
||||
|
||||
.activity
|
||||
margin: 0.5px 0
|
||||
padding: 6px 0;
|
||||
display: flex
|
||||
|
||||
.member
|
||||
width: 32px
|
||||
height: @width
|
||||
|
||||
.activity-member
|
||||
font-weight: 700
|
||||
|
||||
.activity-desc
|
||||
word-wrap: break-word
|
||||
overflow: hidden
|
||||
flex: 1
|
||||
align-self: center
|
||||
margin: 0
|
||||
margin-left: 3px
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
|
||||
.activity-comment
|
||||
display: block
|
||||
border-radius: 3px
|
||||
background: white
|
||||
text-decoration: none
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
margin-top: 5px
|
||||
padding: 5px
|
||||
|
||||
.reactions
|
||||
display: flex
|
||||
margin-top: 5px
|
||||
gap: 5px
|
||||
|
||||
.open-comment-reaction-popup
|
||||
display: flex
|
||||
align-items: center
|
||||
text-decoration: none
|
||||
height: 24px;
|
||||
|
||||
i.fa.fa-smile-o
|
||||
font-size: 17px
|
||||
font-weight: 500
|
||||
margin-left: 2px
|
||||
|
||||
i.fa.fa-plus
|
||||
font-size: 8px;
|
||||
margin-top: -7px;
|
||||
margin-left: 1px;
|
||||
|
||||
.reaction
|
||||
cursor: pointer
|
||||
border: 1px solid grey
|
||||
border-radius: 15px
|
||||
display: flex
|
||||
padding: 2px 5px
|
||||
|
||||
&.selected {
|
||||
background-color: #b0c4de
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #b0c4de
|
||||
}
|
||||
|
||||
.reaction-count
|
||||
font-size: 12px
|
||||
|
||||
.activity-checklist
|
||||
display: block
|
||||
border-radius: 3px
|
||||
background: white
|
||||
text-decoration: none
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
margin-top: 5px
|
||||
padding: 5px
|
||||
.activity-meta
|
||||
font-size: 0.8em
|
||||
color: darken(white, 40%)
|
||||
@@ -0,0 +1,65 @@
|
||||
.new-comment {
|
||||
position: relative;
|
||||
margin: 0 0 20px 38px;
|
||||
}
|
||||
.new-comment .member {
|
||||
opacity: 0.7;
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: -38px;
|
||||
}
|
||||
.new-comment.is-open .member {
|
||||
opacity: 1;
|
||||
}
|
||||
.new-comment.is-open .helper {
|
||||
display: inline-block;
|
||||
}
|
||||
.new-comment.is-open textarea {
|
||||
min-height: 100px;
|
||||
color: #4d4d4d;
|
||||
cursor: auto;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.new-comment .too-long {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.new-comment textarea {
|
||||
background-color: #fff;
|
||||
border: 0;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.23);
|
||||
height: 36px;
|
||||
margin: 4px 4px 6px 0;
|
||||
padding: 9px 11px;
|
||||
width: 100%;
|
||||
}
|
||||
.new-comment textarea:hover,
|
||||
.new-comment textarea:is-open {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.33);
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.new-comment textarea:is-open {
|
||||
cursor: auto;
|
||||
}
|
||||
.comment-item {
|
||||
background-color: #fff;
|
||||
border: 0;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.23);
|
||||
color: #8c8c8c;
|
||||
height: 36px;
|
||||
margin: 4px 4px 6px 0;
|
||||
width: 92%;
|
||||
}
|
||||
.comment-item:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
.comment-item.add-comment {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
}
|
||||
.comment-item.add-comment a {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
|
||||
const commentFormIsOpen = new ReactiveVar(false);
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -24,7 +26,7 @@ BlazeComponent.extendComponent({
|
||||
let boardId = card.boardId;
|
||||
let cardId = card._id;
|
||||
if (card.isLinkedCard()) {
|
||||
boardId = Cards.findOne(card.linkedId).boardId;
|
||||
boardId = ReactiveCache.getCard(card.linkedId).boardId;
|
||||
cardId = card.linkedId;
|
||||
} else if (card.isLinkedBoard()) {
|
||||
boardId = card.linkedId;
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
.new-comment
|
||||
position: relative
|
||||
margin: 0 0 20px 38px
|
||||
|
||||
.member
|
||||
opacity: .7
|
||||
position: absolute
|
||||
top: 1px
|
||||
left: -38px
|
||||
|
||||
&.is-open
|
||||
.member
|
||||
opacity: 1
|
||||
|
||||
.helper
|
||||
display: inline-block
|
||||
|
||||
textarea
|
||||
min-height: 100px
|
||||
color: #4d4d4d
|
||||
cursor: auto
|
||||
overflow: hidden
|
||||
word-wrap: break-word
|
||||
|
||||
.too-long
|
||||
margin-top: 8px
|
||||
|
||||
textarea
|
||||
background-color: #fff
|
||||
border: 0
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
|
||||
height: 36px
|
||||
margin: 4px 4px 6px 0
|
||||
padding: 9px 11px
|
||||
width: 100%
|
||||
|
||||
&:hover,
|
||||
&:is-open
|
||||
background-color: #fff
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .33)
|
||||
border: 0
|
||||
cursor: pointer
|
||||
|
||||
&:is-open
|
||||
cursor: auto
|
||||
|
||||
.comment-item
|
||||
background-color: #fff
|
||||
border: 0
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
|
||||
color: #8c8c8c
|
||||
height: 36px
|
||||
margin: 4px 4px 6px 0
|
||||
width: 92%
|
||||
|
||||
&:hover
|
||||
background: darken(white, 12%)
|
||||
|
||||
&.add-comment
|
||||
display: flex
|
||||
margin: 5px
|
||||
|
||||
a
|
||||
display: block
|
||||
margin: auto
|
||||
@@ -14,6 +14,7 @@ template(name="archivedBoards")
|
||||
i.fa.fa-undo
|
||||
| {{_ 'restore-board'}}
|
||||
= title
|
||||
span {{ moment archivedAt 'LLL' }}
|
||||
else
|
||||
li.no-items-message {{_ 'no-archived-boards'}}
|
||||
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.subscribe('archivedBoards');
|
||||
},
|
||||
|
||||
isBoardAdmin() {
|
||||
return Meteor.user().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
},
|
||||
|
||||
archivedBoards() {
|
||||
return Boards.find(
|
||||
const ret = ReactiveCache.getBoards(
|
||||
{ archived: true },
|
||||
{
|
||||
sort: { archivedAt: -1, modifiedAt: -1 },
|
||||
},
|
||||
);
|
||||
return ret;
|
||||
},
|
||||
|
||||
events() {
|
||||
@@ -25,8 +28,8 @@ BlazeComponent.extendComponent({
|
||||
Meteor.settings &&
|
||||
Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (isSandstorm && Utils.getCurrentBoardId()) {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
currentBoard.archive();
|
||||
}
|
||||
const board = this.currentData();
|
||||
@@ -39,8 +42,8 @@ BlazeComponent.extendComponent({
|
||||
Meteor.settings &&
|
||||
Meteor.settings.public &&
|
||||
Meteor.settings.public.sandstorm;
|
||||
if (isSandstorm && Session.get('currentBoard')) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (isSandstorm && Utils.getCurrentBoardId()) {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
Boards.remove(currentBoard._id);
|
||||
}
|
||||
Boards.remove(this._id);
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
.board-wrapper {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
.board-wrapper .board-canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
transition: margin 0.1s;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.board-wrapper .board-canvas.is-sibling-sidebar-open {
|
||||
margin-right: 248px;
|
||||
}
|
||||
.board-wrapper .board-canvas .board-overlay {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
top: -100px;
|
||||
right: -400px;
|
||||
background: #000;
|
||||
opacity: 0.33;
|
||||
animation: fadeIn 0.2s;
|
||||
z-index: 16;
|
||||
}
|
||||
.board-wrapper .board-canvas.is-dragging-active .open-minicard-composer,
|
||||
.board-wrapper .board-canvas.is-dragging-active .minicard-wrapper.is-checked {
|
||||
display: none;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.board-wrapper .board-canvas .swimlane {
|
||||
border-bottom: 1px solid #ccc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
padding: 0 0px 0px 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
.calendar-event-green {
|
||||
background: #3cb500 !important;
|
||||
border-color: #2a8000;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-yellow {
|
||||
background: #fad900 !important;
|
||||
border-color: #c7ac00;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-orange {
|
||||
background: #ff9f19 !important;
|
||||
border-color: #cc7c14;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-red {
|
||||
background: #eb4646 !important;
|
||||
border-color: #b83737;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-purple {
|
||||
background: #a632db !important;
|
||||
border-color: #7d26a6;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-blue {
|
||||
background: #0079bf !important;
|
||||
border-color: #005a8a;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-pink {
|
||||
background: #ff78cb !important;
|
||||
border-color: #cc62a3;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-sky {
|
||||
background: #00c2e0 !important;
|
||||
border-color: #0094ab;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-black {
|
||||
background: #4d4d4d !important;
|
||||
border-color: #1a1a1a;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-lime {
|
||||
background: #51e898 !important;
|
||||
border-color: #3eb375;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-silver {
|
||||
background: #c0c0c0 !important;
|
||||
border-color: #8c8c8c;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-peachpuff {
|
||||
background: #ffdab9 !important;
|
||||
border-color: #ccaf95;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-crimson {
|
||||
background: #dc143c !important;
|
||||
border-color: #a8112f;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-plum {
|
||||
background: #dda0dd !important;
|
||||
border-color: #a87ba8;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-darkgreen {
|
||||
background: #006400 !important;
|
||||
border-color: #003000;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-slateblue {
|
||||
background: #6a5acd !important;
|
||||
border-color: #4f4399;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-magenta {
|
||||
background: #f0f !important;
|
||||
border-color: #c0c;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-gold {
|
||||
background: #ffd700 !important;
|
||||
border-color: #ca0;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-navy {
|
||||
background: #000080 !important;
|
||||
border-color: #003;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-gray {
|
||||
background: #808080 !important;
|
||||
border-color: #333;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-saddlebrown {
|
||||
background: #8b4513 !important;
|
||||
border-color: #572b0c;
|
||||
color: #fff !important;
|
||||
}
|
||||
.calendar-event-paleturquoise {
|
||||
background: #afeeee !important;
|
||||
border-color: #8ababa;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-mistyrose {
|
||||
background: #ffe4e1 !important;
|
||||
border-color: #ccb8b6;
|
||||
color: #000 !important;
|
||||
}
|
||||
.calendar-event-indigo {
|
||||
background: #4b0082 !important;
|
||||
border-color: #2b004d;
|
||||
color: #fff !important;
|
||||
}
|
||||
/* Modal Styles */
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 9999;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.modal-dialog {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 25%; /* Adjust the height to make it smaller */
|
||||
position: relative;
|
||||
margin: 10% auto; /* This margin will help center the modal vertically */
|
||||
max-width: 400px; /* Adjust the max-width to make it smaller */
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-bottom: 1px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
.modal-title {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: 4px;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
.close {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
font-size: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndex } = Utils;
|
||||
const swimlaneWhileSortingHeight = 150;
|
||||
@@ -41,9 +44,9 @@ BlazeComponent.extendComponent({
|
||||
this.mouseHasEnterCardDetails = false;
|
||||
|
||||
// fix swimlanes sort field if there are null values
|
||||
const currentBoardData = Boards.findOne(Session.get('currentBoard'));
|
||||
const currentBoardData = Utils.getCurrentBoard();
|
||||
const nullSortSwimlanes = currentBoardData.nullSortSwimlanes();
|
||||
if (nullSortSwimlanes.count() > 0) {
|
||||
if (nullSortSwimlanes.length > 0) {
|
||||
const swimlanes = currentBoardData.swimlanes();
|
||||
let count = 0;
|
||||
swimlanes.forEach(s => {
|
||||
@@ -58,7 +61,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
// fix lists sort field if there are null values
|
||||
const nullSortLists = currentBoardData.nullSortLists();
|
||||
if (nullSortLists.count() > 0) {
|
||||
if (nullSortLists.length > 0) {
|
||||
const lists = currentBoardData.lists();
|
||||
let count = 0;
|
||||
lists.forEach(l => {
|
||||
@@ -191,7 +194,7 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
|
||||
this.autorun(() => {
|
||||
if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
|
||||
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
|
||||
$swimlanesDom.sortable({
|
||||
handle: '.js-swimlane-header-handle',
|
||||
});
|
||||
@@ -202,34 +205,27 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member
|
||||
//$swimlanesDom.sortable('option', 'disabled', !userIsMember());
|
||||
$swimlanesDom.sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
!Meteor.user() || !Meteor.user().isBoardAdmin(),
|
||||
!ReactiveCache.getCurrentUser()?.isBoardAdmin(),
|
||||
);
|
||||
});
|
||||
|
||||
function userIsMember() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
}
|
||||
|
||||
// If there is no data in the board (ie, no lists) we autofocus the list
|
||||
// creation form by clicking on the corresponding element.
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (userIsMember() && currentBoard.lists().count() === 0) {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
if (Utils.canModifyBoard() && currentBoard.lists().length === 0) {
|
||||
boardComponent.openNewListForm();
|
||||
}
|
||||
|
||||
Utils.setBackgroundImage();
|
||||
},
|
||||
|
||||
notDisplayThisBoard(){
|
||||
notDisplayThisBoard() {
|
||||
let allowPrivateVisibilityOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly');
|
||||
let currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if(allowPrivateVisibilityOnly !== undefined && allowPrivateVisibilityOnly.booleanValue && currentBoard.permission == 'public'){
|
||||
let currentBoard = Utils.getCurrentBoard();
|
||||
if (allowPrivateVisibilityOnly !== undefined && allowPrivateVisibilityOnly.booleanValue && currentBoard.permission == 'public') {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -237,7 +233,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
isViewSwimlanes() {
|
||||
currentUser = Meteor.user();
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
|
||||
} else {
|
||||
@@ -248,7 +244,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
isViewLists() {
|
||||
currentUser = Meteor.user();
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).boardView === 'board-view-lists';
|
||||
} else {
|
||||
@@ -257,7 +253,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
isViewCalendar() {
|
||||
currentUser = Meteor.user();
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
} else {
|
||||
@@ -267,9 +263,10 @@ BlazeComponent.extendComponent({
|
||||
|
||||
openNewListForm() {
|
||||
if (this.isViewSwimlanes()) {
|
||||
this.childComponents('swimlane')[0]
|
||||
.childComponents('addListAndSwimlaneForm')[0]
|
||||
.open();
|
||||
// The form had been removed in 416b17062e57f215206e93a85b02ef9eb1ab4902
|
||||
// this.childComponents('swimlane')[0]
|
||||
// .childComponents('addListAndSwimlaneForm')[0]
|
||||
// .open();
|
||||
} else if (this.isViewLists()) {
|
||||
this.childComponents('listsGroup')[0]
|
||||
.childComponents('addListForm')[0]
|
||||
@@ -316,15 +313,16 @@ BlazeComponent.extendComponent({
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
this.autorun(function() {
|
||||
this.autorun(function () {
|
||||
$('#calendar-view').fullCalendar('refetchEvents');
|
||||
});
|
||||
},
|
||||
calendarOptions() {
|
||||
return {
|
||||
id: 'calendar-view',
|
||||
defaultView: 'agendaDay',
|
||||
defaultView: 'month',
|
||||
editable: true,
|
||||
selectable: true,
|
||||
timezone: 'local',
|
||||
weekNumbers: true,
|
||||
header: {
|
||||
@@ -346,9 +344,9 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
locale: TAPi18n.getLanguage(),
|
||||
events(start, end, timezone, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
const events = [];
|
||||
const pushEvent = function(card, title, start, end, extraCls) {
|
||||
const pushEvent = function (card, title, start, end, extraCls) {
|
||||
start = start || card.startAt;
|
||||
end = end || card.endAt;
|
||||
title = title || card.title;
|
||||
@@ -372,12 +370,12 @@ BlazeComponent.extendComponent({
|
||||
};
|
||||
currentBoard
|
||||
.cardsInInterval(start.toDate(), end.toDate())
|
||||
.forEach(function(card) {
|
||||
.forEach(function (card) {
|
||||
pushEvent(card);
|
||||
});
|
||||
currentBoard
|
||||
.cardsDueInBetween(start.toDate(), end.toDate())
|
||||
.forEach(function(card) {
|
||||
.forEach(function (card) {
|
||||
pushEvent(
|
||||
card,
|
||||
`${card.title} ${TAPi18n.__('card-due')}`,
|
||||
@@ -385,14 +383,14 @@ BlazeComponent.extendComponent({
|
||||
new Date(card.dueAt.getTime() + 36e5),
|
||||
);
|
||||
});
|
||||
events.sort(function(first, second) {
|
||||
events.sort(function (first, second) {
|
||||
return first.id > second.id ? 1 : -1;
|
||||
});
|
||||
callback(events);
|
||||
},
|
||||
eventResize(event, delta, revertFunc) {
|
||||
let isOk = false;
|
||||
const card = Cards.findOne(event.id);
|
||||
const card = ReactiveCache.getCard(event.id);
|
||||
|
||||
if (card) {
|
||||
card.setEnd(event.end.toDate());
|
||||
@@ -404,12 +402,14 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
eventDrop(event, delta, revertFunc) {
|
||||
let isOk = false;
|
||||
const card = Cards.findOne(event.id);
|
||||
const card = ReactiveCache.getCard(event.id);
|
||||
if (card) {
|
||||
// TODO: add a flag for allDay events
|
||||
if (!event.allDay) {
|
||||
card.setStart(event.start.toDate());
|
||||
card.setEnd(event.end.toDate());
|
||||
// https://github.com/wekan/wekan/issues/2917#issuecomment-1236753962
|
||||
//card.setStart(event.start.toDate());
|
||||
//card.setEnd(event.end.toDate());
|
||||
card.setDue(event.start.toDate());
|
||||
isOk = true;
|
||||
}
|
||||
}
|
||||
@@ -417,10 +417,62 @@ BlazeComponent.extendComponent({
|
||||
revertFunc();
|
||||
}
|
||||
},
|
||||
select: function (startDate) {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
const modalElement = document.createElement('div');
|
||||
modalElement.classList.add('modal', 'fade');
|
||||
modalElement.setAttribute('tabindex', '-1');
|
||||
modalElement.setAttribute('role', 'dialog');
|
||||
modalElement.innerHTML = `
|
||||
<div class="modal-dialog justify-content-center align-items-center" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">${TAPi18n.__('r-create-card')}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<input type="text" class="form-control" id="card-title-input" placeholder="">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" id="create-card-button">${TAPi18n.__('add-card')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const createCardButton = modalElement.querySelector('#create-card-button');
|
||||
createCardButton.addEventListener('click', function () {
|
||||
const myTitle = modalElement.querySelector('#card-title-input').value;
|
||||
if (myTitle) {
|
||||
const firstList = currentBoard.draggableLists()[0];
|
||||
const firstSwimlane = currentBoard.swimlanes()[0];
|
||||
Meteor.call('createCardWithDueDate', currentBoard._id, firstList._id, myTitle, startDate.toDate(), firstSwimlane._id, function(error, result) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
} else {
|
||||
console.log("Card Created", result);
|
||||
}
|
||||
});
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
document.body.appendChild(modalElement);
|
||||
const openModal = function() {
|
||||
modalElement.style.display = 'flex';
|
||||
};
|
||||
const closeModal = function() {
|
||||
modalElement.style.display = 'none';
|
||||
};
|
||||
const closeButton = modalElement.querySelector('[data-dismiss="modal"]');
|
||||
closeButton.addEventListener('click', closeModal);
|
||||
openModal();
|
||||
}
|
||||
};
|
||||
},
|
||||
isViewCalendar() {
|
||||
currentUser = Meteor.user();
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).boardView === 'board-view-cal';
|
||||
} else {
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
position()
|
||||
if arguments[0] == cover || arguments[0] == fixed-cover
|
||||
if arguments[0] == cover
|
||||
position: absolute
|
||||
else
|
||||
position: fixed
|
||||
left: 0
|
||||
right: 0
|
||||
top: 0
|
||||
bottom: 0
|
||||
else
|
||||
position: arguments
|
||||
|
||||
.board-wrapper
|
||||
position: cover
|
||||
overflow-x: hidden
|
||||
overflow-y: hidden
|
||||
|
||||
.board-canvas
|
||||
position: cover
|
||||
transition: margin .1s
|
||||
overflow-y: auto
|
||||
|
||||
&.is-sibling-sidebar-open
|
||||
margin-right: 248px
|
||||
|
||||
.board-overlay
|
||||
position: fixed-cover
|
||||
top: -100px
|
||||
right: -400px
|
||||
background: black
|
||||
opacity: 0.33
|
||||
animation: fadeIn 0.2s
|
||||
z-index: 16
|
||||
|
||||
&.is-dragging-active
|
||||
.open-minicard-composer,
|
||||
.minicard-wrapper.is-checked
|
||||
display: none
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.board-wrapper
|
||||
|
||||
.board-canvas
|
||||
|
||||
.swimlane
|
||||
border-bottom: 1px solid #CCC
|
||||
display: flex
|
||||
flex-direction: column
|
||||
margin: 0
|
||||
padding: 0 0px 0px 0
|
||||
overflow-x: hidden
|
||||
overflow-y: auto
|
||||
|
||||
calendar-event-color(background, borderColor, color...)
|
||||
background: background !important
|
||||
border-color: borderColor
|
||||
if color
|
||||
color: color !important //overwrite text for better visibility
|
||||
|
||||
.calendar-event-green
|
||||
calendar-event-color(#3cb500, #2a8000, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-yellow
|
||||
calendar-event-color(#fad900, #c7ac00, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-orange
|
||||
calendar-event-color(#ff9f19, #cc7c14, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-red
|
||||
calendar-event-color(#eb4646, #b83737, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-purple
|
||||
calendar-event-color(#a632db, #7d26a6, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-blue
|
||||
calendar-event-color(#0079bf, #005a8a, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-pink
|
||||
calendar-event-color(#ff78cb, #cc62a3, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-sky
|
||||
calendar-event-color(#00c2e0, #0094ab, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-black
|
||||
calendar-event-color(#4d4d4d, #1a1a1a, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-lime
|
||||
calendar-event-color(#51e898, #3eb375, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-silver
|
||||
calendar-event-color(#c0c0c0, #8c8c8c, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-peachpuff
|
||||
calendar-event-color(#ffdab9, #ccaf95, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-crimson
|
||||
calendar-event-color(#dc143c, #a8112f, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-plum
|
||||
calendar-event-color(#dda0dd, #a87ba8, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-darkgreen
|
||||
calendar-event-color(#006400, #003000, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-slateblue
|
||||
calendar-event-color(#6a5acd, #4f4399, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-magenta
|
||||
calendar-event-color(#ff00ff, #cc00cc, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-gold
|
||||
calendar-event-color(#ffd700, #ccaa00, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-navy
|
||||
calendar-event-color(#000080, #000033, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-gray
|
||||
calendar-event-color(#808080, #333333, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-saddlebrown
|
||||
calendar-event-color(#8b4513, #572b0c, #ffffff) //White text for better visibility
|
||||
|
||||
.calendar-event-paleturquoise
|
||||
calendar-event-color(#afeeee, #8ababa, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-mistyrose
|
||||
calendar-event-color(#ffe4e1, #ccb8b6, #000) //Black text for better visibility
|
||||
|
||||
.calendar-event-indigo
|
||||
calendar-event-color(#4b0082, #2b004d, #ffffff) //White text for better visibility
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,23 @@
|
||||
.integration-form {
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
.flex,
|
||||
.option {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -moz-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.option {
|
||||
-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;
|
||||
}
|
||||
@@ -39,9 +39,9 @@ template(name="boardHeaderBar")
|
||||
span {{_ watchLevel}}
|
||||
a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
|
||||
i.fa.fa-sort
|
||||
span {{#if isSortActive }}{{_ 'Sort is on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
|
||||
span {{#if isSortActive }}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
|
||||
if isSortActive
|
||||
a.board-header-btn-close.js-sort-reset(title="Remove Sort")
|
||||
a.board-header-btn-close.js-sort-reset(title="{{_ 'remove-sort'}}")
|
||||
i.fa.fa-times-thin
|
||||
|
||||
else
|
||||
@@ -82,9 +82,9 @@ template(name="boardHeaderBar")
|
||||
span {{_ watchLevel}}
|
||||
a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
|
||||
i.fa.fa-sort
|
||||
span {{#if isSortActive }}{{_ 'Sort is on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
|
||||
span {{#if isSortActive }}{{_ 'sort-is-on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
|
||||
if isSortActive
|
||||
a.board-header-btn-close.js-sort-reset(title="Remove Sort")
|
||||
a.board-header-btn-close.js-sort-reset(title="{{_ 'remove-sort'}}")
|
||||
i.fa.fa-times-thin
|
||||
|
||||
else
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
|
||||
/*
|
||||
const DOWNCLS = 'fa-sort-down';
|
||||
const UPCLS = 'fa-sort-up';
|
||||
@@ -16,14 +19,14 @@ Template.boardMenuPopup.events({
|
||||
'click .js-change-board-color': Popup.open('boardChangeColor'),
|
||||
'click .js-change-language': Popup.open('changeLanguage'),
|
||||
'click .js-archive-board ': Popup.afterConfirm('archiveBoard', function() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
currentBoard.archive();
|
||||
// XXX We should have some kind of notification on top of the page to
|
||||
// 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'));
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
Popup.back();
|
||||
Boards.remove(currentBoard._id);
|
||||
FlowRouter.go('home');
|
||||
@@ -32,6 +35,7 @@ Template.boardMenuPopup.events({
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
|
||||
'click .js-card-settings': Popup.open('boardCardSettings'),
|
||||
'click .js-minicard-settings': Popup.open('boardMinicardSettings'),
|
||||
});
|
||||
|
||||
Template.boardChangeTitlePopup.events({
|
||||
@@ -55,24 +59,24 @@ Template.boardChangeTitlePopup.events({
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
watchLevel() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
return currentBoard && currentBoard.getWatchLevel(Meteor.userId());
|
||||
},
|
||||
|
||||
isStarred() {
|
||||
const boardId = Session.get('currentBoard');
|
||||
const user = Meteor.user();
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
return user && user.hasStarred(boardId);
|
||||
},
|
||||
|
||||
// Only show the star counter if the number of star is greater than 2
|
||||
showStarCounter() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
return currentBoard && currentBoard.stars >= 2;
|
||||
},
|
||||
/*
|
||||
showSort() {
|
||||
return Meteor.user().hasSortBy();
|
||||
return ReactiveCache.getCurrentUser().hasSortBy();
|
||||
},
|
||||
directionClass() {
|
||||
return this.currentDirection() === -1 ? DOWNCLS : UPCLS;
|
||||
@@ -82,10 +86,10 @@ BlazeComponent.extendComponent({
|
||||
Meteor.call('setListSortBy', direction + this.currentListSortBy());
|
||||
},
|
||||
currentDirection() {
|
||||
return Meteor.user().getListSortByDirection();
|
||||
return ReactiveCache.getCurrentUser().getListSortByDirection();
|
||||
},
|
||||
currentListSortBy() {
|
||||
return Meteor.user().getListSortBy();
|
||||
return ReactiveCache.getCurrentUser().getListSortBy();
|
||||
},
|
||||
listSortShortDesc() {
|
||||
return `list-label-short-${this.currentListSortBy()}`;
|
||||
@@ -96,7 +100,7 @@ BlazeComponent.extendComponent({
|
||||
{
|
||||
'click .js-edit-board-title': Popup.open('boardChangeTitle'),
|
||||
'click .js-star-board'() {
|
||||
Meteor.user().toggleBoardStar(Session.get('currentBoard'));
|
||||
ReactiveCache.getCurrentUser().toggleBoardStar(Session.get('currentBoard'));
|
||||
},
|
||||
'click .js-open-board-menu': Popup.open('boardMenu'),
|
||||
'click .js-change-visibility': Popup.open('boardChangeVisibility'),
|
||||
@@ -155,13 +159,6 @@ BlazeComponent.extendComponent({
|
||||
}).register('boardHeaderBar');
|
||||
|
||||
Template.boardHeaderBar.helpers({
|
||||
canModifyBoard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
boardView() {
|
||||
return Utils.boardView();
|
||||
},
|
||||
@@ -310,7 +307,7 @@ const CreateBoard = BlazeComponent.extendComponent({
|
||||
onSubmit(event) {
|
||||
super.onSubmit(event);
|
||||
// Immediately star boards crated with the headerbar popup.
|
||||
Meteor.user().toggleBoardStar(this.boardId.get());
|
||||
ReactiveCache.getCurrentUser().toggleBoardStar(this.boardId.get());
|
||||
}
|
||||
}.register('headerBarCreateBoardPopup'));
|
||||
|
||||
@@ -319,12 +316,12 @@ BlazeComponent.extendComponent({
|
||||
return !TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly').booleanValue;
|
||||
},
|
||||
visibilityCheck() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
return this.currentData() === currentBoard.permission;
|
||||
},
|
||||
|
||||
selectBoardVisibility() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
const visibility = this.currentData();
|
||||
currentBoard.setVisibility(visibility);
|
||||
Popup.back();
|
||||
@@ -341,7 +338,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
watchLevel() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
return currentBoard.getWatchLevel(Meteor.userId());
|
||||
},
|
||||
|
||||
@@ -381,7 +378,7 @@ BlazeComponent.extendComponent({
|
||||
allowedSortValues() {
|
||||
const types = [];
|
||||
const pushed = {};
|
||||
Meteor.user()
|
||||
ReactiveCache.getCurrentUser()
|
||||
.getListSortTypes()
|
||||
.forEach(type => {
|
||||
const key = type.replace(/^-/, '');
|
||||
@@ -397,16 +394,16 @@ BlazeComponent.extendComponent({
|
||||
return types;
|
||||
},
|
||||
Direction() {
|
||||
return Meteor.user().getListSortByDirection() === -1
|
||||
return ReactiveCache.getCurrentUser().getListSortByDirection() === -1
|
||||
? this.downClass
|
||||
: this.upClass;
|
||||
},
|
||||
sortby() {
|
||||
return Meteor.user().getListSortBy();
|
||||
return ReactiveCache.getCurrentUser().getListSortBy();
|
||||
},
|
||||
|
||||
setSortBy(type = null) {
|
||||
const user = Meteor.user();
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
if (type === null) {
|
||||
type = user._getListSortBy();
|
||||
} else {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
.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;
|
||||
@@ -0,0 +1,281 @@
|
||||
@import url("../../../css/reset.css") print, screen;
|
||||
|
||||
.board-list {
|
||||
margin: 0 8px;
|
||||
}
|
||||
.board-list li {
|
||||
float: left;
|
||||
width: 20%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
.board-list li.placeholder:after {
|
||||
content: '';
|
||||
display: block;
|
||||
background: #ccc;
|
||||
border-radius: 3px;
|
||||
height: 106px;
|
||||
margin: 8px;
|
||||
}
|
||||
.board-list li.ui-sortable-helper {
|
||||
cursor: grabbing;
|
||||
transform: rotate(4deg);
|
||||
display: block !important;
|
||||
}
|
||||
.board-list li.starred .fa-star,
|
||||
.board-list li.starred .fa-star-o {
|
||||
opacity: 1;
|
||||
}
|
||||
.board-list .board-list-item {
|
||||
overflow: hidden;
|
||||
background-color: #999;
|
||||
color: #f6f6f6;
|
||||
min-height: 100px;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
padding: 8px;
|
||||
margin: 8px;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.board-list .board-list-item.template-container {
|
||||
border: 4px solid #fff;
|
||||
}
|
||||
.board-list .board-list-item.tile {
|
||||
background-size: auto;
|
||||
background-repeat: repeat;
|
||||
}
|
||||
.board-list .board-list-item-sub-name {
|
||||
color: rgba(255,255,255,0.5);
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
}
|
||||
.board-list .board-list-item-desc {
|
||||
color: #fff;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
}
|
||||
.board-list .js-add-board {
|
||||
text-align: center;
|
||||
}
|
||||
.board-list .js-add-board .label {
|
||||
font-weight: normal;
|
||||
line-height: 56px;
|
||||
}
|
||||
.board-list .js-add-board :hover {
|
||||
background-color: #939393;
|
||||
}
|
||||
.board-list .fa-star,
|
||||
.board-list .fa-star-o {
|
||||
bottom: 0;
|
||||
font-size: 14px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
opacity: 0;
|
||||
padding: 9px 9px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition-duration: 0.15s;
|
||||
transition-property: color, font-size, background;
|
||||
}
|
||||
.board-list .fa-circle {
|
||||
bottom: 0;
|
||||
font-size: 10px;
|
||||
height: 10px;
|
||||
line-height: 10px;
|
||||
padding: 9px 9px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
transition-duration: 0.15s;
|
||||
transition-property: color, font-size, background;
|
||||
}
|
||||
.board-list .has-overtime-card-active {
|
||||
color: #eb4646 !important;
|
||||
}
|
||||
.board-list .no-overtime-card-active {
|
||||
color: #3cb500 !important;
|
||||
}
|
||||
.board-list .is-star-active {
|
||||
color: #fff;
|
||||
}
|
||||
.board-list .fa-clone {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
font-size: 14px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
opacity: 0;
|
||||
right: 0;
|
||||
padding: 9px 9px;
|
||||
transition-duration: 0.15s;
|
||||
transition-property: color, font-size, background;
|
||||
}
|
||||
.board-list .fa-archive {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
font-size: 14px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
opacity: 0;
|
||||
left: 0;
|
||||
padding: 9px 9px;
|
||||
transition-duration: 0.15s;
|
||||
transition-property: color, font-size, background;
|
||||
}
|
||||
.board-list li:hover a:hover .fa-star,
|
||||
.board-list li:hover a:hover .fa-clone,
|
||||
.board-list li:hover a:hover .fa-archive,
|
||||
.board-list li:hover a:hover .fa-star-o {
|
||||
color: #fff;
|
||||
}
|
||||
.board-list li:hover a .fa-star,
|
||||
.board-list li:hover a .fa-clone,
|
||||
.board-list li:hover a .fa-archive,
|
||||
.board-list li:hover a .fa-star-o {
|
||||
color: #fff;
|
||||
opacity: 0.75;
|
||||
}
|
||||
.board-list li:hover a .fa-star:hover,
|
||||
.board-list li:hover a .fa-clone:hover,
|
||||
.board-list li:hover a .fa-archive:hover,
|
||||
.board-list li:hover a .fa-star-o:hover {
|
||||
font-size: 18px;
|
||||
opacity: 1;
|
||||
}
|
||||
.board-list li:hover a .fa-star.is-star-active,
|
||||
.board-list li:hover a .fa-clone.is-star-active,
|
||||
.board-list li:hover a .fa-archive.is-star-active,
|
||||
.board-list li:hover a .fa-star-o.is-star-active {
|
||||
opacity: 1;
|
||||
}
|
||||
.board-backgrounds-list .board-background-select {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
float: left;
|
||||
width: 50%;
|
||||
padding-top: 12px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.board-backgrounds-list .board-background-select:nth-child(-n + 2) {
|
||||
padding-top: 0;
|
||||
}
|
||||
.board-backgrounds-list .board-background-select:nth-child(2n) {
|
||||
padding-left: 6px;
|
||||
}
|
||||
.board-backgrounds-list .board-background-select:nth-child(2n+1) {
|
||||
padding-right: 6px;
|
||||
}
|
||||
.board-backgrounds-list .board-background-select .background-box {
|
||||
color: #fff;
|
||||
border-radius: 3px;
|
||||
background-size: cover;
|
||||
display: block;
|
||||
height: 74px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.board-backgrounds-list .board-background-select .background-box i.fa-check {
|
||||
font-size: 25px;
|
||||
color: #fff;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.board-list {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
.board-list li {
|
||||
width: 50%;
|
||||
}
|
||||
.board-list .board-list-item {
|
||||
overflow: hidden;
|
||||
height: 8rem;
|
||||
}
|
||||
.board-list .board-list-item-sub-name {
|
||||
position: relative;
|
||||
top: -100px;
|
||||
left: -100px;
|
||||
}
|
||||
.board-list .board-handle {
|
||||
position: absolute;
|
||||
padding: 7px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 360px) {
|
||||
li {
|
||||
width: 100%;
|
||||
}
|
||||
.board-handle {
|
||||
position: absolute;
|
||||
padding: 7px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
.AllBoardTeamsOrgs {
|
||||
list-style-type: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
.AllBoardTeams,
|
||||
.AllBoardOrgs,
|
||||
.AllBoardBtns {
|
||||
float: left;
|
||||
}
|
||||
.js-AllBoardOrgs {
|
||||
margin-left: 16px;
|
||||
}
|
||||
.AllBoardTeams {
|
||||
margin-left: 16px;
|
||||
}
|
||||
.AllBoardButtonsContainer {
|
||||
margin: 16px;
|
||||
}
|
||||
#filterBtn,
|
||||
#resetBtn {
|
||||
display: inline;
|
||||
}
|
||||
.js-board {
|
||||
display: block;
|
||||
}
|
||||
.minicard-members {
|
||||
padding: 6px 0 6px 8px;
|
||||
width: 100%;
|
||||
margin-bottom: 2px;
|
||||
margin-left: -4px;
|
||||
display: inline-block;
|
||||
}
|
||||
.minicard-lists {
|
||||
margin: 0 auto;
|
||||
max-width: 95%;
|
||||
height: 100%;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.flex-wrap .item {
|
||||
margin: 2px;
|
||||
padding-right: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -13,7 +13,7 @@ template(name="boardList")
|
||||
select.js-AllBoardOrgs#jsAllBoardOrgs("multiple")
|
||||
option(value="-1") {{_ 'organizations'}} :
|
||||
each orgsDatas
|
||||
option(value="{{orgId}}") {{_ orgDisplayName}}
|
||||
option(value="{{orgId}}") {{orgDisplayName}}
|
||||
|
||||
//li.AllBoardTemplates
|
||||
// if userHasTemplates
|
||||
@@ -62,10 +62,10 @@ template(name="boardList")
|
||||
i.fa.js-has-spenttime-cards(
|
||||
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
|
||||
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
i.fa.board-handle(
|
||||
class="fa-arrows"
|
||||
title="{{_ 'Drag board'}}")
|
||||
title="{{_ 'drag-board'}}")
|
||||
else
|
||||
if isSandstorm
|
||||
i.fa.js-clone-board(
|
||||
@@ -94,6 +94,18 @@ template(name="boardList")
|
||||
span.board-list-item-name(title="{{_ 'board-drag-drop-reorder-or-click-open'}}")
|
||||
+viewer
|
||||
= title
|
||||
unless currentSetting.hideBoardMemberList
|
||||
if allowsBoardMemberList
|
||||
.minicard-members
|
||||
each member in boardMembers _id
|
||||
a.name
|
||||
+userAvatar(userId=member noRemove=true)
|
||||
unless currentSetting.hideCardCounterList
|
||||
if allowsCardCounterList
|
||||
.minicard-lists.flex.flex-wrap
|
||||
each list in boardLists _id
|
||||
.item
|
||||
| {{ list }}
|
||||
i.fa.js-star-board(
|
||||
class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
|
||||
title="{{_ 'star-board-title'}}")
|
||||
@@ -104,10 +116,10 @@ template(name="boardList")
|
||||
i.fa.js-has-spenttime-cards(
|
||||
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
|
||||
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
i.fa.board-handle(
|
||||
class="fa-arrows"
|
||||
title="{{_ 'Drag board'}}")
|
||||
title="{{_ 'drag-board'}}")
|
||||
else
|
||||
if isSandstorm
|
||||
i.fa.js-clone-board(
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
|
||||
Template.boardList.helpers({
|
||||
hideCardCounterList() {
|
||||
/* Bug Board icons random dance https://github.com/wekan/wekan/issues/4214
|
||||
return Utils.isMiniScreen() && Session.get('currentBoard'); */
|
||||
return true;
|
||||
},
|
||||
hideBoardMemberList() {
|
||||
/* Bug Board icons random dance https://github.com/wekan/wekan/issues/4214
|
||||
return Utils.isMiniScreen() && Session.get('currentBoard'); */
|
||||
return true;
|
||||
},
|
||||
})
|
||||
|
||||
Template.boardListHeaderBar.events({
|
||||
'click .js-open-archived-board'() {
|
||||
Modal.open('archivedBoards');
|
||||
@@ -15,10 +31,10 @@ Template.boardListHeaderBar.helpers({
|
||||
//}
|
||||
},
|
||||
templatesBoardId() {
|
||||
return Meteor.user() && Meteor.user().getTemplatesBoardId();
|
||||
return ReactiveCache.getCurrentUser()?.getTemplatesBoardId();
|
||||
},
|
||||
templatesBoardSlug() {
|
||||
return Meteor.user() && Meteor.user().getTemplatesBoardSlug();
|
||||
return ReactiveCache.getCurrentUser()?.getTemplatesBoardSlug();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -26,14 +42,13 @@ BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('tableVisibilityModeSettings');
|
||||
let currUser = Meteor.user();
|
||||
let currUser = ReactiveCache.getCurrentUser();
|
||||
let userLanguage;
|
||||
if(currUser && currUser.profile){
|
||||
if (currUser && currUser.profile) {
|
||||
userLanguage = currUser.profile.language
|
||||
}
|
||||
if (userLanguage) {
|
||||
TAPi18n.setLanguage(userLanguage);
|
||||
T9n.setLanguage(userLanguage);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -71,66 +86,48 @@ BlazeComponent.extendComponent({
|
||||
// DOM in its initial state. The card move is then handled reactively by
|
||||
// Blaze with the below query.
|
||||
$boards.sortable('cancel');
|
||||
|
||||
board.move(sortIndex.base);
|
||||
},
|
||||
});
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member or is comment only
|
||||
this.autorun(() => {
|
||||
if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
|
||||
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
|
||||
$boards.sortable({
|
||||
handle: '.board-handle',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
userHasTeams(){
|
||||
if(Meteor.user() != null && Meteor.user().teams && Meteor.user().teams.length > 0)
|
||||
userHasTeams() {
|
||||
if (ReactiveCache.getCurrentUser()?.teams?.length > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
teamsDatas() {
|
||||
if(Meteor.user().teams)
|
||||
return Meteor.user().teams.sort((a, b) => a.teamDisplayName.localeCompare(b.teamDisplayName));
|
||||
const teams = ReactiveCache.getCurrentUser()?.teams
|
||||
if (teams)
|
||||
return teams.sort((a, b) => a.teamDisplayName.localeCompare(b.teamDisplayName));
|
||||
else
|
||||
return [];
|
||||
},
|
||||
userHasOrgs(){
|
||||
if(Meteor.user() != null && Meteor.user().orgs && Meteor.user().orgs.length > 0)
|
||||
userHasOrgs() {
|
||||
if (ReactiveCache.getCurrentUser()?.orgs?.length > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
/*
|
||||
userHasTemplates(){
|
||||
if(Meteor.user() != null && Meteor.user().orgs && Meteor.user().orgs.length > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
*/
|
||||
orgsDatas() {
|
||||
if(Meteor.user().orgs)
|
||||
return Meteor.user().orgs.sort((a, b) => a.orgDisplayName.localeCompare(b.orgDisplayName));
|
||||
const orgs = ReactiveCache.getCurrentUser()?.orgs;
|
||||
if (orgs)
|
||||
return orgs.sort((a, b) => a.orgDisplayName.localeCompare(b.orgDisplayName));
|
||||
else
|
||||
return [];
|
||||
},
|
||||
userHasOrgsOrTeams(){
|
||||
let boolUserHasOrgs;
|
||||
if(Meteor.user() != null && Meteor.user().orgs && Meteor.user().orgs.length > 0)
|
||||
boolUserHasOrgs = true;
|
||||
else
|
||||
boolUserHasOrgs = false;
|
||||
|
||||
let boolUserHasTeams;
|
||||
if(Meteor.user() != null && Meteor.user().teams && Meteor.user().teams.length > 0)
|
||||
boolUserHasTeams = true;
|
||||
else
|
||||
boolUserHasTeams = false;
|
||||
|
||||
return (boolUserHasOrgs || boolUserHasTeams);
|
||||
userHasOrgsOrTeams() {
|
||||
const ret = this.userHasOrgs() || this.userHasTeams();
|
||||
return ret;
|
||||
},
|
||||
boards() {
|
||||
let query = {
|
||||
@@ -138,50 +135,44 @@ BlazeComponent.extendComponent({
|
||||
// { type: { $in: ['board','template-container'] } },
|
||||
$and: [
|
||||
{ archived: false },
|
||||
{ type: { $in: ['board','template-container'] } },
|
||||
{ $or:[] }
|
||||
{ type: { $in: ['board', 'template-container'] } },
|
||||
{ $or: [] },
|
||||
{ title: { $not: { $regex: /^\^.*\^$/ } } }
|
||||
]
|
||||
};
|
||||
|
||||
let allowPrivateVisibilityOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly');
|
||||
|
||||
if (FlowRouter.getRouteName() === 'home'){
|
||||
query.$and[2].$or.push({'members.userId': Meteor.userId()});
|
||||
if (FlowRouter.getRouteName() === 'home') {
|
||||
query.$and[2].$or.push({ 'members.userId': Meteor.userId() });
|
||||
|
||||
if(allowPrivateVisibilityOnly !== undefined && allowPrivateVisibilityOnly.booleanValue){
|
||||
query.$and.push({'permission': 'private'});
|
||||
if (allowPrivateVisibilityOnly !== undefined && allowPrivateVisibilityOnly.booleanValue) {
|
||||
query.$and.push({ 'permission': 'private' });
|
||||
}
|
||||
const currUser = Users.findOne(Meteor.userId());
|
||||
|
||||
// const currUser = Users.findOne(Meteor.userId(), {
|
||||
// fields: {
|
||||
// orgs: 1,
|
||||
// teams: 1,
|
||||
// },
|
||||
// });
|
||||
const currUser = ReactiveCache.getCurrentUser();
|
||||
|
||||
let orgIdsUserBelongs = currUser !== undefined && currUser.teams !== 'undefined' ? currUser.orgIdsUserBelongs() : '';
|
||||
if(orgIdsUserBelongs && orgIdsUserBelongs != ''){
|
||||
if (orgIdsUserBelongs && orgIdsUserBelongs != '') {
|
||||
let orgsIds = orgIdsUserBelongs.split(',');
|
||||
// for(let i = 0; i < orgsIds.length; i++){
|
||||
// query.$and[2].$or.push({'orgs.orgId': orgsIds[i]});
|
||||
// }
|
||||
|
||||
//query.$and[2].$or.push({'orgs': {$elemMatch : {orgId: orgsIds[0]}}});
|
||||
query.$and[2].$or.push({'orgs.orgId': {$in : orgsIds}});
|
||||
query.$and[2].$or.push({ 'orgs.orgId': { $in: orgsIds } });
|
||||
}
|
||||
|
||||
let teamIdsUserBelongs = currUser !== undefined && currUser.teams !== 'undefined' ? currUser.teamIdsUserBelongs() : '';
|
||||
if(teamIdsUserBelongs && teamIdsUserBelongs != ''){
|
||||
if (teamIdsUserBelongs && teamIdsUserBelongs != '') {
|
||||
let teamsIds = teamIdsUserBelongs.split(',');
|
||||
// for(let i = 0; i < teamsIds.length; i++){
|
||||
// query.$or[2].$or.push({'teams.teamId': teamsIds[i]});
|
||||
// }
|
||||
//query.$and[2].$or.push({'teams': { $elemMatch : {teamId: teamsIds[0]}}});
|
||||
query.$and[2].$or.push({'teams.teamId': {$in : teamsIds}});
|
||||
query.$and[2].$or.push({ 'teams.teamId': { $in: teamsIds } });
|
||||
}
|
||||
}
|
||||
else if(allowPrivateVisibilityOnly !== undefined && !allowPrivateVisibilityOnly.booleanValue){
|
||||
else if (allowPrivateVisibilityOnly !== undefined && !allowPrivateVisibilityOnly.booleanValue) {
|
||||
query = {
|
||||
archived: false,
|
||||
//type: { $in: ['board','template-container'] },
|
||||
@@ -190,31 +181,54 @@ BlazeComponent.extendComponent({
|
||||
};
|
||||
}
|
||||
|
||||
return Boards.find(query, {
|
||||
const ret = ReactiveCache.getBoards(query, {
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
});
|
||||
return ret;
|
||||
},
|
||||
boardLists(boardId) {
|
||||
/* Bug Board icons random dance https://github.com/wekan/wekan/issues/4214
|
||||
const lists = ReactiveCache.getLists({ 'boardId': boardId, 'archived': false },{sort: ['sort','asc']});
|
||||
const ret = lists.map(list => {
|
||||
let cardCount = ReactiveCache.getCards({ 'boardId': boardId, 'listId': list._id }).length;
|
||||
return `${list.title}: ${cardCount}`;
|
||||
});
|
||||
return ret;
|
||||
*/
|
||||
return [];
|
||||
},
|
||||
|
||||
boardMembers(boardId) {
|
||||
let boardMembers = [];
|
||||
/* Bug Board icons random dance https://github.com/wekan/wekan/issues/4214
|
||||
const lists = ReactiveCache.getBoard(boardId)
|
||||
let members = lists.members
|
||||
members.forEach(member => {
|
||||
boardMembers.push(member.userId);
|
||||
});
|
||||
*/
|
||||
return boardMembers;
|
||||
},
|
||||
|
||||
isStarred() {
|
||||
const user = Meteor.user();
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
return user && user.hasStarred(this.currentData()._id);
|
||||
},
|
||||
isAdministrable() {
|
||||
const user = Meteor.user();
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
return user && user.isBoardAdmin(this.currentData()._id);
|
||||
},
|
||||
|
||||
hasOvertimeCards() {
|
||||
subManager.subscribe('board', this.currentData()._id, false);
|
||||
return this.currentData().hasOvertimeCards();
|
||||
},
|
||||
|
||||
hasSpentTimeCards() {
|
||||
subManager.subscribe('board', this.currentData()._id, false);
|
||||
return this.currentData().hasSpentTimeCards();
|
||||
},
|
||||
|
||||
isInvited() {
|
||||
const user = Meteor.user();
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
return user && user.isInvitedTo(this.currentData()._id);
|
||||
},
|
||||
|
||||
@@ -224,25 +238,29 @@ BlazeComponent.extendComponent({
|
||||
'click .js-add-board': Popup.open('createBoard'),
|
||||
'click .js-star-board'(evt) {
|
||||
const boardId = this.currentData()._id;
|
||||
Meteor.user().toggleBoardStar(boardId);
|
||||
ReactiveCache.getCurrentUser().toggleBoardStar(boardId);
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click .js-clone-board'(evt) {
|
||||
let title = getSlug(ReactiveCache.getBoard(this.currentData()._id).title) || 'cloned-board';
|
||||
Meteor.call(
|
||||
'copyBoard',
|
||||
this.currentData()._id,
|
||||
{
|
||||
sort: Boards.find({ archived: false }).count(),
|
||||
sort: ReactiveCache.getBoards({ archived: false }).length,
|
||||
type: 'board',
|
||||
title: Boards.findOne(this.currentData()._id).title,
|
||||
title: ReactiveCache.getBoard(this.currentData()._id).title,
|
||||
},
|
||||
(err, res) => {
|
||||
if (err) {
|
||||
this.setError(err.error);
|
||||
console.error(err);
|
||||
} else {
|
||||
Session.set('fromBoard', null);
|
||||
subManager.subscribe('board', res, false);
|
||||
Utils.goBoardId(res);
|
||||
FlowRouter.go('board', {
|
||||
id: res,
|
||||
slug: title,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -266,10 +284,10 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
});
|
||||
},
|
||||
'click #resetBtn'(event){
|
||||
'click #resetBtn'(event) {
|
||||
let allBoards = document.getElementsByClassName("js-board");
|
||||
let currBoard;
|
||||
for(let i=0; i < allBoards.length; i++){
|
||||
for (let i = 0; i < allBoards.length; i++) {
|
||||
currBoard = allBoards[i];
|
||||
currBoard.style.display = "block";
|
||||
}
|
||||
@@ -277,57 +295,55 @@ BlazeComponent.extendComponent({
|
||||
'click #filterBtn'(event) {
|
||||
event.preventDefault();
|
||||
let selectedTeams = document.querySelectorAll('#jsAllBoardTeams option:checked');
|
||||
let selectedTeamsValues = Array.from(selectedTeams).map(function(elt){return elt.value});
|
||||
let selectedTeamsValues = Array.from(selectedTeams).map(function (elt) { return elt.value });
|
||||
let index = selectedTeamsValues.indexOf("-1");
|
||||
if (index > -1) {
|
||||
selectedTeamsValues.splice(index, 1);
|
||||
}
|
||||
|
||||
let selectedOrgs = document.querySelectorAll('#jsAllBoardOrgs option:checked');
|
||||
let selectedOrgsValues = Array.from(selectedOrgs).map(function(elt){return elt.value});
|
||||
let selectedOrgsValues = Array.from(selectedOrgs).map(function (elt) { return elt.value });
|
||||
index = selectedOrgsValues.indexOf("-1");
|
||||
if (index > -1) {
|
||||
selectedOrgsValues.splice(index, 1);
|
||||
}
|
||||
|
||||
if(selectedTeamsValues.length > 0 || selectedOrgsValues.length > 0){
|
||||
if (selectedTeamsValues.length > 0 || selectedOrgsValues.length > 0) {
|
||||
const query = {
|
||||
$and: [
|
||||
{ archived: false },
|
||||
{ type: 'board' },
|
||||
{ $or:[] }
|
||||
{ $or: [] }
|
||||
]
|
||||
};
|
||||
if(selectedTeamsValues.length > 0)
|
||||
{
|
||||
query.$and[2].$or.push({'teams.teamId': {$in : selectedTeamsValues}});
|
||||
if (selectedTeamsValues.length > 0) {
|
||||
query.$and[2].$or.push({ 'teams.teamId': { $in: selectedTeamsValues } });
|
||||
}
|
||||
if(selectedOrgsValues.length > 0)
|
||||
{
|
||||
query.$and[2].$or.push({'orgs.orgId': {$in : selectedOrgsValues}});
|
||||
if (selectedOrgsValues.length > 0) {
|
||||
query.$and[2].$or.push({ 'orgs.orgId': { $in: selectedOrgsValues } });
|
||||
}
|
||||
|
||||
let filteredBoards = Boards.find(query, {}).fetch();
|
||||
let filteredBoards = ReactiveCache.getBoards(query, {});
|
||||
let allBoards = document.getElementsByClassName("js-board");
|
||||
let currBoard;
|
||||
if(filteredBoards.length > 0){
|
||||
if (filteredBoards.length > 0) {
|
||||
let currBoardId;
|
||||
let found;
|
||||
for(let i=0; i < allBoards.length; i++){
|
||||
for (let i = 0; i < allBoards.length; i++) {
|
||||
currBoard = allBoards[i];
|
||||
currBoardId = currBoard.classList[0];
|
||||
found = filteredBoards.find(function(board){
|
||||
found = filteredBoards.find(function (board) {
|
||||
return board._id == currBoardId;
|
||||
});
|
||||
|
||||
if(found !== undefined)
|
||||
if (found !== undefined)
|
||||
currBoard.style.display = "block";
|
||||
else
|
||||
currBoard.style.display = "none";
|
||||
}
|
||||
}
|
||||
else{
|
||||
for(let i=0; i < allBoards.length; i++){
|
||||
else {
|
||||
for (let i = 0; i < allBoards.length; i++) {
|
||||
currBoard = allBoards[i];
|
||||
currBoard.style.display = "none";
|
||||
}
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
$spaceBetweenTiles = 16px
|
||||
|
||||
.board-list
|
||||
margin: 0 ($spaceBetweenTiles/2)
|
||||
|
||||
li
|
||||
float: left
|
||||
width: 20%
|
||||
box-sizing: border-box
|
||||
position: relative
|
||||
|
||||
&.placeholder:after
|
||||
content: '';
|
||||
display: block;
|
||||
background: darken(white, 20%)
|
||||
border-radius: 3px;
|
||||
height: 106px;
|
||||
margin: 8px;
|
||||
|
||||
&.ui-sortable-helper
|
||||
cursor: grabbing
|
||||
transform: rotate(4deg)
|
||||
display: block !important
|
||||
|
||||
&.starred
|
||||
.fa-star,
|
||||
.fa-star-o
|
||||
opacity: 1
|
||||
|
||||
.board-list-item
|
||||
overflow: hidden;
|
||||
background-color: #999
|
||||
color: #f6f6f6
|
||||
min-height: 100px
|
||||
font-size: 16px
|
||||
line-height: 22px
|
||||
border-radius: 3px
|
||||
display: block
|
||||
font-weight: 700
|
||||
padding: 8px
|
||||
margin: ($spaceBetweenTiles/2)
|
||||
position: relative
|
||||
text-decoration: none
|
||||
word-wrap: break-word
|
||||
|
||||
&.template-container
|
||||
border: 4px solid #fff
|
||||
|
||||
&.tile
|
||||
background-size: auto
|
||||
background-repeat: repeat
|
||||
|
||||
.board-list-item-sub-name
|
||||
color: rgba(255, 255, 255, .5)
|
||||
display: block
|
||||
font-size: 14px
|
||||
font-weight: 400
|
||||
line-height: 22px
|
||||
|
||||
.board-list-item-desc
|
||||
color: #fff
|
||||
display: block
|
||||
font-size: 14px
|
||||
font-weight: 400
|
||||
line-height: 18px
|
||||
|
||||
.js-add-board
|
||||
text-align:center
|
||||
|
||||
.label
|
||||
font-weight: normal
|
||||
line-height: 56px
|
||||
|
||||
:hover
|
||||
background-color:#939393
|
||||
|
||||
.fa-star,
|
||||
.fa-star-o
|
||||
bottom: 0
|
||||
font-size: 14px
|
||||
height: 18px
|
||||
line-height: 18px
|
||||
opacity: 0
|
||||
padding: 9px 9px
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
transition-duration: .15s
|
||||
transition-property: color, font-size, background
|
||||
|
||||
.fa-circle
|
||||
bottom: 0;
|
||||
font-size: 10px;
|
||||
height: 10px;
|
||||
line-height: 10px;
|
||||
padding: 9px 9px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
transition-duration: .15s
|
||||
transition-property: color, font-size, background
|
||||
|
||||
.has-overtime-card-active
|
||||
color: #eb4646 !important
|
||||
|
||||
.no-overtime-card-active
|
||||
color: #3cb500 !important
|
||||
|
||||
.is-star-active
|
||||
color: white
|
||||
|
||||
.fa-clone
|
||||
position: absolute;
|
||||
bottom: 0
|
||||
font-size: 14px
|
||||
height: 18px
|
||||
line-height: 18px
|
||||
opacity: 0
|
||||
right: 0
|
||||
padding: 9px 9px
|
||||
transition-duration: .15s
|
||||
transition-property: color, font-size, background
|
||||
|
||||
.fa-archive
|
||||
position: absolute;
|
||||
bottom: 0
|
||||
font-size: 14px
|
||||
height: 18px
|
||||
line-height: 18px
|
||||
opacity: 0
|
||||
left: 0
|
||||
padding: 9px 9px
|
||||
transition-duration: .15s
|
||||
transition-property: color, font-size, background
|
||||
|
||||
li:hover a
|
||||
&:hover
|
||||
.fa-star,
|
||||
.fa-clone,
|
||||
.fa-archive,
|
||||
.fa-star-o
|
||||
color: white
|
||||
|
||||
.fa-star,
|
||||
.fa-clone,
|
||||
.fa-archive,
|
||||
.fa-star-o
|
||||
color: white
|
||||
opacity: .75
|
||||
|
||||
&:hover
|
||||
font-size: 18px
|
||||
opacity: 1
|
||||
|
||||
&.is-star-active
|
||||
opacity: 1
|
||||
|
||||
.board-backgrounds-list
|
||||
|
||||
.board-background-select
|
||||
box-sizing: border-box
|
||||
display: block
|
||||
float: left
|
||||
width: 50%
|
||||
padding-top: 12px
|
||||
position: relative
|
||||
z-index: 1
|
||||
|
||||
&:nth-child(-n + 2)
|
||||
padding-top: 0
|
||||
|
||||
&:nth-child(2n)
|
||||
padding-left: 6px
|
||||
|
||||
&:nth-child(2n+1)
|
||||
padding-right: 6px
|
||||
|
||||
.background-box
|
||||
color: white
|
||||
border-radius: 3px
|
||||
background-size: cover
|
||||
display: block
|
||||
height: 74px
|
||||
position: relative
|
||||
width: 100%
|
||||
cursor: pointer
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
|
||||
i.fa-check
|
||||
font-size: 25px
|
||||
color: white
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.board-list
|
||||
height: 100%
|
||||
overflow: scroll
|
||||
|
||||
li
|
||||
width: 50%
|
||||
|
||||
.board-list-item
|
||||
overflow: hidden
|
||||
height: 8rem
|
||||
|
||||
.board-list-item-sub-name
|
||||
position: relative
|
||||
top: -100px
|
||||
left: -100px
|
||||
|
||||
.board-handle
|
||||
position: absolute
|
||||
padding: 7px
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
right: 10px
|
||||
font-size: 24px
|
||||
|
||||
@media screen and (max-width: 360px)
|
||||
li
|
||||
width: 100%
|
||||
|
||||
.board-handle
|
||||
position: absolute
|
||||
padding: 7px
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
right: 10px
|
||||
font-size: 24px
|
||||
|
||||
.AllBoardTeamsOrgs
|
||||
list-style-type: none;
|
||||
overflow: hidden;
|
||||
|
||||
.AllBoardTeams,.AllBoardOrgs,.AllBoardBtns
|
||||
float: left;
|
||||
|
||||
.js-AllBoardOrgs
|
||||
margin-left: 16px;
|
||||
|
||||
.AllBoardTeams
|
||||
margin-left : 16px;
|
||||
|
||||
.AllBoardButtonsContainer
|
||||
margin: 16px;
|
||||
|
||||
#filterBtn,#resetBtn
|
||||
display: inline;
|
||||
|
||||
.js-board
|
||||
display: block;
|
||||
@@ -0,0 +1,202 @@
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.attachment-upload {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
.attachment-gallery {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.attachment-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.attachment-item:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
.attachment-thumbnail-container {
|
||||
display: block;
|
||||
width: 150px;
|
||||
min-width: 150px;
|
||||
max-height: 150px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
.attachment-thumbnail {
|
||||
max-width: 150px;
|
||||
max-height: 150px;
|
||||
min-height: 2em;
|
||||
cursor: pointer;
|
||||
}
|
||||
.attachment-thumbnail-text {
|
||||
min-height: 2em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2em;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.attachment-details-container {
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.attachment-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-right: 25px; /* Make sure the icons are not to far to the right */
|
||||
}
|
||||
.attachment-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.add-attachment {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed #555;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 1.5em;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.icon:hover {
|
||||
color: #666;
|
||||
}
|
||||
#viewer-overlay {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9999 !important;
|
||||
background: rgba(13, 13, 13, 0.95);
|
||||
}
|
||||
#viewer-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
}
|
||||
#viewer-top-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
}
|
||||
#attachment-name {
|
||||
color: white;
|
||||
font-size: 1.5em;
|
||||
max-width: calc(
|
||||
100% - 50px
|
||||
); /* Make sure the name does not overlap the close button */
|
||||
}
|
||||
#viewer-close {
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 4em;
|
||||
top: 0;
|
||||
right: 8px;
|
||||
position: absolute;
|
||||
}
|
||||
.attachment-arrow {
|
||||
font-size: 4em;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
margin: 0 20px;
|
||||
}
|
||||
#viewer-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
}
|
||||
#image-viewer {
|
||||
background: repeating-conic-gradient(#808080 0% 25%, transparent 0% 50%) 50% /
|
||||
20px 20px; /* Checkerboard background for transparent images */
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
#pdf-viewer {
|
||||
width: 40vw;
|
||||
height: 100%;
|
||||
}
|
||||
#txt-viewer {
|
||||
background-color: white;
|
||||
width: 40vw;
|
||||
height: 100%;
|
||||
}
|
||||
.pdf-preview-error {
|
||||
margin-top: 20vh;
|
||||
display: block;
|
||||
font-size: 2em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1600px) {
|
||||
#pdf-viewer {
|
||||
width: 60vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
#viewer-container {
|
||||
display: block;
|
||||
}
|
||||
.attachment-arrow {
|
||||
position: absolute;
|
||||
bottom: 2.2em;
|
||||
font-size: 1.6em;
|
||||
padding: 16px;
|
||||
}
|
||||
#prev-attachment {
|
||||
left: 0;
|
||||
}
|
||||
#next-attachment {
|
||||
right: 0;
|
||||
}
|
||||
#pdf-viewer {
|
||||
width: 100%;
|
||||
height: calc(
|
||||
100vh - 155px
|
||||
); /* Full height - height of top and bottom bars */
|
||||
}
|
||||
#txt-viewer {
|
||||
width: 100%;
|
||||
height: calc(
|
||||
100vh - 155px
|
||||
); /* Full height - height of top and bottom bars */
|
||||
}
|
||||
#audio-viewer {
|
||||
margin-top: 20%;
|
||||
width: 100%;
|
||||
}
|
||||
.attachment-thumbnail-container {
|
||||
width: 100px;
|
||||
min-width: 100px;
|
||||
}
|
||||
.attachment-thumbnail {
|
||||
max-width: 100px;
|
||||
}
|
||||
.attachment-details {
|
||||
flex-direction: column;
|
||||
margin-right: 0px;
|
||||
}
|
||||
.attachment-actions {
|
||||
flex-direction: row;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +1,142 @@
|
||||
template(name="cardAttachmentsPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
input.js-attach-file.hide(type="file" name="file" multiple)
|
||||
a.js-computer-upload {{_ 'computer'}}
|
||||
li
|
||||
a.js-upload-clipboard-image {{_ 'clipboard'}}
|
||||
if $gt uploads.length 0
|
||||
.attachment-upload {{_ 'uploading'}}
|
||||
table
|
||||
tr
|
||||
th.upload-file-name-descr {{_ 'name'}}
|
||||
th.upload-progress-descr {{_ 'progress'}}
|
||||
th.upload-remaining-descr {{_ 'remaining_time'}}
|
||||
th.upload-speed-descr {{_ 'speed'}}
|
||||
each upload in uploads
|
||||
tr
|
||||
td.upload-file-name-value {{upload.file.name}}
|
||||
td.upload-progress-value {{upload.progress.get}}%
|
||||
td.upload-remaining-value {{getEstimateTime upload}}
|
||||
td.upload-speed-value {{getEstimateSpeed upload}}
|
||||
else
|
||||
ul.pop-over-list
|
||||
li
|
||||
input.js-attach-file.hide(type="file" name="file" multiple)
|
||||
a.js-computer-upload {{_ 'computer'}}
|
||||
li
|
||||
a.js-upload-clipboard-image {{_ 'clipboard'}}
|
||||
|
||||
template(name="previewClipboardImagePopup")
|
||||
p <kbd>Ctrl</kbd>+<kbd>V</kbd> {{_ "paste-or-dragdrop"}}
|
||||
img.preview-clipboard-image()
|
||||
button.primary.js-upload-pasted-image {{_ 'upload'}}
|
||||
|
||||
template(name="previewAttachedImagePopup")
|
||||
img.preview-large-image.js-large-image-clicked(src="{{url}}")
|
||||
|
||||
template(name="attachmentDeletePopup")
|
||||
p {{_ "attachment-delete-pop"}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
||||
template(name="attachmentsGalery")
|
||||
.attachments-galery
|
||||
each attachments
|
||||
.attachment-item
|
||||
a.attachment-thumbnail.swipebox(href="{{url}}" title="{{name}}")
|
||||
if isUploaded
|
||||
if isImage
|
||||
img.attachment-thumbnail-img(src="{{url}}")
|
||||
else if($eq extension 'mp3')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="audio/mpeg")
|
||||
else if($eq extension 'ogg')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="video/ogg")
|
||||
else if($eq extension 'webm')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="video/webm")
|
||||
else if($eq extension 'mp4')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="video/mp4")
|
||||
else
|
||||
span.attachment-thumbnail-ext= extension
|
||||
else
|
||||
+spinner
|
||||
p.attachment-details
|
||||
= name
|
||||
span.file-size ({{fileSize size}} KB)
|
||||
span.attachment-details-actions
|
||||
a.js-download(href="{{url download=true}}")
|
||||
i.fa.fa-download
|
||||
| {{_ 'download'}}
|
||||
if currentUser.isBoardMember
|
||||
unless currentUser.isCommentOnly
|
||||
unless currentUser.isWorker
|
||||
if isImage
|
||||
a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}")
|
||||
i.fa.fa-thumb-tack
|
||||
if($eq ../coverId _id)
|
||||
| {{_ 'remove-cover'}}
|
||||
else
|
||||
| {{_ 'add-cover'}}
|
||||
if currentUser.isBoardAdmin
|
||||
a.js-confirm-delete
|
||||
i.fa.fa-close
|
||||
| {{_ 'delete'}}
|
||||
template(name="attachmentViewer")
|
||||
#viewer-overlay.hidden
|
||||
#viewer-top-bar
|
||||
span#attachment-name
|
||||
a#viewer-close.fa.fa-times-thin
|
||||
|
||||
if currentUser.isBoardMember
|
||||
unless currentUser.isCommentOnly
|
||||
unless currentUser.isWorker
|
||||
//li.attachment-item.add-attachment
|
||||
a.js-add-attachment(title="{{_ 'add-attachment' }}")
|
||||
i.fa.fa-plus
|
||||
#viewer-container
|
||||
i.fa.fa-chevron-left.attachment-arrow#prev-attachment
|
||||
#viewer-content
|
||||
img#image-viewer.hidden
|
||||
video#video-viewer.hidden(controls="true")
|
||||
audio#audio-viewer.hidden(controls="true")
|
||||
object#pdf-viewer.hidden(type="application/pdf")
|
||||
span.pdf-preview-error {{_ 'preview-pdf-not-supported' }}
|
||||
object#txt-viewer.hidden(type="text/plain")
|
||||
i.fa.fa-chevron-right.attachment-arrow#next-attachment
|
||||
|
||||
template(name="attachmentGallery")
|
||||
|
||||
.attachment-gallery
|
||||
|
||||
a.attachment-item.add-attachment.js-add-attachment
|
||||
i.fa.fa-plus.icon
|
||||
|
||||
each attachments
|
||||
|
||||
.attachment-item
|
||||
.attachment-thumbnail-container.open-preview(data-attachment-id="{{_id}}" data-card-id="{{ meta.cardId }}")
|
||||
if link
|
||||
if(isImage)
|
||||
img.attachment-thumbnail(src="{{link}}" title="{{sanitize name}}")
|
||||
else if($eq extension 'svg')
|
||||
img.attachment-thumbnail(src="{{link}}" title="{{sanitize name}}" type="image/svg+xml")
|
||||
else if($eq extension 'mp3')
|
||||
video.attachment-thumbnail(title="{{sanitize name}}")
|
||||
source(src="{{link}}" type="audio/mpeg")
|
||||
else if($eq extension 'ogg')
|
||||
video.attachment-thumbnail(title="{{sanitize name}}")
|
||||
source(src="{{link}}" type="video/ogg")
|
||||
else if($eq extension 'webm')
|
||||
video.attachment-thumbnail(title="{{sanitize name}}")
|
||||
source(src="{{link}}" type="video/webm")
|
||||
else if($eq extension 'mp4')
|
||||
video.attachment-thumbnail(title="{{sanitize name}}")
|
||||
source(src="{{link}}" type="video/mp4")
|
||||
else
|
||||
span.attachment-thumbnail-text= extension
|
||||
|
||||
.attachment-details-container
|
||||
.attachment-details
|
||||
div
|
||||
b
|
||||
= name
|
||||
span.file-size ({{fileSize size}})
|
||||
.attachment-actions
|
||||
a.js-download(href="{{link}}?download=true", download="{{name}}")
|
||||
i.fa.fa-download.icon(title="{{_ 'download'}}")
|
||||
if currentUser.isBoardMember
|
||||
unless currentUser.isCommentOnly
|
||||
unless currentUser.isWorker
|
||||
a.js-rename
|
||||
i.fa.fa-pencil-square-o.icon(title="{{_ 'rename'}}")
|
||||
a.js-confirm-delete
|
||||
i.fa.fa-trash.icon(title="{{_ 'delete'}}")
|
||||
a.fa.fa-navicon.icon.js-open-attachment-menu(data-attachment-link="{{link}}" title="{{_ 'attachmentActionsPopup-title'}}")
|
||||
|
||||
|
||||
template(name="attachmentActionsPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
if isImage
|
||||
a(class="{{#if isCover}}js-remove-cover{{else}}js-add-cover{{/if}}")
|
||||
i.fa.fa-book
|
||||
i.fa.fa-picture-o
|
||||
if isCover
|
||||
| {{_ 'remove-cover'}}
|
||||
else
|
||||
| {{_ 'add-cover'}}
|
||||
if currentUser.isBoardAdmin
|
||||
if isImage
|
||||
a(class="{{#if isBackgroundImage}}js-remove-background-image{{else}}js-add-background-image{{/if}}")
|
||||
i.fa.fa-picture-o
|
||||
if isBackgroundImage
|
||||
| {{_ 'remove-background-image'}}
|
||||
else
|
||||
| {{_ 'add-background-image'}}
|
||||
p.attachment-storage
|
||||
| {{versions.original.storage}}
|
||||
|
||||
if $neq versions.original.storage "fs"
|
||||
a.js-move-storage-fs
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ 'attachment-move-storage-fs'}}
|
||||
|
||||
if $neq versions.original.storage "gridfs"
|
||||
if versions.original.storage
|
||||
a.js-move-storage-gridfs
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ 'attachment-move-storage-gridfs'}}
|
||||
|
||||
if $neq versions.original.storage "s3"
|
||||
if versions.original.storage
|
||||
a.js-move-storage-s3
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ 'attachment-move-storage-s3'}}
|
||||
|
||||
template(name="attachmentRenamePopup")
|
||||
input.js-edit-attachment-name(type='text' autofocus value="{{getNameWithoutExtension}}" dir="auto")
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-attachment-name(type="submit") {{_ 'save'}}
|
||||
@@ -1,112 +1,349 @@
|
||||
Template.attachmentsGalery.events({
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { ObjectID } from 'bson';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
const filesize = require('filesize');
|
||||
const prettyMilliseconds = require('pretty-ms');
|
||||
|
||||
// We store current card ID and the ID of currently opened attachment in a
|
||||
// global var. This is used so that we know what's the next attachment to open
|
||||
// when the user clicks on the prev/next button in the attachment viewer.
|
||||
let cardId = null;
|
||||
let openAttachmentId = null;
|
||||
|
||||
// Used to store the start and end coordinates of a touch event for attachment swiping
|
||||
let touchStartCoords = null;
|
||||
let touchEndCoords = null;
|
||||
|
||||
// Stores link to the attachment for which attachment actions popup was opened
|
||||
attachmentActionsLink = null;
|
||||
|
||||
Template.attachmentGallery.events({
|
||||
'click .open-preview'(event) {
|
||||
|
||||
openAttachmentId = $(event.currentTarget).attr("data-attachment-id");
|
||||
cardId = $(event.currentTarget).attr("data-card-id");
|
||||
|
||||
openAttachmentViewer(openAttachmentId);
|
||||
},
|
||||
'click .js-add-attachment': Popup.open('cardAttachments'),
|
||||
'click .js-confirm-delete': Popup.afterConfirm(
|
||||
'attachmentDelete',
|
||||
function() {
|
||||
Attachments.remove(this._id);
|
||||
Popup.back();
|
||||
},
|
||||
),
|
||||
// If we let this event bubble, FlowRouter will handle it and empty the page
|
||||
// content, see #101.
|
||||
'click .js-download'(event) {
|
||||
event.stopPropagation();
|
||||
},
|
||||
'click .js-add-cover'() {
|
||||
Cards.findOne(this.cardId).setCover(this._id);
|
||||
'click .js-open-attachment-menu': Popup.open('attachmentActions'),
|
||||
'mouseover .js-open-attachment-menu'(event) { // For some reason I cannot combine handlers for "click .js-open-attachment-menu" and "mouseover .js-open-attachment-menu" events so this is a quick workaround.
|
||||
attachmentActionsLink = event.currentTarget.getAttribute("data-attachment-link");
|
||||
},
|
||||
'click .js-remove-cover'() {
|
||||
Cards.findOne(this.cardId).unsetCover();
|
||||
},
|
||||
'click .js-preview-image'(event) {
|
||||
Popup.open('previewAttachedImage').call(this, event);
|
||||
// when multiple thumbnails, if click one then another very fast,
|
||||
// we might get a wrong width from previous img.
|
||||
// when popup reused, onRendered() won't be called, so we cannot get there.
|
||||
// here make sure to get correct size when this img fully loaded.
|
||||
const img = $('img.preview-large-image')[0];
|
||||
if (!img) return;
|
||||
const rePosPopup = () => {
|
||||
const w = img.width;
|
||||
const h = img.height;
|
||||
// if the image is too large, we resize & center the popup.
|
||||
if (w > 300) {
|
||||
$('div.pop-over').css({
|
||||
width: w + 20,
|
||||
position: 'absolute',
|
||||
left: (window.innerWidth - w) / 2,
|
||||
top: (window.innerHeight - h) / 2,
|
||||
});
|
||||
'click .js-rename': Popup.open('attachmentRename'),
|
||||
'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete', function() {
|
||||
Attachments.remove(this._id);
|
||||
Popup.back(2);
|
||||
}),
|
||||
});
|
||||
|
||||
function getNextAttachmentId(currentAttachmentId, offset = 0) {
|
||||
const attachments = ReactiveCache.getAttachments({'meta.cardId': cardId});
|
||||
|
||||
let i = 0;
|
||||
for (; i < attachments.length; i++) {
|
||||
if (attachments[i]._id === currentAttachmentId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return attachments[(i + offset + 1 + attachments.length) % attachments.length]._id;
|
||||
}
|
||||
|
||||
function getPrevAttachmentId(currentAttachmentId, offset = 0) {
|
||||
const attachments = ReactiveCache.getAttachments({'meta.cardId': cardId});
|
||||
|
||||
let i = 0;
|
||||
for (; i < attachments.length; i++) {
|
||||
if (attachments[i]._id === currentAttachmentId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return attachments[(i + offset - 1 + attachments.length) % attachments.length]._id;
|
||||
}
|
||||
|
||||
function attachmentCanBeOpened(attachment) {
|
||||
return (
|
||||
attachment.isImage ||
|
||||
attachment.isPDF ||
|
||||
attachment.isText ||
|
||||
attachment.isJSON ||
|
||||
attachment.isVideo ||
|
||||
attachment.isAudio
|
||||
);
|
||||
}
|
||||
|
||||
function openAttachmentViewer(attachmentId) {
|
||||
const attachment = ReactiveCache.getAttachment(attachmentId);
|
||||
|
||||
// Check if we can open the attachment (if we have a viewer for it) and exit if not
|
||||
if (!attachmentCanBeOpened(attachment)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
Instructions for adding a new viewer:
|
||||
- add a new case to the switch statement below
|
||||
- implement cleanup in the closeAttachmentViewer() function, if necessary
|
||||
- mark attachment type as openable by adding a new condition to the attachmentCanBeOpened function
|
||||
*/
|
||||
switch(true){
|
||||
case (attachment.isImage):
|
||||
$("#image-viewer").attr("src", attachment.link());
|
||||
$("#image-viewer").removeClass("hidden");
|
||||
break;
|
||||
case (attachment.isPDF):
|
||||
$("#pdf-viewer").attr("data", attachment.link());
|
||||
$("#pdf-viewer").removeClass("hidden");
|
||||
break;
|
||||
case (attachment.isVideo):
|
||||
// We have to create a new <source> DOM element and append it to the video
|
||||
// element, otherwise the video won't load
|
||||
let videoSource = document.createElement('source');
|
||||
videoSource.setAttribute('src', attachment.link());
|
||||
$("#video-viewer").append(videoSource);
|
||||
|
||||
$("#video-viewer").removeClass("hidden");
|
||||
break;
|
||||
case (attachment.isAudio):
|
||||
// We have to create a new <source> DOM element and append it to the audio
|
||||
// element, otherwise the audio won't load
|
||||
let audioSource = document.createElement('source');
|
||||
audioSource.setAttribute('src', attachment.link());
|
||||
$("#audio-viewer").append(audioSource);
|
||||
|
||||
$("#audio-viewer").removeClass("hidden");
|
||||
break;
|
||||
case (attachment.isText):
|
||||
case (attachment.isJSON):
|
||||
$("#txt-viewer").attr("data", attachment.link());
|
||||
$("#txt-viewer").removeClass("hidden");
|
||||
break;
|
||||
}
|
||||
|
||||
$('#attachment-name').text(attachment.name);
|
||||
$('#viewer-overlay').removeClass('hidden');
|
||||
}
|
||||
|
||||
function closeAttachmentViewer() {
|
||||
$("#viewer-overlay").addClass("hidden");
|
||||
|
||||
// We need to reset the viewers to avoid showing previous attachments
|
||||
$("#image-viewer").attr("src", "");
|
||||
$("#image-viewer").addClass("hidden");
|
||||
|
||||
$("#pdf-viewer").attr("data", "");
|
||||
$("#pdf-viewer").addClass("hidden");
|
||||
|
||||
$("#txt-viewer").attr("data", "");
|
||||
$("#txt-viewer").addClass("hidden");
|
||||
|
||||
$("#video-viewer").get(0).pause(); // Stop playback
|
||||
$("#video-viewer").get(0).currentTime = 0;
|
||||
$("#video-viewer").empty();
|
||||
$("#video-viewer").addClass("hidden");
|
||||
|
||||
$("#audio-viewer").get(0).pause(); // Stop playback
|
||||
$("#audio-viewer").get(0).currentTime = 0;
|
||||
$("#audio-viewer").empty();
|
||||
$("#audio-viewer").addClass("hidden");
|
||||
}
|
||||
|
||||
function openNextAttachment() {
|
||||
closeAttachmentViewer();
|
||||
|
||||
let i = 0;
|
||||
// Find an attachment that can be opened
|
||||
while (true) {
|
||||
const id = getNextAttachmentId(openAttachmentId, i);
|
||||
const attachment = ReactiveCache.getAttachment(id);
|
||||
if (attachmentCanBeOpened(attachment)) {
|
||||
openAttachmentId = id;
|
||||
openAttachmentViewer(id);
|
||||
break;
|
||||
}
|
||||
};
|
||||
const url = $(event.currentTarget).attr('src');
|
||||
if (img.src === url && img.complete) rePosPopup();
|
||||
else img.onload = rePosPopup;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
function openPrevAttachment() {
|
||||
closeAttachmentViewer();
|
||||
|
||||
let i = 0;
|
||||
// Find an attachment that can be opened
|
||||
while (true) {
|
||||
const id = getPrevAttachmentId(openAttachmentId, i);
|
||||
const attachment = ReactiveCache.getAttachment(id);
|
||||
if (attachmentCanBeOpened(attachment)) {
|
||||
openAttachmentId = id;
|
||||
openAttachmentViewer(id);
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
function processTouch(){
|
||||
|
||||
xDist = touchEndCoords.x - touchStartCoords.x;
|
||||
yDist = touchEndCoords.y - touchStartCoords.y;
|
||||
|
||||
console.log("xDist: " + xDist);
|
||||
|
||||
// Left swipe
|
||||
if (Math.abs(xDist) > Math.abs(yDist) && xDist < 0) {
|
||||
openNextAttachment();
|
||||
}
|
||||
|
||||
// Right swipe
|
||||
if (Math.abs(xDist) > Math.abs(yDist) && xDist > 0) {
|
||||
openPrevAttachment();
|
||||
}
|
||||
|
||||
// Up swipe
|
||||
if (Math.abs(yDist) > Math.abs(xDist) && yDist < 0) {
|
||||
closeAttachmentViewer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Template.attachmentViewer.events({
|
||||
'touchstart #viewer-container'(event) {
|
||||
console.log("touchstart")
|
||||
touchStartCoords = {
|
||||
x: event.changedTouches[0].screenX,
|
||||
y: event.changedTouches[0].screenY
|
||||
}
|
||||
},
|
||||
'touchend #viewer-container'(event) {
|
||||
console.log("touchend")
|
||||
touchEndCoords = {
|
||||
x: event.changedTouches[0].screenX,
|
||||
y: event.changedTouches[0].screenY
|
||||
}
|
||||
processTouch();
|
||||
},
|
||||
'click #viewer-container'(event) {
|
||||
|
||||
// Make sure the click was on #viewer-container and not on any of its children
|
||||
if(event.target !== event.currentTarget) {
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
closeAttachmentViewer();
|
||||
},
|
||||
'click #viewer-content'(event) {
|
||||
|
||||
// Make sure the click was on #viewer-content and not on any of its children
|
||||
if(event.target !== event.currentTarget) {
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
closeAttachmentViewer();
|
||||
},
|
||||
'click #viewer-close'() {
|
||||
closeAttachmentViewer();
|
||||
},
|
||||
'click #next-attachment'() {
|
||||
openNextAttachment();
|
||||
},
|
||||
'click #prev-attachment'() {
|
||||
openPrevAttachment();
|
||||
},
|
||||
});
|
||||
|
||||
Template.attachmentsGalery.helpers({
|
||||
Template.attachmentGallery.helpers({
|
||||
isBoardAdmin() {
|
||||
return Meteor.user().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
},
|
||||
fileSize(size) {
|
||||
return Math.round(size / 1024);
|
||||
const ret = filesize(size);
|
||||
return ret;
|
||||
},
|
||||
sanitize(value) {
|
||||
return DOMPurify.sanitize(value);
|
||||
},
|
||||
});
|
||||
|
||||
Template.previewAttachedImagePopup.events({
|
||||
'click .js-large-image-clicked'() {
|
||||
Popup.back();
|
||||
Template.cardAttachmentsPopup.onCreated(function() {
|
||||
this.uploads = new ReactiveVar([]);
|
||||
});
|
||||
|
||||
Template.cardAttachmentsPopup.helpers({
|
||||
getEstimateTime(upload) {
|
||||
const ret = prettyMilliseconds(upload.estimateTime.get());
|
||||
return ret;
|
||||
},
|
||||
getEstimateSpeed(upload) {
|
||||
const ret = filesize(upload.estimateSpeed.get(), {round: 0}) + "/s";
|
||||
return ret;
|
||||
},
|
||||
uploads() {
|
||||
return Template.instance().uploads.get();
|
||||
}
|
||||
});
|
||||
|
||||
Template.cardAttachmentsPopup.events({
|
||||
'change .js-attach-file'(event) {
|
||||
'change .js-attach-file'(event, templateInstance) {
|
||||
const card = this;
|
||||
const processFile = f => {
|
||||
Utils.processUploadedAttachment(card, f, attachment => {
|
||||
if (attachment && attachment._id && attachment.isImage()) {
|
||||
card.setCover(attachment._id);
|
||||
}
|
||||
Popup.back();
|
||||
});
|
||||
};
|
||||
const files = event.currentTarget.files;
|
||||
if (files) {
|
||||
let uploads = [];
|
||||
for (const file of files) {
|
||||
const fileId = new ObjectID().toString();
|
||||
let fileName = DOMPurify.sanitize(file.name);
|
||||
|
||||
FS.Utility.eachFile(event, f => {
|
||||
if (
|
||||
MAX_IMAGE_PIXEL > 0 &&
|
||||
typeof f.type === 'string' &&
|
||||
f.type.match(/^image/)
|
||||
) {
|
||||
// is image
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const dataurl = e && e.target && e.target.result;
|
||||
if (dataurl !== undefined) {
|
||||
Utils.shrinkImage({
|
||||
dataurl,
|
||||
maxSize: MAX_IMAGE_PIXEL,
|
||||
ratio: COMPRESS_RATIO,
|
||||
toBlob: true,
|
||||
callback(blob) {
|
||||
if (blob === false) {
|
||||
processFile(f);
|
||||
} else {
|
||||
blob.name = f.name;
|
||||
processFile(blob);
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// couldn't process it let other function handle it?
|
||||
processFile(f);
|
||||
// If sanitized filename is not same as original filename,
|
||||
// it could be XSS that is already fixed with sanitize,
|
||||
// or just normal mistake, so it is not a problem.
|
||||
// That is why here is no warning.
|
||||
if (fileName !== file.name) {
|
||||
// If filename is empty, only in that case add some filename
|
||||
if (fileName.length === 0) {
|
||||
fileName = 'Empty-filename-after-sanitize.txt';
|
||||
}
|
||||
}
|
||||
|
||||
const config = {
|
||||
file: file,
|
||||
fileId: fileId,
|
||||
fileName: fileName,
|
||||
meta: Utils.getCommonAttachmentMetaFrom(card),
|
||||
chunkSize: 'dynamic',
|
||||
};
|
||||
reader.readAsDataURL(f);
|
||||
} else {
|
||||
processFile(f);
|
||||
config.meta.fileId = fileId;
|
||||
const uploader = Attachments.insert(
|
||||
config,
|
||||
false,
|
||||
);
|
||||
uploader.on('start', function() {
|
||||
uploads.push(this);
|
||||
templateInstance.uploads.set(uploads);
|
||||
});
|
||||
uploader.on('uploaded', (error, fileRef) => {
|
||||
if (!error) {
|
||||
if (fileRef.isImage) {
|
||||
card.setCover(fileRef._id);
|
||||
}
|
||||
}
|
||||
});
|
||||
uploader.on('end', (error, fileRef) => {
|
||||
uploads = uploads.filter(_upload => _upload.config.fileId != fileRef._id);
|
||||
templateInstance.uploads.set(uploads);
|
||||
if (uploads.length == 0 ) {
|
||||
Popup.back();
|
||||
}
|
||||
});
|
||||
uploader.start();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-computer-upload'(event, templateInstance) {
|
||||
templateInstance.find('.js-attach-file').click();
|
||||
@@ -154,30 +391,119 @@ Template.previewClipboardImagePopup.onRendered(() => {
|
||||
|
||||
Template.previewClipboardImagePopup.events({
|
||||
'click .js-upload-pasted-image'() {
|
||||
const results = pastedResults;
|
||||
if (results && results.file) {
|
||||
const card = this;
|
||||
if (pastedResults && pastedResults.file) {
|
||||
const file = pastedResults.file;
|
||||
window.oPasted = pastedResults;
|
||||
const card = this;
|
||||
const file = new FS.File(results.file);
|
||||
if (!results.name) {
|
||||
// if no filename, it's from clipboard. then we give it a name, with ext name from MIME type
|
||||
if (typeof results.file.type === 'string') {
|
||||
file.name(results.file.type.replace('image/', 'clipboard.'));
|
||||
const fileId = new ObjectID().toString();
|
||||
const config = {
|
||||
file,
|
||||
fileId: fileId,
|
||||
meta: Utils.getCommonAttachmentMetaFrom(card),
|
||||
fileName: file.name || file.type.replace('image/', 'clipboard.'),
|
||||
chunkSize: 'dynamic',
|
||||
};
|
||||
config.meta.fileId = fileId;
|
||||
const uploader = Attachments.insert(
|
||||
config,
|
||||
false,
|
||||
);
|
||||
uploader.on('uploaded', (error, fileRef) => {
|
||||
if (!error) {
|
||||
if (fileRef.isImage) {
|
||||
card.setCover(fileRef._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
file.updatedAt(new Date());
|
||||
file.boardId = card.boardId;
|
||||
file.cardId = card._id;
|
||||
file.userId = Meteor.userId();
|
||||
const attachment = Attachments.insert(file);
|
||||
|
||||
if (attachment && attachment._id && attachment.isImage()) {
|
||||
card.setCover(attachment._id);
|
||||
}
|
||||
|
||||
pastedResults = null;
|
||||
$(document.body).pasteImageReader(() => {});
|
||||
Popup.back();
|
||||
});
|
||||
uploader.on('end', (error, fileRef) => {
|
||||
pastedResults = null;
|
||||
$(document.body).pasteImageReader(() => {});
|
||||
Popup.back();
|
||||
});
|
||||
uploader.start();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
isCover() {
|
||||
const ret = ReactiveCache.getCard(this.data().meta.cardId).coverId == this.data()._id;
|
||||
return ret;
|
||||
},
|
||||
isBackgroundImage() {
|
||||
//const currentBoard = Utils.getCurrentBoard();
|
||||
//return currentBoard.backgroundImageURL === $(".attachment-thumbnail-img").attr("src");
|
||||
return false;
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-add-cover'() {
|
||||
ReactiveCache.getCard(this.data().meta.cardId).setCover(this.data()._id);
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-remove-cover'() {
|
||||
ReactiveCache.getCard(this.data().meta.cardId).unsetCover();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-add-background-image'() {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
currentBoard.setBackgroundImageURL(attachmentActionsLink);
|
||||
Utils.setBackgroundImage(attachmentActionsLink);
|
||||
Popup.back();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-remove-background-image'() {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
currentBoard.setBackgroundImageURL("");
|
||||
Utils.setBackgroundImage("");
|
||||
Popup.back();
|
||||
Utils.reload();
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-move-storage-fs'() {
|
||||
Meteor.call('moveAttachmentToStorage', this.data()._id, "fs");
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-move-storage-gridfs'() {
|
||||
Meteor.call('moveAttachmentToStorage', this.data()._id, "gridfs");
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-move-storage-s3'() {
|
||||
Meteor.call('moveAttachmentToStorage', this.data()._id, "s3");
|
||||
Popup.back();
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
}).register('attachmentActionsPopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
getNameWithoutExtension() {
|
||||
const ret = this.data().name.replace(new RegExp("\." + this.data().extension + "$"), "");
|
||||
return ret;
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keydown input.js-edit-attachment-name'(evt) {
|
||||
// enter = save
|
||||
if (evt.keyCode === 13) {
|
||||
this.find('button[type=submit]').click();
|
||||
}
|
||||
},
|
||||
'click button.js-submit-edit-attachment-name'(event) {
|
||||
// save button pressed
|
||||
event.preventDefault();
|
||||
const name = this.$('.js-edit-attachment-name')[0]
|
||||
.value
|
||||
.trim() + this.data().extensionWithDot;
|
||||
if (name === DOMPurify.sanitize(name)) {
|
||||
Meteor.call('renameAttachment', this.data()._id, name);
|
||||
}
|
||||
Popup.back(2);
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
}).register('attachmentRenamePopup');
|
||||
@@ -1,85 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
.attachments-galery
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
|
||||
.attachment-item
|
||||
width: 33.33% - 2%
|
||||
margin: 10px 1% 0
|
||||
text-align: center
|
||||
border-radius: 3px
|
||||
overflow: auto
|
||||
background: darken(white, 7%)
|
||||
min-height: 120px
|
||||
|
||||
&:hover
|
||||
background: darken(white, 12%)
|
||||
|
||||
&.add-attachment
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
a
|
||||
display: block
|
||||
margin: auto
|
||||
|
||||
.attachment-thumbnail
|
||||
height: 80px
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
position: relative
|
||||
|
||||
.attachment-thumbnail-img
|
||||
max-height: 100%
|
||||
max-width: 100%
|
||||
|
||||
.attachment-thumbnail-ext
|
||||
text-transform: uppercase
|
||||
font-size: 1.6em
|
||||
|
||||
.attachment-details
|
||||
font-size: 0.75em
|
||||
margin: 3px
|
||||
|
||||
.attachment-details-actions a
|
||||
display: block
|
||||
|
||||
.attachment-image-preview
|
||||
max-width: 100px
|
||||
display: block
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
|
||||
.preview-large-image
|
||||
max-width: 1000px
|
||||
display: block
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
|
||||
.preview-clipboard-image
|
||||
width: 280px
|
||||
max-width: 100%;
|
||||
height: 200px
|
||||
display: block
|
||||
border: 1px solid black
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.attachments-galery
|
||||
flex-direction
|
||||
row
|
||||
.attachment-item
|
||||
width: 50% - 2%
|
||||
|
||||
.attachment-thumbnail
|
||||
height: 130px
|
||||
.attachment-details
|
||||
font-size: 1.1em
|
||||
|
||||
@media screen and (max-width: 360px)
|
||||
.attachments-galery
|
||||
.attachment-item
|
||||
width: 100%
|
||||
|
||||
.attachment-thumbnail
|
||||
height: 200px
|
||||
@@ -1,5 +1,8 @@
|
||||
import moment from 'moment/min/moment-with-locales';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
import Cards from '/models/cards';
|
||||
import { CustomFieldStringTemplate } from '/client/lib/customFields'
|
||||
|
||||
Template.cardCustomFieldsPopup.helpers({
|
||||
hasCustomField() {
|
||||
@@ -34,14 +37,6 @@ const CardCustomField = BlazeComponent.extendComponent({
|
||||
self.card = Utils.getCurrentCard();
|
||||
self.customFieldId = this.data()._id;
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
CardCustomField.register('cardCustomField');
|
||||
|
||||
@@ -243,21 +238,18 @@ CardCustomField.register('cardCustomField');
|
||||
}.register('cardCustomField-dropdown'));
|
||||
|
||||
// cardCustomField-stringtemplate
|
||||
(class extends CardCustomField {
|
||||
class CardCustomFieldStringTemplate extends CardCustomField {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
|
||||
this.stringtemplateFormat = this.data().definition.settings.stringtemplateFormat;
|
||||
this.stringtemplateSeparator = this.data().definition.settings.stringtemplateSeparator;
|
||||
this.customField = new CustomFieldStringTemplate(this.data().definition);
|
||||
|
||||
this.stringtemplateItems = new ReactiveVar(this.data().value ?? []);
|
||||
}
|
||||
|
||||
formattedValue() {
|
||||
return (this.data().value ?? [])
|
||||
.filter(value => !!value.trim())
|
||||
.map(value => this.stringtemplateFormat.replace(/%\{value\}/gi, value))
|
||||
.join(this.stringtemplateSeparator ?? '');
|
||||
const ret = this.customField.getFormattedValue(this.data().value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
getItems() {
|
||||
@@ -328,4 +320,5 @@ CardCustomField.register('cardCustomField');
|
||||
},
|
||||
];
|
||||
}
|
||||
}.register('cardCustomField-stringtemplate'));
|
||||
}
|
||||
CardCustomFieldStringTemplate.register('cardCustomField-stringtemplate');
|
||||
@@ -0,0 +1,67 @@
|
||||
.card-date {
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
padding: 1px 3px;
|
||||
background-color: #dbdbdb;
|
||||
}
|
||||
.card-date:hover,
|
||||
.card-date.is-active {
|
||||
background-color: #b3b3b3;
|
||||
}
|
||||
.card-date.current,
|
||||
.card-date.almost-due,
|
||||
.card-date.due,
|
||||
.card-date.long-overdue {
|
||||
color: #fff;
|
||||
}
|
||||
.card-date.current {
|
||||
background-color: #5ba639;
|
||||
}
|
||||
.card-date.current:hover,
|
||||
.card-date.current.is-active {
|
||||
background-color: #46802c;
|
||||
}
|
||||
.card-date.almost-due {
|
||||
background-color: #edc909;
|
||||
}
|
||||
.card-date.almost-due:hover,
|
||||
.card-date.almost-due.is-active {
|
||||
background-color: #bc9f07;
|
||||
}
|
||||
.card-date.due {
|
||||
background-color: #fa3f00;
|
||||
}
|
||||
.card-date.due:hover,
|
||||
.card-date.due.is-active {
|
||||
background-color: #c73200;
|
||||
}
|
||||
.card-date.long-overdue {
|
||||
background-color: #fd5d47;
|
||||
}
|
||||
.card-date.long-overdue:hover,
|
||||
.card-date.long-overdue.is-active {
|
||||
background-color: #fd3e24;
|
||||
}
|
||||
.card-date.end-date time::before {
|
||||
content: "\f253";
|
||||
}
|
||||
.card-date.due-date time::before {
|
||||
content: "\f090";
|
||||
}
|
||||
.card-date.start-date time::before {
|
||||
content: "\f251";
|
||||
}
|
||||
.card-date.received-date time::before {
|
||||
content: "\f08b";
|
||||
}
|
||||
.card-date time::before {
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
font-size: inherit;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
.customfield-date {
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
padding: 1px 3px;
|
||||
}
|
||||
@@ -1,16 +1,7 @@
|
||||
import moment from 'moment/min/moment-with-locales';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
|
||||
Template.dateBadge.helpers({
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// editCardReceivedDatePopup
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
@@ -317,31 +308,31 @@ CardCustomFieldDate.register('cardCustomFieldDate');
|
||||
|
||||
(class extends CardReceivedDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
return this.date.get().format('L');
|
||||
}
|
||||
}.register('minicardReceivedDate'));
|
||||
|
||||
(class extends CardStartDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
return this.date.get().format('L');
|
||||
}
|
||||
}.register('minicardStartDate'));
|
||||
|
||||
(class extends CardDueDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
return this.date.get().format('L');
|
||||
}
|
||||
}.register('minicardDueDate'));
|
||||
|
||||
(class extends CardEndDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
return this.date.get().format('L');
|
||||
}
|
||||
}.register('minicardEndDate'));
|
||||
|
||||
(class extends CardCustomFieldDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
return this.date.get().format('L');
|
||||
}
|
||||
}.register('minicardCustomFieldDate'));
|
||||
|
||||
@@ -358,7 +349,7 @@ class VoteEndDate extends CardDate {
|
||||
return classes;
|
||||
}
|
||||
showDate() {
|
||||
return this.date.get().format('l LT');
|
||||
return this.date.get().format('L LT');
|
||||
}
|
||||
showTitle() {
|
||||
return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`;
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
.card-date
|
||||
display: block
|
||||
border-radius: 4px
|
||||
padding: 1px 3px
|
||||
|
||||
background-color: #dbdbdb
|
||||
&:hover, &.is-active
|
||||
background-color: #b3b3b3
|
||||
|
||||
&.current, &.almost-due, &.due, &.long-overdue
|
||||
color: #fff
|
||||
|
||||
&.current
|
||||
background-color: #5ba639
|
||||
&:hover, &.is-active
|
||||
background-color: darken(#5ba639, 10)
|
||||
|
||||
&.almost-due
|
||||
background-color: #edc909
|
||||
&:hover, &.is-active
|
||||
background-color: darken(#edc909, 10)
|
||||
|
||||
&.due
|
||||
background-color: #fa3f00
|
||||
&:hover, &.is-active
|
||||
background-color: darken(#fa3f00, 10)
|
||||
|
||||
&.long-overdue
|
||||
background-color: #fd5d47
|
||||
&:hover, &.is-active
|
||||
background-color: darken(#fd5d47, 7)
|
||||
|
||||
&.end-date
|
||||
time
|
||||
&::before
|
||||
content: "\f253" // symbol: fa-hourglass-end
|
||||
|
||||
&.due-date
|
||||
time
|
||||
&::before
|
||||
content: "\f090" // symbol: fa-sign-in
|
||||
|
||||
&.start-date
|
||||
time
|
||||
&::before
|
||||
content: "\f251" // symbol: fa-hourglass-start
|
||||
|
||||
&.received-date
|
||||
time
|
||||
&::before
|
||||
content: "\f08b" // symbol: fa-sign-out
|
||||
|
||||
time
|
||||
&::before
|
||||
font: normal normal normal 14px/1 FontAwesome
|
||||
font-size: inherit
|
||||
-webkit-font-smoothing: antialiased
|
||||
margin-right: 0.3em
|
||||
|
||||
.customfield-date
|
||||
display: block
|
||||
border-radius: 4px
|
||||
padding: 1px 3px
|
||||
@@ -0,0 +1,56 @@
|
||||
.new-description {
|
||||
position: relative;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
.new-description.is-open .helper {
|
||||
display: inline-block;
|
||||
}
|
||||
.new-description.is-open textarea {
|
||||
min-height: 100px;
|
||||
color: #4d4d4d;
|
||||
cursor: auto;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.new-description .too-long {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.new-description textarea {
|
||||
background-color: #fff;
|
||||
border: 0;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.23);
|
||||
height: 36px;
|
||||
margin: 4px 4px 6px 0;
|
||||
padding: 9px 11px;
|
||||
width: 100%;
|
||||
}
|
||||
.new-description textarea:hover,
|
||||
.new-description textarea:is-open {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.33);
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.new-description textarea:is-open {
|
||||
cursor: auto;
|
||||
}
|
||||
.description-item {
|
||||
background-color: #fff;
|
||||
border: 0;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.23);
|
||||
color: #8c8c8c;
|
||||
height: 36px;
|
||||
margin: 4px 4px 6px 0;
|
||||
width: 92%;
|
||||
}
|
||||
.description-item:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
.description-item.add-description {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
}
|
||||
.description-item.add-description a {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
.new-description
|
||||
position: relative
|
||||
margin: 0 0 20px 0
|
||||
|
||||
|
||||
&.is-open
|
||||
.helper
|
||||
display: inline-block
|
||||
|
||||
textarea
|
||||
min-height: 100px
|
||||
color: #4d4d4d
|
||||
cursor: auto
|
||||
overflow: hidden
|
||||
word-wrap: break-word
|
||||
|
||||
.too-long
|
||||
margin-top: 8px
|
||||
|
||||
textarea
|
||||
background-color: #fff
|
||||
border: 0
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
|
||||
height: 36px
|
||||
margin: 4px 4px 6px 0
|
||||
padding: 9px 11px
|
||||
width: 100%
|
||||
|
||||
&:hover,
|
||||
&:is-open
|
||||
background-color: #fff
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .33)
|
||||
border: 0
|
||||
cursor: pointer
|
||||
|
||||
&:is-open
|
||||
cursor: auto
|
||||
|
||||
.description-item
|
||||
background-color: #fff
|
||||
border: 0
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
|
||||
color: #8c8c8c
|
||||
height: 36px
|
||||
margin: 4px 4px 6px 0
|
||||
width: 92%
|
||||
|
||||
&:hover
|
||||
background: darken(white, 12%)
|
||||
|
||||
&.add-description
|
||||
display: flex
|
||||
margin: 5px
|
||||
|
||||
a
|
||||
display: block
|
||||
margin: auto
|
||||
@@ -0,0 +1,598 @@
|
||||
.assignee {
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
position: relative;
|
||||
float: left;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin: 0 4px 4px 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
z-index: 1;
|
||||
text-decoration: none;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.assignee .avatar {
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.assignee .avatar.avatar-assignee-initials {
|
||||
height: 70%;
|
||||
width: 70%;
|
||||
padding: 15%;
|
||||
background-color: #dbdbdb;
|
||||
color: #444;
|
||||
position: absolute;
|
||||
}
|
||||
.assignee .avatar.avatar-image {
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.assignee .assignee-presence-status {
|
||||
background-color: #b3b3b3;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 50%;
|
||||
height: 7px;
|
||||
width: 7px;
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
bottom: -1px;
|
||||
border: 1px solid #fff;
|
||||
z-index: 15;
|
||||
}
|
||||
.assignee .assignee-presence-status.active {
|
||||
background: #64c464;
|
||||
border-color: #daf1da;
|
||||
}
|
||||
.assignee .assignee-presence-status.idle {
|
||||
background: #e4e467;
|
||||
border-color: #f7f7d4;
|
||||
}
|
||||
.assignee .assignee-presence-status.disconnected {
|
||||
background: #bdbdbd;
|
||||
border-color: #ededed;
|
||||
}
|
||||
.assignee .assignee-presence-status.pending {
|
||||
background: #e44242;
|
||||
border-color: #f1dada;
|
||||
}
|
||||
.assignee.add-assignee {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 0 2px #bfbfbf inset;
|
||||
}
|
||||
.assignee.add-assignee:hover,
|
||||
.assignee.add-assignee.is-active {
|
||||
box-shadow: 0 0 0 2px #666 inset;
|
||||
}
|
||||
.copied-tooltip {
|
||||
display: none;
|
||||
padding: 0px 10px;
|
||||
background-color: rgba(0,0,0,0.875);
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.card-details {
|
||||
padding: 0;
|
||||
flex-shrink: 0;
|
||||
flex-basis: 600px;
|
||||
will-change: flex-basis;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
background: #f7f7f7;
|
||||
border-radius: bottom 3px;
|
||||
z-index: 30;
|
||||
animation: flexGrowIn 0.1s;
|
||||
box-shadow: 0 0 7px 0 #b3b3b3;
|
||||
transition: flex-basis 0.1s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.card-details .mCustomScrollBox {
|
||||
padding-left: 0;
|
||||
}
|
||||
.card-details .card-details-canvas {
|
||||
width: auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
.card-details .card-details-header {
|
||||
margin: 0 -20px 5px;
|
||||
padding: 7px 20px;
|
||||
background: #ededed;
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
z-index: 500;
|
||||
}
|
||||
.card-details .card-details-header .card-number {
|
||||
color: #b3b3b3;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.card-details .card-details-header .close-card-details,
|
||||
.card-details .card-details-header .maximize-card-details,
|
||||
.card-details .card-details-header .minimize-card-details,
|
||||
.card-details .card-details-header .card-details-menu,
|
||||
.card-details .card-details-header .card-copy-button,
|
||||
.card-details .card-details-header .card-copy-mobile-button,
|
||||
.card-details .card-details-header .close-card-details-mobile-web,
|
||||
.card-details .card-details-header .card-details-menu-mobile-web,
|
||||
.card-details .card-details-header .copied-tooltip {
|
||||
float: right;
|
||||
}
|
||||
.card-details .card-details-header .close-card-details,
|
||||
.card-details .card-details-header .maximize-card-details,
|
||||
.card-details .card-details-header .minimize-card-details {
|
||||
font-size: 24px;
|
||||
padding: 5px 10px 5px 10px;
|
||||
margin-right: -8px;
|
||||
}
|
||||
.card-details .card-details-header .close-card-details-mobile-web {
|
||||
font-size: 24px;
|
||||
padding: 5px;
|
||||
margin-right: 40px;
|
||||
}
|
||||
.card-details .card-details-header .card-copy-button {
|
||||
font-size: 17px;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.card-details .card-details-header .card-copy-mobile-button {
|
||||
font-size: 17px;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.card-details .card-details-header .card-details-menu {
|
||||
font-size: 17px;
|
||||
padding: 10px;
|
||||
}
|
||||
.card-details .card-details-header .card-details-menu-mobile-web {
|
||||
font-size: 17px;
|
||||
padding: 10px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
.card-details .card-details-header .card-details-watch {
|
||||
font-size: 17px;
|
||||
padding-left: 7px;
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.card-details .card-details-header .card-details-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.33em;
|
||||
margin: 7px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
.card-details .card-details-header .linked-card-location {
|
||||
font-style: italic;
|
||||
font-size: 1em;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.card-details .card-details-header .linked-card-location p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.card-details .card-details-header form.inlined-form {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.card-details .card-details-header form.inlined-form .copied-tooltip {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
.card-details .card-details-header .card-details-list {
|
||||
font-size: 0.85em;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.card-details .card-details-header .card-details-list a.card-details-list-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
.card-details .card-details-header .card-details-list a.card-details-list-title.is-editable {
|
||||
display: inline-block;
|
||||
background: #e6e6e6;
|
||||
border-radius: 3px;
|
||||
padding: 0px 5px;
|
||||
}
|
||||
.card-details .card-details-header .copied-tooltip {
|
||||
margin-right: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
.card-details .card-description i.fa.fa-pencil-square-o {
|
||||
float: right;
|
||||
}
|
||||
.card-details .card-description textarea {
|
||||
min-height: 100px;
|
||||
}
|
||||
.card-details .card-details-items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 15px 0;
|
||||
}
|
||||
.card-details .card-details-items .card-details-item {
|
||||
margin-right: 0.5em;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.card-details .card-details-items .card-details-item:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.card-details .card-details-items .card-details-item.card-details-item-labels {
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
max-width: 95%;
|
||||
}
|
||||
.card-details .card-details-items .card-details-item.card-details-item-members,
|
||||
.card-details .card-details-items .card-details-item.card-details-item-assignees,
|
||||
.card-details .card-details-items .card-details-item.card-details-item-customfield,
|
||||
.card-details .card-details-items .card-details-item.card-details-item-name {
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
max-width: 36%;
|
||||
}
|
||||
.card-details .card-details-items .card-details-item.card-details-item-creator,
|
||||
.card-details .card-details-items .card-details-item.card-details-item-received,
|
||||
.card-details .card-details-items .card-details-item.card-details-item-start,
|
||||
.card-details .card-details-items .card-details-item.card-details-item-due,
|
||||
.card-details .card-details-items .card-details-item.card-details-item-end {
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
max-width: 28%;
|
||||
}
|
||||
.card-details .card-details-items .card-details-item.custom-fields {
|
||||
padding-left: 10px;
|
||||
}
|
||||
.card-details .card-details-item-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #4d4d4d;
|
||||
}
|
||||
.card-details .activities {
|
||||
padding-top: 10px;
|
||||
}
|
||||
@media screen and (min-width: 801px) {
|
||||
.card-details {
|
||||
top: 97px;
|
||||
left: calc(50% - (600px / 2));
|
||||
width: 600px;
|
||||
bottom: 0;
|
||||
position: fixed;
|
||||
resize: both;
|
||||
}
|
||||
.card-details-maximized {
|
||||
padding: 0;
|
||||
flex-shrink: 0;
|
||||
flex-basis: calc(100% - 20px);
|
||||
will-change: flex-basis;
|
||||
overflow-y: scroll;
|
||||
overflow-x: scroll;
|
||||
background: #f7f7f7;
|
||||
border-radius: bottom 3px;
|
||||
z-index: 100;
|
||||
animation: flexGrowIn 0.1s;
|
||||
box-shadow: 0 0 7px 0 #b3b3b3;
|
||||
transition: flex-basis 0.1s;
|
||||
box-sizing: border-box;
|
||||
top: 97px;
|
||||
left: 0px;
|
||||
height: calc(100% - 20px);
|
||||
width: calc(100% - 20px);
|
||||
float: left;
|
||||
}
|
||||
.card-details-maximized .card-details-left {
|
||||
float: left;
|
||||
top: 60px;
|
||||
left: 20px;
|
||||
width: 47%;
|
||||
border-right: solid 2px #dbdbdb;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.card-details-maximized .card-details-right {
|
||||
position: absolute;
|
||||
float: right;
|
||||
left: 50%;
|
||||
margin: 15px 0;
|
||||
}
|
||||
.card-details-maximized .card-details-header {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
input[type="text"].attachment-add-link-input {
|
||||
float: left;
|
||||
margin: 0 0 8px;
|
||||
width: 80%;
|
||||
}
|
||||
input[type="submit"].attachment-add-link-submit {
|
||||
float: left;
|
||||
margin: 0 0 8px 4px;
|
||||
padding: 6px 12px;
|
||||
width: 18%;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.card-details {
|
||||
width: calc(100% - 1px);
|
||||
padding: 0px 20px 0px 20px;
|
||||
margin: 0px;
|
||||
transition: none;
|
||||
overflow-y: revert;
|
||||
overflow-x: revert;
|
||||
}
|
||||
.card-details .card-details-canvas {
|
||||
width: 100%;
|
||||
padding-left: 0px;
|
||||
}
|
||||
.card-details .card-details-header .close-card-details {
|
||||
margin-right: 0px;
|
||||
}
|
||||
.card-details .card-details-header .card-details-menu {
|
||||
margin-right: 40px;
|
||||
}
|
||||
.card-details .card-details-header .maximize-card-details {
|
||||
margin-right: 40px;
|
||||
}
|
||||
.card-details .card-details-header .minimize-card-details {
|
||||
margin-right: 40px;
|
||||
}
|
||||
.card-details-popup {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
.pop-over > .content-wrapper > .popup-container-depth-0 {
|
||||
width: 100%;
|
||||
}
|
||||
.pop-over > .content-wrapper > .popup-container-depth-0 > .content {
|
||||
width: calc(100% - 10px);
|
||||
}
|
||||
.pop-over > .content-wrapper > .popup-container-depth-0 > .content > .card-details-popup hr {
|
||||
margin: 15px 0px;
|
||||
}
|
||||
.pop-over > .content-wrapper > .popup-container-depth-0 .card-details-header {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.card-details-white {
|
||||
background: #fff !important;
|
||||
color: #000 !important;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.card-details-green {
|
||||
background: #3cb500 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-yellow {
|
||||
background: #fad900 !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-orange {
|
||||
background: #ff9f19 !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-red {
|
||||
background: #eb4646 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-purple {
|
||||
background: #a632db !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-blue {
|
||||
background: #0079bf !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-pink {
|
||||
background: #ff78cb !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-sky {
|
||||
background: #00c2e0 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-black {
|
||||
background: #4d4d4d !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-lime {
|
||||
background: #51e898 !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-silver {
|
||||
background: #c0c0c0 !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-peachpuff {
|
||||
background: #ffdab9 !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-crimson {
|
||||
background: #dc143c !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-plum {
|
||||
background: #dda0dd !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-darkgreen {
|
||||
background: #006400 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-slateblue {
|
||||
background: #6a5acd !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-magenta {
|
||||
background: #f0f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-gold {
|
||||
background: #ffd700 !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-navy {
|
||||
background: #000080 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-gray {
|
||||
background: #808080 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-saddlebrown {
|
||||
background: #8b4513 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.card-details-paleturquoise {
|
||||
background: #afeeee !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-mistyrose {
|
||||
background: #ffe4e1 !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.card-details-indigo {
|
||||
background: #4b0082 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.voted {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.vote-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.vote-title .js-edit-date {
|
||||
align-self: baseline;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.vote-result {
|
||||
display: flex;
|
||||
}
|
||||
.js-show-positive-votes {
|
||||
cursor: pointer;
|
||||
}
|
||||
.poker-voted {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.poker-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.poker-title .js-edit-date {
|
||||
align-self: baseline;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.poker-result {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.js-show-positive-poker-votes {
|
||||
cursor: pointer;
|
||||
}
|
||||
.poker-deck {
|
||||
display: grid;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
.poker-card-result {
|
||||
width: 32px;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
padding: 4px 2px 4px 2px;
|
||||
cursor: default;
|
||||
}
|
||||
.winner {
|
||||
font-weight: bold;
|
||||
outline: #2d2d2d solid 2px;
|
||||
}
|
||||
.loser {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.responsive-table {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.poker-table {
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.poker-table-row {
|
||||
display: table-row;
|
||||
}
|
||||
.poker-table-heading {
|
||||
background-color: #eee;
|
||||
display: table-header-group;
|
||||
}
|
||||
.poker-table-cell {
|
||||
display: table-cell;
|
||||
padding: 0 0 5px 2px;
|
||||
border-bottom: 1px solid #d2d0d0;
|
||||
text-align: center;
|
||||
min-width: 45px;
|
||||
}
|
||||
.poker-table-cell-who {
|
||||
width: 150px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.poker-table-heading-left,
|
||||
.poker-table-heading-right {
|
||||
display: table-header-group;
|
||||
font-weight: bold;
|
||||
border-top: 1px solid #808080;
|
||||
}
|
||||
@media (max-width: 400px) {
|
||||
.poker-table-heading-right {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.poker-table-body {
|
||||
display: table-row-group;
|
||||
}
|
||||
.poker-table-side-left,
|
||||
.poker-table-side-right {
|
||||
display: inline-block;
|
||||
}
|
||||
.poker-table-side-right {
|
||||
padding-left: 10px;
|
||||
}
|
||||
@media (max-width: 400px) {
|
||||
.poker-table-side-right {
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
.estimation-add {
|
||||
display: block;
|
||||
overflow: auto;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.estimation-add input {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
margin: auto;
|
||||
margin-right: 10px;
|
||||
width: 100px;
|
||||
}
|
||||
.estimation-add button {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
margin: auto;
|
||||
}
|
||||
.poker-card {
|
||||
width: 48px;
|
||||
height: 72px;
|
||||
float: left;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
display: table;
|
||||
box-sizing: border-box;
|
||||
padding: 5px;
|
||||
margin: 3px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
text-shadow: #2d2d2d 1px 1px 0;
|
||||
box-shadow: 0 0 5px #aaa;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.poker-card .inner {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
background-color: #cecece;
|
||||
}
|
||||
@@ -2,6 +2,9 @@ template(name="cardDetailsPopup")
|
||||
+cardDetails(popupCard)
|
||||
|
||||
template(name="cardDetails")
|
||||
|
||||
+attachmentViewer
|
||||
|
||||
section.card-details.js-card-details(class='{{#if cardMaximized}}card-details-maximized{{/if}}' class='{{#if isPopup}}card-details-popup{{/if}}'): .card-details-canvas
|
||||
.card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}')
|
||||
+inlinedForm(classNames="js-card-details-title")
|
||||
@@ -234,6 +237,15 @@ template(name="cardDetails")
|
||||
+viewer
|
||||
= sort
|
||||
|
||||
if currentBoard.allowsShowLists
|
||||
.card-details-item.card-details-show-lists
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-list
|
||||
| {{_ 'list'}}
|
||||
select.js-select-card-details-lists(disabled="{{#unless canModifyCard}}disabled{{/unless}}")
|
||||
each currentBoard.lists
|
||||
option(value="{{_id}}" selected="{{#if isCurrentListId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
//.card-details-items
|
||||
if getSpentTime
|
||||
.card-details-item.card-details-item-spent
|
||||
@@ -257,12 +269,14 @@ template(name="cardDetails")
|
||||
i.fa.fa-list-alt
|
||||
= definition.name
|
||||
+cardCustomField
|
||||
.material-toggle-switch(title="{{_ 'change'}} {{_ 'custom-fields'}} {{_ 'layout'}}")
|
||||
if customFieldsGrid
|
||||
input.toggle-switch(type="checkbox" id="toggleCustomFieldsGridButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleCustomFieldsGridButton")
|
||||
label.toggle-label(for="toggleCustomFieldsGridButton")
|
||||
|
||||
if $gt customFieldsWD.length 1
|
||||
.material-toggle-switch(title="{{_ 'change'}} {{_ 'custom-fields'}} {{_ 'layout'}}")
|
||||
if customFieldsGrid
|
||||
input.toggle-switch(type="checkbox" id="toggleCustomFieldsGridButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleCustomFieldsGridButton")
|
||||
label.toggle-label(for="toggleCustomFieldsGridButton")
|
||||
a.fa.fa-plus.js-custom-fields.card-details-item.custom-fields(title="{{_ 'custom-fields'}}")
|
||||
|
||||
if getVoteQuestion
|
||||
@@ -510,12 +524,11 @@ template(name="cardDetails")
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
if currentBoard.allowsDescriptionText
|
||||
a.js-open-inlined-form
|
||||
if getDescription
|
||||
+viewer
|
||||
= getDescription
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
a.js-open-inlined-form.right(title="{{_ 'edit'}}" value=title)
|
||||
i.fa.fa-pencil-square-o
|
||||
if getDescription
|
||||
+viewer
|
||||
= getDescription
|
||||
if (hasUnsavedValue 'cardDescription' _id)
|
||||
p.quiet
|
||||
| {{_ 'unsaved-description'}}
|
||||
@@ -530,8 +543,8 @@ template(name="cardDetails")
|
||||
+viewer
|
||||
= getDescription
|
||||
|
||||
.card-checklist-attachmentGalerys
|
||||
.card-checklist-attachmentGalery.card-checklists
|
||||
.card-checklist-attachmentGalleries
|
||||
.card-checklist-attachmentGallery.card-checklists
|
||||
if currentBoard.allowsChecklists
|
||||
hr
|
||||
+checklists(cardId = _id)
|
||||
@@ -543,13 +556,20 @@ template(name="cardDetails")
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'attachments'}}
|
||||
.card-checklist-attachmentGalery.card-attachmentGalery
|
||||
+attachmentsGalery
|
||||
if Meteor.settings.public.attachmentsUploadMaxSize
|
||||
| {{_ 'max-upload-filesize'}} {{Meteor.settings.public.attachmentsUploadMaxSize}}
|
||||
br
|
||||
if Meteor.settings.public.attachmentsUploadMimeTypes
|
||||
| {{_ 'allowed-upload-filetypes'}} {{Meteor.settings.public.attachmentsUploadMimeTypes}}
|
||||
br
|
||||
| {{_ 'invalid-file'}}
|
||||
.card-checklist-attachmentGallery.card-attachmentGallery
|
||||
+attachmentGallery
|
||||
hr
|
||||
|
||||
.card-details-right
|
||||
|
||||
unless currentUser.isNoComments
|
||||
hr
|
||||
.activity-title
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-history
|
||||
@@ -673,13 +693,12 @@ template(name="cardDetailsActionsPopup")
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyCardPopup-title'}}
|
||||
unless currentUser.isWorker
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-copy-checklist-cards
|
||||
i.fa.fa-list
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyChecklistToManyCardsPopup-title'}}
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyManyCardsPopup-title'}}
|
||||
unless archived
|
||||
hr
|
||||
ul.pop-over-list
|
||||
@@ -703,39 +722,42 @@ template(name="exportCardPopup")
|
||||
| {{_ 'export-card-pdf'}}
|
||||
|
||||
template(name="moveCardPopup")
|
||||
+boardsAndLists
|
||||
+copyAndMoveCard
|
||||
|
||||
template(name="copyCardPopup")
|
||||
label(for='copy-card-title') {{_ 'title'}}:
|
||||
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
|
||||
= getTitle
|
||||
+boardsAndLists
|
||||
+copyAndMoveCard
|
||||
|
||||
template(name="copyChecklistToManyCardsPopup")
|
||||
label(for='copy-checklist-cards-title') {{_ 'copyChecklistToManyCardsPopup-instructions'}}:
|
||||
template(name="copyManyCardsPopup")
|
||||
label(for='copy-checklist-cards-title') {{_ 'copyManyCardsPopup-instructions'}}:
|
||||
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
|
||||
| {{_ 'copyChecklistToManyCardsPopup-format'}}
|
||||
+boardsAndLists
|
||||
| {{_ 'copyManyCardsPopup-format'}}
|
||||
+copyAndMoveCard
|
||||
|
||||
template(name="boardsAndLists")
|
||||
template(name="convertChecklistItemToCardPopup")
|
||||
label(for='convert-checklist-item-to-card-title') {{_ 'title'}}:
|
||||
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
|
||||
= item.title
|
||||
+copyAndMoveCard
|
||||
|
||||
template(name="copyAndMoveCard")
|
||||
unless currentUser.isWorker
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards(autofocus)
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionBoardId _id}}selected{{/if}}") {{title}}
|
||||
option(value="{{_id}}" selected="{{#if isDialogOptionBoardId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'swimlanes'}}:
|
||||
select.js-select-swimlanes
|
||||
each swimlanes
|
||||
option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
|
||||
option(value="{{_id}}" selected="{{#if isDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'lists'}}:
|
||||
select.js-select-lists
|
||||
each aBoardLists
|
||||
option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionListId _id}}selected{{/if}}") {{title}}
|
||||
each lists
|
||||
option(value="{{_id}}" selected="{{#if isDialogOptionListId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import moment from 'moment/min/moment-with-locales';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
import Cards from '/models/cards';
|
||||
import Boards from '/models/boards';
|
||||
@@ -7,8 +10,8 @@ import Users from '/models/users';
|
||||
import Lists from '/models/lists';
|
||||
import CardComments from '/models/cardComments';
|
||||
import { ALLOWED_COLORS } from '/config/const';
|
||||
import moment from 'moment';
|
||||
import { UserAvatar } from '../users/userAvatar';
|
||||
import { DialogWithBoardSwimlaneList } from '/client/lib/dialogWithBoardSwimlaneList';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndexData } = Utils;
|
||||
@@ -32,7 +35,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
this.currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
this.currentBoard = Utils.getCurrentBoard();
|
||||
this.isLoaded = new ReactiveVar(false);
|
||||
|
||||
if (this.parentComponent() && this.parentComponent().parentComponent()) {
|
||||
@@ -61,69 +64,16 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
hiddenSystemMessages() {
|
||||
return Meteor.user().hasHiddenSystemMessages();
|
||||
return ReactiveCache.getCurrentUser().hasHiddenSystemMessages();
|
||||
},
|
||||
|
||||
customFieldsGrid() {
|
||||
return Meteor.user().hasCustomFieldsGrid();
|
||||
return ReactiveCache.getCurrentUser().hasCustomFieldsGrid();
|
||||
},
|
||||
|
||||
|
||||
cardMaximized() {
|
||||
return Meteor.user().hasCardMaximized();
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
scrollParentContainer() {
|
||||
const cardPanelWidth = 600;
|
||||
const parentComponent = this.parentComponent();
|
||||
// TODO sometimes parentComponent is not available, maybe because it's not
|
||||
// yet created?!
|
||||
if (!parentComponent) return;
|
||||
const bodyBoardComponent = 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');
|
||||
// TODO sometimes cardContainer is not available, maybe because it's not yet
|
||||
// created?!
|
||||
if (!$cardContainer) return;
|
||||
const cardContainerScroll = $cardContainer.scrollLeft();
|
||||
const cardContainerWidth = $cardContainer.width();
|
||||
|
||||
const cardViewStart = $cardView.offset().left;
|
||||
const cardViewEnd = cardViewStart + cardPanelWidth;
|
||||
|
||||
let offset = false;
|
||||
if (cardViewStart < 0) {
|
||||
offset = cardViewStart;
|
||||
} else if (cardViewEnd > cardContainerWidth) {
|
||||
offset = cardViewEnd - cardContainerWidth;
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
bodyBoardComponent.scrollLeft(cardContainerScroll + offset);
|
||||
}
|
||||
|
||||
//Scroll top
|
||||
const cardViewStartTop = $cardView.offset().top;
|
||||
const cardContainerScrollTop = $cardContainer.scrollTop();
|
||||
|
||||
let topOffset = false;
|
||||
if (cardViewStartTop !== 100) {
|
||||
topOffset = cardViewStartTop - 100;
|
||||
}
|
||||
if (topOffset !== false) {
|
||||
bodyBoardComponent.scrollTop(cardContainerScrollTop + topOffset);
|
||||
}
|
||||
return !Utils.getPopupCardId() && ReactiveCache.getCurrentUser().hasCardMaximized();
|
||||
},
|
||||
|
||||
presentParentTask() {
|
||||
@@ -138,7 +88,7 @@ BlazeComponent.extendComponent({
|
||||
const card = this.currentData();
|
||||
let result = '#';
|
||||
if (card) {
|
||||
const board = Boards.findOne(card.boardId);
|
||||
const board = ReactiveCache.getBoard(card.boardId);
|
||||
if (board) {
|
||||
result = FlowRouter.path('card', {
|
||||
boardId: card.boardId,
|
||||
@@ -168,6 +118,15 @@ BlazeComponent.extendComponent({
|
||||
);
|
||||
},
|
||||
|
||||
/** returns if the list id is the current list id
|
||||
* @param listId list id to check
|
||||
* @return is the list id the current list id ?
|
||||
*/
|
||||
isCurrentListId(listId) {
|
||||
const ret = this.data().listId == listId;
|
||||
return ret;
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
if (Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED) {
|
||||
// Send Webhook but not create Activities records ---
|
||||
@@ -178,15 +137,15 @@ BlazeComponent.extendComponent({
|
||||
cardId: card._id,
|
||||
boardId: card.boardId,
|
||||
listId: card.listId,
|
||||
user: Meteor.user().username,
|
||||
user: ReactiveCache.getCurrentUser().username,
|
||||
url: '',
|
||||
};
|
||||
|
||||
const integrations = Integrations.find({
|
||||
const integrations = ReactiveCache.getIntegrations({
|
||||
boardId: { $in: [card.boardId, Integrations.Const.GLOBAL_WEBHOOK_ID] },
|
||||
enabled: true,
|
||||
activities: { $in: ['CardDetailsRendered', 'all'] },
|
||||
}).fetch();
|
||||
});
|
||||
|
||||
if (integrations.length > 0) {
|
||||
integrations.forEach((integration) => {
|
||||
@@ -202,11 +161,6 @@ BlazeComponent.extendComponent({
|
||||
//-------------
|
||||
}
|
||||
|
||||
if (!Utils.isMiniScreen()) {
|
||||
Meteor.setTimeout(() => {
|
||||
this.scrollParentContainer();
|
||||
}, 500);
|
||||
}
|
||||
const $checklistsDom = this.$('.card-checklist-items');
|
||||
|
||||
$checklistsDom.sortable({
|
||||
@@ -278,7 +232,7 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
|
||||
function userIsMember() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardMember();
|
||||
}
|
||||
|
||||
// Disable sorting if the current user is not a board member
|
||||
@@ -289,7 +243,7 @@ BlazeComponent.extendComponent({
|
||||
$checklistsDom.data('sortable')
|
||||
) {
|
||||
$checklistsDom.sortable('option', 'disabled', disabled);
|
||||
if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
|
||||
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
|
||||
$checklistsDom.sortable({ handle: '.checklist-handle' });
|
||||
}
|
||||
}
|
||||
@@ -379,6 +333,14 @@ BlazeComponent.extendComponent({
|
||||
card.move(card.boardId, card.swimlaneId, card.listId, sort);
|
||||
}
|
||||
},
|
||||
'change .js-select-card-details-lists'(event) {
|
||||
let card = this.data();
|
||||
const listSelect = this.$('.js-select-card-details-lists')[0];
|
||||
const listId = listSelect.options[listSelect.selectedIndex].value;
|
||||
|
||||
const minOrder = card.getMinSort(listId, card.swimlaneId);
|
||||
card.move(card.boardId, card.swimlaneId, listId, minOrder - 1);
|
||||
},
|
||||
'click .js-go-to-linked-card'() {
|
||||
Utils.goCardId(this.data().linkedId);
|
||||
},
|
||||
@@ -624,15 +586,7 @@ Template.cardDetailsActionsPopup.helpers({
|
||||
},
|
||||
|
||||
isBoardAdmin() {
|
||||
return Meteor.user().isBoardAdmin();
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -652,25 +606,17 @@ Template.cardDetailsActionsPopup.events({
|
||||
'click .js-move-card': Popup.open('moveCard'),
|
||||
'click .js-copy-card': Popup.open('copyCard'),
|
||||
'click .js-convert-checklist-item-to-card': Popup.open('convertChecklistItemToCard'),
|
||||
'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
|
||||
'click .js-copy-checklist-cards': Popup.open('copyManyCards'),
|
||||
'click .js-set-card-color': Popup.open('setCardColor'),
|
||||
'click .js-move-card-to-top'(event) {
|
||||
event.preventDefault();
|
||||
const minOrder = _.min(
|
||||
this.list()
|
||||
.cardsUnfiltered(this.swimlaneId)
|
||||
.map((c) => c.sort),
|
||||
);
|
||||
const minOrder = this.getMinSort();
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-move-card-to-bottom'(event) {
|
||||
event.preventDefault();
|
||||
const maxOrder = _.max(
|
||||
this.list()
|
||||
.cardsUnfiltered(this.swimlaneId)
|
||||
.map((c) => c.sort),
|
||||
);
|
||||
const maxOrder = this.getMaxSort();
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
|
||||
Popup.back();
|
||||
},
|
||||
@@ -716,19 +662,8 @@ BlazeComponent.extendComponent({
|
||||
}).register('editCardTitleForm');
|
||||
|
||||
Template.cardMembersPopup.onCreated(function () {
|
||||
let currBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let currBoard = Utils.getCurrentBoard();
|
||||
let members = currBoard.activeMembers();
|
||||
|
||||
// let query = {
|
||||
// "teams.teamId": { $in: currBoard.teams.map(t => t.teamId) },
|
||||
// };
|
||||
|
||||
// let boardTeamUsers = Users.find(query, {
|
||||
// sort: { sort: 1 },
|
||||
// });
|
||||
|
||||
// members = currBoard.activeMembers2(members, boardTeamUsers);
|
||||
|
||||
this.members = new ReactiveVar(members);
|
||||
});
|
||||
|
||||
@@ -741,33 +676,23 @@ Template.cardMembersPopup.events({
|
||||
|
||||
Template.cardMembersPopup.helpers({
|
||||
members() {
|
||||
return Template.instance().members.get();
|
||||
return _.sortBy(Template.instance().members.get(),'fullname');
|
||||
},
|
||||
});
|
||||
|
||||
const filterMembers = (filterTerm) => {
|
||||
let currBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let currBoard = Utils.getCurrentBoard();
|
||||
let members = currBoard.activeMembers();
|
||||
|
||||
// let query = {
|
||||
// "teams.teamId": { $in: currBoard.teams.map(t => t.teamId) },
|
||||
// };
|
||||
|
||||
// let boardTeamUsers = Users.find(query, {
|
||||
// sort: { sort: 1 },
|
||||
// });
|
||||
|
||||
// members = currBoard.activeMembers2(members, boardTeamUsers);
|
||||
|
||||
if (filterTerm) {
|
||||
members = members
|
||||
.map(member => ({
|
||||
member,
|
||||
user: Users.findOne(member.userId)
|
||||
user: ReactiveCache.getUser(member.userId)
|
||||
}))
|
||||
.filter(({ user }) =>
|
||||
(user.profile.fullname !== undefined && user.profile.fullname.toLowerCase().indexOf(filterTerm.toLowerCase()) !== -1)
|
||||
|| user.profile.fullname === undefined && user.profile.username !== undefined && user.profile.username.toLowerCase().indexOf(filterTerm.toLowerCase()) !== -1)
|
||||
(user.profile.fullname !== undefined && user.profile.fullname.toLowerCase().indexOf(filterTerm.toLowerCase()) !== -1)
|
||||
|| user.profile.fullname === undefined && user.profile.username !== undefined && user.profile.username.toLowerCase().indexOf(filterTerm.toLowerCase()) !== -1)
|
||||
.map(({ member }) => member);
|
||||
}
|
||||
return members;
|
||||
@@ -799,178 +724,58 @@ Template.editCardAssignerForm.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.moveCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(this._id);
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
let boardId;
|
||||
// if we are a worker, we won't have a board select so we just use the
|
||||
// current boardId of the card.
|
||||
if (bSelect) {
|
||||
boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
} else {
|
||||
boardId = card.boardId;
|
||||
}
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
const listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||
card.move(boardId, swimlaneId, listId, 0);
|
||||
|
||||
// set new id's to card object in case the card is moved to top by the comment "moveCard" after this command (.js-move-card)
|
||||
this.boardId = boardId;
|
||||
this.swimlaneId = swimlaneId;
|
||||
this.listId = listId;
|
||||
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentBoardId = Utils.getCurrentBoardId();
|
||||
this.selectedBoardId = new ReactiveVar(this.currentBoardId);
|
||||
this.setMoveAndCopyDialogOption(this.currentBoardId);
|
||||
},
|
||||
|
||||
/** set the last confirmed dialog field values
|
||||
* @param boardId the current board id
|
||||
*/
|
||||
setMoveAndCopyDialogOption(boardId) {
|
||||
this.moveAndCopyDialogOption = {
|
||||
'boardId' : "",
|
||||
'swimlaneId' : "",
|
||||
'listId' : "",
|
||||
}
|
||||
|
||||
let currentOptions = Meteor.user().getMoveAndCopyDialogOptions();
|
||||
if (currentOptions && boardId && currentOptions[boardId]) {
|
||||
this.moveAndCopyDialogOption = currentOptions[boardId];
|
||||
if (this.moveAndCopyDialogOption.boardId) {
|
||||
this.selectedBoardId.set(this.moveAndCopyDialogOption.boardId)
|
||||
}
|
||||
}
|
||||
subManager.subscribe('board', this.selectedBoardId.get(), false);
|
||||
},
|
||||
|
||||
/** returns if the board id was the last confirmed one
|
||||
* @param boardId check this board id
|
||||
* @return if the board id was the last confirmed one
|
||||
*/
|
||||
isMoveAndCopyDialogOptionBoardId(boardId) {
|
||||
let ret = this.moveAndCopyDialogOption.boardId == boardId;
|
||||
/** Move Card Dialog */
|
||||
(class extends DialogWithBoardSwimlaneList {
|
||||
getDialogOptions() {
|
||||
const ret = ReactiveCache.getCurrentUser().getMoveAndCopyDialogOptions();
|
||||
return ret;
|
||||
},
|
||||
}
|
||||
setDone(boardId, swimlaneId, listId, options) {
|
||||
ReactiveCache.getCurrentUser().setMoveAndCopyDialogOption(this.currentBoardId, options);
|
||||
const card = this.data();
|
||||
const minOrder = card.getMinSort(listId, swimlaneId);
|
||||
card.move(boardId, swimlaneId, listId, minOrder - 1);
|
||||
}
|
||||
}).register('moveCardPopup');
|
||||
|
||||
/** returns if the swimlane id was the last confirmed one
|
||||
* @param swimlaneId check this swimlane id
|
||||
* @return if the swimlane id was the last confirmed one
|
||||
*/
|
||||
isMoveAndCopyDialogOptionSwimlaneId(swimlaneId) {
|
||||
let ret = this.moveAndCopyDialogOption.swimlaneId == swimlaneId;
|
||||
/** Copy Card Dialog */
|
||||
(class extends DialogWithBoardSwimlaneList {
|
||||
getDialogOptions() {
|
||||
const ret = ReactiveCache.getCurrentUser().getMoveAndCopyDialogOptions();
|
||||
return ret;
|
||||
},
|
||||
}
|
||||
setDone(boardId, swimlaneId, listId, options) {
|
||||
ReactiveCache.getCurrentUser().setMoveAndCopyDialogOption(this.currentBoardId, options);
|
||||
const card = this.data();
|
||||
|
||||
/** returns if the list id was the last confirmed one
|
||||
* @param listId check this list id
|
||||
* @return if the list id was the last confirmed one
|
||||
*/
|
||||
isMoveAndCopyDialogOptionListId(listId) {
|
||||
let ret = this.moveAndCopyDialogOption.listId == listId;
|
||||
return ret;
|
||||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Meteor.user().getTemplatesBoardId() },
|
||||
},
|
||||
{
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
swimlanes() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
return board.swimlanes();
|
||||
},
|
||||
|
||||
aBoardLists() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
return board.lists();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-done'() {
|
||||
const boardSelect = this.$('.js-select-boards')[0];
|
||||
const boardId = boardSelect.options[boardSelect.selectedIndex].value;
|
||||
|
||||
const listSelect = this.$('.js-select-lists')[0];
|
||||
const listId = listSelect.options[listSelect.selectedIndex].value;
|
||||
|
||||
const swimlaneSelect = this.$('.js-select-swimlanes')[0];
|
||||
const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
|
||||
|
||||
const options = {
|
||||
'boardId' : boardId,
|
||||
'swimlaneId' : swimlaneId,
|
||||
'listId' : listId,
|
||||
}
|
||||
Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
|
||||
},
|
||||
'change .js-select-boards'(event) {
|
||||
const boardId = $(event.currentTarget).val();
|
||||
this.selectedBoardId.set(boardId);
|
||||
subManager.subscribe('board', boardId, false);
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('boardsAndLists');
|
||||
|
||||
Template.copyCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Utils.getCurrentCard();
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
const listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
const boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
const textarea = $('#copy-card-title');
|
||||
// const textarea = $('#copy-card-title');
|
||||
const textarea = this.$('#copy-card-title');
|
||||
const title = textarea.val().trim();
|
||||
// insert new card to the bottom of new list
|
||||
card.sort = Lists.findOne(card.listId).cards().count();
|
||||
|
||||
if (title) {
|
||||
card.title = title;
|
||||
card.coverId = '';
|
||||
const _id = card.copy(boardId, swimlaneId, listId);
|
||||
// insert new card to the top of new list
|
||||
const newCardId = Meteor.call('copyCard', card._id, boardId, swimlaneId, listId, true, {title: title});
|
||||
|
||||
// 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);
|
||||
|
||||
Popup.back();
|
||||
Filter.addException(newCardId);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}).register('copyCardPopup');
|
||||
|
||||
Template.convertChecklistItemToCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Utils.getCurrentCard();
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
const listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
const boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
const textarea = $('#copy-card-title');
|
||||
/** Convert Checklist-Item to card dialog */
|
||||
(class extends DialogWithBoardSwimlaneList {
|
||||
getDialogOptions() {
|
||||
const ret = ReactiveCache.getCurrentUser().getMoveAndCopyDialogOptions();
|
||||
return ret;
|
||||
}
|
||||
setDone(boardId, swimlaneId, listId, options) {
|
||||
ReactiveCache.getCurrentUser().setMoveAndCopyDialogOption(this.currentBoardId, options);
|
||||
const card = this.data();
|
||||
|
||||
const textarea = this.$('#copy-card-title');
|
||||
const title = textarea.val().trim();
|
||||
|
||||
if (title) {
|
||||
@@ -981,68 +786,42 @@ Template.convertChecklistItemToCardPopup.events({
|
||||
swimlaneId: swimlaneId,
|
||||
sort: 0,
|
||||
});
|
||||
const card = ReactiveCache.getCard(_id);
|
||||
const minOrder = card.getMinSort();
|
||||
card.move(card.boardId, card.swimlaneId, card.listId, minOrder - 1);
|
||||
|
||||
Filter.addException(_id);
|
||||
|
||||
Popup.back();
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}).register('convertChecklistItemToCardPopup');
|
||||
|
||||
Template.copyChecklistToManyCardsPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Utils.getCurrentCard();
|
||||
const oldId = card._id;
|
||||
card._id = null;
|
||||
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(card.listId).cards().count();
|
||||
/** Copy many cards dialog */
|
||||
(class extends DialogWithBoardSwimlaneList {
|
||||
getDialogOptions() {
|
||||
const ret = ReactiveCache.getCurrentUser().getMoveAndCopyDialogOptions();
|
||||
return ret;
|
||||
}
|
||||
setDone(boardId, swimlaneId, listId, options) {
|
||||
ReactiveCache.getCurrentUser().setMoveAndCopyDialogOption(this.currentBoardId, options);
|
||||
const card = this.data();
|
||||
|
||||
const textarea = this.$('#copy-card-title');
|
||||
const title = textarea.val().trim();
|
||||
|
||||
if (title) {
|
||||
const titleList = JSON.parse(title);
|
||||
for (const obj of titleList) {
|
||||
const newCardId = Meteor.call('copyCard', card._id, boardId, swimlaneId, listId, false, {title: obj.title, description: obj.description});
|
||||
|
||||
if (titleEntry) {
|
||||
const titleList = JSON.parse(titleEntry);
|
||||
for (let i = 0; i < titleList.length; i++) {
|
||||
const obj = titleList[i];
|
||||
card.title = obj.title;
|
||||
card.description = obj.description;
|
||||
card.coverId = '';
|
||||
const _id = Cards.insert(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);
|
||||
|
||||
// copy checklists
|
||||
Checklists.find({ cardId: oldId }).forEach((ch) => {
|
||||
ch.copy(_id);
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
const cursor = Cards.find({ parentId: oldId });
|
||||
cursor.forEach(function () {
|
||||
'use strict';
|
||||
const subtask = arguments[0];
|
||||
subtask.parentId = _id;
|
||||
subtask._id = null;
|
||||
/* const newSubtaskId = */ Cards.insert(subtask);
|
||||
});
|
||||
|
||||
// copy card comments
|
||||
CardComments.find({ cardId: oldId }).forEach((cmt) => {
|
||||
cmt.copy(_id);
|
||||
});
|
||||
Filter.addException(newCardId);
|
||||
}
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}).register('copyManyCardsPopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
@@ -1095,27 +874,27 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find(
|
||||
const ret = ReactiveCache.getBoards(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {
|
||||
$ne: Meteor.user().getTemplatesBoardId(),
|
||||
},
|
||||
_id: { $ne: ReactiveCache.getCurrentUser().getTemplatesBoardId() },
|
||||
},
|
||||
{
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return ret;
|
||||
},
|
||||
|
||||
cards() {
|
||||
const currentId = Utils.getCurrentCardId();
|
||||
if (this.parentBoard.get()) {
|
||||
return Cards.find({
|
||||
const ret = ReactiveCache.getCards({
|
||||
boardId: this.parentBoard.get(),
|
||||
_id: { $ne: currentId },
|
||||
});
|
||||
return ret;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
@@ -1139,7 +918,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
setParentCardId(cardId) {
|
||||
if (cardId) {
|
||||
this.parentCard = Cards.findOne(cardId);
|
||||
this.parentCard = ReactiveCache.getCard(cardId);
|
||||
} else {
|
||||
this.parentCard = null;
|
||||
}
|
||||
@@ -1158,7 +937,7 @@ BlazeComponent.extendComponent({
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function () {
|
||||
Popup.close();
|
||||
// verify that there are no linked cards
|
||||
if (Cards.find({ linkedId: this._id }).count() === 0) {
|
||||
if (ReactiveCache.getCards({ linkedId: this._id }).length === 0) {
|
||||
Cards.remove(this._id);
|
||||
} else {
|
||||
// TODO: Maybe later we can list where the linked cards are.
|
||||
@@ -1695,10 +1474,29 @@ BlazeComponent.extendComponent({
|
||||
EscapeActions.register(
|
||||
'detailsPane',
|
||||
() => {
|
||||
// if card description diverges from database due to editing
|
||||
// ask user whether changes should be applied
|
||||
if (ReactiveCache.getCurrentUser()) {
|
||||
if (ReactiveCache.getCurrentUser().profile.rescueCardDescription == true) {
|
||||
currentDescription = document.getElementsByClassName("editor js-new-description-input").item(0)
|
||||
if (currentDescription?.value && !(currentDescription.value === Utils.getCurrentCard().getDescription())) {
|
||||
if (confirm(TAPi18n.__('rescue-card-description-dialogue'))) {
|
||||
Utils.getCurrentCard().setDescription(document.getElementsByClassName("editor js-new-description-input").item(0).value);
|
||||
// Save it!
|
||||
console.log(document.getElementsByClassName("editor js-new-description-input").item(0).value);
|
||||
console.log("current description", Utils.getCurrentCard().getDescription());
|
||||
} else {
|
||||
// Do nothing!
|
||||
console.log('Description changes were not saved to the database.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Session.get('cardDetailsIsDragging')) {
|
||||
// Reset dragging status as the mouse landed outside the cardDetails template area and this will prevent a mousedown event from firing
|
||||
Session.set('cardDetailsIsDragging', false);
|
||||
Session.set('cardDetailsIsMouseDown', false);
|
||||
|
||||
} else {
|
||||
// Prevent close card when the user is selecting text and moves the mouse cursor outside the card detail area
|
||||
Utils.goBoardId(Session.get('currentBoard'));
|
||||
@@ -1713,19 +1511,8 @@ EscapeActions.register(
|
||||
);
|
||||
|
||||
Template.cardAssigneesPopup.onCreated(function () {
|
||||
let currBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let currBoard = Utils.getCurrentBoard();
|
||||
let members = currBoard.activeMembers();
|
||||
|
||||
// let query = {
|
||||
// "teams.teamId": { $in: currBoard.teams.map(t => t.teamId) },
|
||||
// };
|
||||
|
||||
// let boardTeamUsers = Users.find(query, {
|
||||
// sort: { sort: 1 },
|
||||
// });
|
||||
|
||||
// members = currBoard.activeMembers2(members, boardTeamUsers);
|
||||
|
||||
this.members = new ReactiveVar(members);
|
||||
});
|
||||
|
||||
@@ -1751,23 +1538,17 @@ Template.cardAssigneesPopup.helpers({
|
||||
},
|
||||
|
||||
members() {
|
||||
return Template.instance().members.get();
|
||||
return _.sortBy(Template.instance().members.get(),'fullname');
|
||||
},
|
||||
|
||||
user() {
|
||||
return Users.findOne(this.userId);
|
||||
return ReactiveCache.getUser(this.userId);
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardAssigneePopup.helpers({
|
||||
userData() {
|
||||
// We need to handle a special case for the search results provided by the
|
||||
// `matteodem:easy-search` package. Since these results gets published in a
|
||||
// separate collection, and not in the standard Meteor.Users collection as
|
||||
// expected, we use a component parameter ("property") to distinguish the
|
||||
// two cases.
|
||||
const userCollection = this.esSearch ? ESSearchResults : Users;
|
||||
return userCollection.findOne(this.userId, {
|
||||
return ReactiveCache.getUser(this.userId, {
|
||||
fields: {
|
||||
profile: 1,
|
||||
username: 1,
|
||||
@@ -1776,20 +1557,10 @@ Template.cardAssigneePopup.helpers({
|
||||
},
|
||||
|
||||
memberType() {
|
||||
const user = Users.findOne(this.userId);
|
||||
const user = ReactiveCache.getUser(this.userId);
|
||||
return user && user.isBoardAdmin() ? 'admin' : 'normal';
|
||||
},
|
||||
|
||||
presenceStatusClassName() {
|
||||
const user = Users.findOne(this.userId);
|
||||
const userPresence = presences.findOne({ userId: this.userId });
|
||||
if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
|
||||
else if (!userPresence) return 'disconnected';
|
||||
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
|
||||
return 'active';
|
||||
else return 'idle';
|
||||
},
|
||||
|
||||
isCardAssignee() {
|
||||
const card = Template.parentData();
|
||||
const cardAssignees = card.getAssignees();
|
||||
@@ -1798,13 +1569,13 @@ Template.cardAssigneePopup.helpers({
|
||||
},
|
||||
|
||||
user() {
|
||||
return Users.findOne(this.userId);
|
||||
return ReactiveCache.getUser(this.userId);
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardAssigneePopup.events({
|
||||
'click .js-remove-assignee'() {
|
||||
Cards.findOne(this.cardId).unassignAssignee(this.userId);
|
||||
ReactiveCache.getCard(this.cardId).unassignAssignee(this.userId);
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-edit-profile': Popup.open('editProfile'),
|
||||
|
||||
@@ -1,561 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
// Assignee, code copied from wekan/client/users/userAvatar.styl
|
||||
|
||||
avatar-radius = 50%
|
||||
|
||||
.assignee
|
||||
border-radius: 3px
|
||||
display: block
|
||||
position: relative
|
||||
float: left
|
||||
height: 30px
|
||||
width: @height
|
||||
margin: 0 4px 4px 0
|
||||
cursor: pointer
|
||||
user-select: none
|
||||
z-index: 1
|
||||
text-decoration: none
|
||||
border-radius: avatar-radius
|
||||
|
||||
.avatar
|
||||
overflow: hidden
|
||||
border-radius: avatar-radius
|
||||
|
||||
&.avatar-assignee-initials
|
||||
height: 70%
|
||||
width: @height
|
||||
padding: 15%
|
||||
background-color: #dbdbdb
|
||||
color: #444444
|
||||
position: absolute
|
||||
|
||||
&.avatar-image
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
height: 100%
|
||||
width: @height
|
||||
|
||||
.assignee-presence-status
|
||||
background-color: #b3b3b3
|
||||
border: 1px solid #fff
|
||||
border-radius: 50%
|
||||
height: 7px
|
||||
width: @height
|
||||
position: absolute
|
||||
right: -1px
|
||||
bottom: -1px
|
||||
border: 1px solid white
|
||||
z-index: 15
|
||||
|
||||
&.active
|
||||
background: #64c464
|
||||
border-color: #daf1da
|
||||
|
||||
&.idle
|
||||
background: #e4e467
|
||||
border-color: #f7f7d4
|
||||
|
||||
&.disconnected
|
||||
background: #bdbdbd
|
||||
border-color: #ededed
|
||||
|
||||
&.pending
|
||||
background: #e44242
|
||||
border-color: #f1dada
|
||||
|
||||
|
||||
|
||||
&.add-assignee
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
box-shadow: 0 0 0 2px darken(white, 25%) inset
|
||||
|
||||
&:hover, &.is-active
|
||||
box-shadow: 0 0 0 2px darken(white, 60%) inset
|
||||
|
||||
// Other card details
|
||||
.copied-tooltip
|
||||
display: none
|
||||
padding: 0px 10px;
|
||||
background-color: #000000df;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
|
||||
.card-details
|
||||
padding: 0
|
||||
flex-shrink: 0
|
||||
flex-basis: 600px
|
||||
will-change: flex-basis
|
||||
overflow-y: scroll
|
||||
overflow-x: hidden
|
||||
background: darken(white, 3%)
|
||||
border-radius: bottom 3px
|
||||
z-index: 20
|
||||
animation: flexGrowIn 0.1s
|
||||
box-shadow: 0 0 7px 0 darken(white, 30%)
|
||||
transition: flex-basis 0.1s
|
||||
box-sizing: border-box
|
||||
|
||||
.mCustomScrollBox
|
||||
padding-left: 0
|
||||
|
||||
.card-details-canvas
|
||||
width: auto
|
||||
padding: 0 20px
|
||||
|
||||
.card-details-header
|
||||
margin: 0 -20px 5px
|
||||
padding: 7px 20px
|
||||
background: darken(white, 7%)
|
||||
border-bottom: 1px solid darken(white, 14%)
|
||||
|
||||
.card-number {
|
||||
color: darken(white, 30%);
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.close-card-details,
|
||||
.maximize-card-details,
|
||||
.minimize-card-details,
|
||||
.card-details-menu,
|
||||
.card-copy-button,
|
||||
.card-copy-mobile-button,
|
||||
.close-card-details-mobile-web,
|
||||
.card-details-menu-mobile-web,
|
||||
.copied-tooltip
|
||||
float: right
|
||||
|
||||
.close-card-details,
|
||||
.maximize-card-details,
|
||||
.minimize-card-details
|
||||
font-size: 24px
|
||||
padding: 5px 10px 5px 10px
|
||||
margin-right: -8px
|
||||
|
||||
.close-card-details-mobile-web
|
||||
font-size: 24px
|
||||
padding: 5px
|
||||
margin-right: 40px
|
||||
|
||||
.card-copy-button
|
||||
font-size: 17px
|
||||
padding: 10px
|
||||
margin-right: 10px
|
||||
|
||||
.card-copy-mobile-button
|
||||
font-size: 17px
|
||||
padding: 10px
|
||||
margin-right: 10px
|
||||
|
||||
.card-details-menu
|
||||
font-size: 17px
|
||||
padding: 10px
|
||||
|
||||
.card-details-menu-mobile-web
|
||||
font-size: 17px
|
||||
padding: 10px
|
||||
margin-right: 30px
|
||||
|
||||
.card-details-watch
|
||||
font-size: 17px
|
||||
padding-left: 7px
|
||||
color: #a6a6a6
|
||||
|
||||
.card-details-title
|
||||
font-weight: bold
|
||||
font-size: 1.33em
|
||||
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
|
||||
margin-bottom: 10px
|
||||
|
||||
.copied-tooltip
|
||||
padding: 0px 10px
|
||||
|
||||
.card-details-list
|
||||
font-size: 0.85em
|
||||
margin-bottom: 3px
|
||||
|
||||
a.card-details-list-title
|
||||
font-weight: bold
|
||||
|
||||
&.is-editable
|
||||
display: inline-block
|
||||
background: darken(white, 10%)
|
||||
border-radius: 3px
|
||||
padding: 0px 5px
|
||||
|
||||
.copied-tooltip
|
||||
margin-right: 10px
|
||||
padding: 10px;
|
||||
|
||||
.card-description textarea
|
||||
min-height: 100px
|
||||
|
||||
.card-details-items
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
margin: 15px 0
|
||||
|
||||
.card-details-item
|
||||
margin-right: 0.5em
|
||||
&:last-child
|
||||
margin-right: 0
|
||||
&.card-details-item-labels
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 95%
|
||||
flex-grow: 1
|
||||
&.card-details-item-members,
|
||||
&.card-details-item-assignees,
|
||||
&.card-details-item-customfield,
|
||||
&.card-details-item-name
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 36%
|
||||
flex-grow: 1
|
||||
&.card-details-item-creator,
|
||||
&.card-details-item-received,
|
||||
&.card-details-item-start,
|
||||
&.card-details-item-due,
|
||||
&.card-details-item-end
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 28%
|
||||
flex-grow: 1
|
||||
&.custom-fields
|
||||
padding-left: 10px
|
||||
|
||||
|
||||
.card-details-item-title
|
||||
font-size: 16px
|
||||
font-weight: bold
|
||||
color: #4d4d4d
|
||||
|
||||
.activities
|
||||
padding-top: 10px
|
||||
|
||||
@media screen and (min-width: 801px)
|
||||
.card-details-maximized
|
||||
padding: 0
|
||||
flex-shrink: 0
|
||||
flex-basis: calc(100% - 20px)
|
||||
will-change: flex-basis
|
||||
overflow-y: scroll
|
||||
overflow-x: scroll
|
||||
background: darken(white, 3%)
|
||||
border-radius: bottom 3px
|
||||
z-index: 100
|
||||
animation: flexGrowIn 0.1s
|
||||
box-shadow: 0 0 7px 0 darken(white, 30%)
|
||||
transition: flex-basis 0.1s
|
||||
box-sizing: border-box
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
height: calc(100% - 20px)
|
||||
width: calc(100% - 20px)
|
||||
float: left
|
||||
|
||||
.card-details-left
|
||||
float: left
|
||||
top: 60px
|
||||
left: 20px
|
||||
width: 47%
|
||||
|
||||
.card-details-right
|
||||
position: absolute
|
||||
float: right
|
||||
top: 20px
|
||||
left: 50%
|
||||
|
||||
.card-details-header
|
||||
width: 47%
|
||||
|
||||
input[type="text"].attachment-add-link-input
|
||||
float: left
|
||||
margin: 0 0 8px
|
||||
width: 80%
|
||||
|
||||
input[type="submit"].attachment-add-link-submit
|
||||
float: left
|
||||
margin: 0 0 8px 4px
|
||||
padding: 6px 12px
|
||||
width: 18%
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.card-details
|
||||
width: calc(100% - 1px)
|
||||
padding: 0px 20px 0px 20px
|
||||
margin: 0px
|
||||
transition: none
|
||||
overflow-y: revert
|
||||
overflow-x: revert
|
||||
|
||||
.card-details-canvas
|
||||
width: 100%
|
||||
padding-left: 0px
|
||||
|
||||
.card-details-header
|
||||
.close-card-details
|
||||
margin-right: 0px
|
||||
|
||||
.card-details-menu
|
||||
margin-right: 40px
|
||||
|
||||
.maximize-card-details
|
||||
margin-right: 40px
|
||||
|
||||
.minimize-card-details
|
||||
margin-right: 40px
|
||||
|
||||
.card-details-popup
|
||||
padding: 0px 10px
|
||||
|
||||
.pop-over > .content-wrapper > .popup-container-depth-0
|
||||
width: 100%
|
||||
|
||||
& > .content
|
||||
width: calc(100% - 10px)
|
||||
|
||||
& > .content > .card-details-popup hr
|
||||
margin: 15px 0px
|
||||
|
||||
.card-details-header
|
||||
margin: 0
|
||||
|
||||
card-details-color(background, color...)
|
||||
background: background !important
|
||||
if color
|
||||
color: color !important //overwrite text for better visibility
|
||||
|
||||
.card-details-white
|
||||
card-details-color(unset, #000) //Black text for better visibility
|
||||
border: 1px solid #eee
|
||||
|
||||
.card-details-green
|
||||
card-details-color(#3cb500, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-yellow
|
||||
card-details-color(#fad900, #000) //Black text for better visibility
|
||||
|
||||
.card-details-orange
|
||||
card-details-color(#ff9f19, #000) //Black text for better visibility
|
||||
|
||||
.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, #000) //Black text for better visibility
|
||||
|
||||
.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, #000) //Black text for better visibility
|
||||
|
||||
.card-details-silver
|
||||
card-details-color(#c0c0c0, #000) //Black text for better visibility
|
||||
|
||||
.card-details-peachpuff
|
||||
card-details-color(#ffdab9, #000) //Black text for better visibility
|
||||
|
||||
.card-details-crimson
|
||||
card-details-color(#dc143c, #ffffff) //White text for better visibility
|
||||
|
||||
.card-details-plum
|
||||
card-details-color(#dda0dd, #000) //Black text for better visibility
|
||||
|
||||
.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, #000) //Black text for better visibility
|
||||
|
||||
.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, #000) //Black text for better visibility
|
||||
|
||||
.card-details-mistyrose
|
||||
card-details-color(#ffe4e1, #000) //Black text for better visibility
|
||||
|
||||
.card-details-indigo
|
||||
card-details-color(#4b0082, #ffffff) //White text for better visibility
|
||||
|
||||
.voted
|
||||
opacity: .7
|
||||
.vote-title
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
|
||||
.js-edit-date
|
||||
align-self: baseline
|
||||
margin-left: 5px
|
||||
|
||||
.vote-result
|
||||
display: flex
|
||||
.js-show-positive-votes
|
||||
cursor: pointer
|
||||
|
||||
.poker-voted
|
||||
opacity: .7
|
||||
|
||||
.poker-title
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
|
||||
.js-edit-date
|
||||
align-self: baseline
|
||||
margin-left: 5px
|
||||
|
||||
.poker-result
|
||||
display: flex
|
||||
flex-flow: row wrap
|
||||
.js-show-positive-poker-votes
|
||||
cursor: pointer
|
||||
|
||||
.poker-deck
|
||||
display: grid
|
||||
flex-direction: column
|
||||
text-align: center
|
||||
|
||||
.poker-card-result
|
||||
width: 32px
|
||||
font-size: 1em
|
||||
font-weight: bold
|
||||
padding: 4px 2px 4px 2px
|
||||
cursor: default
|
||||
|
||||
.winner
|
||||
font-weight: bold
|
||||
outline: #2d2d2d solid 2px
|
||||
|
||||
.loser
|
||||
opacity: .5
|
||||
|
||||
.responsive-table
|
||||
overflow-x: auto
|
||||
|
||||
.poker-table
|
||||
display: table
|
||||
width: 100%
|
||||
padding-top: 10px
|
||||
|
||||
.poker-table-row
|
||||
display: table-row
|
||||
|
||||
.poker-table-heading
|
||||
background-color: #EEE
|
||||
display: table-header-group
|
||||
|
||||
.poker-table-cell
|
||||
display: table-cell
|
||||
padding: 0 0 5px 2px
|
||||
border-bottom: 1px solid #d2d0d0
|
||||
text-align: center
|
||||
min-width: 45px
|
||||
|
||||
.poker-table-cell-who
|
||||
width: 150px
|
||||
vertical-align: middle
|
||||
|
||||
.poker-table-heading-left,
|
||||
.poker-table-heading-right
|
||||
display: table-header-group
|
||||
font-weight: bold
|
||||
border-top: 1px solid #808080
|
||||
|
||||
@media (max-width: 400px)
|
||||
.poker-table-heading-right
|
||||
display: none
|
||||
|
||||
.poker-table-body
|
||||
display: table-row-group
|
||||
|
||||
.poker-table-side-left,
|
||||
.poker-table-side-right
|
||||
display: inline-block
|
||||
|
||||
.poker-table-side-right
|
||||
padding-left: 10px
|
||||
|
||||
@media (max-width: 400px)
|
||||
.poker-table-side-right
|
||||
padding-left: 0px
|
||||
|
||||
.estimation-add
|
||||
display: block
|
||||
overflow: auto
|
||||
margin-top: 15px
|
||||
margin-bottom: 5px
|
||||
input
|
||||
display: inline-block
|
||||
float: right
|
||||
margin: auto
|
||||
margin-right: 10px
|
||||
width: 100px
|
||||
button
|
||||
display: inline-block
|
||||
float: right
|
||||
margin: auto
|
||||
|
||||
.poker-card
|
||||
width:48px
|
||||
height:72px
|
||||
float:left
|
||||
background:#fff
|
||||
border-radius:5px
|
||||
display:table
|
||||
box-sizing:border-box
|
||||
padding:5px
|
||||
margin:3px
|
||||
font-size:20px
|
||||
font-weight: bold
|
||||
text-shadow: #2d2d2d 1px 1px 0
|
||||
box-shadow:0 0 5px #aaaaaa
|
||||
text-align:center
|
||||
position:relative
|
||||
cursor: pointer
|
||||
|
||||
.inner
|
||||
display:table-cell
|
||||
vertical-align:middle
|
||||
border-radius:5px
|
||||
overflow:hidden
|
||||
background-color: #cecece
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
.card-time {
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
padding: 1px 3px;
|
||||
color: #fff;
|
||||
background-color: #dbdbdb;
|
||||
}
|
||||
.card-time:hover,
|
||||
.card-time.is-active {
|
||||
background-color: #b3b3b3;
|
||||
}
|
||||
.card-time time::before {
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
font-size: inherit;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: "\f017";
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
template() {
|
||||
return 'editCardSpentTime';
|
||||
@@ -81,13 +83,3 @@ BlazeComponent.extendComponent({
|
||||
];
|
||||
},
|
||||
}).register('cardSpentTime');
|
||||
|
||||
Template.timeBadge.helpers({
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly()
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
.card-time
|
||||
display: block
|
||||
border-radius: 4px
|
||||
padding: 1px 3px
|
||||
color: #fff
|
||||
|
||||
background-color: #dbdbdb
|
||||
&:hover, &.is-active
|
||||
background-color: #b3b3b3
|
||||
|
||||
time
|
||||
&::before
|
||||
font: normal normal normal 14px/1 FontAwesome
|
||||
font-size: inherit
|
||||
-webkit-font-smoothing: antialiased
|
||||
content: "\f017" // clock symbol
|
||||
margin-right: 0.3em
|
||||
@@ -0,0 +1,204 @@
|
||||
.js-add-checklist {
|
||||
color: #8c8c8c;
|
||||
}
|
||||
textarea.js-add-checklist-item,
|
||||
textarea.js-edit-checklist-item {
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
resize: none;
|
||||
height: 34px;
|
||||
}
|
||||
.card-details .text-show-at-minicard {
|
||||
width: 350px;
|
||||
text-align: left;
|
||||
}
|
||||
.minicard .text-show-at-minicard {
|
||||
display: none;
|
||||
}
|
||||
.text-some-space {
|
||||
width: 20px;
|
||||
}
|
||||
.text-hide-checked-items {
|
||||
width: 400px;
|
||||
text-align: left;
|
||||
}
|
||||
.delete-text,
|
||||
.js-delete-checklist-item,
|
||||
.js-convert-checklist-item-to-card {
|
||||
color: #8c8c8c;
|
||||
text-decoration: underline;
|
||||
word-wrap: break-word;
|
||||
float: right;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.delete-text:hover,
|
||||
.js-delete-checklist-item:hover,
|
||||
.js-convert-checklist-item-to-card:hover {
|
||||
color: inherit;
|
||||
}
|
||||
.checklists-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
|
||||
.checklist-progress-bar-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.checklist-progress-bar-container .checklist-progress-text {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.checklist-progress-bar-container .checklist-progress-bar {
|
||||
width: 80%;
|
||||
height: 10px;
|
||||
}
|
||||
.checklist-progress-bar-container .checklist-progress-bar .checklist-progress {
|
||||
color: #fff !important;
|
||||
background-color: #2196f3 !important;
|
||||
padding: 0.01em 16px;
|
||||
border-radius: 16px;
|
||||
height: 100%;
|
||||
}
|
||||
.checklist-title .checkbox {
|
||||
float: left;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.checklist-title .title {
|
||||
font-size: 18px;
|
||||
line-height: 25px;
|
||||
}
|
||||
.checklist-title .checklist-stat {
|
||||
margin: 0 0.5em;
|
||||
float: right;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.checklist-title .checklist-stat.is-finished {
|
||||
color: #3cb500;
|
||||
}
|
||||
.checklist-title span.fa.checklist-handle {
|
||||
padding-right: 20px;
|
||||
padding-top: 3px;
|
||||
float: left;
|
||||
}
|
||||
#card-details-overlay {
|
||||
top: 0;
|
||||
bottom: -600px;
|
||||
right: 0;
|
||||
}
|
||||
.checklist {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
.checklist.placeholder {
|
||||
background: #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.checklist.ui-sortable-helper {
|
||||
box-shadow: -2px 2px 8px rgba(0,0,0,0.3), 0 0 1px rgba(0,0,0,0.5);
|
||||
transform: rotate(4deg);
|
||||
cursor: grabbing;
|
||||
}
|
||||
.checklist-item {
|
||||
margin: 0 0 0 0.1em;
|
||||
line-height: 18px;
|
||||
font-size: 1.1em;
|
||||
margin-top: 3px;
|
||||
display: flex;
|
||||
background: #f7f7f7;
|
||||
opacity: 1;
|
||||
transition: height 0ms 400ms, opacity 400ms 0ms;
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
.checklist-item.is-checked.invisible {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
transition: height 0ms 0ms, opacity 600ms 0ms;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.checklist-item.placeholder {
|
||||
background: #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.checklist-item.ui-sortable-helper {
|
||||
box-shadow: -2px 2px 8px rgba(0,0,0,0.3), 0 0 1px rgba(0,0,0,0.5);
|
||||
transform: rotate(4deg);
|
||||
cursor: grabbing;
|
||||
}
|
||||
.checklist-item:hover {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
.checklist-item .check-box-container {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.checklist-item .check-box {
|
||||
margin: 0.1em 0 0 0;
|
||||
}
|
||||
.checklist-item .check-box.is-checked {
|
||||
border-bottom: 2px solid #3cb500;
|
||||
border-right: 2px solid #3cb500;
|
||||
}
|
||||
.checklist-item .item-title {
|
||||
flex: 1;
|
||||
}
|
||||
.checklist-item .item-title.is-checked {
|
||||
color: #8c8c8c;
|
||||
font-style: italic;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.checklist-item .item-title .viewer p {
|
||||
margin-bottom: 2px;
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
max-width: 420px;
|
||||
}
|
||||
.checklist-item span.fa.checklistitem-handle {
|
||||
padding-top: 2px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.js-delete-checklist-item,
|
||||
.js-convert-checklist-item-to-card {
|
||||
margin: 0 0 0.5em 1.33em;
|
||||
padding: 12px 0 0 0;
|
||||
}
|
||||
.add-checklist-item {
|
||||
margin: 0.2em 0 0.5em 1.33em;
|
||||
}
|
||||
.add-checklist-item.js-open-inlined-form,
|
||||
.add-checklist.js-open-inlined-form {
|
||||
display: block;
|
||||
width: 50%;
|
||||
}
|
||||
.add-checklist-item.js-open-inlined-form:hover,
|
||||
.add-checklist.js-open-inlined-form:hover {
|
||||
background: #dbdbdb;
|
||||
color: #222;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||
}
|
||||
.add-checklist-top {
|
||||
/* more space to checklists title */
|
||||
padding-left: 20px;
|
||||
/* + is easier clickable */
|
||||
padding-right: 20px;
|
||||
}
|
||||
.add-checklist-top.js-open-inlined-form:hover {
|
||||
background: #dbdbdb;
|
||||
color: #222;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2);
|
||||
}
|
||||
.card-details-item-title {
|
||||
/* max width for adding checklist at top */
|
||||
width: 100%;
|
||||
}
|
||||
.checklist-details-menu {
|
||||
float: right;
|
||||
padding: 6px 10px 6px 10px;
|
||||
}
|
||||
.edit-controls label.toggle-label {
|
||||
margin-left: 2px;
|
||||
}
|
||||
@@ -3,18 +3,16 @@ template(name="checklists")
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-check
|
||||
| {{_ 'checklists'}}
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch(title="{{_ 'hide-checked-items'}}")
|
||||
//span.toggle-switch-title
|
||||
if hideCheckedItems
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton" checked="checked")
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId position="top")
|
||||
+addChecklistItemForm
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton")
|
||||
label.toggle-label(for="toggleHideCheckedItemsButton")
|
||||
a.add-checklist-top.js-open-inlined-form(title="{{_ 'add-checklist'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
.card-checklist-items
|
||||
each checklist in checklists
|
||||
+checklistDetail(checklist = checklist)
|
||||
+checklistDetail(checklist=checklist)
|
||||
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
|
||||
@@ -34,20 +32,21 @@ template(name="checklistDetail")
|
||||
a.fa.fa-navicon.checklist-details-menu.js-open-checklist-details-menu(title="{{_ 'checklistActionsPopup-title'}}")
|
||||
|
||||
if canModifyCard
|
||||
h2.title.js-open-inlined-form.is-editable
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
h4.title.js-open-inlined-form.is-editable
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
span.fa.checklist-handle(class="fa-arrows" title="{{_ 'dragChecklist'}}")
|
||||
+viewer
|
||||
= checklist.title
|
||||
else
|
||||
h2.title
|
||||
h4.title
|
||||
+viewer
|
||||
= checklist.title
|
||||
|
||||
.checklist-progress-bar-container
|
||||
.checklist-progress-text {{#if $gt finishedPercent 0}}{{finishedPercent}}%{{/if}}
|
||||
.checklist-progress-bar
|
||||
.checklist-progress(style="width:{{finishedPercent}}%;{{#if $eq finishedPercent 0}}display:none{{/if}}")
|
||||
if $gt finishedPercent 0
|
||||
.checklist-progress-bar-container
|
||||
.checklist-progress-text {{finishedPercent}}%
|
||||
.checklist-progress-bar
|
||||
.checklist-progress(style="width:{{finishedPercent}}%")
|
||||
+checklistItems(checklist = checklist)
|
||||
|
||||
template(name="checklistDeletePopup")
|
||||
@@ -60,7 +59,7 @@ template(name="addChecklistItemForm")
|
||||
textarea.js-add-checklist-item(rows='1' autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-add-checklist-item-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close-add-checklist-item'}}")
|
||||
if showNewlineBecomesNewChecklistItem
|
||||
.material-toggle-switch(title="{{_ 'newlineBecomesNewChecklistItem'}}")
|
||||
input.toggle-switch(type="checkbox" id="toggleNewlineBecomesNewChecklistItem")
|
||||
@@ -76,7 +75,7 @@ template(name="editChecklistItemForm")
|
||||
= checklist.title
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-checklist-item-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
a.fa.fa-times-thin.js-close-inlined-form(title="{{_ 'close-edit-checklist-item'}}")
|
||||
span(title=createdAt) {{ moment createdAt }}
|
||||
if canModifyCard
|
||||
a.js-delete-checklist-item {{_ "delete"}}...
|
||||
@@ -85,6 +84,13 @@ template(name="editChecklistItemForm")
|
||||
| {{_ 'convertChecklistItemToCardPopup-title'}}
|
||||
|
||||
template(name="checklistItems")
|
||||
if checklist.items.length
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist position="top")
|
||||
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=true)
|
||||
else
|
||||
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
|
||||
i.fa.fa-plus
|
||||
.checklist-items.js-checklist-items
|
||||
each item in checklist.items
|
||||
+inlinedForm(classNames="js-edit-checklist-item" item = item checklist = checklist)
|
||||
@@ -99,11 +105,12 @@ template(name="checklistItems")
|
||||
i.fa.fa-plus
|
||||
|
||||
template(name='checklistItemDetail')
|
||||
.js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if hideCheckedItems}} invisible{{/if}}{{/if}}")
|
||||
.js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if hideCheckedItems}} invisible{{/if}}{{/if}}"
|
||||
role="checkbox" aria-checked="{{#if item.isFinished }}true{{else}}false{{/if}}" tabindex="0")
|
||||
if canModifyCard
|
||||
.check-box-container
|
||||
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
span.fa.checklistitem-handle(class="fa-arrows" title="{{_ 'dragChecklistItem'}}")
|
||||
.item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
+viewer
|
||||
@@ -114,36 +121,28 @@ template(name='checklistItemDetail')
|
||||
+viewer
|
||||
= item.title
|
||||
|
||||
template(name="convertChecklistItemToCardPopup")
|
||||
label(for='convert-checklist-item-to-card-title') {{_ 'title'}}:
|
||||
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
|
||||
= item.title
|
||||
+boardsSwimlanesAndLists
|
||||
|
||||
template(name="boardsSwimlanesAndLists")
|
||||
unless currentUser.isWorker
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards(autofocus)
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
option(value="{{_id}}") {{title}}
|
||||
|
||||
label {{_ 'swimlanes'}}:
|
||||
select.js-select-swimlanes
|
||||
each swimlanes
|
||||
option(value="{{_id}}") {{title}}
|
||||
|
||||
label {{_ 'lists'}}:
|
||||
select.js-select-lists
|
||||
each aBoardLists
|
||||
option(value="{{_id}}") {{title}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
|
||||
template(name="checklistActionsPopup")
|
||||
if currentUser.isBoardMember
|
||||
span.text-show-at-minicard
|
||||
| {{_ 'show-at-minicard'}}
|
||||
.material-toggle-switch(title="{{_ 'show-checklist-at-minicard'}}")
|
||||
if showAtMinicard
|
||||
input.toggle-switch(type="checkbox" id="toggleShowChecklistAtMinicardButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleShowChecklistAtMinicardButton")
|
||||
label.toggle-label(for="toggleShowChecklistAtMinicardButton")
|
||||
hr
|
||||
span.text-hide-checked-items
|
||||
| {{_ 'hide-checked-items'}}
|
||||
.material-toggle-switch(title="{{_ 'hide-checked-items'}}")
|
||||
//span.toggle-switch-title
|
||||
//.check-square-icon.i.fa.fa-check-square-o
|
||||
if hideCheckedItems
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton")
|
||||
label.toggle-label(for="toggleHideCheckedItemsButton")
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-delete-checklist.delete-checklist
|
||||
@@ -152,31 +151,37 @@ template(name="checklistActionsPopup")
|
||||
a.js-move-checklist.move-checklist
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ "moveChecklist"}} ...
|
||||
a.js-copy-checklist.copy-checklist
|
||||
i.fa.fa-copy
|
||||
| {{_ "copyChecklist"}} ...
|
||||
|
||||
template(name="copyChecklistPopup")
|
||||
+copyAndMoveChecklist
|
||||
|
||||
template(name="moveChecklistPopup")
|
||||
+copyAndMoveChecklist
|
||||
|
||||
template(name="copyAndMoveChecklist")
|
||||
unless currentUser.isWorker
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards(autofocus)
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
option(value="{{_id}}" selected="{{#if isMoveChecklistDialogOptionBoardId _id}}selected{{/if}}") {{title}}
|
||||
option(value="{{_id}}" selected="{{#if isDialogOptionBoardId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'swimlanes'}}:
|
||||
select.js-select-swimlanes
|
||||
each swimlanes
|
||||
option(value="{{_id}}" selected="{{#if isMoveChecklistDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
|
||||
option(value="{{_id}}" selected="{{#if isDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'lists'}}:
|
||||
select.js-select-lists
|
||||
each lists
|
||||
option(value="{{_id}}" selected="{{#if isMoveChecklistDialogOptionListId _id}}selected{{/if}}") {{title}}
|
||||
option(value="{{_id}}" selected="{{#if isDialogOptionListId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'cards'}}:
|
||||
select.js-select-cards
|
||||
each cards
|
||||
option(value="{{_id}}" selected="{{#if isMoveChecklistDialogOptionCardId _id}}selected{{/if}}") {{title}}
|
||||
option(value="{{_id}}" selected="{{#if isDialogOptionCardId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
@@ -1,5 +1,8 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
import Cards from '/models/cards';
|
||||
import Boards from '/models/boards';
|
||||
import { DialogWithBoardSwimlaneListCard } from '/client/lib/dialogWithBoardSwimlaneListCard';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndexData, capitalize } = Utils;
|
||||
@@ -47,12 +50,12 @@ BlazeComponent.extendComponent({
|
||||
const self = this;
|
||||
self.itemsDom = this.$('.js-checklist-items');
|
||||
initSorting(self.itemsDom);
|
||||
self.itemsDom.mousedown(function(evt) {
|
||||
self.itemsDom.mousedown(function (evt) {
|
||||
evt.stopPropagation();
|
||||
});
|
||||
|
||||
function userIsMember() {
|
||||
return Meteor.user() && Meteor.user().isBoardMember();
|
||||
return ReactiveCache.getCurrentUser()?.isBoardMember();
|
||||
}
|
||||
|
||||
// Disable sorting if the current user is not a board member
|
||||
@@ -60,7 +63,7 @@ BlazeComponent.extendComponent({
|
||||
const $itemsDom = $(self.itemsDom);
|
||||
if ($itemsDom.data('uiSortable') || $itemsDom.data('sortable')) {
|
||||
$(self.itemsDom).sortable('option', 'disabled', !userIsMember());
|
||||
if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
|
||||
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
|
||||
$(self.itemsDom).sortable({
|
||||
handle: 'span.fa.checklistitem-handle',
|
||||
});
|
||||
@@ -69,15 +72,6 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
/** returns the finished percent of the checklist */
|
||||
finishedPercent() {
|
||||
const ret = this.data().checklist.finishedPercent();
|
||||
@@ -91,20 +85,32 @@ BlazeComponent.extendComponent({
|
||||
const textarea = this.find('textarea.js-add-checklist-item');
|
||||
const title = textarea.value.trim();
|
||||
let cardId = this.currentData().cardId;
|
||||
const card = Cards.findOne(cardId);
|
||||
const card = ReactiveCache.getCard(cardId);
|
||||
//if (card.isLinked()) cardId = card.linkedId;
|
||||
if (card.isLinkedCard()) cardId = card.linkedId;
|
||||
if (card.isLinkedCard()) {
|
||||
cardId = card.linkedId;
|
||||
}
|
||||
|
||||
let sortIndex;
|
||||
let checklistItemIndex;
|
||||
if (this.currentData().position === 'top') {
|
||||
sortIndex = Utils.calculateIndexData(null, card.firstChecklist()).base;
|
||||
checklistItemIndex = 0;
|
||||
} else {
|
||||
sortIndex = Utils.calculateIndexData(card.lastChecklist(), null).base;
|
||||
checklistItemIndex = -1;
|
||||
}
|
||||
|
||||
if (title) {
|
||||
Checklists.insert({
|
||||
cardId,
|
||||
title,
|
||||
sort: card.checklists().count(),
|
||||
sort: sortIndex,
|
||||
});
|
||||
this.closeAllInlinedForms();
|
||||
setTimeout(() => {
|
||||
this.$('.add-checklist-item')
|
||||
.last()
|
||||
.eq(checklistItemIndex)
|
||||
.click();
|
||||
}, 100);
|
||||
}
|
||||
@@ -120,13 +126,22 @@ BlazeComponent.extendComponent({
|
||||
let checklistItems = [title];
|
||||
if (newlineBecomesNewChecklistItem.checked) {
|
||||
checklistItems = title.split('\n').map(_value => _value.trim());
|
||||
if (this.currentData().position === 'top') {
|
||||
checklistItems = checklistItems.reverse();
|
||||
}
|
||||
}
|
||||
for (let checklistItem of checklistItems) {
|
||||
let sortIndex;
|
||||
if (this.currentData().position === 'top') {
|
||||
sortIndex = Utils.calculateIndexData(null, checklist.firstItem()).base;
|
||||
} else {
|
||||
sortIndex = Utils.calculateIndexData(checklist.lastItem(), null).base;
|
||||
}
|
||||
ChecklistItems.insert({
|
||||
title: checklistItem,
|
||||
checklistId: checklist._id,
|
||||
cardId: checklist.cardId,
|
||||
sort: Utils.calculateIndexData(checklist.lastItem()).base,
|
||||
sort: sortIndex,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -135,15 +150,6 @@ BlazeComponent.extendComponent({
|
||||
textarea.focus();
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
deleteItem() {
|
||||
const checklist = this.currentData().checklist;
|
||||
const item = this.currentData().item;
|
||||
@@ -196,9 +202,6 @@ BlazeComponent.extendComponent({
|
||||
|
||||
events() {
|
||||
const events = {
|
||||
'click #toggleHideCheckedItemsButton'() {
|
||||
Meteor.call('toggleHideCheckedItems');
|
||||
},
|
||||
};
|
||||
|
||||
return [
|
||||
@@ -227,25 +230,26 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find(
|
||||
const ret = ReactiveCache.getBoards(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Meteor.user().getTemplatesBoardId() },
|
||||
_id: { $ne: ReactiveCache.getCurrentUser().getTemplatesBoardId() },
|
||||
},
|
||||
{
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return ret;
|
||||
},
|
||||
|
||||
swimlanes() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const board = ReactiveCache.getBoard(this.selectedBoardId.get());
|
||||
return board.swimlanes();
|
||||
},
|
||||
|
||||
aBoardLists() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const board = ReactiveCache.getBoard(this.selectedBoardId.get());
|
||||
return board.lists();
|
||||
},
|
||||
|
||||
@@ -263,12 +267,17 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Template.checklists.helpers({
|
||||
checklists() {
|
||||
const card = Cards.findOne(this.cardId);
|
||||
const card = ReactiveCache.getCard(this.cardId);
|
||||
const ret = card.checklists();
|
||||
return ret;
|
||||
},
|
||||
showAtMinicard() {
|
||||
const card = ReactiveCache.getCard(this.cardId);
|
||||
const ret = card.checklists({'showAtMinicard':1});
|
||||
return ret;
|
||||
},
|
||||
hideCheckedItems() {
|
||||
const currentUser = Meteor.user();
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) return currentUser.hasHideCheckedItems();
|
||||
return false;
|
||||
},
|
||||
@@ -278,14 +287,6 @@ BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
autosize(this.$('textarea.js-add-checklist-item'));
|
||||
},
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
@@ -302,17 +303,35 @@ BlazeComponent.extendComponent({
|
||||
}).register('addChecklistItemForm');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
toggleItem() {
|
||||
const checklist = this.currentData().checklist;
|
||||
const item = this.currentData().item;
|
||||
if (checklist && item && item._id) {
|
||||
item.toggleItem();
|
||||
}
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-delete-checklist' : Popup.afterConfirm('checklistDelete', function () {
|
||||
'click .js-checklist-item .check-box-container': this.toggleItem,
|
||||
'click #toggleShowChecklistAtMinicardButton'() {
|
||||
const checklist = this.checklist;
|
||||
if (checklist && checklist._id) {
|
||||
Meteor.call('toggleShowChecklistAtMinicard', checklist._id);
|
||||
}
|
||||
},
|
||||
'click #toggleHideCheckedItemsButton'() {
|
||||
Meteor.call('toggleHideCheckedItems');
|
||||
},
|
||||
'click .js-delete-checklist': Popup.afterConfirm('checklistDelete', function () {
|
||||
Popup.back(2);
|
||||
const checklist = this.checklist;
|
||||
if (checklist && checklist._id) {
|
||||
Checklists.remove(checklist._id);
|
||||
}
|
||||
}),
|
||||
'click .js-move-checklist' : Popup.open('moveChecklist'),
|
||||
'click .js-move-checklist': Popup.open('moveChecklist'),
|
||||
'click .js-copy-checklist': Popup.open('copyChecklist'),
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -322,14 +341,6 @@ BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
autosize(this.$('textarea.js-edit-checklist-item'));
|
||||
},
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
@@ -346,16 +357,8 @@ BlazeComponent.extendComponent({
|
||||
}).register('editChecklistItemForm');
|
||||
|
||||
Template.checklistItemDetail.helpers({
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
hideCheckedItems() {
|
||||
const user = Meteor.user();
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
if (user) return user.hasHideCheckedItems();
|
||||
return false;
|
||||
},
|
||||
@@ -378,187 +381,26 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('checklistItemDetail');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentBoardId = Utils.getCurrentBoardId();
|
||||
this.selectedBoardId = new ReactiveVar(this.currentBoardId);
|
||||
this.selectedSwimlaneId = new ReactiveVar('');
|
||||
this.selectedListId = new ReactiveVar('');
|
||||
this.setMoveChecklistDialogOption(this.currentBoardId);
|
||||
},
|
||||
|
||||
/** set the last confirmed dialog field values
|
||||
* @param boardId the current board id
|
||||
*/
|
||||
setMoveChecklistDialogOption(boardId) {
|
||||
this.moveChecklistDialogOption = {
|
||||
'boardId' : "",
|
||||
'swimlaneId' : "",
|
||||
'listId' : "",
|
||||
'cardId': "",
|
||||
}
|
||||
|
||||
let currentOptions = Meteor.user().getMoveChecklistDialogOptions();
|
||||
if (currentOptions && boardId && currentOptions[boardId]) {
|
||||
this.moveChecklistDialogOption = currentOptions[boardId];
|
||||
if (this.moveChecklistDialogOption.boardId &&
|
||||
this.moveChecklistDialogOption.swimlaneId &&
|
||||
this.moveChecklistDialogOption.listId
|
||||
)
|
||||
{
|
||||
this.selectedBoardId.set(this.moveChecklistDialogOption.boardId)
|
||||
this.selectedSwimlaneId.set(this.moveChecklistDialogOption.swimlaneId);
|
||||
this.selectedListId.set(this.moveChecklistDialogOption.listId);
|
||||
}
|
||||
}
|
||||
this.getBoardData(boardId);
|
||||
if (!this.selectedSwimlaneId.get() || !Swimlanes.findOne({_id: this.selectedSwimlaneId.get(), boardId: this.selectedBoardId.get()})) {
|
||||
this.setFirstSwimlaneId();
|
||||
}
|
||||
if (!this.selectedListId.get() || !Lists.findOne({_id: this.selectedListId.get(), boardId: this.selectedBoardId.get()})) {
|
||||
this.setFirstListId();
|
||||
}
|
||||
},
|
||||
/** sets the first swimlane id */
|
||||
setFirstSwimlaneId() {
|
||||
try {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const swimlaneId = board.swimlanes().fetch()[0]._id;
|
||||
this.selectedSwimlaneId.set(swimlaneId);
|
||||
} catch (e) {}
|
||||
},
|
||||
/** sets the first list id */
|
||||
setFirstListId() {
|
||||
try {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const listId = board.lists().fetch()[0]._id;
|
||||
this.selectedListId.set(listId);
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
/** returns if the board id was the last confirmed one
|
||||
* @param boardId check this board id
|
||||
* @return if the board id was the last confirmed one
|
||||
*/
|
||||
isMoveChecklistDialogOptionBoardId(boardId) {
|
||||
let ret = this.moveChecklistDialogOption.boardId == boardId;
|
||||
/** Move Checklist Dialog */
|
||||
(class extends DialogWithBoardSwimlaneListCard {
|
||||
getDialogOptions() {
|
||||
const ret = ReactiveCache.getCurrentUser().getMoveChecklistDialogOptions();
|
||||
return ret;
|
||||
},
|
||||
|
||||
/** returns if the swimlane id was the last confirmed one
|
||||
* @param swimlaneId check this swimlane id
|
||||
* @return if the swimlane id was the last confirmed one
|
||||
*/
|
||||
isMoveChecklistDialogOptionSwimlaneId(swimlaneId) {
|
||||
let ret = this.moveChecklistDialogOption.swimlaneId == swimlaneId;
|
||||
return ret;
|
||||
},
|
||||
|
||||
/** returns if the list id was the last confirmed one
|
||||
* @param listId check this list id
|
||||
* @return if the list id was the last confirmed one
|
||||
*/
|
||||
isMoveChecklistDialogOptionListId(listId) {
|
||||
let ret = this.moveChecklistDialogOption.listId == listId;
|
||||
return ret;
|
||||
},
|
||||
|
||||
/** returns if the card id was the last confirmed one
|
||||
* @param cardId check this card id
|
||||
* @return if the card id was the last confirmed one
|
||||
*/
|
||||
isMoveChecklistDialogOptionCardId(cardId) {
|
||||
let ret = this.moveChecklistDialogOption.cardId == cardId;
|
||||
return ret;
|
||||
},
|
||||
|
||||
boards() {
|
||||
const ret = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Meteor.user().getTemplatesBoardId() },
|
||||
},
|
||||
{
|
||||
sort: { sort: 1 },
|
||||
},
|
||||
);
|
||||
return ret;
|
||||
},
|
||||
|
||||
swimlanes() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const ret = board.swimlanes();
|
||||
return ret;
|
||||
},
|
||||
|
||||
lists() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const ret = board.lists();
|
||||
return ret;
|
||||
},
|
||||
|
||||
cards() {
|
||||
const list = Lists.findOne(this.selectedListId.get());
|
||||
const ret = list.cards(this.selectedSwimlaneId.get());
|
||||
return ret;
|
||||
},
|
||||
|
||||
/** get the board data from the server
|
||||
* @param boardId get the board data of this board id
|
||||
*/
|
||||
getBoardData(boardId) {
|
||||
const self = this;
|
||||
Meteor.subscribe('board', boardId, false, {
|
||||
onReady() {
|
||||
self.selectedBoardId.set(boardId);
|
||||
|
||||
// reset swimlane id (for selection in cards())
|
||||
self.setFirstSwimlaneId();
|
||||
|
||||
// reset list id (for selection in cards())
|
||||
self.setFirstListId();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-done'() {
|
||||
const boardSelect = this.$('.js-select-boards')[0];
|
||||
const boardId = boardSelect.options[boardSelect.selectedIndex].value;
|
||||
|
||||
const listSelect = this.$('.js-select-lists')[0];
|
||||
const listId = listSelect.options[listSelect.selectedIndex].value;
|
||||
|
||||
const swimlaneSelect = this.$('.js-select-swimlanes')[0];
|
||||
const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
|
||||
|
||||
const cardSelect = this.$('.js-select-cards')[0];
|
||||
const cardId = cardSelect.options[cardSelect.selectedIndex].value;
|
||||
|
||||
const options = {
|
||||
'boardId' : boardId,
|
||||
'swimlaneId' : swimlaneId,
|
||||
'listId' : listId,
|
||||
'cardId': cardId,
|
||||
}
|
||||
Meteor.user().setMoveChecklistDialogOption(this.currentBoardId, options);
|
||||
this.data().checklist.move(cardId);
|
||||
Popup.back(2);
|
||||
},
|
||||
'change .js-select-boards'(event) {
|
||||
const boardId = $(event.currentTarget).val();
|
||||
this.getBoardData(boardId);
|
||||
},
|
||||
'change .js-select-swimlanes'(event) {
|
||||
this.selectedSwimlaneId.set($(event.currentTarget).val());
|
||||
},
|
||||
'change .js-select-lists'(event) {
|
||||
this.selectedListId.set($(event.currentTarget).val());
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}
|
||||
setDone(cardId, options) {
|
||||
ReactiveCache.getCurrentUser().setMoveChecklistDialogOption(this.currentBoardId, options);
|
||||
this.data().checklist.move(cardId);
|
||||
}
|
||||
}).register('moveChecklistPopup');
|
||||
|
||||
/** Copy Checklist Dialog */
|
||||
(class extends DialogWithBoardSwimlaneListCard {
|
||||
getDialogOptions() {
|
||||
const ret = ReactiveCache.getCurrentUser().getCopyChecklistDialogOptions();
|
||||
return ret;
|
||||
}
|
||||
setDone(cardId, options) {
|
||||
ReactiveCache.getCurrentUser().setCopyChecklistDialogOption(this.currentBoardId, options);
|
||||
this.data().checklist.copy(cardId);
|
||||
}
|
||||
}).register('copyChecklistPopup');
|
||||
@@ -1,168 +0,0 @@
|
||||
.js-add-checklist
|
||||
color: #8c8c8c
|
||||
|
||||
textarea.js-add-checklist-item, textarea.js-edit-checklist-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
|
||||
|
||||
.checklists-title
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
|
||||
.checklist-progress-bar-container
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: center
|
||||
|
||||
.checklist-progress-text
|
||||
margin-right: 10px
|
||||
|
||||
.checklist-progress-bar
|
||||
width: 80%
|
||||
height: 10px
|
||||
|
||||
.checklist-progress
|
||||
color: #fff !important
|
||||
background-color: #2196F3 !important
|
||||
padding: 0.01em 16px
|
||||
border-radius: 16px
|
||||
height: 100%
|
||||
|
||||
.checklist-title
|
||||
.checkbox
|
||||
float: left
|
||||
width: 30px
|
||||
height 30px
|
||||
font-size: 18px
|
||||
line-height: 30px
|
||||
|
||||
.title
|
||||
font-size: 18px
|
||||
line-height: 25px
|
||||
|
||||
.checklist-stat
|
||||
margin: 0 0.5em
|
||||
float: right
|
||||
padding-top: 6px
|
||||
&.is-finished
|
||||
color: #3cb500
|
||||
|
||||
span.fa.checklist-handle
|
||||
padding-right: 20px
|
||||
padding-top: 3px
|
||||
float: left
|
||||
|
||||
#card-details-overlay
|
||||
top: 0
|
||||
bottom: -600px
|
||||
right: 0
|
||||
|
||||
.checklist
|
||||
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
|
||||
|
||||
|
||||
.checklist-item
|
||||
margin: 0 0 0 0.1em
|
||||
line-height: 18px
|
||||
font-size: 1.1em
|
||||
margin-top: 3px
|
||||
display: flex
|
||||
background: darken(white, 3%)
|
||||
opacity: 1
|
||||
transition: height 0ms 400ms, opacity 400ms 0ms
|
||||
height: auto
|
||||
overflow: hidden
|
||||
|
||||
&.is-checked.invisible
|
||||
opacity: 0
|
||||
height: 0
|
||||
transition: height 0ms 0ms, opacity 600ms 0ms
|
||||
margin-top: 0
|
||||
margin-bottom: 0
|
||||
|
||||
&.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-container
|
||||
padding-right: 10px;
|
||||
|
||||
.check-box
|
||||
margin: 0.1em 0 0 0;
|
||||
&.is-checked
|
||||
border-bottom: 2px solid #3cb500
|
||||
border-right: 2px solid #3cb500
|
||||
|
||||
.item-title
|
||||
flex: 1
|
||||
&.is-checked
|
||||
color: #8c8c8c
|
||||
font-style: italic
|
||||
text-decoration: line-through
|
||||
& .viewer
|
||||
p
|
||||
margin-bottom: 2px
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 420px
|
||||
|
||||
span.fa.checklistitem-handle
|
||||
padding-top: 2px
|
||||
padding-right: 10px;
|
||||
|
||||
.js-delete-checklist-item,
|
||||
.js-convert-checklist-item-to-card
|
||||
margin: 0 0 0.5em 1.33em
|
||||
@extends .delete-text
|
||||
padding: 12px 0 0 0
|
||||
|
||||
.add-checklist-item
|
||||
margin: 0.2em 0 0.5em 1.33em
|
||||
|
||||
.add-checklist-item,.add-checklist
|
||||
&.js-open-inlined-form
|
||||
display: block
|
||||
width: 50%
|
||||
|
||||
&:hover
|
||||
background: #dbdbdb
|
||||
color: #222
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
|
||||
.checklist-details-menu
|
||||
float: right
|
||||
padding: 6px 10px 6px 10px
|
||||
|
||||
.edit-controls
|
||||
label.toggle-label
|
||||
margin-left: 2px
|
||||
@@ -0,0 +1,230 @@
|
||||
.card-label {
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
margin-right: 4px;
|
||||
margin-bottom: 5px;
|
||||
padding: 3px 8px;
|
||||
max-width: 210px;
|
||||
min-width: 8px;
|
||||
word-wrap: break-word;
|
||||
min-height: 18px;
|
||||
vertical-align: middle;
|
||||
white-space: initial;
|
||||
overflow: initial;
|
||||
}
|
||||
.card-label:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.card-label.square {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
padding: 0;
|
||||
}
|
||||
.card-label.add-label {
|
||||
box-shadow: 0 0 0 2px #bfbfbf inset;
|
||||
border: initial;
|
||||
}
|
||||
.card-label.add-label:hover,
|
||||
.card-label.add-label.is-active {
|
||||
box-shadow: 0 0 0 2px #666 inset;
|
||||
}
|
||||
.card-label p {
|
||||
margin: 0px;
|
||||
}
|
||||
.palette-colors {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.palette-colors .palette-color {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.card-label-white {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.card-label-white:hover {
|
||||
color: #aaa;
|
||||
}
|
||||
.card-label-green {
|
||||
background-color: #3cb500;
|
||||
}
|
||||
.card-label-green:hover {
|
||||
color: #000;
|
||||
}
|
||||
.card-label-yellow {
|
||||
background-color: #fad900;
|
||||
color: #000;
|
||||
}
|
||||
.card-label-orange {
|
||||
background-color: #ff9f19;
|
||||
color: #000;
|
||||
}
|
||||
.card-label-red {
|
||||
background-color: #eb4646;
|
||||
}
|
||||
.card-label-purple {
|
||||
background-color: #a632db;
|
||||
}
|
||||
.card-label-blue {
|
||||
background-color: #0079bf;
|
||||
}
|
||||
.card-label-pink {
|
||||
background-color: #ff78cb;
|
||||
color: #000;
|
||||
}
|
||||
.card-label-sky {
|
||||
background-color: #00c2e0;
|
||||
}
|
||||
.card-label-black {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
.card-label-lime {
|
||||
background-color: #51e898;
|
||||
color: #000;
|
||||
}
|
||||
.card-label-silver {
|
||||
background-color: #c0c0c0;
|
||||
color: #000;
|
||||
}
|
||||
.card-label-peachpuff {
|
||||
background-color: #ffdab9;
|
||||
color: #000;
|
||||
}
|
||||
.card-label-crimson {
|
||||
background-color: #dc143c;
|
||||
}
|
||||
.card-label-plum {
|
||||
background-color: #dda0dd;
|
||||
color: #000;
|
||||
}
|
||||
.card-label-darkgreen {
|
||||
background-color: #006400;
|
||||
}
|
||||
.card-label-slateblue {
|
||||
background-color: #6a5acd;
|
||||
}
|
||||
.card-label-magenta {
|
||||
background-color: #f0f;
|
||||
}
|
||||
.card-label-gold {
|
||||
background-color: #ffd700;
|
||||
color: #000;
|
||||
}
|
||||
.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: #000;
|
||||
}
|
||||
.card-label-mistyrose {
|
||||
background-color: #ffe4e1;
|
||||
color: #000;
|
||||
}
|
||||
.card-label-indigo {
|
||||
background-color: #4b0082;
|
||||
}
|
||||
.edit-label .card-label,
|
||||
.create-label .card-label {
|
||||
float: left;
|
||||
height: 25px;
|
||||
margin: 0px 3% 7px 0px;
|
||||
width: 10.5%;
|
||||
max-width: 10.5%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.edit-labels input[type="text"] {
|
||||
margin: 4px 0 6px 38px;
|
||||
width: 243px;
|
||||
}
|
||||
.edit-labels .card-label {
|
||||
height: 30px;
|
||||
left: 0;
|
||||
padding: 1px 5px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 24px;
|
||||
}
|
||||
.edit-labels .labels-static .card-label {
|
||||
line-height: 30px;
|
||||
margin-bottom: 4px;
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: 0;
|
||||
width: 260px;
|
||||
}
|
||||
.edit-labels-pop-over {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.edit-labels-pop-over .card-label .viewer p {
|
||||
margin: 0;
|
||||
}
|
||||
.edit-labels-pop-over .shortcut {
|
||||
display: inline-block;
|
||||
}
|
||||
.card-label-selectable {
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
margin-bottom: 3px;
|
||||
width: 190px;
|
||||
min-height: 18px;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
transition: margin-right 0.1s;
|
||||
}
|
||||
.card-label-selectable .card-label-selectable-icon {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: -20px;
|
||||
}
|
||||
.card-label-selectable.active:hover,
|
||||
.card-label-selectable.active,
|
||||
.card-label-selectable.active.selected:hover,
|
||||
.card-label-selectable.active.selected {
|
||||
padding-right: 32px;
|
||||
}
|
||||
.card-label-selectable.active:hover .card-label-selectable-icon,
|
||||
.card-label-selectable.active .card-label-selectable-icon,
|
||||
.card-label-selectable.active.selected:hover .card-label-selectable-icon,
|
||||
.card-label-selectable.active.selected .card-label-selectable-icon {
|
||||
right: 6px;
|
||||
}
|
||||
.card-label-selectable.selected,
|
||||
.card-label-selectable:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.active .card-label-selectable,
|
||||
.active .card-label-selectable:hover {
|
||||
margin-right: 0;
|
||||
}
|
||||
.active .card-label-selectable .card-label-selectable-icon {
|
||||
right: 8px;
|
||||
}
|
||||
.card-label-edit-button {
|
||||
border-radius: 3px;
|
||||
float: right;
|
||||
padding: 8px;
|
||||
}
|
||||
.card-label-edit-button:hover {
|
||||
background: #dbdbdb;
|
||||
}
|
||||
ul.edit-labels-pop-over span.fa.label-handle {
|
||||
padding-right: 10px;
|
||||
}
|
||||
ul.edit-labels-pop-over span.fa.label-handle + .card-label {
|
||||
max-width: 180px;
|
||||
}
|
||||
@@ -29,7 +29,7 @@ template(name="cardLabelsPopup")
|
||||
each board.labels
|
||||
li.js-card-label-item
|
||||
a.card-label-edit-button.fa.fa-pencil.js-edit-label
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
span.fa.label-handle(class="fa-arrows" title="{{_ 'dragLabel'}}")
|
||||
span.card-label.card-label-selectable.js-select-label.card-label-wrapper(class="card-label-{{color}}"
|
||||
class="{{# if isLabelSelected ../_id }}active{{/if}}")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
|
||||
let labelColors;
|
||||
Meteor.startup(() => {
|
||||
labelColors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
|
||||
@@ -32,7 +34,7 @@ Template.createLabelPopup.helpers({
|
||||
// is not already used in the board (although it's not a problem if two
|
||||
// labels have the same color).
|
||||
defaultColor() {
|
||||
const labels = Boards.findOne(Session.get('currentBoard')).labels;
|
||||
const labels = Utils.getCurrentBoard().labels;
|
||||
const usedColors = _.pluck(labels, 'color');
|
||||
const availableColors = _.difference(labelColors, usedColors);
|
||||
return availableColors.length > 1 ? availableColors[0] : labelColors[0];
|
||||
@@ -50,7 +52,7 @@ BlazeComponent.extendComponent({
|
||||
appendTo: '.edit-labels-pop-over',
|
||||
helper(element, currentItem) {
|
||||
let ret = currentItem.clone();
|
||||
if (currentItem.closest('.popup-container-depth-0').size() == 0)
|
||||
if (currentItem.closest('.popup-container-depth-0').length == 0)
|
||||
{ // only set css transform at every sub-popup, not at the main popup
|
||||
const content = currentItem.closest('.content')[0]
|
||||
const offsetLeft = content.offsetLeft;
|
||||
@@ -76,7 +78,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member or is comment only
|
||||
this.autorun(() => {
|
||||
if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
|
||||
if (Utils.isTouchScreenOrShowDesktopDragHandles()) {
|
||||
$labels.sortable({
|
||||
handle: '.label-handle',
|
||||
});
|
||||
@@ -118,7 +120,7 @@ Template.createLabelPopup.events({
|
||||
// Create the new label
|
||||
'submit .create-label'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const board = Boards.findOne(Session.get('currentBoard'));
|
||||
const board = Utils.getCurrentBoard();
|
||||
const name = templateInstance
|
||||
.$('#labelName')
|
||||
.val()
|
||||
@@ -130,14 +132,14 @@ Template.createLabelPopup.events({
|
||||
});
|
||||
|
||||
Template.editLabelPopup.events({
|
||||
'click .js-delete-label': Popup.afterConfirm('deleteLabel', function() {
|
||||
const board = Boards.findOne(Session.get('currentBoard'));
|
||||
'click .js-delete-label': Popup.afterConfirm('deleteLabel', function () {
|
||||
const board = Utils.getCurrentBoard();
|
||||
board.removeLabel(this._id);
|
||||
Popup.back(2);
|
||||
}),
|
||||
'submit .edit-label'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const board = Boards.findOne(Session.get('currentBoard'));
|
||||
const board = Utils.getCurrentBoard();
|
||||
const name = templateInstance
|
||||
.$('#labelName')
|
||||
.val()
|
||||
@@ -150,6 +152,6 @@ Template.editLabelPopup.events({
|
||||
|
||||
Template.cardLabelsPopup.helpers({
|
||||
isLabelSelected(cardId) {
|
||||
return _.contains(Cards.findOne(cardId).labelIds, this._id);
|
||||
return _.contains(ReactiveCache.getCard(cardId).labelIds, this._id);
|
||||
},
|
||||
});
|
||||
@@ -1,231 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
// XXX Use .board-widget-labels as a flexbox container
|
||||
.card-label
|
||||
border: 1px solid #000000
|
||||
border-radius: 4px
|
||||
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
|
||||
margin-right: 4px
|
||||
margin-bottom: 5px
|
||||
padding: 3px 8px
|
||||
max-width: 210px
|
||||
min-width: 8px
|
||||
word-wrap: break-word
|
||||
min-height: 18px
|
||||
vertical-align: middle
|
||||
white-space: initial
|
||||
overflow: initial
|
||||
|
||||
&:hover
|
||||
color: white
|
||||
|
||||
&.square
|
||||
height: 30px
|
||||
width: @height
|
||||
padding: 0
|
||||
|
||||
&.add-label
|
||||
box-shadow: 0 0 0 2px darken(white, 25%) inset
|
||||
border: initial
|
||||
|
||||
&:hover, &.is-active
|
||||
box-shadow: 0 0 0 2px darken(white, 60%) inset
|
||||
|
||||
p
|
||||
margin: 0px
|
||||
|
||||
.palette-colors
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
|
||||
.palette-color
|
||||
flex-grow: 1
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
|
||||
.card-label-white
|
||||
background-color: #ffffff
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-white:hover
|
||||
color: #aaaaaa //grey text for better visibility
|
||||
|
||||
.card-label-green
|
||||
background-color: #3cb500
|
||||
|
||||
.card-label-green:hover
|
||||
color: #000000 //Black hover text for better visibility
|
||||
|
||||
.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
|
||||
|
||||
.card-label-purple
|
||||
background-color: #a632db
|
||||
|
||||
.card-label-blue
|
||||
background-color: #0079bf
|
||||
|
||||
.card-label-pink
|
||||
background-color: #ff78cb
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
.card-label-sky
|
||||
background-color: #00c2e0
|
||||
|
||||
.card-label-black
|
||||
background-color: #4d4d4d
|
||||
|
||||
.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
|
||||
.card-label
|
||||
float: left
|
||||
height: 25px
|
||||
margin: 0px 3% 7px 0px
|
||||
width: 10.5%
|
||||
max-width: 10.5%
|
||||
cursor: pointer
|
||||
|
||||
.edit-labels
|
||||
input[type="text"]
|
||||
margin: 4px 0 6px 38px
|
||||
width: 243px
|
||||
|
||||
.card-label
|
||||
height: 30px
|
||||
left: 0
|
||||
padding: 1px 5px
|
||||
position: absolute
|
||||
top: 0
|
||||
width: 24px
|
||||
|
||||
.labels-static .card-label
|
||||
line-height: 30px
|
||||
margin-bottom: 4px
|
||||
position: relative
|
||||
top: auto
|
||||
left: 0
|
||||
width: 260px
|
||||
|
||||
.edit-labels-pop-over
|
||||
margin-bottom: 8px
|
||||
.card-label .viewer p
|
||||
margin: 0
|
||||
|
||||
.edit-labels-pop-over .shortcut
|
||||
display: inline-block
|
||||
|
||||
.card-label-selectable
|
||||
border-radius: 3px
|
||||
cursor: pointer
|
||||
margin: 0
|
||||
margin-bottom: 3px
|
||||
width: 190px
|
||||
min-height: 18px
|
||||
padding: 8px
|
||||
position: relative
|
||||
transition: margin-right .1s
|
||||
|
||||
.card-label-selectable-icon
|
||||
position: absolute
|
||||
top: 8px
|
||||
right: -20px
|
||||
|
||||
&.active:hover,
|
||||
&.active,
|
||||
&.active.selected:hover,
|
||||
&.active.selected
|
||||
padding-right: 32px
|
||||
|
||||
.card-label-selectable-icon
|
||||
right: 6px
|
||||
|
||||
&.selected,
|
||||
&:hover
|
||||
opacity: .8
|
||||
|
||||
.active .card-label-selectable
|
||||
&,
|
||||
&:hover
|
||||
margin-right: 0
|
||||
|
||||
.card-label-selectable-icon
|
||||
right: 8px
|
||||
|
||||
.card-label-edit-button
|
||||
border-radius: 3px
|
||||
float: right
|
||||
padding: 8px
|
||||
|
||||
&:hover
|
||||
background: #dbdbdb
|
||||
|
||||
ul.edit-labels-pop-over
|
||||
span.fa.label-handle
|
||||
padding-right: 10px;
|
||||
|
||||
span.fa.label-handle + .card-label
|
||||
max-width: 180px
|
||||
@@ -0,0 +1,598 @@
|
||||
.minicard .checklists-title,
|
||||
.minicard .add-checklist,
|
||||
.minicard .add-checklist-item,
|
||||
.minicard .checklist-details-menu {
|
||||
display: none;
|
||||
}
|
||||
.minicard .checklist-progress-bar-container {
|
||||
width: 190px; /* TODO: Add adjustable width https://github.com/wekan/wekan/pull/4964 */
|
||||
}
|
||||
.minicard-wrapper {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
.minicard-wrapper.placeholder {
|
||||
background: #ccc;
|
||||
border-radius: 9px;
|
||||
}
|
||||
.minicard-wrapper.ui-sortable-helper {
|
||||
cursor: grabbing;
|
||||
transform: rotate(4deg);
|
||||
display: block !important;
|
||||
}
|
||||
.minicard-wrapper.ui-sortable-helper .and-n-other {
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
padding: 4px;
|
||||
background-color: #f2f2f2;
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.minicard-wrapper.ui-sortable-helper .multi-selection-checkbox {
|
||||
display: none;
|
||||
}
|
||||
.minicard-wrapper .multi-selection-checkbox + .minicard {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@media only screen {
|
||||
.minicard {
|
||||
padding: 6px 8px 2px;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
flex-wrap: wrap;
|
||||
background-color: #fff;
|
||||
min-height: 20px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
|
||||
border-radius: 2px;
|
||||
color: #4d4d4d;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s, border-radius 0.2s;
|
||||
}
|
||||
}
|
||||
.minicard-details-menu-with-handle {
|
||||
float: right;
|
||||
font-size: 18px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
.minicard-details-menu {
|
||||
float: right;
|
||||
font-size: 18px;
|
||||
}
|
||||
@media print {
|
||||
.minicard-details-menu,
|
||||
.minicard-details-menu-with-handle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.minicard.linked-board .linked-icon,
|
||||
.minicard.linked-card .linked-icon {
|
||||
display: inline-block;
|
||||
margin-right: 11px;
|
||||
vertical-align: baseline;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.minicard.linked-board .linked-archived,
|
||||
.minicard.linked-card .linked-archived {
|
||||
color: #937760;
|
||||
}
|
||||
.is-selected .minicard {
|
||||
transform: translateX(11px);
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
z-index: 25;
|
||||
box-shadow: -2px 1px 2px rgba(0,0,0,0.2);
|
||||
}
|
||||
.minicard:hover:not(.minicard-composer),
|
||||
.is-selected .minicard,
|
||||
.draggable-hover-card .minicard {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
.draggable-hover-card .minicard {
|
||||
background: #ededed;
|
||||
}
|
||||
.minicard .minicard-cover {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
height: 145px;
|
||||
user-select: none;
|
||||
margin: -6px -8px 6px -8px;
|
||||
border-radius: top 2px;
|
||||
}
|
||||
.minicard .minicard-labels {
|
||||
float: none;
|
||||
}
|
||||
.minicard .minicard-labels .minicard-label {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border-radius: 2px;
|
||||
margin-right: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.minicard .minicard-labels-no-text {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.minicard .minicard-custom-fields {
|
||||
display: block;
|
||||
}
|
||||
.minicard .minicard-custom-field {
|
||||
display: flex;
|
||||
}
|
||||
.minicard .minicard-custom-field-item {
|
||||
flex-grow: 1;
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
max-width: 100px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.minicard .minicard-custom-field-item-fullwidth {
|
||||
flex-grow: 1;
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
margin-right: 4px;
|
||||
}
|
||||
/*
|
||||
.minicard .checklists-title,
|
||||
.minicard .add-checklist,
|
||||
.minicard .add-checklist-item,
|
||||
.minicard .checklist-details-menu {
|
||||
display: none;
|
||||
}
|
||||
*/
|
||||
.minicard .handle {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
display: none;
|
||||
}
|
||||
@media only screen {
|
||||
.minicard .handle {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.minicard .handle .fa-arrows {
|
||||
font-size: 20px;
|
||||
color: #ccc;
|
||||
}
|
||||
.minicard .minicard-title .card-number {
|
||||
color: #b3b3b3;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
@media only screen {
|
||||
.minicard .minicard-title p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.minicard .minicard-title .viewer {
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
max-width: 230px;
|
||||
}
|
||||
}
|
||||
.minicard .dates {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.minicard .date {
|
||||
margin-right: 3px;
|
||||
}
|
||||
.minicard .badges {
|
||||
float: left;
|
||||
margin-top: 7px;
|
||||
color: #808080;
|
||||
}
|
||||
.minicard .badges:empty {
|
||||
display: none;
|
||||
}
|
||||
.minicard .badges .badge {
|
||||
float: left;
|
||||
margin-right: 11px;
|
||||
margin-bottom: 3px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.minicard .badges .badge.is-finished {
|
||||
background: #3cb500;
|
||||
padding: 0px 3px;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
}
|
||||
.minicard .badges .badge:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
.minicard .badges .badge .badge-icon,
|
||||
.minicard .badges .badge .badge-text {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.minicard .badges .badge .badge-icon.badge-comment,
|
||||
.minicard .badges .badge .badge-text.badge-comment {
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
.minicard .badges .badge .badge-text {
|
||||
font-size: 0.9em;
|
||||
padding-left: 2px;
|
||||
line-height: 14px;
|
||||
}
|
||||
.minicard .badges .badge .check-list-text {
|
||||
padding-left: 0px;
|
||||
line-height: 12px;
|
||||
}
|
||||
.minicard .minicard-members,
|
||||
.minicard .minicard-assignees,
|
||||
.minicard .minicard-creator {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.minicard .minicard-members .member,
|
||||
.minicard .minicard-assignees .member,
|
||||
.minicard .minicard-creator .member {
|
||||
float: right;
|
||||
border-radius: 50%;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.minicard .minicard-members .assignee,
|
||||
.minicard .minicard-assignees .assignee,
|
||||
.minicard .minicard-creator .assignee {
|
||||
float: right;
|
||||
border-radius: 50%;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
}
|
||||
.minicard .minicard-members + .badges,
|
||||
.minicard .minicard-assignees + .badges,
|
||||
.minicard .minicard-creator + .badges {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.minicard .minicard-assignees {
|
||||
border-bottom: 1px solid #f00;
|
||||
}
|
||||
.minicard .minicard-creator {
|
||||
border-bottom: 1px solid #008000;
|
||||
}
|
||||
.minicard .minicard-members:empty,
|
||||
.minicard .minicard-assignees:empty {
|
||||
display: none;
|
||||
}
|
||||
.minicard .minicard-description {
|
||||
padding: 6px 0 0 8px;
|
||||
color: #000;
|
||||
background-color: #eee;
|
||||
width: 100%;
|
||||
margin-bottom: 2px;
|
||||
margin-left: -4px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
.minicard.minicard-composer {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.minicard.minicard-composer textarea.minicard-composer-textarea,
|
||||
.minicard.minicard-composer textarea.minicard-composer-textarea:focus {
|
||||
resize: none;
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 162px;
|
||||
min-height: 36px;
|
||||
margin-bottom: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.parent-prefix {
|
||||
color: #b3b3b3;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.parent-subtext {
|
||||
color: #b3b3b3;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.is-selected .minicard {
|
||||
transform: translateX(0px);
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
z-index: 15;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
|
||||
}
|
||||
}
|
||||
/* https://github.com/wekan/wekan/issues/4254#issuecomment-1037192960 */
|
||||
.minicard-green {
|
||||
background-color: #3cb500 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-green:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-green,
|
||||
.draggable-hover-card .minicard-green {
|
||||
background: #3ab000;
|
||||
}
|
||||
.draggable-hover-card .minicard-green {
|
||||
background: #38a800;
|
||||
}
|
||||
.minicard-yellow {
|
||||
background-color: #fad900 !important;
|
||||
}
|
||||
.minicard-yellow:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-yellow,
|
||||
.draggable-hover-card .minicard-yellow {
|
||||
background: #f3d200;
|
||||
}
|
||||
.draggable-hover-card .minicard-yellow {
|
||||
background: #e9ca00;
|
||||
}
|
||||
.minicard-orange {
|
||||
background-color: #ff9f19 !important;
|
||||
}
|
||||
.minicard-orange:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-orange,
|
||||
.draggable-hover-card .minicard-orange {
|
||||
background: #ff9b11;
|
||||
}
|
||||
.draggable-hover-card .minicard-orange {
|
||||
background: #ff9705;
|
||||
}
|
||||
.minicard-red {
|
||||
background-color: #eb4646 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-red:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-red,
|
||||
.draggable-hover-card .minicard-red {
|
||||
background: #ea3e3e;
|
||||
}
|
||||
.draggable-hover-card .minicard-red {
|
||||
background: #e93333;
|
||||
}
|
||||
.minicard-purple {
|
||||
background-color: #a632db !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-purple:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-purple,
|
||||
.draggable-hover-card .minicard-purple {
|
||||
background: #a32bda;
|
||||
}
|
||||
.draggable-hover-card .minicard-purple {
|
||||
background: #9e25d5;
|
||||
}
|
||||
.minicard-blue {
|
||||
background-color: #0079bf !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-blue:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-blue,
|
||||
.draggable-hover-card .minicard-blue {
|
||||
background: #0075b9;
|
||||
}
|
||||
.draggable-hover-card .minicard-blue {
|
||||
background: #0071b2;
|
||||
}
|
||||
.minicard-pink {
|
||||
background-color: #ff78cb !important;
|
||||
}
|
||||
.minicard-pink:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-pink,
|
||||
.draggable-hover-card .minicard-pink {
|
||||
background: #ff6dc7;
|
||||
}
|
||||
.draggable-hover-card .minicard-pink {
|
||||
background: #ff5ec1;
|
||||
}
|
||||
.minicard-sky {
|
||||
background-color: #00c2e0 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-sky:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-sky,
|
||||
.draggable-hover-card .minicard-sky {
|
||||
background: #00bcd9;
|
||||
}
|
||||
.draggable-hover-card .minicard-sky {
|
||||
background: #00b4d0;
|
||||
}
|
||||
.minicard-black {
|
||||
background-color: #4d4d4d !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-black:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-black,
|
||||
.draggable-hover-card .minicard-black {
|
||||
background: #4b4b4b;
|
||||
}
|
||||
.draggable-hover-card .minicard-black {
|
||||
background: #484848;
|
||||
}
|
||||
.minicard-lime {
|
||||
background-color: #51e898 !important;
|
||||
}
|
||||
.minicard-lime:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-lime,
|
||||
.draggable-hover-card .minicard-lime {
|
||||
background: #49e793;
|
||||
}
|
||||
.draggable-hover-card .minicard-lime {
|
||||
background: #3ee58d;
|
||||
}
|
||||
.minicard-silver {
|
||||
background-color: #c0c0c0 !important;
|
||||
}
|
||||
.minicard-silver:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-silver,
|
||||
.draggable-hover-card .minicard-silver {
|
||||
background: #bababa;
|
||||
}
|
||||
.draggable-hover-card .minicard-silver {
|
||||
background: #b3b3b3;
|
||||
}
|
||||
.minicard-peachpuff {
|
||||
background-color: #ffdab9 !important;
|
||||
}
|
||||
.minicard-peachpuff:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-peachpuff,
|
||||
.draggable-hover-card .minicard-peachpuff {
|
||||
background: #ffd3ac;
|
||||
}
|
||||
.draggable-hover-card .minicard-peachpuff {
|
||||
background: #ffca9a;
|
||||
}
|
||||
.minicard-crimson {
|
||||
background-color: #dc143c !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-crimson:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-crimson,
|
||||
.draggable-hover-card .minicard-crimson {
|
||||
background: #d5133a;
|
||||
}
|
||||
.draggable-hover-card .minicard-crimson {
|
||||
background: #cd1338;
|
||||
}
|
||||
.minicard-plum {
|
||||
background-color: #dda0dd !important;
|
||||
}
|
||||
.minicard-plum:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-plum,
|
||||
.draggable-hover-card .minicard-plum {
|
||||
background: #da98da;
|
||||
}
|
||||
.draggable-hover-card .minicard-plum {
|
||||
background: #d68cd6;
|
||||
}
|
||||
.minicard-darkgreen {
|
||||
background-color: #006400 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-darkgreen:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-darkgreen,
|
||||
.draggable-hover-card .minicard-darkgreen {
|
||||
background: #006100;
|
||||
}
|
||||
.draggable-hover-card .minicard-darkgreen {
|
||||
background: #005d00;
|
||||
}
|
||||
.minicard-slateblue {
|
||||
background-color: #6a5acd !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-slateblue:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-slateblue,
|
||||
.draggable-hover-card .minicard-slateblue {
|
||||
background: #6453cb;
|
||||
}
|
||||
.draggable-hover-card .minicard-slateblue {
|
||||
background: #5c4ac8;
|
||||
}
|
||||
.minicard-magenta {
|
||||
background-color: #f0f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-magenta:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-magenta,
|
||||
.draggable-hover-card .minicard-magenta {
|
||||
background: #f700f7;
|
||||
}
|
||||
.draggable-hover-card .minicard-magenta {
|
||||
background: #ed00ed;
|
||||
}
|
||||
.minicard-gold {
|
||||
background-color: #ffd700 !important;
|
||||
}
|
||||
.minicard-gold:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-gold,
|
||||
.draggable-hover-card .minicard-gold {
|
||||
background: #f7d100;
|
||||
}
|
||||
.draggable-hover-card .minicard-gold {
|
||||
background: #edc800;
|
||||
}
|
||||
.minicard-navy {
|
||||
background-color: #000080 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-navy:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-navy,
|
||||
.draggable-hover-card .minicard-navy {
|
||||
background: #00007c;
|
||||
}
|
||||
.draggable-hover-card .minicard-navy {
|
||||
background: #007;
|
||||
}
|
||||
.minicard-gray {
|
||||
background-color: #808080 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-gray:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-gray,
|
||||
.draggable-hover-card .minicard-gray {
|
||||
background: #7c7c7c;
|
||||
}
|
||||
.draggable-hover-card .minicard-gray {
|
||||
background: #777;
|
||||
}
|
||||
.minicard-saddlebrown {
|
||||
background-color: #8b4513 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-saddlebrown:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-saddlebrown,
|
||||
.draggable-hover-card .minicard-saddlebrown {
|
||||
background: #874312;
|
||||
}
|
||||
.draggable-hover-card .minicard-saddlebrown {
|
||||
background: #814012;
|
||||
}
|
||||
.minicard-paleturquoise {
|
||||
background-color: #afeeee !important;
|
||||
}
|
||||
.minicard-paleturquoise:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-paleturquoise,
|
||||
.draggable-hover-card .minicard-paleturquoise {
|
||||
background: #a5ecec;
|
||||
}
|
||||
.draggable-hover-card .minicard-paleturquoise {
|
||||
background: #97e9e9;
|
||||
}
|
||||
.minicard-mistyrose {
|
||||
background-color: #ffe4e1 !important;
|
||||
}
|
||||
.minicard-mistyrose:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-mistyrose,
|
||||
.draggable-hover-card .minicard-mistyrose {
|
||||
background: #ffd7d3;
|
||||
}
|
||||
.draggable-hover-card .minicard-mistyrose {
|
||||
background: #ffc6bf;
|
||||
}
|
||||
.minicard-indigo {
|
||||
background-color: #4b0082 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.minicard-indigo:hover:not(.minicard-composer),
|
||||
.is-selected .minicard-indigo,
|
||||
.draggable-hover-card .minicard-indigo {
|
||||
background: #49007e;
|
||||
}
|
||||
.draggable-hover-card .minicard-indigo {
|
||||
background: #460079;
|
||||
}
|
||||
.text-red {
|
||||
color: #f00;
|
||||
}
|
||||
.text-green {
|
||||
color: #008000;
|
||||
}
|
||||
@@ -3,20 +3,33 @@ template(name="minicard")
|
||||
class="{{#if isLinkedCard}}linked-card{{/if}}"
|
||||
class="{{#if isLinkedBoard}}linked-board{{/if}}"
|
||||
class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}")
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
a.fa.fa-navicon.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
else
|
||||
a.fa.fa-navicon.minicard-details-menu.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
.dates
|
||||
if getReceived
|
||||
unless getStart
|
||||
unless getDue
|
||||
unless getEnd
|
||||
.date
|
||||
+minicardReceivedDate
|
||||
if getStart
|
||||
.date
|
||||
+minicardStartDate
|
||||
if getDue
|
||||
.date
|
||||
+minicardDueDate
|
||||
if getEnd
|
||||
+minicardEndDate
|
||||
if getSpentTime
|
||||
.date
|
||||
+cardSpentTime
|
||||
if cover
|
||||
.minicard-cover(style="background-image: url('{{cover.url}}');")
|
||||
if labels
|
||||
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
|
||||
each labels
|
||||
unless hiddenMinicardLabelText
|
||||
span.js-card-label.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if hiddenMinicardLabelText
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
if currentBoard.allowsCoverAttachmentOnMinicard
|
||||
.minicard-cover(style="background-image: url('{{cover.link 'original'}}?dummyReloadAfterSessionEstablished={{sess}}');")
|
||||
.minicard-title
|
||||
if $eq 'prefix-with-full-path' currentBoard.presentParentTask
|
||||
.parent-prefix
|
||||
@@ -43,50 +56,66 @@ template(name="minicard")
|
||||
if $eq 'subtext-with-parent' currentBoard.presentParentTask
|
||||
.parent-subtext
|
||||
| {{ parentCardName }}
|
||||
|
||||
.dates
|
||||
if getReceived
|
||||
unless getStart
|
||||
unless getDue
|
||||
unless getEnd
|
||||
.date
|
||||
+minicardReceivedDate
|
||||
if getStart
|
||||
.date
|
||||
+minicardStartDate
|
||||
if getDue
|
||||
.date
|
||||
+minicardDueDate
|
||||
if getEnd
|
||||
+minicardEndDate
|
||||
if getSpentTime
|
||||
.date
|
||||
+cardSpentTime
|
||||
if labels
|
||||
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
|
||||
each labels
|
||||
unless hiddenMinicardLabelText
|
||||
span.js-card-label.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if hiddenMinicardLabelText
|
||||
.minicard-label(class="card-label-{{color}}" title="{{name}}")
|
||||
|
||||
.minicard-custom-fields
|
||||
each customFieldsWD
|
||||
if definition.showOnCard
|
||||
if trueValue
|
||||
.minicard-custom-field
|
||||
// If there is custom field label, show label at left,
|
||||
// and value at right
|
||||
if definition.showLabelOnMiniCard
|
||||
.minicard-custom-field-item
|
||||
+viewer
|
||||
= definition.name
|
||||
.minicard-custom-field-item
|
||||
if $eq definition.type "currency"
|
||||
+viewer
|
||||
= formattedCurrencyCustomFieldValue(definition)
|
||||
else if $eq definition.type "date"
|
||||
.date
|
||||
+minicardCustomFieldDate
|
||||
else if $eq definition.type "checkbox"
|
||||
.materialCheckBox(class="{{#if value }}is-checked{{/if}}")
|
||||
else if $eq definition.type "stringtemplate"
|
||||
+viewer
|
||||
= formattedStringtemplateCustomFieldValue(definition)
|
||||
else
|
||||
+viewer
|
||||
= trueValue
|
||||
.minicard-custom-field-item
|
||||
if $eq definition.type "currency"
|
||||
+viewer
|
||||
= formattedCurrencyCustomFieldValue(definition)
|
||||
else if $eq definition.type "date"
|
||||
.date
|
||||
+minicardCustomFieldDate
|
||||
else if $eq definition.type "checkbox"
|
||||
.materialCheckBox(class="{{#if value }}is-checked{{/if}}")
|
||||
else if $eq definition.type "stringtemplate"
|
||||
+viewer
|
||||
= formattedStringtemplateCustomFieldValue(definition)
|
||||
else
|
||||
+viewer
|
||||
= trueValue
|
||||
else
|
||||
// If there is no custom field label,
|
||||
// show value full width
|
||||
.minicard-custom-field-item-fullwidth
|
||||
if $eq definition.type "currency"
|
||||
+viewer
|
||||
= formattedCurrencyCustomFieldValue(definition)
|
||||
else if $eq definition.type "date"
|
||||
.date
|
||||
+minicardCustomFieldDate
|
||||
else if $eq definition.type "checkbox"
|
||||
.materialCheckBox(class="{{#if value }}is-checked{{/if}}")
|
||||
else if $eq definition.type "stringtemplate"
|
||||
+viewer
|
||||
= formattedStringtemplateCustomFieldValue(definition)
|
||||
else
|
||||
+viewer
|
||||
= trueValue
|
||||
|
||||
.card-checklist-attachmentGalleries
|
||||
.card-checklist-attachmentGallery.card-checklists
|
||||
if currentBoard.allowsChecklists
|
||||
//hr
|
||||
//+checklists(cardId=_id showAtMinicard=true)
|
||||
|
||||
if showAssignee
|
||||
if getAssignees
|
||||
@@ -106,16 +135,17 @@ template(name="minicard")
|
||||
|
||||
.badges
|
||||
unless currentUser.isNoComments
|
||||
if comments.count
|
||||
.badge(title="{{_ 'card-comments-title' comments.count }}")
|
||||
span.badge-icon.fa.fa-comment-o.badge-comment
|
||||
if comments.length
|
||||
.badge(title="{{_ 'card-comments-title' comments.length }}")
|
||||
span.badge-icon.fa.fa-comment-o.badge-comment.badge-text
|
||||
= ' '
|
||||
= comments.count
|
||||
= comments.length
|
||||
//span.badge-comment.badge-text
|
||||
//| {{_ 'comment'}}
|
||||
if getDescription
|
||||
.badge.badge-state-image-only(title=getDescription)
|
||||
span.badge-icon.fa.fa-align-left
|
||||
unless currentBoard.allowsDescriptionTextOnMinicard
|
||||
.badge.badge-state-image-only(title=getDescription)
|
||||
span.badge-icon.fa.fa-align-left
|
||||
if getVoteQuestion
|
||||
.badge.badge-state-image-only(title=getVoteQuestion)
|
||||
span.badge-icon.fa.fa-thumbs-up(class="{{#if voteState}}text-green{{/if}}")
|
||||
@@ -127,11 +157,12 @@ template(name="minicard")
|
||||
span.badge-icon.fa.fa-check(class="{{#if pokerState}}text-green{{/if}}")
|
||||
if expiredPoker
|
||||
span.badge-text {{ getPokerEstimation }}
|
||||
if attachments.count
|
||||
.badge
|
||||
span.badge-icon.fa.fa-paperclip
|
||||
span.badge-text= attachments.count
|
||||
if checklists.count
|
||||
if attachments.length
|
||||
if currentBoard.allowsBadgeAttachmentOnMinicard
|
||||
.badge
|
||||
span.badge-icon.fa.fa-paperclip
|
||||
span.badge-text= attachments.length
|
||||
if checklists.length
|
||||
.badge(class="{{#if checklistFinished}}is-finished{{/if}}")
|
||||
span.badge-icon.fa.fa-check-square-o
|
||||
span.badge-text.check-list-text {{checklistFinishedCount}}/{{checklistItemCount}}
|
||||
@@ -141,11 +172,62 @@ template(name="minicard")
|
||||
span.badge-text.check-list-text {{subtasksFinishedCount}}/{{allSubtasksCount}}
|
||||
//{{subtasksFinishedCount}}/{{subtasksCount}} does not work because when a subtaks is archived, the count goes down
|
||||
if currentBoard.allowsCardSortingByNumber
|
||||
.badge
|
||||
span.badge-icon.fa.fa-sort
|
||||
span.badge-text.check-list-sort {{ sort }}
|
||||
if currentBoard.allowsCardSortingByNumberOnMinicard
|
||||
.badge
|
||||
span.badge-icon.fa.fa-sort
|
||||
span.badge-text.check-list-sort {{ sort }}
|
||||
if currentBoard.allowsDescriptionTextOnMinicard
|
||||
if getDescription
|
||||
.minicard-description
|
||||
+viewer
|
||||
| {{ getDescription }}
|
||||
|
||||
template(name="editCardSortOrderPopup")
|
||||
input.js-edit-card-sort-popup(type='text' autofocus value=sort dir="auto")
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-sort-popup(type="submit") {{_ 'save'}}
|
||||
|
||||
template(name="minicardDetailsActionsPopup")
|
||||
ul.pop-over-list
|
||||
if currentUser.isBoardAdmin
|
||||
li
|
||||
a.js-move-card
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ 'moveCardPopup-title'}}
|
||||
unless currentUser.isWorker
|
||||
li
|
||||
a.js-copy-card
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyCardPopup-title'}}
|
||||
hr
|
||||
li
|
||||
a.js-archive
|
||||
i.fa.fa-arrow-right
|
||||
i.fa.fa-archive
|
||||
| {{_ 'archive-card'}}
|
||||
hr
|
||||
li
|
||||
a.js-move-card-to-top
|
||||
i.fa.fa-arrow-up
|
||||
| {{_ 'moveCardToTop-title'}}
|
||||
li
|
||||
a.js-move-card-to-bottom
|
||||
i.fa.fa-arrow-down
|
||||
| {{_ 'moveCardToBottom-title'}}
|
||||
hr
|
||||
li
|
||||
a.js-add-labels
|
||||
i.fa.fa-tags
|
||||
| {{_ 'card-edit-labels'}}
|
||||
li
|
||||
a.js-due-date
|
||||
i.fa.fa-sign-in
|
||||
| {{_ 'editCardDueDatePopup-title'}}
|
||||
li
|
||||
a.js-set-card-color
|
||||
i.fa.fa-paint-brush
|
||||
| {{_ 'setCardColorPopup-title'}}
|
||||
li
|
||||
a.js-link
|
||||
i.fa.fa-link
|
||||
| {{_ 'link-card'}}
|
||||
@@ -1,3 +1,7 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
import { CustomFieldStringTemplate } from '/client/lib/customFields'
|
||||
|
||||
// Template.cards.events({
|
||||
// 'click .member': Popup.open('cardMember')
|
||||
// });
|
||||
@@ -29,46 +33,50 @@ BlazeComponent.extendComponent({
|
||||
const customFieldTrueValue =
|
||||
customField && customField.trueValue ? customField.trueValue : [];
|
||||
|
||||
return customFieldTrueValue
|
||||
.filter(value => !!value.trim())
|
||||
.map(value =>
|
||||
definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, value),
|
||||
)
|
||||
.join(definition.settings.stringtemplateSeparator ?? '');
|
||||
const ret = new CustomFieldStringTemplate(definition).getFormattedValue(customFieldTrueValue);
|
||||
return ret;
|
||||
},
|
||||
|
||||
showCreator() {
|
||||
if (this.data().board()) {
|
||||
return (
|
||||
this.data().board.allowsCreator === null ||
|
||||
this.data().board().allowsCreator === undefined ||
|
||||
this.data().board().allowsCreator
|
||||
);
|
||||
// return this.data().board().allowsCreator;
|
||||
// cache "board" to reduce the mini-mongodb access
|
||||
const board = this.data().board();
|
||||
let ret = false;
|
||||
if (board) {
|
||||
ret =
|
||||
board.allowsCreator === null ||
|
||||
board.allowsCreator === undefined ||
|
||||
board.allowsCreator
|
||||
;
|
||||
}
|
||||
return false;
|
||||
return ret;
|
||||
},
|
||||
|
||||
showMembers() {
|
||||
if (this.data().board()) {
|
||||
return (
|
||||
this.data().board.allowsMembers === null ||
|
||||
this.data().board().allowsMembers === undefined ||
|
||||
this.data().board().allowsMembers
|
||||
);
|
||||
// cache "board" to reduce the mini-mongodb access
|
||||
const board = this.data().board();
|
||||
let ret = false;
|
||||
if (board) {
|
||||
ret =
|
||||
board.allowsMembers === null ||
|
||||
board.allowsMembers === undefined ||
|
||||
board.allowsMembers
|
||||
;
|
||||
}
|
||||
return false;
|
||||
return ret;
|
||||
},
|
||||
|
||||
showAssignee() {
|
||||
if (this.data().board()) {
|
||||
return (
|
||||
this.data().board.allowsAssignee === null ||
|
||||
this.data().board().allowsAssignee === undefined ||
|
||||
this.data().board().allowsAssignee
|
||||
);
|
||||
// cache "board" to reduce the mini-mongodb access
|
||||
const board = this.data().board();
|
||||
let ret = false;
|
||||
if (board) {
|
||||
ret =
|
||||
board.allowsAssignee === null ||
|
||||
board.allowsAssignee === undefined ||
|
||||
board.allowsAssignee
|
||||
;
|
||||
}
|
||||
return false;
|
||||
return ret;
|
||||
},
|
||||
|
||||
/** opens the card label popup only if clicked onto a label
|
||||
@@ -84,6 +92,11 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .minicard-checklists'() {
|
||||
// Prevents clicking checklist at minicard from opening card details,
|
||||
// while still allowing checking checlist items.
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-linked-link'() {
|
||||
if (this.data().isLinkedCard()) Utils.goCardId(this.data().linkedId);
|
||||
else if (this.data().isLinkedBoard())
|
||||
@@ -98,6 +111,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
'click span.badge-icon.fa.fa-sort, click span.badge-text.check-list-sort' : Popup.open("editCardSortOrder"),
|
||||
'click .minicard-labels' : this.cardLabelsPopup,
|
||||
'click .js-open-minicard-details-menu': Popup.open('minicardDetailsActions'),
|
||||
}
|
||||
];
|
||||
},
|
||||
@@ -105,7 +119,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Template.minicard.helpers({
|
||||
hiddenMinicardLabelText() {
|
||||
currentUser = Meteor.user();
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).hiddenMinicardLabelText;
|
||||
} else if (window.localStorage.getItem('hiddenMinicardLabelText')) {
|
||||
@@ -114,6 +128,12 @@ Template.minicard.helpers({
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// XXX resolve this nasty hack for https://github.com/veliovgroup/Meteor-Files/issues/763
|
||||
sess() {
|
||||
return Meteor.connection && Meteor.connection._lastSessionId
|
||||
? Meteor.connection._lastSessionId
|
||||
: null;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -142,3 +162,29 @@ BlazeComponent.extendComponent({
|
||||
]
|
||||
}
|
||||
}).register('editCardSortOrderPopup');
|
||||
|
||||
Template.minicardDetailsActionsPopup.events({
|
||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||
'click .js-move-card': Popup.open('moveCard'),
|
||||
'click .js-copy-card': Popup.open('copyCard'),
|
||||
'click .js-set-card-color': Popup.open('setCardColor'),
|
||||
'click .js-add-labels': Popup.open('cardLabels'),
|
||||
'click .js-link': Popup.open('linkCard'),
|
||||
'click .js-move-card-to-top'(event) {
|
||||
event.preventDefault();
|
||||
const minOrder = this.getMinSort();
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-move-card-to-bottom'(event) {
|
||||
event.preventDefault();
|
||||
const maxOrder = this.getMaxSort();
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-archive': Popup.afterConfirm('cardArchive', function () {
|
||||
Popup.close();
|
||||
this.archive();
|
||||
Utils.goBoardId(this.boardId);
|
||||
}),
|
||||
});
|
||||
@@ -1,323 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
.minicard-wrapper
|
||||
cursor: pointer
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
margin-bottom: 9px
|
||||
|
||||
&.placeholder
|
||||
background: darken(white, 20%)
|
||||
border-radius: 9px
|
||||
|
||||
&.ui-sortable-helper
|
||||
cursor: grabbing
|
||||
transform: rotate(4deg)
|
||||
display: block !important
|
||||
|
||||
.and-n-other
|
||||
width: 100%
|
||||
height: 16px
|
||||
padding: 4px
|
||||
background-color: darken(white, 5%)
|
||||
text-align: center
|
||||
border-radius: 3px
|
||||
|
||||
.multi-selection-checkbox
|
||||
display: none
|
||||
|
||||
.multi-selection-checkbox + .minicard
|
||||
margin-left: 8px
|
||||
|
||||
.minicard
|
||||
padding: 6px 8px 2px
|
||||
position: relative
|
||||
flex: 1
|
||||
flex-wrap: wrap
|
||||
background-color: #fff
|
||||
min-height: 20px
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.15)
|
||||
border-radius: 2px
|
||||
color: #4d4d4d
|
||||
overflow: hidden
|
||||
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
|
||||
border-top-right-radius: 0
|
||||
z-index: 25
|
||||
box-shadow: -2px 1px 2px rgba(0,0,0,.2)
|
||||
|
||||
&:hover:not(.minicard-composer),
|
||||
.is-selected &,
|
||||
.draggable-hover-card &
|
||||
background: darken(white, 3%)
|
||||
|
||||
.draggable-hover-card &
|
||||
background: darken(white, 7%)
|
||||
|
||||
.minicard-cover
|
||||
background-position: center
|
||||
background-repeat: no-repeat
|
||||
background-size: contain
|
||||
height: 145px
|
||||
user-select: none
|
||||
margin: -6px -8px 6px -8px
|
||||
border-radius: top 2px
|
||||
|
||||
.minicard-labels
|
||||
float: none
|
||||
|
||||
.minicard-label
|
||||
width: 11px
|
||||
height: @width
|
||||
border-radius: 2px
|
||||
margin-right: 3px
|
||||
margin-bottom: 3px
|
||||
|
||||
.minicard-labels-no-text
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
|
||||
.minicard-custom-fields
|
||||
display:block;
|
||||
.minicard-custom-field
|
||||
display:flex;
|
||||
.minicard-custom-field-item
|
||||
flex-grow: 1
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 100px
|
||||
margin-right: 4px
|
||||
.handle
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
display:none;
|
||||
@media only screen {
|
||||
display:block;
|
||||
}
|
||||
.fa-arrows
|
||||
font-size:20px;
|
||||
color: #ccc;
|
||||
.minicard-title
|
||||
.card-number
|
||||
color: darken(white, 30%);
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
p:last-child
|
||||
margin-bottom: 0
|
||||
.viewer
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 230px
|
||||
.dates
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
.date
|
||||
margin-right: 3px
|
||||
.badges
|
||||
float: left
|
||||
margin-top: 7px
|
||||
color: darken(white, 50%)
|
||||
|
||||
&:empty
|
||||
display: none
|
||||
|
||||
.badge
|
||||
float: left
|
||||
margin-right: 11px
|
||||
margin-bottom: 3px
|
||||
font-size: 0.9em
|
||||
&.is-finished
|
||||
background: #3cb500
|
||||
padding: 0px 3px
|
||||
border-radius: 3px
|
||||
color: white
|
||||
|
||||
&:last-of-type
|
||||
margin-right: 0
|
||||
|
||||
.badge-icon,
|
||||
.badge-text
|
||||
vertical-align: middle
|
||||
&.badge-comment
|
||||
margin-bottom: 0.1rem
|
||||
|
||||
.badge-text
|
||||
font-size: 0.9em
|
||||
padding-left: 2px
|
||||
line-height: 14px
|
||||
.check-list-text
|
||||
padding-left: 0px
|
||||
line-height: 12px
|
||||
|
||||
.minicard-members,
|
||||
.minicard-assignees,
|
||||
.minicard-creator
|
||||
float: right
|
||||
margin-left: 5px
|
||||
margin-bottom: 4px
|
||||
|
||||
.member
|
||||
float: right
|
||||
border-radius: 50%
|
||||
height: 28px
|
||||
width: @height
|
||||
margin-bottom: 4px
|
||||
|
||||
.assignee
|
||||
float: right
|
||||
border-radius: 50%
|
||||
height: 28px
|
||||
width: @height
|
||||
|
||||
+ .badges
|
||||
margin-top: 5px
|
||||
|
||||
.minicard-assignees
|
||||
border-bottom: 1px solid red
|
||||
|
||||
.minicard-creator
|
||||
border-bottom: 1px solid green
|
||||
|
||||
.minicard-members:empty,
|
||||
.minicard-assignees:empty
|
||||
display: none
|
||||
|
||||
&.minicard-composer
|
||||
margin-bottom: 10px
|
||||
|
||||
textarea.minicard-composer-textarea,
|
||||
textarea.minicard-composer-textarea:focus
|
||||
resize: none
|
||||
background: none
|
||||
border: none
|
||||
box-shadow: none
|
||||
height: auto
|
||||
margin: 0
|
||||
padding: 0
|
||||
max-height: 162px
|
||||
min-height: 36px
|
||||
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 &
|
||||
transform: translateX(0px)
|
||||
border-bottom-right-radius: 0
|
||||
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
|
||||
|
||||
.text-red
|
||||
color:red
|
||||
.text-green
|
||||
color:green
|
||||
@@ -0,0 +1,25 @@
|
||||
.result-card-list-wrapper {
|
||||
margin: 1rem;
|
||||
border-radius: 5px;
|
||||
padding: 1.5rem;
|
||||
padding-top: 0.75rem;
|
||||
display: inline-block;
|
||||
min-width: 250px;
|
||||
max-width: 350px;
|
||||
}
|
||||
.result-card-wrapper {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.result-card-context {
|
||||
display: inline-block;
|
||||
}
|
||||
.result-card-context-separator {
|
||||
font-weight: bold;
|
||||
}
|
||||
.result-card-context-list {
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
.result-card-block-wrapper {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
.result-card-list-wrapper
|
||||
margin: 1rem
|
||||
border-radius: 5px
|
||||
padding: 1.5rem
|
||||
padding-top: 0.75rem
|
||||
display: inline-block
|
||||
min-width: 250px
|
||||
max-width: 350px
|
||||
|
||||
.result-card-wrapper
|
||||
margin-top: 0
|
||||
margin-bottom: 10px
|
||||
|
||||
.result-card-context
|
||||
display: inline-block
|
||||
|
||||
.result-card-context-separator
|
||||
font-weight: bold
|
||||
|
||||
.result-card-context-list
|
||||
margin-bottom: 0.7rem
|
||||
|
||||
.result-card-block-wrapper
|
||||
display: inline-block
|
||||
@@ -0,0 +1,152 @@
|
||||
.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,
|
||||
.subtask-title .js-delete-subtask,
|
||||
.subtask-title .js-view-subtask,
|
||||
.js-delete-subtask-item {
|
||||
color: #8c8c8c;
|
||||
text-decoration: underline;
|
||||
word-wrap: break-word;
|
||||
float: right;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.delete-text:hover,
|
||||
.subtask-title .js-delete-subtask:hover,
|
||||
.subtask-title .js-view-subtask:hover,
|
||||
.js-delete-subtask-item:hover {
|
||||
color: inherit;
|
||||
}
|
||||
.subtask-title .checkbox {
|
||||
float: left;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.subtask-title .title {
|
||||
font-size: 18px;
|
||||
line-height: 25px;
|
||||
}
|
||||
.subtask-title .subtasks-stat {
|
||||
margin: 0 0.5em;
|
||||
float: right;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.subtask-title .subtasks-stat.is-finished {
|
||||
color: #3cb500;
|
||||
}
|
||||
.subtask-title .js-delete-subtask {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
.js-confirm-subtask-delete {
|
||||
background-color: #f7f7f7;
|
||||
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;
|
||||
}
|
||||
.js-confirm-subtask-delete p {
|
||||
position: relative;
|
||||
margin-top: 3%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.js-confirm-subtask-delete p span {
|
||||
font-weight: bold;
|
||||
}
|
||||
.js-confirm-subtask-delete p i {
|
||||
font-size: 2em;
|
||||
}
|
||||
.js-confirm-subtask-delete .js-subtask-delete-buttons {
|
||||
position: relative;
|
||||
padding: left 2% right 2%;
|
||||
}
|
||||
.js-confirm-subtask-delete .js-subtask-delete-buttons .confirm-subtask-delete {
|
||||
margin-left: 12%;
|
||||
float: left;
|
||||
}
|
||||
.js-confirm-subtask-delete .js-subtask-delete-buttons .toggle-delete-subtask-dialog {
|
||||
margin-right: 12%;
|
||||
float: right;
|
||||
}
|
||||
#card-details-overlay {
|
||||
top: 0;
|
||||
bottom: -600px;
|
||||
right: 0;
|
||||
}
|
||||
.subtasks {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
.subtasks.placeholder {
|
||||
background: #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.subtasks.ui-sortable-helper {
|
||||
box-shadow: -2px 2px 8px rgba(0,0,0,0.3), 0 0 1px rgba(0,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: #f7f7f7;
|
||||
}
|
||||
.subtasks-item.placeholder {
|
||||
background: #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.subtasks-item.ui-sortable-helper {
|
||||
box-shadow: -2px 2px 8px rgba(0,0,0,0.3), 0 0 1px rgba(0,0,0,0.5);
|
||||
transform: rotate(4deg);
|
||||
cursor: grabbing;
|
||||
}
|
||||
.subtasks-item:hover {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
.subtasks-item .check-box {
|
||||
margin: 0.1em 0 0 0;
|
||||
}
|
||||
.subtasks-item .check-box.is-checked {
|
||||
border-bottom: 2px solid #3cb500;
|
||||
border-right: 2px solid #3cb500;
|
||||
}
|
||||
.subtasks-item .item-title {
|
||||
flex: 1;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.subtasks-item .item-title.is-checked {
|
||||
color: #8c8c8c;
|
||||
font-style: italic;
|
||||
}
|
||||
.subtasks-item .item-title .viewer p {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.js-delete-subtask-item {
|
||||
margin: 0 0 0.5em 1.33em;
|
||||
padding: 12px 0 0 0;
|
||||
}
|
||||
.add-subtask-item {
|
||||
margin: 0.2em 0 0.5em 1.33em;
|
||||
display: inline-block;
|
||||
}
|
||||
.subtask-details-menu {
|
||||
float: right;
|
||||
padding: 6px 10px 6px 10px;
|
||||
}
|
||||
@@ -25,11 +25,8 @@ template(name="subtaskDetail")
|
||||
else
|
||||
.subtask-title
|
||||
span
|
||||
a.js-view-subtask(title="{{ subtask.title }}") {{_ "view-it"}}
|
||||
if canModifyCard
|
||||
if currentUser.isBoardAdmin
|
||||
a.js-delete-subtask.toggle-delete-subtask-dialog {{_ "delete"}}...
|
||||
|
||||
a.fa.fa-navicon.subtask-details-menu.js-open-subtask-details-menu(title="{{_ 'subtaskActionsPopup-title'}}")
|
||||
if canModifyCard
|
||||
h2.title.js-open-inlined-form.is-editable
|
||||
+viewer
|
||||
@@ -39,18 +36,6 @@ template(name="subtaskDetail")
|
||||
+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 dir="auto")
|
||||
.edit-controls.clearfix
|
||||
@@ -98,3 +83,19 @@ template(name='subtaskItemDetail')
|
||||
.item-title(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
+viewer
|
||||
= item.title
|
||||
|
||||
template(name="subtaskDeletePopup")
|
||||
p {{_ 'confirm-subtask-delete-popup'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
||||
template(name="subtaskActionsPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-view-subtask(title="{{ subtask.title }}")
|
||||
i.fa.fa-eye
|
||||
| {{_ "view-it"}}
|
||||
if currentUser.isBoardAdmin
|
||||
a.js-delete-subtask.delete-subtask
|
||||
i.fa.fa-trash
|
||||
| {{_ "delete"}} ...
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
BlazeComponent.extendComponent({
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
}).register('subtaskDetail');
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
addSubtask(event) {
|
||||
@@ -15,19 +6,19 @@ BlazeComponent.extendComponent({
|
||||
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 card = ReactiveCache.getCard(cardId);
|
||||
const sortIndex = -1;
|
||||
const crtBoard = Boards.findOne(card.boardId);
|
||||
const crtBoard = ReactiveCache.getBoard(card.boardId);
|
||||
const targetBoard = crtBoard.getDefaultSubtasksBoard();
|
||||
const listId = targetBoard.getDefaultSubtasksListId();
|
||||
|
||||
//Get the full swimlane data for the parent task.
|
||||
const parentSwimlane = Swimlanes.findOne({
|
||||
const parentSwimlane = ReactiveCache.getSwimlane({
|
||||
boardId: crtBoard._id,
|
||||
_id: card.swimlaneId,
|
||||
});
|
||||
//find the swimlane of the same name in the target board.
|
||||
const targetSwimlane = Swimlanes.findOne({
|
||||
const targetSwimlane = ReactiveCache.getSwimlane({
|
||||
boardId: targetBoard._id,
|
||||
title: parentSwimlane.title,
|
||||
});
|
||||
@@ -70,23 +61,17 @@ BlazeComponent.extendComponent({
|
||||
textarea.focus();
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
deleteSubtask() {
|
||||
const subtask = this.currentData().subtask;
|
||||
if (subtask && subtask._id) {
|
||||
subtask.archive();
|
||||
this.toggleDeleteDialog.set(false);
|
||||
}
|
||||
},
|
||||
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
},
|
||||
|
||||
editSubtask(event) {
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-edit-subtask-item');
|
||||
@@ -95,11 +80,6 @@ BlazeComponent.extendComponent({
|
||||
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
|
||||
@@ -111,75 +91,59 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
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,
|
||||
'click .js-open-subtask-details-menu': Popup.open('subtaskActions'),
|
||||
'submit .js-add-subtask': this.addSubtask,
|
||||
'submit .js-edit-subtask-title': this.editSubtask,
|
||||
'click .confirm-subtask-delete': this.deleteSubtask,
|
||||
'click .js-delete-subtask-item': 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() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
// ...
|
||||
}).register('subtaskItemDetail');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'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,
|
||||
});
|
||||
}
|
||||
},
|
||||
'click .js-delete-subtask' : Popup.afterConfirm('subtaskDelete', function () {
|
||||
Popup.back(2);
|
||||
const subtask = this.subtask;
|
||||
if (subtask && subtask._id) {
|
||||
subtask.archive();
|
||||
}
|
||||
}),
|
||||
}
|
||||
]
|
||||
}
|
||||
}).register('subtaskActionsPopup');
|
||||
|
||||
Template.editSubtaskItemForm.helpers({
|
||||
user() {
|
||||
return ReactiveCache.getUser(this.userId);
|
||||
},
|
||||
isBoardAdmin() {
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
.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,22 @@
|
||||
.datepicker-container .fields .left {
|
||||
width: 56%;
|
||||
}
|
||||
.datepicker-container .fields .right {
|
||||
width: 38%;
|
||||
}
|
||||
.datepicker-container .datepicker {
|
||||
width: 100%;
|
||||
}
|
||||
.datepicker-container .datepicker table {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.datepicker-container .datepicker table thead {
|
||||
background: none;
|
||||
}
|
||||
.datepicker-container .datepicker table td,
|
||||
.datepicker-container .datepicker table th {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
.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
|
||||
@@ -0,0 +1,741 @@
|
||||
select,
|
||||
textarea,
|
||||
input:not([type=file]),
|
||||
button {
|
||||
box-sizing: border-box;
|
||||
background-color: #ebebeb;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
margin-bottom: 12px;
|
||||
min-height: 34px;
|
||||
padding: 7px;
|
||||
}
|
||||
select.full,
|
||||
textarea.full,
|
||||
input:not([type=file]).full,
|
||||
button.full {
|
||||
width: 100%;
|
||||
}
|
||||
select.input-error,
|
||||
textarea.input-error,
|
||||
input:not([type=file]).input-error,
|
||||
button.input-error {
|
||||
background-color: #ece9e9;
|
||||
border-color: #ba1212;
|
||||
}
|
||||
select:focus,
|
||||
textarea:focus,
|
||||
input:not([type=file]):focus,
|
||||
button:focus {
|
||||
outline: 0;
|
||||
}
|
||||
input[type="file"] {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
input[type="radio"] {
|
||||
-webkit-appearance: radio;
|
||||
min-height: inherit;
|
||||
}
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"] {
|
||||
transition: background 85ms ease-in, border-color 85ms ease-in;
|
||||
width: 250px;
|
||||
}
|
||||
input[type="text"].inline-input,
|
||||
input[type="password"].inline-input,
|
||||
input[type="email"].inline-input {
|
||||
background: none;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
min-height: 0;
|
||||
height: 18px;
|
||||
width: 200px;
|
||||
}
|
||||
input[type="text"].full-line,
|
||||
input[type="password"].full-line,
|
||||
input[type="email"].full-line {
|
||||
width: 100%;
|
||||
}
|
||||
input[type="email"]:invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
input[type="text"]:hover,
|
||||
input[type="password"]:hover,
|
||||
input[type="email"]:hover,
|
||||
textarea:hover {
|
||||
border-color: #999;
|
||||
}
|
||||
input[type="text"]:hover.input-error,
|
||||
input[type="password"]:hover.input-error,
|
||||
input[type="email"]:hover.input-error,
|
||||
textarea:hover.input-error {
|
||||
border-color: #ba1212;
|
||||
}
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="email"]:focus,
|
||||
textarea:focus {
|
||||
background: #fff;
|
||||
border-color: #318ec4;
|
||||
box-shadow: 0 0 2px #318ec4;
|
||||
}
|
||||
input[type="text"]:focus.input-error,
|
||||
input[type="password"]:focus.input-error,
|
||||
input[type="email"]:focus.input-error,
|
||||
textarea:focus.input-error {
|
||||
background-color: #f8f7f7;
|
||||
border-color: #ba1212;
|
||||
box-shadow: 0 0 2px #d11515;
|
||||
}
|
||||
input[type="text"]:disabled,
|
||||
input[type="password"]:disabled,
|
||||
input[type="email"]:disabled,
|
||||
textarea:disabled {
|
||||
background-color: #dcdcdc;
|
||||
border-color: #bfbfbf;
|
||||
color: #8c8c8c;
|
||||
-webkit-touch-callout: none;
|
||||
user-select: none;
|
||||
}
|
||||
select {
|
||||
max-height: 300px;
|
||||
width: 256px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
select.inline {
|
||||
width: 100%;
|
||||
}
|
||||
option[disabled] {
|
||||
color: #8c8c8c;
|
||||
}
|
||||
textarea {
|
||||
height: 150px;
|
||||
transition: background 85ms ease-in, border-color 85ms ease-in;
|
||||
resize: vertical;
|
||||
width: 100%;
|
||||
}
|
||||
textarea.editor {
|
||||
resize: none;
|
||||
padding-bottom: 22px;
|
||||
}
|
||||
.button {
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
input[type="submit"],
|
||||
button {
|
||||
background: #cfcfcf;
|
||||
background: linear-gradient(#cfcfcf, #c2c2c2);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-weight: 700;
|
||||
line-height: 22px;
|
||||
padding: 7px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
input[type="submit"] .wide,
|
||||
button .wide {
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
input[type="submit"]:hover,
|
||||
button:hover,
|
||||
input[type="submit"]:focus,
|
||||
button:focus {
|
||||
background: #c2c2c2;
|
||||
background: linear-gradient(#c2c2c2, #b5b5b5);
|
||||
}
|
||||
input[type="submit"]:active,
|
||||
button:active {
|
||||
background: #b5b5b5;
|
||||
background: linear-gradient(#b5b5b5, #a8a8a8);
|
||||
box-shadow: inset 0 3px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
input[type="submit"]:active:hover,
|
||||
button:active:hover,
|
||||
input[type="submit"]:active:focus,
|
||||
button:active:focus,
|
||||
input[type="submit"]:active:active,
|
||||
button:active:active {
|
||||
background: #e6e6e6;
|
||||
background: linear-gradient(#e6e6e6, #e6e6e6);
|
||||
}
|
||||
input[type="submit"].primary,
|
||||
button.primary {
|
||||
background: #005377;
|
||||
box-shadow: 0 1px 0 #4d4d4d;
|
||||
color: #fff;
|
||||
}
|
||||
input[type="submit"].primary:hover,
|
||||
button.primary:hover,
|
||||
input[type="submit"].primary:focus,
|
||||
button.primary:focus {
|
||||
background: #004766;
|
||||
}
|
||||
input[type="submit"].primary:active,
|
||||
button.primary:active {
|
||||
background: #01628c;
|
||||
}
|
||||
input[type="submit"].negate:hover,
|
||||
button.negate:hover,
|
||||
input[type="submit"].negate:focus,
|
||||
button.negate:focus {
|
||||
background: #990f0f;
|
||||
background: linear-gradient(#990f0f, #7d0c0c);
|
||||
box-shadow: 0 1px 0 #4d4d4d;
|
||||
color: #fff;
|
||||
}
|
||||
input[type="submit"].negate:active,
|
||||
button.negate:active {
|
||||
background: #7d0c0c;
|
||||
box-shadow: 0 1px 0 #4d4d4d;
|
||||
color: #fff;
|
||||
}
|
||||
input[type="submit"] i.fa,
|
||||
button i.fa {
|
||||
margin-right: 10px;
|
||||
}
|
||||
input[type="submit"].disabled,
|
||||
input[type="submit"]:disabled,
|
||||
input[type="button"].disabled,
|
||||
button.disabled,
|
||||
.button.disabled,
|
||||
input[type="submit"].disabled:hover,
|
||||
input[type="submit"]:disabled:hover,
|
||||
input[type="button"].disabled:hover,
|
||||
button.disabled:hover,
|
||||
.button.disabled:hover,
|
||||
input[type="submit"].disabled:active,
|
||||
input[type="submit"]:disabled:active,
|
||||
input[type="button"].disabled:active,
|
||||
button.disabled:active,
|
||||
.button.disabled:active {
|
||||
background: #cfcfcf;
|
||||
cursor: default;
|
||||
box-shadow: none;
|
||||
color: #a8a8a8;
|
||||
}
|
||||
fieldset {
|
||||
border: 1px solid #bfbfbf;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
input[type="hidden"] {
|
||||
display: none;
|
||||
}
|
||||
.radio-div,
|
||||
.check-div {
|
||||
display: block;
|
||||
margin: 0 0 4px 20px;
|
||||
min-height: 20px;
|
||||
position: relative;
|
||||
}
|
||||
.radio-div input,
|
||||
.check-div input {
|
||||
left: -18px;
|
||||
min-height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
}
|
||||
.radio-div label,
|
||||
.check-div label {
|
||||
font-weight: 400;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
label.form-error {
|
||||
color: #ba1212;
|
||||
}
|
||||
input::-webkit-input-placeholder,
|
||||
textarea::-webkit-input-placeholder,
|
||||
input::-moz-placeholder,
|
||||
textarea::-moz-placeholder {
|
||||
color: #8c8c8c;
|
||||
}
|
||||
.edit-controls,
|
||||
.add-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
@media print {
|
||||
.add-controls {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.edit-controls button[type=submit],
|
||||
.add-controls button[type=submit],
|
||||
.edit-controls input[type=button],
|
||||
.add-controls input[type=button] {
|
||||
float: left;
|
||||
height: 32px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.edit-controls .fa-times-thin,
|
||||
.add-controls .fa-times-thin {
|
||||
font-size: 26px;
|
||||
margin: 3px 10px;
|
||||
}
|
||||
[type="checkbox"]:not(:checked),
|
||||
[type="checkbox"]:checked {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
.materialCheckBox {
|
||||
position: relative;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
z-index: 0;
|
||||
border: 2px solid #5a5a5a;
|
||||
border-radius: 1px;
|
||||
transition: 0.2s;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.materialCheckBox.is-checked {
|
||||
top: -4px;
|
||||
left: -3px;
|
||||
width: 7px;
|
||||
height: 15px;
|
||||
margin-right: 6px;
|
||||
border-top: 2px solid transparent;
|
||||
border-left: 2px solid transparent;
|
||||
transform: rotate(40deg);
|
||||
-webkit-backface-visibility: hidden;
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
.button-link {
|
||||
background: #fff;
|
||||
background: linear-gradient(#fff, #f5f5f5);
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
user-select: none;
|
||||
border: 1px solid #e3e3e3;
|
||||
border-bottom-color: #c2c2c2;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
height: 34px;
|
||||
margin-top: 6px;
|
||||
max-width: 300px;
|
||||
padding: 7px;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
overflow: ellipsis;
|
||||
}
|
||||
.button-link .on {
|
||||
background: #48b512;
|
||||
background: linear-gradient(#48b512, #3d990f);
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
display: none;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
height: 17px;
|
||||
line-height: 17px;
|
||||
margin: 0;
|
||||
padding: 2px 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.button-link.is-on {
|
||||
padding-right: 30px;
|
||||
max-width: 196px;
|
||||
}
|
||||
.button-link.is-on .on {
|
||||
display: block;
|
||||
}
|
||||
.button-link.inline {
|
||||
color: #666;
|
||||
padding: 2px 14px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.button-link.setting {
|
||||
height: 52px;
|
||||
float: left;
|
||||
position: relative;
|
||||
margin-top: 0;
|
||||
}
|
||||
.button-link.setting.disabled {
|
||||
background: #fff;
|
||||
border-color: #e9e9e9;
|
||||
color: #8c8c8c;
|
||||
cursor: default;
|
||||
}
|
||||
.button-link.setting.disabled select {
|
||||
display: none;
|
||||
}
|
||||
.button-link.setting.disabled:hover .label {
|
||||
color: #8c8c8c;
|
||||
}
|
||||
.button-link.setting.disabled,
|
||||
.button-link.setting.disabled:hover,
|
||||
.button-link.setting.disabled:active,
|
||||
.button-link.setting.disabled.primary,
|
||||
.button-link.setting.disabled.primary:hover,
|
||||
.button-link.setting.disabled.primary:active {
|
||||
background: #cfcfcf;
|
||||
border-color: #c2c2c2;
|
||||
border-bottom-color: #b5b5b5;
|
||||
cursor: default;
|
||||
box-shadow: none;
|
||||
color: #a8a8a8;
|
||||
}
|
||||
.button-link.setting .label {
|
||||
color: #8c8c8c;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.button-link.setting:hover .label {
|
||||
color: #eee;
|
||||
}
|
||||
.button-link.setting .value {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.button-link.setting label {
|
||||
display: none;
|
||||
}
|
||||
.button-link.setting select {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
height: 50px;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
}
|
||||
.button-link:hover {
|
||||
background: #318ec4;
|
||||
background: linear-gradient(#318ec4, #2b7cab);
|
||||
border-color: #2e85b8;
|
||||
color: #fff;
|
||||
}
|
||||
.button-link:hover .on {
|
||||
background-image: none;
|
||||
background-color: rgba(255,255,255,0.3);
|
||||
border-color: transparent;
|
||||
}
|
||||
.button-link:active {
|
||||
background: #2e85b8;
|
||||
background: linear-gradient(#2e85b8, #28739f);
|
||||
border-color: #2b7cab;
|
||||
color: #fff;
|
||||
}
|
||||
.button-link .button-link.negate:hover {
|
||||
background: #990f0f;
|
||||
background: linear-gradient(#990f0f, #7d0c0c);
|
||||
border-color: linear-gradient(#990f0f, #7d0c0c);
|
||||
}
|
||||
.button-link .button-link.negate:active {
|
||||
background: #7d0c0c;
|
||||
border-color: #990f0f;
|
||||
}
|
||||
.button-link.primary {
|
||||
background: #48b512;
|
||||
background: linear-gradient(#48b512, #3d990f);
|
||||
border: 1px solid;
|
||||
border-color: #3d990f;
|
||||
color: #fff;
|
||||
}
|
||||
.button-link.primary:hover {
|
||||
background: #3d990f;
|
||||
background: linear-gradient(#3d990f, #327d0c);
|
||||
border-color: #3d990f;
|
||||
}
|
||||
.button-link.danger {
|
||||
background: #ba1212;
|
||||
background: linear-gradient(#ba1212, #8b0e0e);
|
||||
border: 1px solid;
|
||||
border-color: #a21010;
|
||||
color: #fff;
|
||||
}
|
||||
.button-link.danger:hover {
|
||||
background: #a21010;
|
||||
background: linear-gradient(#a21010, #740b0b);
|
||||
border-color: #8b0e0e;
|
||||
}
|
||||
button.quiet-button,
|
||||
button.loud-text-button {
|
||||
background: none;
|
||||
text-align: left;
|
||||
line-height: normal;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
button.quiet-button:active,
|
||||
button.loud-text-button:active {
|
||||
color: #4d4d4d;
|
||||
background: #d3d3d3;
|
||||
box-shadow: none;
|
||||
}
|
||||
button.quiet-button {
|
||||
font-weight: 400;
|
||||
text-decoration: underline;
|
||||
}
|
||||
button.loud-text-button {
|
||||
width: 100%;
|
||||
}
|
||||
button.loud-text-button:hover {
|
||||
color: #111;
|
||||
}
|
||||
.emphasis-button,
|
||||
.quiet-button {
|
||||
border-radius: 3px;
|
||||
user-select: none;
|
||||
color: #8c8c8c;
|
||||
display: block;
|
||||
margin: 2px 0;
|
||||
padding: 6px 8px;
|
||||
position: relative;
|
||||
}
|
||||
.emphasis-button.w-img,
|
||||
.quiet-button.w-img {
|
||||
padding-left: 28px;
|
||||
}
|
||||
.emphasis-button:hover,
|
||||
.quiet-button:hover {
|
||||
color: #4d4d4d;
|
||||
background: #dcdcdc;
|
||||
}
|
||||
.emphasis-button:active,
|
||||
.quiet-button:active {
|
||||
color: #4d4d4d;
|
||||
background: #d3d3d3;
|
||||
}
|
||||
.quiet-button-large {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
.emphasis-button {
|
||||
color: #74663e;
|
||||
background: #ecdfbb;
|
||||
}
|
||||
.emphasis-button:hover {
|
||||
color: #53492d;
|
||||
background: #e7d6a7;
|
||||
}
|
||||
.emphasis-button:active {
|
||||
color: #53492d;
|
||||
background: #e1cc93;
|
||||
}
|
||||
.is-editable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.big-link {
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
margin: 6px 0 6px 40px;
|
||||
padding: 11px;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.big-link .text {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.big-link:hover {
|
||||
background: #dcdcdc;
|
||||
}
|
||||
.big-link.options {
|
||||
padding-right: 41px;
|
||||
}
|
||||
.big-link .option {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 6px;
|
||||
}
|
||||
.big-link.none {
|
||||
color: #8c8c8c;
|
||||
text-decoration: none;
|
||||
}
|
||||
.big-link.none:hover {
|
||||
background: transparent;
|
||||
}
|
||||
.big-link.avatar-changer {
|
||||
padding-right: 51px;
|
||||
}
|
||||
.big-link.avatar-changer .member {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
.big-link.avatar-changer .member .member-avatar {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
.big-link.avatar-changer .member .member-initials {
|
||||
font-size: 16px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
max-height: 40px;
|
||||
}
|
||||
.show-more {
|
||||
border-radius: 3px;
|
||||
color: #8c8c8c;
|
||||
display: block;
|
||||
padding: 16px 8px 16px 40px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
.show-more:hover {
|
||||
background: #dcdcdc;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.show-more.compact {
|
||||
padding: 12px 8px;
|
||||
margin: 8px 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
.board-widget .show-more {
|
||||
padding: 12px 8px 12px 40px;
|
||||
}
|
||||
.uploader {
|
||||
clear: both;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
}
|
||||
.uploader .realfile {
|
||||
cursor: pointer;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
font-size: 23px;
|
||||
}
|
||||
.uploader .realfile input[type="file"] {
|
||||
cursor: pointer;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
font-size: 23px;
|
||||
}
|
||||
.uploader:hover .fakefile {
|
||||
background: #318ec4;
|
||||
background: linear-gradient(#318ec4, #2b7cab);
|
||||
border-color: #2e85b8;
|
||||
color: #fff;
|
||||
}
|
||||
.dropdown-menu {
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.dropdown-menu li {
|
||||
border-top: none;
|
||||
}
|
||||
.dropdown-menu li a {
|
||||
padding: 4px 12px 4px 8px;
|
||||
}
|
||||
.dropdown-menu li a img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.dropdown-menu li a .minicard-label {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border-radius: 2px;
|
||||
margin: 2px 7px -2px -2px;
|
||||
display: inline-block;
|
||||
}
|
||||
.dropdown-menu li.active {
|
||||
background: #005377;
|
||||
}
|
||||
.dropdown-menu li.active a,
|
||||
.dropdown-menu li.active .quiet {
|
||||
color: #fff;
|
||||
}
|
||||
.material-toggle-switch {
|
||||
display: flex;
|
||||
}
|
||||
.toggle-label {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 20px;
|
||||
width: 44px;
|
||||
background-color: #a6a6a6;
|
||||
border-radius: 100px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.toggle-label:after {
|
||||
position: absolute;
|
||||
left: -2px;
|
||||
top: -3px;
|
||||
display: block;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 100px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 3px 3px rgba(0,0,0,0.05);
|
||||
content: '';
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.toggle-label:active:after {
|
||||
transform: scale(1.15, 0.85);
|
||||
}
|
||||
.toggle-switch:checked ~ .toggle-label {
|
||||
background-color: #6fbeb5;
|
||||
}
|
||||
.toggle-switch:checked ~ .toggle-label:after {
|
||||
left: 20px;
|
||||
background-color: #179588;
|
||||
}
|
||||
.toggle-switch:checked:disabled ~ .toggle-label {
|
||||
background-color: #d5d5d5;
|
||||
pointer-events: none;
|
||||
}
|
||||
.toggle-switch:checked:disabled ~ .toggle-label:after {
|
||||
background-color: #bcbdbc;
|
||||
}
|
||||
.toggle-switch {
|
||||
display: none;
|
||||
}
|
||||
.toggle-switch-title {
|
||||
margin: 0 0.5em;
|
||||
display: flex;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.edit-controls .fa-times-thin,
|
||||
.add-controls .fa-times-thin {
|
||||
margin: 3px 20px;
|
||||
}
|
||||
}
|
||||
@@ -1,695 +0,0 @@
|
||||
@import 'nib'
|
||||
|
||||
select,
|
||||
textarea,
|
||||
input:not([type=file]),
|
||||
button
|
||||
box-sizing: border-box
|
||||
background-color: #ebebeb
|
||||
border: 1px solid #ccc
|
||||
border-radius: 3px
|
||||
display: block
|
||||
margin-bottom: 12px
|
||||
min-height: 34px
|
||||
padding: 7px
|
||||
|
||||
&.full
|
||||
width: 100%
|
||||
|
||||
&.input-error
|
||||
background-color: #ece9e9
|
||||
border-color: #ba1212
|
||||
|
||||
&:focus
|
||||
outline: 0
|
||||
|
||||
input[type="file"]
|
||||
margin-bottom: 16px
|
||||
|
||||
input[type="radio"]
|
||||
-webkit-appearance: radio
|
||||
min-height: inherit
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"]
|
||||
transition: background 85ms ease-in,
|
||||
border-color 85ms ease-in
|
||||
width: 250px
|
||||
|
||||
&.inline-input
|
||||
background: none
|
||||
border: 0
|
||||
margin: 0
|
||||
padding: 2px
|
||||
min-height: 0
|
||||
height: 18px
|
||||
width: 200px
|
||||
|
||||
&.full-line
|
||||
width: 100%
|
||||
|
||||
input[type="email"]:invalid
|
||||
box-shadow: none
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="email"],
|
||||
textarea
|
||||
|
||||
&:hover
|
||||
border-color: #999
|
||||
|
||||
&.input-error
|
||||
border-color: #ba1212
|
||||
|
||||
&:focus
|
||||
background: #fff
|
||||
border-color: #318ec4
|
||||
box-shadow: 0 0 2px #318ec4
|
||||
|
||||
&.input-error
|
||||
background-color: #f8f7f7
|
||||
border-color: #ba1212
|
||||
box-shadow: 0 0 2px #d11515
|
||||
|
||||
&:disabled
|
||||
background-color: #dcdcdc
|
||||
border-color: #bfbfbf
|
||||
color: #8c8c8c
|
||||
-webkit-touch-callout: none
|
||||
user-select: none
|
||||
|
||||
select
|
||||
max-height: 300px
|
||||
width: 256px
|
||||
margin-bottom: 8px
|
||||
|
||||
&.inline
|
||||
width: 100%
|
||||
|
||||
option[disabled]
|
||||
color: #8c8c8c
|
||||
|
||||
textarea
|
||||
height: 150px
|
||||
transition: background 85ms ease-in,
|
||||
border-color 85ms ease-in
|
||||
resize: vertical
|
||||
width: 100%
|
||||
|
||||
&.editor
|
||||
resize: none
|
||||
padding-bottom: 22px
|
||||
|
||||
|
||||
.button
|
||||
border-radius: 3px
|
||||
text-decoration: none
|
||||
position: relative
|
||||
|
||||
input[type="submit"],
|
||||
button
|
||||
background: #cfcfcf
|
||||
background: linear-gradient(#cfcfcf, #c2c2c2)
|
||||
border: none
|
||||
cursor: pointer
|
||||
display: inline-block
|
||||
font-weight: 700
|
||||
line-height: 22px
|
||||
padding: 7px 20px
|
||||
text-align: center
|
||||
|
||||
.wide
|
||||
padding-left: 30px
|
||||
padding-right: 30px
|
||||
|
||||
&:hover,
|
||||
&:focus
|
||||
background: #c2c2c2
|
||||
background: linear-gradient(#c2c2c2, #b5b5b5)
|
||||
|
||||
&:active
|
||||
background: #b5b5b5
|
||||
background: linear-gradient(#b5b5b5, #a8a8a8)
|
||||
box-shadow: inset 0 3px 6px rgba(0, 0, 0, .1)
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active
|
||||
background: #e6e6e6
|
||||
background: linear-gradient(#e6e6e6, #e6e6e6)
|
||||
|
||||
&.primary
|
||||
background: #005377
|
||||
box-shadow: 0 1px 0 #4d4d4d
|
||||
color: white
|
||||
|
||||
&:hover,
|
||||
&:focus
|
||||
background: #004766
|
||||
|
||||
&:active
|
||||
background: #01628C
|
||||
|
||||
&.negate
|
||||
&:hover,
|
||||
&:focus
|
||||
background: #990f0f
|
||||
background: linear-gradient(#990f0f, #7d0c0c)
|
||||
box-shadow: 0 1px 0 #4d4d4d
|
||||
color: #fff
|
||||
|
||||
&:active
|
||||
background: #7d0c0c
|
||||
box-shadow: 0 1px 0 #4d4d4d
|
||||
color: #fff
|
||||
|
||||
i.fa
|
||||
margin-right: 10px
|
||||
|
||||
input[type="submit"].disabled,
|
||||
input[type="submit"]:disabled,
|
||||
input[type="button"].disabled,
|
||||
button.disabled,
|
||||
.button.disabled
|
||||
|
||||
&,
|
||||
&:hover,
|
||||
&:active
|
||||
background: #cfcfcf
|
||||
cursor: default
|
||||
box-shadow: none
|
||||
color: #a8a8a8
|
||||
|
||||
fieldset
|
||||
border: 1px solid #bfbfbf
|
||||
padding: 15px
|
||||
margin-bottom: 15px
|
||||
|
||||
input[type="hidden"]
|
||||
display: none
|
||||
|
||||
.radio-div,
|
||||
.check-div
|
||||
display: block
|
||||
margin: 0 0 4px 20px
|
||||
min-height: 20px
|
||||
position: relative
|
||||
|
||||
input
|
||||
left: -18px
|
||||
min-height: 0
|
||||
margin: 0
|
||||
padding: 0
|
||||
position: absolute
|
||||
top: 2px
|
||||
|
||||
label
|
||||
font-weight: 400
|
||||
|
||||
label
|
||||
display: block
|
||||
font-weight: 700
|
||||
margin-bottom: 4px
|
||||
|
||||
&.form-error
|
||||
color: #ba1212
|
||||
|
||||
input,
|
||||
textarea
|
||||
&::-webkit-input-placeholder,
|
||||
&::-moz-placeholder
|
||||
color: #8c8c8c
|
||||
|
||||
.edit-controls,
|
||||
.add-controls
|
||||
display: flex
|
||||
align-items: center
|
||||
margin-top: 0px
|
||||
margin-bottom: 12px
|
||||
|
||||
button[type=submit]
|
||||
input[type=button]
|
||||
float: left
|
||||
height: 32px
|
||||
margin-bottom: 0px
|
||||
|
||||
.fa-times-thin
|
||||
font-size: 26px
|
||||
margin: 3px 10px
|
||||
|
||||
// Material Design checkboxes
|
||||
[type="checkbox"]:not(:checked),
|
||||
[type="checkbox"]:checked
|
||||
position: absolute
|
||||
left: -9999px
|
||||
visibility: hidden
|
||||
display: none
|
||||
|
||||
.materialCheckBox
|
||||
position: relative
|
||||
width: 13px
|
||||
height: @width
|
||||
z-index: 0
|
||||
border: 2px solid #5a5a5a
|
||||
border-radius: 1px
|
||||
transition: .2s
|
||||
margin: 0
|
||||
cursor: pointer
|
||||
|
||||
&.is-checked
|
||||
top: -4px
|
||||
left: -3px
|
||||
width: 7px
|
||||
height: 15px
|
||||
margin-right: 6px
|
||||
border-top: 2px solid transparent
|
||||
border-left: 2px solid transparent
|
||||
transform: rotate(40deg)
|
||||
-webkit-backface-visibility: hidden
|
||||
transform-origin: 100% 100%
|
||||
|
||||
.button-link
|
||||
background: #fff
|
||||
background: linear-gradient(#fff, #f5f5f5)
|
||||
border-radius: 3px
|
||||
box-sizing: border-box
|
||||
user-select: none
|
||||
border: 1px solid #e3e3e3
|
||||
border-bottom-color: #c2c2c2
|
||||
cursor: pointer
|
||||
display: block
|
||||
font-weight: 700
|
||||
height: 34px
|
||||
margin-top: 6px
|
||||
max-width: 300px
|
||||
padding: 7px
|
||||
position: relative
|
||||
text-decoration: none
|
||||
overflow: ellipsis
|
||||
|
||||
.on
|
||||
background: #48b512
|
||||
background: linear-gradient(#48b512, #3d990f)
|
||||
border-radius: 3px
|
||||
color: #fff
|
||||
display: none
|
||||
font-size: 12px
|
||||
font-weight: 700
|
||||
height: 17px
|
||||
line-height: @height
|
||||
margin: 0
|
||||
padding: 2px 4px
|
||||
position: absolute
|
||||
right: 5px
|
||||
top: 5px
|
||||
text-align: center
|
||||
|
||||
&.is-on
|
||||
padding-right: 30px
|
||||
max-width: 196px
|
||||
|
||||
.on
|
||||
display: block
|
||||
|
||||
&.inline
|
||||
color: #666
|
||||
padding: 2px 14px
|
||||
margin-left: 4px
|
||||
|
||||
&.setting
|
||||
height: 52px
|
||||
float: left
|
||||
position: relative
|
||||
margin-top: 0
|
||||
|
||||
&.disabled
|
||||
background: #fff
|
||||
border-color: #e9e9e9
|
||||
color: #8c8c8c
|
||||
cursor: default
|
||||
|
||||
select
|
||||
display: none
|
||||
|
||||
&:hover .label
|
||||
color: #8c8c8c
|
||||
|
||||
&,
|
||||
&:hover,
|
||||
&:active,
|
||||
&.primary,
|
||||
&.primary:hover,
|
||||
&.primary:active
|
||||
background: #cfcfcf
|
||||
border-color: #c2c2c2
|
||||
border-bottom-color: #b5b5b5
|
||||
cursor: default
|
||||
box-shadow: none
|
||||
color: #a8a8a8
|
||||
|
||||
.label
|
||||
color: #8c8c8c
|
||||
display: block
|
||||
font-size: 12px
|
||||
line-height: 14px
|
||||
margin-bottom: 0
|
||||
|
||||
&:hover .label
|
||||
color: #eee
|
||||
|
||||
.value
|
||||
display: block
|
||||
font-size: 18px
|
||||
line-height: 24px
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
|
||||
label
|
||||
display: none
|
||||
|
||||
select
|
||||
border: none
|
||||
cursor: pointer
|
||||
height: 50px
|
||||
left: 0
|
||||
margin: 0
|
||||
opacity: 0
|
||||
position: absolute
|
||||
top: 0
|
||||
z-index: 2
|
||||
width: 100%
|
||||
|
||||
&:hover
|
||||
background: #318ec4
|
||||
background: linear-gradient(#318ec4, #2b7cab)
|
||||
border-color: #2e85b8
|
||||
color: #fff
|
||||
|
||||
.on
|
||||
background-image: none
|
||||
background-color: rgba(255, 255, 255, .3)
|
||||
border-color: transparent
|
||||
|
||||
&:active
|
||||
background: #2e85b8
|
||||
background: linear-gradient(#2e85b8, #28739f)
|
||||
border-color: #2b7cab
|
||||
color: #fff
|
||||
|
||||
.button-link.negate
|
||||
|
||||
&:hover
|
||||
background: #990f0f
|
||||
background: linear-gradient(#990f0f, #7d0c0c)
|
||||
border-color: @background
|
||||
|
||||
&:active
|
||||
background: #7d0c0c
|
||||
border-color: #990f0f
|
||||
|
||||
|
||||
&.primary
|
||||
background: #48b512
|
||||
background: linear-gradient(#48b512, #3d990f)
|
||||
border: 1px solid
|
||||
border-color: #3d990f
|
||||
color: #fff
|
||||
|
||||
&:hover
|
||||
background: #3d990f
|
||||
background: linear-gradient(#3d990f, #327d0c)
|
||||
border-color: #3d990f
|
||||
|
||||
&.danger
|
||||
background: #ba1212
|
||||
background: linear-gradient(#ba1212, #8b0e0e)
|
||||
border: 1px solid
|
||||
border-color: #a21010
|
||||
color: #fff
|
||||
|
||||
&:hover
|
||||
background: #a21010
|
||||
background: linear-gradient(#a21010, #740b0b)
|
||||
border-color: #8b0e0e
|
||||
|
||||
button
|
||||
&.quiet-button,
|
||||
&.loud-text-button
|
||||
background: none
|
||||
text-align: left
|
||||
line-height: normal
|
||||
border: none
|
||||
box-shadow: none
|
||||
|
||||
&:active
|
||||
color: #4d4d4d
|
||||
background: #d3d3d3
|
||||
box-shadow: none
|
||||
|
||||
&.quiet-button
|
||||
font-weight: 400
|
||||
text-decoration: underline
|
||||
|
||||
&.loud-text-button
|
||||
width: 100%
|
||||
|
||||
&:hover
|
||||
color: #111
|
||||
|
||||
.emphasis-button,
|
||||
.quiet-button
|
||||
border-radius: 3px
|
||||
user-select: none
|
||||
color: #8c8c8c
|
||||
display: block
|
||||
margin: 2px 0
|
||||
padding: 6px 8px
|
||||
position: relative
|
||||
|
||||
&.w-img
|
||||
padding-left: 28px
|
||||
|
||||
&:hover
|
||||
color: #4d4d4d
|
||||
background: #dcdcdc
|
||||
|
||||
&:active
|
||||
color: #4d4d4d
|
||||
background: #d3d3d3
|
||||
|
||||
.quiet-button-large
|
||||
padding: 16px 24px
|
||||
|
||||
.emphasis-button
|
||||
color: #74663e
|
||||
background: #ecdfbb
|
||||
|
||||
&:hover
|
||||
color: #53492d
|
||||
background: #e7d6a7
|
||||
|
||||
&:active
|
||||
color: #53492d
|
||||
background: #e1cc93
|
||||
|
||||
.is-editable
|
||||
cursor: pointer
|
||||
|
||||
.big-link
|
||||
border-radius: 3px
|
||||
display: block
|
||||
margin: 6px 0 6px 40px
|
||||
padding: 11px
|
||||
position: relative
|
||||
text-decoration: none
|
||||
font-size: 16px
|
||||
line-height: 20px
|
||||
|
||||
.text
|
||||
text-decoration: underline
|
||||
|
||||
&:hover
|
||||
background: #dcdcdc
|
||||
|
||||
&.options
|
||||
padding-right: 41px
|
||||
|
||||
.option
|
||||
height: 30px
|
||||
width: @height
|
||||
position: absolute
|
||||
right: 6px
|
||||
top: 6px
|
||||
|
||||
&.none
|
||||
color: #8c8c8c
|
||||
text-decoration: none
|
||||
|
||||
&:hover
|
||||
background: transparent
|
||||
|
||||
&.avatar-changer
|
||||
padding-right: 51px
|
||||
|
||||
.member
|
||||
border: 1px solid #ccc
|
||||
border-radius: 3px
|
||||
height: 40px
|
||||
width: @height
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
|
||||
.member-avatar
|
||||
height: 40px
|
||||
width: @height
|
||||
|
||||
.member-initials
|
||||
font-size: 16px
|
||||
height: 40px
|
||||
line-height: @height
|
||||
max-height: @height
|
||||
|
||||
.show-more
|
||||
border-radius: 3px
|
||||
color: #8c8c8c
|
||||
display: block
|
||||
padding: 16px 8px 16px 40px
|
||||
margin: 8px 0
|
||||
|
||||
&:hover
|
||||
background: #dcdcdc
|
||||
text-decoration: underline
|
||||
|
||||
&.compact
|
||||
padding: 12px 8px
|
||||
margin: 8px 0 0
|
||||
text-align: center
|
||||
|
||||
.board-widget .show-more
|
||||
padding: 12px 8px 12px 40px
|
||||
|
||||
.uploader
|
||||
clear: both
|
||||
cursor: pointer
|
||||
position: relative
|
||||
height: 34px
|
||||
width: 100%
|
||||
|
||||
.realfile
|
||||
cursor: pointer
|
||||
height: 34px
|
||||
line-height: @height
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
width: 100%
|
||||
z-index: 2
|
||||
font-size: 23px
|
||||
|
||||
input[type="file"]
|
||||
cursor: pointer
|
||||
height: 34px
|
||||
line-height: @height
|
||||
margin: 0
|
||||
opacity: 0
|
||||
padding: 0
|
||||
width: 100%
|
||||
z-index: 2
|
||||
font-size: 23px
|
||||
|
||||
&:hover .fakefile
|
||||
background: #318ec4
|
||||
background: linear-gradient(#318ec4, #2b7cab)
|
||||
border-color: #2e85b8
|
||||
color: #fff
|
||||
|
||||
.dropdown-menu
|
||||
border-radius: 2px
|
||||
overflow: hidden
|
||||
|
||||
li
|
||||
border-top: none
|
||||
|
||||
a
|
||||
padding: 4px 12px 4px 8px
|
||||
|
||||
img
|
||||
width: 18px
|
||||
height: @width
|
||||
margin-right: 5px
|
||||
vertical-align: middle
|
||||
|
||||
.minicard-label
|
||||
width: 11px
|
||||
height: @width
|
||||
border-radius: 2px
|
||||
margin: 2px 7px -2px -2px
|
||||
display: inline-block
|
||||
|
||||
&.active
|
||||
background: #005377
|
||||
|
||||
a, .quiet
|
||||
color: white
|
||||
|
||||
// Material Design Toggle Switch
|
||||
.material-toggle-switch
|
||||
display: flex
|
||||
|
||||
.toggle-label
|
||||
position: relative
|
||||
display: block
|
||||
height: 20px
|
||||
width: 44px
|
||||
background-color: #a6a6a6
|
||||
border-radius: 100px
|
||||
cursor: pointer
|
||||
transition: all 0.3s ease
|
||||
|
||||
&:after
|
||||
position: absolute
|
||||
left: -2px
|
||||
top: -3px
|
||||
display: block
|
||||
width: 26px
|
||||
height: 26px
|
||||
border-radius: 100px
|
||||
background-color: #fff
|
||||
box-shadow: 0px 3px 3px rgba(0,0,0,0.05)
|
||||
content: ''
|
||||
transition: all 0.3s ease
|
||||
|
||||
&:active
|
||||
&:after
|
||||
transform: scale(1.15, 0.85)
|
||||
|
||||
.toggle-switch:checked ~ .toggle-label
|
||||
background-color: #6fbeb5
|
||||
|
||||
&:after
|
||||
left: 20px
|
||||
background-color: #179588
|
||||
|
||||
.toggle-switch:checked:disabled ~ .toggle-label
|
||||
background-color: #d5d5d5
|
||||
pointer-events: none
|
||||
|
||||
&:after
|
||||
background-color: #bcbdbc
|
||||
|
||||
.toggle-switch
|
||||
display: none
|
||||
|
||||
.toggle-switch-title
|
||||
margin: 0 0.5em
|
||||
display: flex
|
||||
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.edit-controls,
|
||||
.add-controls
|
||||
.fa-times-thin
|
||||
margin: 3px 20px
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
|
||||
export function csvGetMembersToMap(data) {
|
||||
// we will work on the list itself (an ordered array of objects) when a
|
||||
// mapping is done, we add a 'wekan' field to the object representing the
|
||||
@@ -28,7 +30,7 @@ export function csvGetMembersToMap(data) {
|
||||
username: importedMember,
|
||||
id: importedMember,
|
||||
};
|
||||
const wekanUser = Users.findOne({ username: importedMember.username });
|
||||
const wekanUser = ReactiveCache.getUser({ username: importedMember.username });
|
||||
if (wekanUser) importedMember.wekanId = wekanUser._id;
|
||||
membersToMap.push(importedMember);
|
||||
}
|
||||
|
||||
Loaded 100 of 1272 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user