Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46e881bea3 | ||
|
|
5d1714dbd5 | ||
|
|
bd1de94312 | ||
|
|
06937d0aa0 | ||
|
|
9e39731133 | ||
|
|
3908cd5413 | ||
|
|
6be1a33093 | ||
|
|
c656cbf6ae | ||
|
|
83045a6512 | ||
|
|
199485640f | ||
|
|
2e41ff1121 | ||
|
|
487c6fae8d | ||
|
|
ea64b17b82 | ||
|
|
6d0578fd5a | ||
|
|
76a4deda78 | ||
|
|
595abd303c | ||
|
|
5dd6466c0a | ||
|
|
38655ee478 | ||
|
|
23ca25a1ca | ||
|
|
120cb4820e | ||
|
|
3ee0add6f6 | ||
|
|
8911fe5c8d | ||
|
|
1d9346513e | ||
|
|
62150ce6c4 | ||
|
|
b2d4135dca | ||
|
|
49e3e01ece | ||
|
|
23c2ff0e29 | ||
|
|
a550c255e6 | ||
|
|
6b897faaa8 | ||
|
|
48255f6f1e | ||
|
|
45bc3bd3db | ||
|
|
5ab20a9257 | ||
|
|
88e4918208 | ||
|
|
521ef8b6da | ||
|
|
0d86dfa571 | ||
|
|
472350e56f | ||
|
|
68569b9640 | ||
|
|
823f11d5dd | ||
|
|
ae19018831 | ||
|
|
d5b09c7b3e | ||
|
|
d3b4aa4dc7 | ||
|
|
c2da477735 | ||
|
|
843ff8eaaa | ||
|
|
c5143e2c54 | ||
|
|
0596f5e864 | ||
|
|
8ca1b25daf | ||
|
|
1f76159bfe | ||
|
|
39ece35f52 | ||
|
|
63c1bb9e17 | ||
|
|
354622c5ad | ||
|
|
df7fd8c900 | ||
|
|
7957690fca | ||
|
|
63d19447a0 | ||
|
|
018a84ec59 | ||
|
|
e5c0e623a0 | ||
|
|
9a2d3e15e8 | ||
|
|
c07e262e68 | ||
|
|
cacb2b91ee | ||
|
|
6f269d598f | ||
|
|
57ea254bff | ||
|
|
7e183c838f | ||
|
|
3ffdfd9736 | ||
|
|
621c701bef | ||
|
|
3c24c9966c | ||
|
|
995de525d9 | ||
|
|
ae30b1220a | ||
|
|
e380ad26c2 | ||
|
|
9bb31d2380 | ||
|
|
8ec1e4f3fa | ||
|
|
9f0f6841b0 | ||
|
|
5d18f98b44 | ||
|
|
e738177e08 | ||
|
|
bb201935b9 | ||
|
|
bcfe6bd616 | ||
|
|
9c6352a7bc | ||
|
|
9110e41f20 | ||
|
|
7ae871d3a6 | ||
|
|
0ddf027d43 | ||
|
|
45124a39f3 | ||
|
|
4940af0573 | ||
|
|
924b5d1733 | ||
|
|
2119a3a51e | ||
|
|
dcda99ddf3 | ||
|
|
0e35dcd5ad | ||
|
|
991d42ca0b | ||
|
|
ac242c96b7 | ||
|
|
79d45c8e33 | ||
|
|
8e9f7102f9 | ||
|
|
1952dc309b | ||
|
|
5e7767247f | ||
|
|
676bf686c7 | ||
|
|
5cd6985368 | ||
|
|
807e72dcce | ||
|
|
71702b5821 | ||
|
|
40c56248f1 | ||
|
|
74ac7d8d3f | ||
|
|
e7186b47b0 | ||
|
|
12aa7ea2cb | ||
|
|
ef584e2826 | ||
|
|
f902be10b6 | ||
|
|
29b835de59 | ||
|
|
6ff9c5b58d | ||
|
|
1b86aeb4f5 | ||
|
|
dce99c00be | ||
|
|
e47c700c36 | ||
|
|
09506c78f3 | ||
|
|
9ec1fc0902 | ||
|
|
0b3ec2c19e | ||
|
|
a94bec921c | ||
|
|
3884da3c06 | ||
|
|
d3fa177c7f | ||
|
|
d2176e2c08 | ||
|
|
8d9c59486d | ||
|
|
0e7c2b4b94 | ||
|
|
5c9bc1c745 | ||
|
|
5e7efa0098 | ||
|
|
424862e313 | ||
|
|
9b80ad5a3c | ||
|
|
96c6fa0276 | ||
|
|
69305e1833 | ||
|
|
8aa74ab65e | ||
|
|
aee12c2494 | ||
|
|
83976b3229 | ||
|
|
39f8a138d7 | ||
|
|
120e4af7b2 | ||
|
|
15bddbc86a | ||
|
|
4e8b193801 | ||
|
|
37b65996a0 | ||
|
|
9cd0d3488f | ||
|
|
eaf07cef07 | ||
|
|
02a8775537 | ||
|
|
f2d5698acb | ||
|
|
b0c0e456af | ||
|
|
0857a2ea91 | ||
|
|
1f2e6d33ce | ||
|
|
250e0690d1 | ||
|
|
e709094533 | ||
|
|
4a5b16ee2e | ||
|
|
378d64f99a | ||
|
|
4395760a40 | ||
|
|
8417fae89c | ||
|
|
6c8fae944c | ||
|
|
decab9256b | ||
|
|
183ed35ce8 | ||
|
|
2fcf85fd61 | ||
|
|
3613c1c75e | ||
|
|
e7813046f6 | ||
|
|
9871bf1963 | ||
|
|
b32e7b659b | ||
|
|
2201372744 | ||
|
|
7431a082e2 | ||
|
|
6fe3edebb1 | ||
|
|
4b6406c264 | ||
|
|
86f6bdf9af | ||
|
|
1eeed6645d | ||
|
|
bb8c4325c6 | ||
|
|
07a3301414 | ||
|
|
1590701fc9 | ||
|
|
1d4a65d0b4 | ||
|
|
c845d25ae5 | ||
|
|
ceec27bf90 | ||
|
|
4b90424773 | ||
|
|
31eb9b26b4 | ||
|
|
a8a3c9ad19 | ||
|
|
5cfa03ec07 | ||
|
|
17ab5978ad | ||
|
|
d4c69c3932 | ||
|
|
3778a9ebcf | ||
|
|
880d94e220 | ||
|
|
37607f72b2 | ||
|
|
b1c26e6f64 | ||
|
|
8164af281d | ||
|
|
af676c8f26 | ||
|
|
b8dc7ff18e | ||
|
|
4aee129cdc | ||
|
|
03d29dd674 | ||
|
|
5a6f84ce34 | ||
|
|
0e593c7d81 | ||
|
|
4de830d663 | ||
|
|
769bb7a55d | ||
|
|
ed6c6e5a25 | ||
|
|
302ba75729 | ||
|
|
69dc8f304c | ||
|
|
e43002d5ad | ||
|
|
edd07befe2 | ||
|
|
5cc6a2f7d0 | ||
|
|
926f39c96b | ||
|
|
abd38082de | ||
|
|
5ca90f4d22 | ||
|
|
c9586e579a | ||
|
|
4cadc5f4f3 | ||
|
|
8dfb6916c5 | ||
|
|
6066be944a | ||
|
|
e9f8279dbe | ||
|
|
454d3b5bbe | ||
|
|
fe185dd38a | ||
|
|
522b14d96d | ||
|
|
d98b489263 | ||
|
|
47ecc654b8 | ||
|
|
b9a4ae654e | ||
|
|
c40668be3f | ||
|
|
a9d77b975e | ||
|
|
085bf3b6e6 | ||
|
|
91dae9795e | ||
|
|
42081db0d8 | ||
|
|
3af417209c | ||
|
|
b6f60c08e5 | ||
|
|
03d7940829 | ||
|
|
0b263cf582 | ||
|
|
8ef60a06ad | ||
|
|
91eb4b941d | ||
|
|
2171690d22 | ||
|
|
6819303047 | ||
|
|
88e19bd011 | ||
|
|
09b9f690b1 | ||
|
|
7a9c397264 | ||
|
|
c2b7525864 | ||
|
|
e2ecd490ff | ||
|
|
1d3673e9d3 | ||
|
|
5bd85f975c | ||
|
|
89ab99b551 | ||
|
|
01de3f187c | ||
|
|
dcd7925571 | ||
|
|
34b6aa0858 | ||
|
|
73fb62e941 | ||
|
|
55ced412a0 | ||
|
|
04a8d85836 | ||
|
|
392b701956 | ||
|
|
dee098c125 | ||
|
|
56e26a65ae | ||
|
|
69509d597f | ||
|
|
bc7b18abe7 | ||
|
|
4a95cb42cd | ||
|
|
db311f677b | ||
|
|
685fb98e59 | ||
|
|
6dd98285a6 | ||
|
|
e225d29d7a | ||
|
|
eff7d123f2 | ||
|
|
4d62fe83f4 | ||
|
|
a37ca01703 | ||
|
|
ba05f383ca | ||
|
|
4ea4913d72 | ||
|
|
856f3bf864 | ||
|
|
238b0f0725 | ||
|
|
a79a5c7299 | ||
|
|
59580e4b0f | ||
|
|
7e05e02137 | ||
|
|
d4a1611b86 | ||
|
|
77abde7836 | ||
|
|
4c60916191 | ||
|
|
634eec7eb6 | ||
|
|
4ccbbc78cf | ||
|
|
4e1c0fdce8 | ||
|
|
9fa1d846e6 | ||
|
|
969dca292e | ||
|
|
84fde1ecfc | ||
|
|
db9ee59d9f | ||
|
|
20a2ea3e7a | ||
|
|
39b4ada26d | ||
|
|
3ddfe7c1fd | ||
|
|
f7955d5fd7 | ||
|
|
f115afb64e | ||
|
|
3145ec65a3 | ||
|
|
e2f690d0db | ||
|
|
e6c8c04394 | ||
|
|
a7741df5e4 | ||
|
|
912577cab3 | ||
|
|
cec43a63a7 | ||
|
|
10c9fc25a9 | ||
|
|
16665bccf9 | ||
|
|
103cfb936d | ||
|
|
4bb8f3ab27 | ||
|
|
36a4740429 | ||
|
|
ed54c09b48 | ||
|
|
6eb90238b1 | ||
|
|
aad300613d | ||
|
|
a2f8e54b67 | ||
|
|
c9a10efe5c | ||
|
|
7e2f802169 | ||
|
|
9df5a0f530 | ||
|
|
16971ff1d8 | ||
|
|
bef544ddc8 | ||
|
|
2c1f40357d | ||
|
|
ea544caf83 | ||
|
|
be9e98b2a1 | ||
|
|
59a3ac1f37 | ||
|
|
be22ca332b | ||
|
|
983e34ef80 | ||
|
|
c9830bc262 | ||
|
|
fc21c6d1d6 | ||
|
|
960f332dce | ||
|
|
d532a3591f | ||
|
|
03a9f4766a | ||
|
|
96f3e3a958 | ||
|
|
a249ffc805 | ||
|
|
5f53f8cc01 | ||
|
|
ca3f621572 | ||
|
|
a0e1b6f918 | ||
|
|
c76d44b5c7 | ||
|
|
ccc9efb270 | ||
|
|
2ebc34e901 | ||
|
|
04566d3f40 | ||
|
|
1083a92816 | ||
|
|
bb7490a32b | ||
|
|
9e8399612b | ||
|
|
df54863e72 | ||
|
|
8f340326b0 | ||
|
|
42975bc8ab | ||
|
|
adb5487cdc | ||
|
|
4aad9ca498 | ||
|
|
972d023e95 | ||
|
|
acabef4652 | ||
|
|
f77651ba05 | ||
|
|
23e349d277 | ||
|
|
37d9a6659a | ||
|
|
8796c46810 | ||
|
|
0ebb427a00 | ||
|
|
4960891bd9 | ||
|
|
43482d9e48 | ||
|
|
299410f8b4 | ||
|
|
07aa20a3cd | ||
|
|
ee323d97b4 | ||
|
|
d6b447f70a | ||
|
|
06cae36199 | ||
|
|
183404a5f8 | ||
|
|
6def7d6f70 | ||
|
|
097cae1f8c | ||
|
|
ba00311dd4 | ||
|
|
203fafdf7c | ||
|
|
62e90723d1 | ||
|
|
77372ccce1 | ||
|
|
849b608933 | ||
|
|
fdb4980b68 | ||
|
|
ff4f5e6c04 | ||
|
|
0bb3b67075 | ||
|
|
e16b06b44a | ||
|
|
ac9b23f00f | ||
|
|
f562bdd801 | ||
|
|
0c05942b45 | ||
|
|
9db3c93116 | ||
|
|
ce1f3365bb | ||
|
|
4ed5c3e4b8 | ||
|
|
9061180983 | ||
|
|
be970e4cea | ||
|
|
8f978c23b4 | ||
|
|
5f53c71b0c | ||
|
|
58020863a8 | ||
|
|
3ce3612047 | ||
|
|
44c9eebc4c | ||
|
|
cf6cc0e59d | ||
|
|
2b1fc8d07f | ||
|
|
e7d7ddd698 | ||
|
|
1d6bfc5dd6 | ||
|
|
0ccdbdcbe9 | ||
|
|
463346b867 | ||
|
|
f293700db3 | ||
|
|
b4e343369a | ||
|
|
da1d94775c | ||
|
|
28582b7e77 | ||
|
|
0f4a97f3a6 | ||
|
|
0f75234ed2 | ||
|
|
bc2556139a | ||
|
|
c1f52a5e5a | ||
|
|
ab24b3262c | ||
|
|
42994efa9d | ||
|
|
28aebaa06b | ||
|
|
44cf82caab | ||
|
|
a44ca39eb8 | ||
|
|
3f70c395e1 | ||
|
|
04383c0302 | ||
|
|
55d022eefc | ||
|
|
7d3917adb7 | ||
|
|
7eeeab8486 | ||
|
|
88a46ef59a | ||
|
|
e4a9dc25ec | ||
|
|
dfb563a330 | ||
|
|
329256712b | ||
|
|
16dbb88ee3 | ||
|
|
626f435edf | ||
|
|
d4dd5d7a61 | ||
|
|
de13b8b9ba | ||
|
|
0aabb0b364 | ||
|
|
3605072650 | ||
|
|
6272aabefb | ||
|
|
bf2446614c | ||
|
|
65b8220fe5 | ||
|
|
e0b544fc7e | ||
|
|
d296918fcd | ||
|
|
c9a2d439e5 | ||
|
|
73f36bcf33 | ||
|
|
a1bda1169e | ||
|
|
8f5089fd46 | ||
|
|
ae8b17e0fb | ||
|
|
3453690fc2 | ||
|
|
bc1f607c8e | ||
|
|
e0382139b6 | ||
|
|
65d280da8a | ||
|
|
e618070068 | ||
|
|
8181074238 | ||
|
|
b0e4aedd3d | ||
|
|
faa101224a | ||
|
|
eb7fc0fb26 | ||
|
|
a63a61e7fc | ||
|
|
223cc07139 | ||
|
|
62b0d371ee | ||
|
|
884b678bd0 | ||
|
|
2930e9cadb | ||
|
|
6103265488 | ||
|
|
c1f5dfc02e | ||
|
|
8e911a42f5 | ||
|
|
f41c1153d2 | ||
|
|
ff03171f55 | ||
|
|
5fe58dc9ae | ||
|
|
0c1cff52b2 | ||
|
|
8e29a52d71 | ||
|
|
12a0e73587 | ||
|
|
a3229ea965 | ||
|
|
43f40c4085 | ||
|
|
250e79f53c | ||
|
|
c8b2c19010 | ||
|
|
fe1add57f1 | ||
|
|
332f830cc2 | ||
|
|
ccd69dfcf7 | ||
|
|
7e13d09c3c | ||
|
|
71c0c62ee8 | ||
|
|
c599e60e75 | ||
|
|
f2241ba3de | ||
|
|
85fb240b69 | ||
|
|
71725f1b26 | ||
|
|
64d4c3f971 | ||
|
|
4c24b042d0 | ||
|
|
a43736b5c6 | ||
|
|
874c2a6212 | ||
|
|
94492b01cd | ||
|
|
ab6f8397ce | ||
|
|
b105088c28 | ||
|
|
1070805a0f | ||
|
|
799ae886c5 | ||
|
|
1276b23817 | ||
|
|
6ecb5b5c64 | ||
|
|
cbadf5e57c | ||
|
|
3a2deb0039 | ||
|
|
0480c2195c | ||
|
|
fde6a65933 | ||
|
|
6f6249690b | ||
|
|
c23aca78ba | ||
|
|
a195b52d34 | ||
|
|
09a13ef75f | ||
|
|
70e7534933 | ||
|
|
c95edbe25a | ||
|
|
4f9b4059a6 | ||
|
|
9be19bf811 | ||
|
|
0a728f805b | ||
|
|
c7276ee614 | ||
|
|
b419e17cac | ||
|
|
b9405bfb64 | ||
|
|
5d8856a1c7 | ||
|
|
076fd7e83c | ||
|
|
5073c0e9ad | ||
|
|
d95df7245c | ||
|
|
41360ceb71 | ||
|
|
042594ffca | ||
|
|
a8a9ee91bf | ||
|
|
726be664c8 | ||
|
|
c02b71e0e1 | ||
|
|
9cbef4ba4b | ||
|
|
24035329b8 | ||
|
|
457977f954 | ||
|
|
373dc5cadb | ||
|
|
a35cfe660e | ||
|
|
db666eec65 | ||
|
|
5b51a36c62 | ||
|
|
586473aaa7 | ||
|
|
2226ece4b7 | ||
|
|
950e41e8f7 | ||
|
|
49df8f4b43 | ||
|
|
2e9ad941c0 | ||
|
|
dec21f34e9 | ||
|
|
c8605b0cdf | ||
|
|
3d6d191d59 | ||
|
|
8f61278b33 | ||
|
|
5723c0c96f | ||
|
|
2830bdc075 | ||
|
|
37ab621a7e | ||
|
|
f9f286e896 | ||
|
|
e47545aa91 | ||
|
|
799417e0e8 | ||
|
|
cea5f0a30c | ||
|
|
ce61e80261 | ||
|
|
ec23124242 | ||
|
|
678cc56836 | ||
|
|
adf94b5e21 | ||
|
|
d4e40c376a | ||
|
|
3cbf678671 | ||
|
|
c7ec07ed47 | ||
|
|
754d7d099b | ||
|
|
c4cc3f176e | ||
|
|
79a2921e1d | ||
|
|
d64a61bab9 | ||
|
|
0ee78865e1 | ||
|
|
2c29b150c5 | ||
|
|
0801a2fbc4 | ||
|
|
af7ef25c9d | ||
|
|
f852474b56 | ||
|
|
54cfe5984b | ||
|
|
1bc07b1b4a | ||
|
|
b8d52a5aa8 | ||
|
|
af58b1039e | ||
|
|
a124f1dc38 | ||
|
|
fa3e6267fd | ||
|
|
011f86f368 | ||
|
|
0f6edc477e | ||
|
|
bade652c02 | ||
|
|
03ab067d3a | ||
|
|
e3a29ae82f | ||
|
|
3a9ce0f6ce | ||
|
|
d6d40b35a5 | ||
|
|
8baecb5911 | ||
|
|
3bb8fe9076 | ||
|
|
b93d66ce0b | ||
|
|
b71fde7ff3 | ||
|
|
5c7b060f19 | ||
|
|
1f544fff52 | ||
|
|
019520a793 | ||
|
|
29c22efed0 | ||
|
|
650519683c | ||
|
|
bcf5a586c7 | ||
|
|
5d8f5beb2a | ||
|
|
7e58627eaf | ||
|
|
89be0f80b9 | ||
|
|
e8b6b78dfa | ||
|
|
d5ea44a760 | ||
|
|
0638d43cae | ||
|
|
c5d133aaff | ||
|
|
067c2a6fe0 | ||
|
|
9e73a72b8b | ||
|
|
ae23b7611d | ||
|
|
e34a284036 | ||
|
|
116aae0828 | ||
|
|
f33c31aa83 | ||
|
|
c7a742d1de | ||
|
|
d0df3a2915 | ||
|
|
1c494803b0 | ||
|
|
0bd0a70564 | ||
|
|
5cd6470358 | ||
|
|
a7d0576736 | ||
|
|
76e9e2076e | ||
|
|
dcec5b5cb0 | ||
|
|
b4352ada27 | ||
|
|
76047e5351 | ||
|
|
60fedad3fe | ||
|
|
d57eb6a2fc | ||
|
|
c33d39f5d0 | ||
|
|
81aa17f3f0 | ||
|
|
8475201db8 | ||
|
|
40907ef1d8 | ||
|
|
b79fc53265 | ||
|
|
2972351e79 | ||
|
|
c82de08886 | ||
|
|
7806945965 | ||
|
|
9a6a0b0047 | ||
|
|
e0c94e3cbb | ||
|
|
9f328bdee3 | ||
|
|
7a96adf957 | ||
|
|
2111e19d59 | ||
|
|
d93cf08a27 | ||
|
|
a24b64f7ca | ||
|
|
625577f145 | ||
|
|
e13fa0d2d1 | ||
|
|
d470281a5e | ||
|
|
6021438e2e | ||
|
|
737b425aca | ||
|
|
d2a35453ef | ||
|
|
77160a76e8 | ||
|
|
f012a67182 | ||
|
|
2c5e4fad79 | ||
|
|
1486f5f620 | ||
|
|
eb1209cb00 | ||
|
|
bcdaf77a9c | ||
|
|
e8e97838ef | ||
|
|
bec3f309bd | ||
|
|
39bfbbbe0a | ||
|
|
57a98e7a5d | ||
|
|
ea697f2238 | ||
|
|
fd20a9218f | ||
|
|
b47794cbc1 | ||
|
|
c3ce44562e | ||
|
|
4f7803c384 | ||
|
|
6e313d1760 | ||
|
|
0f7a8a25ad | ||
|
|
c692b7847b | ||
|
|
207893fee8 | ||
|
|
9ec3574ff6 | ||
|
|
b0522836e6 | ||
|
|
c62396e389 | ||
|
|
d52e85f298 | ||
|
|
05ebae7329 | ||
|
|
b697bb656e | ||
|
|
4d7de2adce | ||
|
|
8a9528e787 | ||
|
|
6c6cba51fa | ||
|
|
28e23f9d87 | ||
|
|
e31192e97e | ||
|
|
26b7137b5c | ||
|
|
912200a2ec | ||
|
|
c42d0d7423 | ||
|
|
3da66a0fe3 | ||
|
|
41e1f8fbc3 | ||
|
|
130ac3c917 | ||
|
|
3774060d32 | ||
|
|
d8c8856548 | ||
|
|
efec00a550 | ||
|
|
3d3cf1d551 | ||
|
|
c52d17b6fb | ||
|
|
c0b9ef5990 | ||
|
|
e303159b98 | ||
|
|
f274b3c26b | ||
|
|
81fdc9a865 | ||
|
|
f40c9804f8 | ||
|
|
4d13d51038 | ||
|
|
5c4421dc38 | ||
|
|
76ef6a81ab | ||
|
|
a7b43d5b57 | ||
|
|
e4f50d4713 | ||
|
|
1e71237627 | ||
|
|
678c8f6bf6 | ||
|
|
786c27bbec | ||
|
|
710643d8cb | ||
|
|
e595aba189 | ||
|
|
ea67e94c47 | ||
|
|
b2c2067f7d | ||
|
|
4fc2d7b935 | ||
|
|
b1b9dd208b | ||
|
|
ee2f7c077f | ||
|
|
a385d6f4fd | ||
|
|
7031b7a3c7 | ||
|
|
0f0a5fba1f | ||
|
|
1d7af3f011 | ||
|
|
6fd85f97b3 | ||
|
|
a6d17f3813 | ||
|
|
4e830ea416 | ||
|
|
fc5c936272 | ||
|
|
b2af684c4d | ||
|
|
0446999c63 | ||
|
|
6920c39752 | ||
|
|
62ba8ed8d8 | ||
|
|
6eeef96886 | ||
|
|
4acf852650 | ||
|
|
9b3f1562a5 | ||
|
|
49774e46a3 | ||
|
|
ae2447393d | ||
|
|
3435192640 | ||
|
|
6ba4da9711 | ||
|
|
010a1d07d9 | ||
|
|
94d09b0014 | ||
|
|
d6ff442622 | ||
|
|
f7cc1abc8e | ||
|
|
bf7b1789ec | ||
|
|
cf9b482546 | ||
|
|
ad6da9bf37 | ||
|
|
d647884b91 | ||
|
|
5adaaa7f22 | ||
|
|
5b77543ab5 | ||
|
|
0ff215f78f | ||
|
|
ff787bb6ca | ||
|
|
334dfa5cb6 | ||
|
|
a17e628872 | ||
|
|
e87ce4cac5 | ||
|
|
8678e77c65 | ||
|
|
66f99f9ad7 | ||
|
|
b1cdcda8ed | ||
|
|
4c6084ab68 | ||
|
|
fa51ce6fb0 | ||
|
|
5708954268 | ||
|
|
5f23e9caa7 | ||
|
|
37372466cc | ||
|
|
442a7b0fcd | ||
|
|
0adde3e06d | ||
|
|
e0576502d9 | ||
|
|
f8e2fe5794 | ||
|
|
c10f32cc30 | ||
|
|
feb477ca48 | ||
|
|
2d1e4e8a1d | ||
|
|
ef166468e9 | ||
|
|
e58de99d0f | ||
|
|
56c7b7a8f1 | ||
|
|
2df0a54cb8 | ||
|
|
78a1d08a17 | ||
|
|
4effccf96e | ||
|
|
282505cde2 | ||
|
|
d2d4c19196 | ||
|
|
e6d42f21a2 | ||
|
|
71483e8b04 | ||
|
|
54a55a4a23 | ||
|
|
bad4cc12b4 | ||
|
|
6289acd109 | ||
|
|
4e8fc46475 | ||
|
|
4bd3329d07 | ||
|
|
4163841bcf | ||
|
|
7ad1171c5f | ||
|
|
6a32424a08 | ||
|
|
2c30714c6f | ||
|
|
aa22d4e569 | ||
|
|
ac020a401f | ||
|
|
be238ac743 | ||
|
|
f377e66f02 | ||
|
|
6ea79dff51 | ||
|
|
ba336de74b | ||
|
|
b6b3682d06 | ||
|
|
927d15f2d8 | ||
|
|
31f89121fe | ||
|
|
1189b66748 | ||
|
|
a9ceba690e | ||
|
|
f6370782ec | ||
|
|
4c0bd359f9 | ||
|
|
76491515d9 | ||
|
|
2977f7cf47 | ||
|
|
931bff608c | ||
|
|
fea445c902 | ||
|
|
027771b302 | ||
|
|
f2e7296d19 | ||
|
|
20ab2f039b | ||
|
|
158a0807d9 | ||
|
|
814ae5e840 | ||
|
|
37d76e9e06 | ||
|
|
6ba2bb5087 | ||
|
|
4faab518a7 | ||
|
|
e0c6f64788 | ||
|
|
211d27352a | ||
|
|
74ad7b9f8d | ||
|
|
c66a48e530 | ||
|
|
91ef8ca1ae | ||
|
|
9b6288e49c | ||
|
|
9eca4566bf | ||
|
|
907bf4ffdc | ||
|
|
5b194285f5 | ||
|
|
ae1269aa68 | ||
|
|
d1c3c85af8 | ||
|
|
143f259904 | ||
|
|
082b16d422 | ||
|
|
dd163b9923 | ||
|
|
7a5aad9cd8 | ||
|
|
9b813dd6a1 | ||
|
|
66f5f254d0 | ||
|
|
b40d53df84 | ||
|
|
0c54260762 | ||
|
|
5346f89f77 | ||
|
|
7909af6a56 | ||
|
|
ef37cb762d | ||
|
|
3799839539 | ||
|
|
81d496bac7 | ||
|
|
528c0d212a | ||
|
|
3b876b2d20 | ||
|
|
a7bd8d42e8 | ||
|
|
f8f14613f9 | ||
|
|
c9db8e0a26 | ||
|
|
8dd088d727 | ||
|
|
550f7201bb | ||
|
|
e1c14b04ad | ||
|
|
fb690e47e8 | ||
|
|
b537275707 | ||
|
|
545c72fca3 | ||
|
|
39413faff0 | ||
|
|
953cfd6ecd | ||
|
|
36919a25be | ||
|
|
7a66cb46a0 | ||
|
|
3a2926c22e | ||
|
|
591a8cd807 | ||
|
|
4a4c8c2404 | ||
|
|
9d2bb505ae | ||
|
|
2531cf549e | ||
|
|
80c395906b | ||
|
|
da12c84609 | ||
|
|
69c0a3cba3 | ||
|
|
124bce8f80 | ||
|
|
1d37f170fd | ||
|
|
99bdee5b12 | ||
|
|
5476a5984c | ||
|
|
74d93a6adc | ||
|
|
fb9524bba8 | ||
|
|
dc4a0bb462 | ||
|
|
a0418b460a | ||
|
|
0c093b539a | ||
|
|
1971037049 | ||
|
|
57af4ae70d | ||
|
|
8f553497e4 | ||
|
|
0373da44b3 | ||
|
|
1df060b8f6 | ||
|
|
b249fcbb2e | ||
|
|
ff8a36653a | ||
|
|
1c7a9e4de8 | ||
|
|
f56caa745a | ||
|
|
fc0ee2e41b | ||
|
|
319783b008 | ||
|
|
0c3b92567e | ||
|
|
3513a3bd62 | ||
|
|
a5f6dd6399 | ||
|
|
7ced6318a5 | ||
|
|
61c691a267 | ||
|
|
52f920db12 | ||
|
|
16bb0c81f8 | ||
|
|
7ac9448ddc | ||
|
|
23c1723ae1 | ||
|
|
dfe3a52571 | ||
|
|
7deff0e14f | ||
|
|
49a0451b6f | ||
|
|
bafda03fd9 | ||
|
|
27d05ee828 | ||
|
|
484276b80d | ||
|
|
1c61350988 | ||
|
|
01ef81ca58 | ||
|
|
e1192d1200 | ||
|
|
436134e0e3 | ||
|
|
8f506f1267 | ||
|
|
6d9928ea8f | ||
|
|
ad759f2f9f | ||
|
|
5553277a1e | ||
|
|
f0c9740c13 | ||
|
|
b365599bc8 | ||
|
|
56a530058b | ||
|
|
a1dbeda9ce | ||
|
|
1a049e4499 | ||
|
|
a6b10ebfbb | ||
|
|
8e4eade002 | ||
|
|
059e9524b3 | ||
|
|
f0b46d5508 | ||
|
|
a556953a11 | ||
|
|
d1f44a152c | ||
|
|
3a96f71b91 | ||
|
|
b8ab5e0e8d | ||
|
|
f25e01323e | ||
|
|
4fa0c2da4d | ||
|
|
b09d656636 | ||
|
|
5ff6fc0246 | ||
|
|
fc68354e83 | ||
|
|
22c868b4e6 | ||
|
|
f476143e92 | ||
|
|
855151a8d1 | ||
|
|
b9338a78f4 | ||
|
|
815b32c9b6 | ||
|
|
2714032e77 | ||
|
|
e463f79532 | ||
|
|
4cbc3cb71f | ||
|
|
b793716e85 | ||
|
|
451acb1c30 | ||
|
|
d7c663819c | ||
|
|
63d933a51d | ||
|
|
0afe8abdb8 | ||
|
|
413f91d0c8 | ||
|
|
fbb98e23c3 | ||
|
|
c4ea7457dc | ||
|
|
d0083d37da | ||
|
|
6f18a75e5c | ||
|
|
c89a0eb694 | ||
|
|
6b8939c02e | ||
|
|
86e5356b0f | ||
|
|
c0f748bcb5 | ||
|
|
0acb8f5d69 | ||
|
|
37db870475 | ||
|
|
0d5f33299e | ||
|
|
78f818bbb0 | ||
|
|
edafb6a6d2 | ||
|
|
c0984aa740 | ||
|
|
6253bbdc0b | ||
|
|
74f89ef80d | ||
|
|
254a9abad2 | ||
|
|
83e77f8d82 | ||
|
|
2726643586 | ||
|
|
b8f2a468d8 | ||
|
|
fb771d46dd | ||
|
|
ecd4c7fb02 | ||
|
|
da2be59185 | ||
|
|
93a3fa9c75 | ||
|
|
cf546c71e3 | ||
|
|
2791b7da22 | ||
|
|
48fc442b85 | ||
|
|
2e6b18dfa7 | ||
|
|
7f0da49e0a | ||
|
|
b5124d0f6a | ||
|
|
e70a1f0a60 | ||
|
|
7e8475e64d | ||
|
|
e161ada214 | ||
|
|
409b8559d7 | ||
|
|
d74dc92681 | ||
|
|
5f62d7307f | ||
|
|
3866ed3196 | ||
|
|
8059856c39 | ||
|
|
a3518a3bcf | ||
|
|
7b8d67de63 | ||
|
|
7a5aa469cd | ||
|
|
1e415b38d2 | ||
|
|
ff626fb559 |
No files matched your search
@@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.20.1 \
|
||||
NODE_VERSION=v12.22.1 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
@@ -19,6 +19,7 @@ ENV \
|
||||
ARCHITECTURE=linux-x64 \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
RESULTS_PER_PAGE="" \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
|
||||
|
||||
@@ -81,7 +81,7 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.20.1
|
||||
node-engine: 12.22.1
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
|
||||
@@ -5,7 +5,7 @@ echo " with 'sudo dpkg-reconfigure locales' , so that MongoDB works correct
|
||||
echo " You can still use any other locale as your main locale."
|
||||
|
||||
#Below script installs newest node 8.x for Debian/Ubuntu/Mint.
|
||||
#NODE_VERSION=12.18.2
|
||||
#NODE_VERSION=12.21.0
|
||||
#X64NODE="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
|
||||
function pause(){
|
||||
@@ -80,7 +80,7 @@ do
|
||||
curl -0 -L https://npmjs.org/install.sh | sudo sh
|
||||
sudo chown -R $(id -u):$(id -g) $HOME/.npm
|
||||
sudo npm -g install n
|
||||
sudo n 12.18.2
|
||||
sudo n 12.21.0
|
||||
#curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||
#sudo apt-get install -y nodejs
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
|
||||
@@ -83,7 +83,7 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.20.1
|
||||
node-engine: 12.22.1
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
## Issue
|
||||
|
||||
If you can not login for any reason:
|
||||
- https://github.com/wekan/wekan/wiki/Forgot-Password
|
||||
|
||||
Email settings:
|
||||
- https://github.com/wekan/wekan/wiki/Troubleshooting-Mail
|
||||
|
||||
Add these issues to elsewhere:
|
||||
- Snap: https://github.com/wekan/wekan-snap/issues
|
||||
- LDAP: https://github.com/wekan/wekan-ldap/issues
|
||||
- SECURITY ISSUES: https://github.com/wekan/wekan/blob/master/SECURITY.md
|
||||
- UCS: https://github.com/wekan/univention/issues
|
||||
|
||||
Other Wekan issues can be added here.
|
||||
|
||||
@@ -31,5 +31,4 @@ Thumbs.db
|
||||
ehthumbs.db
|
||||
.eslintcache
|
||||
.meteor/local
|
||||
.meteor-1.6-snap/.meteor/local
|
||||
.devcontainer/docker-compose.extend.yml
|
||||
+9
-9
@@ -6,8 +6,8 @@
|
||||
meteor-base@1.4.0
|
||||
|
||||
# Build system
|
||||
ecmascript@0.14.4
|
||||
standard-minifier-css@1.7.1
|
||||
ecmascript@0.15.1
|
||||
standard-minifier-css@1.7.2
|
||||
standard-minifier-js@2.6.0
|
||||
mquandalle:jade
|
||||
coffeescript@2.4.1!
|
||||
@@ -17,13 +17,13 @@ es5-shim@4.8.0
|
||||
|
||||
# Collections
|
||||
aldeed:collection2
|
||||
cfs:standard-packages
|
||||
wekan-cfs-standard-packages
|
||||
cottz:publish-relations
|
||||
dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
matb33:collection-hooks
|
||||
matteodem:easy-search
|
||||
mongo@1.10.1
|
||||
mongo@1.11.0
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
@@ -72,10 +72,10 @@ simple:rest-accounts-password
|
||||
useraccounts:core
|
||||
email@2.0.0
|
||||
horka:swipebox
|
||||
dynamic-import@0.5.5
|
||||
dynamic-import@0.6.0
|
||||
|
||||
accounts-password@1.6.3
|
||||
cfs:gridfs
|
||||
accounts-password@1.7.0
|
||||
wekan-cfs-gridfs
|
||||
rzymek:fullcalendar
|
||||
momentjs:moment@2.22.2
|
||||
browser-policy-framing@1.1.0
|
||||
@@ -91,8 +91,7 @@ meteorhacks:aggregate@1.3.0
|
||||
wekan-markdown
|
||||
konecty:mongo-counter
|
||||
percolate:synced-cron
|
||||
easylogic:summernote
|
||||
cfs:filesystem
|
||||
wekan-cfs-filesystem
|
||||
tmeasday:check-npm-versions
|
||||
steffo:meteor-accounts-saml
|
||||
rajit:bootstrap3-datepicker-fi
|
||||
@@ -145,3 +144,4 @@ rajit:bootstrap3-datepicker-zh-cn
|
||||
rajit:bootstrap3-datepicker-zh-tw
|
||||
staringatlights:fast-render
|
||||
spacebars
|
||||
easylogic:summernote
|
||||
+1
-1
@@ -1 +1 @@
|
||||
METEOR@1.12.1
|
||||
METEOR@2.2
|
||||
+52
-50
@@ -1,7 +1,7 @@
|
||||
3stack:presence@1.1.2
|
||||
accounts-base@1.7.1
|
||||
accounts-base@1.9.0
|
||||
accounts-oauth@1.2.0
|
||||
accounts-password@1.6.3
|
||||
accounts-password@1.7.1
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
@@ -10,37 +10,20 @@ aldeed:simple-schema@1.5.4
|
||||
allow-deny@1.1.0
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
audit-argument-checks@1.0.7
|
||||
autoupdate@1.6.0
|
||||
babel-compiler@7.5.5
|
||||
autoupdate@1.7.0
|
||||
babel-compiler@7.6.1
|
||||
babel-runtime@1.5.0
|
||||
base64@1.0.12
|
||||
binary-heap@1.0.11
|
||||
blaze@2.3.4
|
||||
blaze-tools@1.0.10
|
||||
blaze@2.4.0
|
||||
blaze-tools@1.1.1
|
||||
boilerplate-generator@1.7.1
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
caching-compiler@1.2.2
|
||||
caching-html-compiler@1.1.3
|
||||
caching-html-compiler@1.2.0
|
||||
callback-hook@1.3.0
|
||||
cfs:access-point@0.1.49
|
||||
cfs:base-package@0.0.30
|
||||
cfs:collection@0.5.5
|
||||
cfs:collection-filters@0.2.4
|
||||
cfs:data-man@0.0.6
|
||||
cfs:file@0.1.17
|
||||
cfs:filesystem@0.1.2
|
||||
cfs:gridfs@0.0.34
|
||||
cfs:http-methods@0.0.32
|
||||
cfs:http-publish@0.0.13
|
||||
cfs:power-queue@0.9.11
|
||||
cfs:reactive-list@0.0.9
|
||||
cfs:reactive-property@0.0.4
|
||||
cfs:standard-packages@0.5.10
|
||||
cfs:storage-adapter@0.2.4
|
||||
cfs:tempstore@0.1.6
|
||||
cfs:upload-http@0.0.20
|
||||
cfs:worker@0.1.5
|
||||
check@1.3.1
|
||||
chuangbo:cookie@1.1.0
|
||||
coagmano:stylus@1.1.0
|
||||
@@ -49,15 +32,15 @@ coffeescript-compiler@2.4.1
|
||||
cottz:publish-relations@2.0.8
|
||||
dburles:collection-helpers@1.1.0
|
||||
ddp@1.4.0
|
||||
ddp-client@2.3.3
|
||||
ddp-client@2.4.1
|
||||
ddp-common@1.4.0
|
||||
ddp-rate-limiter@1.0.9
|
||||
ddp-server@2.3.2
|
||||
ddp-server@2.3.3
|
||||
deps@1.0.12
|
||||
diff-sequence@1.1.1
|
||||
dynamic-import@0.5.5
|
||||
dynamic-import@0.6.0
|
||||
easylogic:summernote@0.8.8
|
||||
ecmascript@0.14.4
|
||||
ecmascript@0.15.1
|
||||
ecmascript-runtime@0.7.0
|
||||
ecmascript-runtime-client@0.11.0
|
||||
ecmascript-runtime-server@0.10.0
|
||||
@@ -70,10 +53,10 @@ fortawesome:fontawesome@4.7.0
|
||||
geojson-utils@1.0.10
|
||||
horka:swipebox@1.0.2
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.0.11
|
||||
htmljs@1.0.11
|
||||
http@1.4.2
|
||||
id-map@1.1.0
|
||||
html-tools@1.1.1
|
||||
htmljs@1.1.0
|
||||
http@1.4.4
|
||||
id-map@1.1.1
|
||||
idmontie:migrations@1.0.3
|
||||
inter-process-messaging@0.1.1
|
||||
jquery@1.11.11
|
||||
@@ -84,10 +67,10 @@ 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.0
|
||||
launch-screen@1.2.1
|
||||
livedata@1.0.18
|
||||
localstorage@1.2.0
|
||||
logging@1.1.20
|
||||
logging@1.2.0
|
||||
lucasantoniassi:accounts-lockout@1.0.0
|
||||
matb33:collection-hooks@0.9.1
|
||||
matteodem:easy-search@1.6.4
|
||||
@@ -100,19 +83,19 @@ meteorhacks:collection-utils@1.2.0
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
meteorspark:util@0.2.0
|
||||
minifier-css@1.5.3
|
||||
minifier-css@1.5.4
|
||||
minifier-js@2.6.0
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.6.1
|
||||
minimongo@1.6.2
|
||||
mobile-status-bar@1.1.0
|
||||
modern-browsers@0.1.5
|
||||
modules@0.15.0
|
||||
modules@0.16.0
|
||||
modules-runtime@0.12.0
|
||||
momentjs:moment@2.29.1
|
||||
mongo@1.10.1
|
||||
mongo@1.11.1
|
||||
mongo-decimal@0.1.2
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.7
|
||||
mongo-id@1.0.8
|
||||
mongo-livedata@1.0.12
|
||||
mousetrap:mousetrap@1.4.6_1
|
||||
mquandalle:autofocus@1.0.0
|
||||
@@ -125,7 +108,7 @@ mquandalle:moment@1.0.1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
msavin:usercache@1.8.0
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@3.8.1
|
||||
npm-mongo@3.9.0
|
||||
oauth@1.3.2
|
||||
oauth2@1.3.0
|
||||
observe-sequence@1.0.16
|
||||
@@ -191,6 +174,7 @@ 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
|
||||
reload@1.3.1
|
||||
@@ -207,22 +191,22 @@ 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.1
|
||||
socket-stream-client@0.3.3
|
||||
softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.0.15
|
||||
spacebars-compiler@1.1.3
|
||||
spacebars@1.1.0
|
||||
spacebars-compiler@1.2.1
|
||||
srp@1.1.0
|
||||
standard-minifier-css@1.7.1
|
||||
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
|
||||
templates:tabs@2.3.0
|
||||
templating@1.3.2
|
||||
templating-compiler@1.3.3
|
||||
templating-runtime@1.3.2
|
||||
templating-tools@1.1.2
|
||||
templating@1.4.0
|
||||
templating-compiler@1.4.0
|
||||
templating-runtime@1.4.0
|
||||
templating-tools@1.2.0
|
||||
tmeasday:check-npm-versions@0.3.2
|
||||
tracker@1.2.0
|
||||
twbs:bootstrap@3.3.6
|
||||
@@ -233,10 +217,28 @@ useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.9.1
|
||||
webapp-hashing@1.0.9
|
||||
webapp@1.10.1
|
||||
webapp-hashing@1.1.0
|
||||
wekan-accounts-cas@0.1.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-ldap@0.0.2
|
||||
wekan-markdown@1.0.9
|
||||
wekan-oidc@1.0.12
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ sudo: required
|
||||
|
||||
env:
|
||||
TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
|
||||
TRAVIS_NODE_VERSION: 12.20.1
|
||||
TRAVIS_NODE_VERSION: 12.22.1
|
||||
TRAVIS_NPM_VERSION: latest
|
||||
|
||||
before_install:
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ host = https://www.transifex.com
|
||||
# tap:i18n requires us to use `-` separator in the language identifiers whereas
|
||||
# Transifex uses a `_` separator, without an option to customize it on one side
|
||||
# or the other, so we need to do a Manual mapping.
|
||||
lang_map = bg_BG:bg, en_GB:en-GB, es_AR:es-AR, es_CL:es-CL, es_PE:es-PE, es_MX:es-MX, es_TX:es-TX, es_PY:es-PY, el_GR:el, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, no:nb, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, sl_SI:sl, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
|
||||
lang_map = ar_EG:ar-EG, bg_BG:bg, de_CH:de-CH, en_GB:en-GB, 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, fa_IR:fa-IR, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, sl_SI:sl, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
|
||||
|
||||
[wekan.application]
|
||||
file_filter = i18n/<lang>.i18n.json
|
||||
|
||||
+1040
-1
File diff suppressed because it is too large.
Load diff
+8
-5
@@ -1,4 +1,4 @@
|
||||
FROM quay.io/wekan/ubuntu:groovy-20201125.2
|
||||
FROM quay.io/wekan/ubuntu:groovy-20210115
|
||||
LABEL maintainer="wekan"
|
||||
|
||||
# 2020-12-03:
|
||||
@@ -12,7 +12,7 @@ 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.20.1 \
|
||||
NODE_VERSION=v12.22.1 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
@@ -21,6 +21,7 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
ARCHITECTURE=linux-x64 \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
RESULTS_PER_PAGE="" \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \
|
||||
@@ -274,11 +275,12 @@ RUN \
|
||||
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 /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 && \
|
||||
#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 && \
|
||||
#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
|
||||
@@ -291,10 +293,11 @@ RUN \
|
||||
#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 && \
|
||||
# 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 && \
|
||||
rm -rf /home/wekan/app_build/bundle/programs/web.browser.legacy && \
|
||||
mv /home/wekan/app_build/bundle /build && \
|
||||
\
|
||||
# Put back the original tar
|
||||
|
||||
+9
-9
@@ -4,16 +4,16 @@ FROM amd64/alpine:3.7 AS builder
|
||||
ENV QEMU_VERSION=v4.2.0-6 \
|
||||
QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.20.1 \
|
||||
WEKAN_VERSION=3.96 \
|
||||
NODE_VERSION=v12.22.1 \
|
||||
WEKAN_VERSION=latest \
|
||||
WEKAN_ARCHITECTURE=arm64
|
||||
|
||||
# Install dependencies
|
||||
RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
\
|
||||
# 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 && \
|
||||
\
|
||||
# Install dependencies
|
||||
RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
\
|
||||
# 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/raspi3/wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
|
||||
wget https://releases.wekan.team/raspi3/SHA256SUMS.txt && \
|
||||
@@ -40,7 +40,7 @@ LABEL maintainer="wekan"
|
||||
# Set the environment variables (defaults where required)
|
||||
ENV QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.20.1 \
|
||||
NODE_VERSION=v12.22.1 \
|
||||
NODE_ENV=production \
|
||||
NPM_VERSION=latest \
|
||||
WITH_API=true \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[](https://gitpod.io/#https://github.com/wekan/wekan)
|
||||
[](https://gitpod.io/#https://github.com/wekan/wekan)
|
||||
|
||||
# Wekan - Open Source kanban
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
[](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)
|
||||
|
||||
## [Translate Wekan at Transifex](https://transifex.com/wekan/wekan)
|
||||
|
||||
@@ -20,19 +21,18 @@ New English strings of new features can be added as PRs to edge branch file weka
|
||||
|
||||
## [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).
|
||||
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.
|
||||
|
||||
## Chat
|
||||
|
||||
[![Wekan Chat][vanila_badge]][wekan_chat] - Most Wekan community and developers are here. Works on webbrowser
|
||||
and PWA app that can be added as icon on Android and bookmark on iOS, used like native app.
|
||||
[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)
|
||||
|
||||
## FAQ
|
||||
|
||||
**NOTE**:
|
||||
**NOTE**:
|
||||
- Please read the [FAQ](https://github.com/wekan/wekan/wiki/FAQ) first
|
||||
- Please don't feed the [trolls](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-troll) and [spammers](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-spammer) that are mentioned in the FAQ :)
|
||||
|
||||
@@ -52,7 +52,7 @@ 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 60 languages.
|
||||
- Wekan has been [translated](https://transifex.com/wekan/wekan) to about 63 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.
|
||||
@@ -64,7 +64,7 @@ that by providing one-click installation on various platforms.
|
||||
[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,
|
||||
each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.
|
||||
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.
|
||||
@@ -114,8 +114,6 @@ with [Meteor](https://www.meteor.com).
|
||||
[translate_wekan]: https://www.transifex.com/wekan/wekan/
|
||||
[open_source]: https://en.wikipedia.org/wiki/Open-source_software
|
||||
[free_software]: https://en.wikipedia.org/wiki/Free_software
|
||||
[vanila_badge]: https://vanila.io/img/join-chat-button2.png
|
||||
[wekan_chat]: https://community.vanila.io/wekan
|
||||
|
||||
[discussions]: https://github.com/wekan/wekan/discussions
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwekan%2Fwekan?ref=badge_large)
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
Security is very important to us. If you discover any issue regarding security, please disclose
|
||||
the information responsibly by sending an email to security (at) wekan.team and not by
|
||||
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
|
||||
creating a GitHub issue. We will respond swiftly to fix verifiable security issues.
|
||||
|
||||
We thank you with a place at our hall of fame page, that is
|
||||
at https://wekan.github.io/hall-of-fame . Others have just posted public GitHub issue,
|
||||
so they are not at that hall-of-fame page.
|
||||
at https://wekan.github.io/hall-of-fame
|
||||
|
||||
## How should reports be formatted?
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v4.79.0"
|
||||
appVersion: "v5.29.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -82,7 +82,7 @@ template(name="activity")
|
||||
+viewer
|
||||
= activity.checklist.title
|
||||
else
|
||||
a.activity-checklist(href="{{ activity.card.absoluteUrl }}")
|
||||
a.activity-checklist(href="{{ activity.card.originRelativeUrl }}")
|
||||
+viewer
|
||||
= activity.checklist.title
|
||||
|
||||
@@ -103,7 +103,7 @@ template(name="activity")
|
||||
|
||||
if($eq activity.activityType 'addChecklistItem')
|
||||
| {{{_ 'activity-checklist-item-added' (sanitize activity.checklist.title) cardLink}}}.
|
||||
.activity-checklist(href="{{ activity.card.absoluteUrl }}")
|
||||
.activity-checklist(href="{{ activity.card.originRelativeUrl }}")
|
||||
+viewer
|
||||
= activity.checklistItem.title
|
||||
|
||||
@@ -139,7 +139,7 @@ template(name="activity")
|
||||
//- if we are not in card mode we only display a summary of the comment
|
||||
if($eq activity.activityType 'addComment')
|
||||
| {{{_ 'activity-on' cardLink}}}
|
||||
a.activity-comment(href="{{ activity.card.absoluteUrl }}")
|
||||
a.activity-comment(href="{{ activity.card.originRelativeUrl }}")
|
||||
+viewer
|
||||
= activity.comment.text
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import sanitizeXss from 'xss';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
const activitiesPerPage = 20;
|
||||
const activitiesPerPage = 500;
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
@@ -162,11 +162,15 @@ BlazeComponent.extendComponent({
|
||||
{
|
||||
href: source.url,
|
||||
},
|
||||
sanitizeXss(source.system),
|
||||
DOMPurify.sanitize(source.system, {
|
||||
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||
}),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return sanitizeXss(source.system);
|
||||
return DOMPurify.sanitize(source.system, {
|
||||
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -190,10 +194,10 @@ BlazeComponent.extendComponent({
|
||||
href: attachment.url({ download: true }),
|
||||
target: '_blank',
|
||||
},
|
||||
sanitizeXss(attachment.name()),
|
||||
DOMPurify.sanitize(attachment.name()),
|
||||
),
|
||||
)) ||
|
||||
sanitizeXss(this.currentData().activity.attachmentName)
|
||||
DOMPurify.sanitize(this.currentData().activity.attachmentName)
|
||||
);
|
||||
},
|
||||
|
||||
@@ -232,7 +236,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Template.activity.helpers({
|
||||
sanitize(value) {
|
||||
return sanitizeXss(value);
|
||||
return DOMPurify.sanitize(value, { ALLOW_UNKNOWN_PROTOCOLS: true });
|
||||
},
|
||||
});
|
||||
|
||||
@@ -243,10 +247,10 @@ function createCardLink(card) {
|
||||
Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: card.absoluteUrl(),
|
||||
href: card.originRelativeUrl(),
|
||||
class: 'action-card',
|
||||
},
|
||||
sanitizeXss(card.title),
|
||||
DOMPurify.sanitize(card.title, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
),
|
||||
)
|
||||
);
|
||||
@@ -260,10 +264,10 @@ function createBoardLink(board, list) {
|
||||
Blaze.toHTML(
|
||||
HTML.A(
|
||||
{
|
||||
href: board.absoluteUrl(),
|
||||
href: board.originRelativeUrl(),
|
||||
class: 'action-board',
|
||||
},
|
||||
sanitizeXss(text),
|
||||
DOMPurify.sanitize(text, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
template(name="commentForm")
|
||||
.new-comment.js-new-comment(
|
||||
class="{{#if commentFormIsOpen}}is-open{{/if}}")
|
||||
+userAvatar(userId=currentUser._id)
|
||||
+userAvatar(userId=currentUser._id noRemove=true)
|
||||
form.js-new-comment-form
|
||||
+editor(class="js-new-comment-input")
|
||||
| {{getUnsavedValue 'cardComment' currentCard._id}}
|
||||
|
||||
@@ -3,6 +3,7 @@ const commentFormIsOpen = new ReactiveVar(false);
|
||||
BlazeComponent.extendComponent({
|
||||
onDestroyed() {
|
||||
commentFormIsOpen.set(false);
|
||||
$('.note-popover').hide();
|
||||
},
|
||||
|
||||
commentFormIsOpen() {
|
||||
|
||||
@@ -11,7 +11,7 @@ BlazeComponent.extendComponent({
|
||||
return Boards.find(
|
||||
{ archived: true },
|
||||
{
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
sort: { archivedAt: -1, modifiedAt: -1 },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -360,7 +360,7 @@ BlazeComponent.extendComponent({
|
||||
end: end || card.endAt,
|
||||
allDay:
|
||||
Math.abs(end.getTime() - start.getTime()) / 1000 === 24 * 3600,
|
||||
url: FlowRouter.url('card', {
|
||||
url: FlowRouter.path('card', {
|
||||
boardId: currentBoard._id,
|
||||
slug: currentBoard.slug,
|
||||
cardId: card._id,
|
||||
|
||||
@@ -654,7 +654,6 @@ setBoardClear(color1,color2)
|
||||
|
||||
/* Card changes */
|
||||
.minicard
|
||||
background: #FFF
|
||||
padding: 15px 15px 10px
|
||||
box-shadow: 0 3px 8px rgba(0,0,0,.05)
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
template(name="boardHeaderBar")
|
||||
h1.header-board-menu
|
||||
with currentBoard
|
||||
a(class="{{#if currentUser.isBoardAdmin}}js-edit-board-title{{else}}is-disabled{{/if}}")
|
||||
+viewer
|
||||
= title
|
||||
+viewer
|
||||
= title
|
||||
|
||||
.board-header-btns.left
|
||||
unless isMiniScreen
|
||||
if currentBoard
|
||||
if currentUser
|
||||
with currentBoard
|
||||
a.board-header-btn(class="{{#if currentUser.isBoardAdmin}}js-edit-board-title{{else}}is-disabled{{/if}}" title="{{_ 'edit'}}" value=title)
|
||||
i.fa.fa-pencil-square-o
|
||||
|
||||
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
|
||||
title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
|
||||
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
|
||||
@@ -31,6 +34,12 @@ template(name="boardHeaderBar")
|
||||
if $eq watchLevel "muted"
|
||||
i.fa.fa-bell-slash
|
||||
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}}
|
||||
if isSortActive
|
||||
a.board-header-btn-close.js-sort-reset(title="Remove Sort")
|
||||
i.fa.fa-times-thin
|
||||
|
||||
else
|
||||
a.board-header-btn.js-log-in(
|
||||
@@ -42,6 +51,10 @@ template(name="boardHeaderBar")
|
||||
if currentBoard
|
||||
if isMiniScreen
|
||||
if currentUser
|
||||
with currentBoard
|
||||
a.board-header-btn(class="{{#if currentUser.isBoardAdmin}}js-edit-board-title{{else}}is-disabled{{/if}}" title="{{_ 'edit'}}" value=title)
|
||||
i.fa.fa-pencil-square-o
|
||||
|
||||
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
|
||||
title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
|
||||
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
|
||||
@@ -247,3 +260,19 @@ template(name="boardChangeTitlePopup")
|
||||
template(name="boardCreateRulePopup")
|
||||
p {{_ 'close-board-pop'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
|
||||
|
||||
|
||||
template(name="cardsSortPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-sort-due {{_ 'due-date'}}
|
||||
hr
|
||||
li
|
||||
a.js-sort-title {{_ 'title-alphabetically'}}
|
||||
hr
|
||||
li
|
||||
a.js-sort-created-desc {{_ 'created-at-newest-first'}}
|
||||
hr
|
||||
li
|
||||
a.js-sort-created-asc {{_ 'created-at-oldest-first'}}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const DOWNCLS = 'fa-sort-down';
|
||||
const UPCLS = 'fa-sort-up';
|
||||
*/
|
||||
const sortCardsBy = new ReactiveVar('');
|
||||
Template.boardMenuPopup.events({
|
||||
'click .js-rename-board': Popup.open('boardChangeTitle'),
|
||||
'click .js-custom-fields'() {
|
||||
@@ -110,6 +111,7 @@ BlazeComponent.extendComponent({
|
||||
'click .js-open-filter-view'() {
|
||||
Sidebar.setView('filter');
|
||||
},
|
||||
'click .js-sort-cards': Popup.open('cardsSort'),
|
||||
/*
|
||||
'click .js-open-sort-view'(evt) {
|
||||
const target = evt.target;
|
||||
@@ -127,6 +129,9 @@ BlazeComponent.extendComponent({
|
||||
Sidebar.setView();
|
||||
Filter.reset();
|
||||
},
|
||||
'click .js-sort-reset'() {
|
||||
Session.set('sortBy', '');
|
||||
},
|
||||
'click .js-open-search-view'() {
|
||||
Sidebar.setView('search');
|
||||
},
|
||||
@@ -160,6 +165,9 @@ Template.boardHeaderBar.helpers({
|
||||
boardView() {
|
||||
return Utils.boardView();
|
||||
},
|
||||
isSortActive() {
|
||||
return Session.get('sortBy') ? true : false;
|
||||
},
|
||||
});
|
||||
|
||||
Template.boardChangeViewPopup.events({
|
||||
@@ -368,3 +376,44 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('listsortPopup');
|
||||
*/
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-sort-due'() {
|
||||
const sortBy = {
|
||||
dueAt: 1,
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('due-date'));
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-sort-title'() {
|
||||
const sortBy = {
|
||||
title: 1,
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('title'));
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-sort-created-asc'() {
|
||||
const sortBy = {
|
||||
createdAt: 1,
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('date-created-newest-first'));
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-sort-created-desc'() {
|
||||
const sortBy = {
|
||||
createdAt: -1,
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('date-created-oldest-first'));
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('cardsSortPopup');
|
||||
@@ -124,9 +124,13 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
'click .js-clone-board'(evt) {
|
||||
Meteor.call(
|
||||
'cloneBoard',
|
||||
'copyBoard',
|
||||
this.currentData()._id,
|
||||
Session.get('fromBoard'),
|
||||
{
|
||||
sort: Boards.find({ archived: false }).count(),
|
||||
type: 'board',
|
||||
title: Boards.findOne(this.currentData()._id).title,
|
||||
},
|
||||
(err, res) => {
|
||||
if (err) {
|
||||
this.setError(err.error);
|
||||
|
||||
@@ -52,6 +52,14 @@ template(name="cardCustomField-number")
|
||||
if value
|
||||
= value
|
||||
|
||||
template(name="cardCustomField-checkbox")
|
||||
.js-checklist-item.checklist-item(class="{{#if data.value }}is-checked{{/if}}")
|
||||
if canModifyCard
|
||||
.check-box-container
|
||||
.check-box.materialCheckBox(class="{{#if data.value }}is-checked{{/if}}")
|
||||
else
|
||||
.materialCheckBox(class="{{#if data.value }}is-checked{{/if}}")
|
||||
|
||||
template(name="cardCustomField-currency")
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-customfield-currency")
|
||||
@@ -111,3 +119,24 @@ template(name="cardCustomField-dropdown")
|
||||
if value
|
||||
+viewer
|
||||
= selectedItem
|
||||
|
||||
template(name="cardCustomField-stringtemplate")
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-customfield-stringtemplate")
|
||||
each item in stringtemplateItems.get
|
||||
input.js-card-customfield-stringtemplate-item(type="text" value=item placeholder="")
|
||||
input.js-card-customfield-stringtemplate-item.last(type="text" value="" placeholder="{{_ 'custom-field-stringtemplate-item-placeholder'}}")
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if value
|
||||
+viewer
|
||||
= formattedValue
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
if value
|
||||
+viewer
|
||||
= formattedValue
|
||||
@@ -1,3 +1,6 @@
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
import Cards from '/models/cards';
|
||||
|
||||
Template.cardCustomFieldsPopup.helpers({
|
||||
hasCustomField() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
@@ -80,6 +83,25 @@ CardCustomField.register('cardCustomField');
|
||||
}
|
||||
}.register('cardCustomField-number'));
|
||||
|
||||
// cardCustomField-checkbox
|
||||
(class extends CardCustomField {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
}
|
||||
|
||||
toggleItem() {
|
||||
this.card.setCustomField(this.customFieldId, !this.data().value);
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-checklist-item .check-box-container': this.toggleItem,
|
||||
},
|
||||
];
|
||||
}
|
||||
}.register('cardCustomField-checkbox'));
|
||||
|
||||
// cardCustomField-currency
|
||||
(class extends CardCustomField {
|
||||
onCreated() {
|
||||
@@ -102,7 +124,8 @@ CardCustomField.register('cardCustomField');
|
||||
{
|
||||
'submit .js-card-customfield-currency'(event) {
|
||||
event.preventDefault();
|
||||
const value = Number(this.find('input').value, 10);
|
||||
// To allow input separated by comma, the comma is replaced by a period.
|
||||
const value = Number(this.find('input').value.replace(/,/i, '.'), 10);
|
||||
this.card.setCustomField(this.customFieldId, value);
|
||||
},
|
||||
},
|
||||
@@ -214,3 +237,90 @@ CardCustomField.register('cardCustomField');
|
||||
];
|
||||
}
|
||||
}.register('cardCustomField-dropdown'));
|
||||
|
||||
// cardCustomField-stringtemplate
|
||||
(class extends CardCustomField {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
|
||||
this.stringtemplateFormat = this.data().definition.settings.stringtemplateFormat;
|
||||
this.stringtemplateSeparator = this.data().definition.settings.stringtemplateSeparator;
|
||||
|
||||
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 ?? '');
|
||||
}
|
||||
|
||||
getItems() {
|
||||
return Array.from(this.findAll('input'))
|
||||
.map(input => input.value)
|
||||
.filter(value => !!value.trim());
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'submit .js-card-customfield-stringtemplate'(event) {
|
||||
event.preventDefault();
|
||||
const items = this.getItems();
|
||||
this.card.setCustomField(this.customFieldId, items);
|
||||
},
|
||||
|
||||
'keydown .js-card-customfield-stringtemplate-item'(event) {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
this.find('button[type=submit]').click();
|
||||
} else if (event.target.value.trim()) {
|
||||
const inputLast = this.find('input.last');
|
||||
|
||||
let items = this.getItems();
|
||||
|
||||
if (event.target === inputLast) {
|
||||
inputLast.value = '';
|
||||
} else if (event.target.nextSibling === inputLast) {
|
||||
inputLast.focus();
|
||||
} else {
|
||||
event.target.blur();
|
||||
|
||||
const idx = Array.from(this.findAll('input')).indexOf(
|
||||
event.target,
|
||||
);
|
||||
items.splice(idx + 1, 0, '');
|
||||
|
||||
Tracker.afterFlush(() => {
|
||||
const element = this.findAll('input')[idx + 1];
|
||||
element.focus();
|
||||
element.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
this.stringtemplateItems.set(items);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'blur .js-card-customfield-stringtemplate-item'(event) {
|
||||
if (
|
||||
!event.target.value.trim() ||
|
||||
event.target === this.find('input.last')
|
||||
) {
|
||||
const items = this.getItems();
|
||||
this.stringtemplateItems.set(items);
|
||||
this.find('input.last').value = '';
|
||||
}
|
||||
},
|
||||
|
||||
'click .js-close-inlined-form'(event) {
|
||||
this.stringtemplateItems.set(this.data().value ?? []);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}.register('cardCustomField-stringtemplate'));
|
||||
@@ -8,3 +8,7 @@ template(name="dateBadge")
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
|
||||
template(name="dateCustomField")
|
||||
a(title="{{showTitle}}" class="{{classes}}")
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
@@ -1,96 +1,4 @@
|
||||
// Edit received, start, due & end dates
|
||||
BlazeComponent.extendComponent({
|
||||
template() {
|
||||
return 'editCardDate';
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.card = this.data();
|
||||
this.date = new ReactiveVar(moment.invalid());
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
const $picker = this.$('.js-datepicker')
|
||||
.datepicker({
|
||||
todayHighlight: true,
|
||||
todayBtn: 'linked',
|
||||
language: TAPi18n.getLanguage(),
|
||||
})
|
||||
.on(
|
||||
'changeDate',
|
||||
function(evt) {
|
||||
this.find('#date').value = moment(evt.date).format('L');
|
||||
this.error.set('');
|
||||
this.find('#time').focus();
|
||||
}.bind(this),
|
||||
);
|
||||
|
||||
if (this.date.get().isValid()) {
|
||||
$picker.datepicker('update', this.date.get().toDate());
|
||||
}
|
||||
},
|
||||
|
||||
showDate() {
|
||||
if (this.date.get().isValid()) return this.date.get().format('L');
|
||||
return '';
|
||||
},
|
||||
showTime() {
|
||||
if (this.date.get().isValid()) return this.date.get().format('LT');
|
||||
return '';
|
||||
},
|
||||
dateFormat() {
|
||||
return moment.localeData().longDateFormat('L');
|
||||
},
|
||||
timeFormat() {
|
||||
return moment.localeData().longDateFormat('LT');
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keyup .js-date-field'() {
|
||||
// parse for localized date format in strict mode
|
||||
const dateMoment = moment(this.find('#date').value, 'L', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
|
||||
}
|
||||
},
|
||||
'keyup .js-time-field'() {
|
||||
// parse for localized time format in strict mode
|
||||
const dateMoment = moment(this.find('#time').value, 'LT', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
}
|
||||
},
|
||||
'submit .edit-date'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
// if no time was given, init with 12:00
|
||||
const time =
|
||||
evt.target.time.value ||
|
||||
moment(new Date().setHours(12, 0, 0)).format('LT');
|
||||
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
const newDate = moment(dateString, 'L LT', true);
|
||||
if (newDate.isValid()) {
|
||||
this._storeDate(newDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.error.set('invalid-date');
|
||||
evt.target.date.focus();
|
||||
}
|
||||
},
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
this._deleteDate();
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
|
||||
Template.dateBadge.helpers({
|
||||
canModifyCard() {
|
||||
@@ -279,7 +187,7 @@ class CardStartDate extends CardDate {
|
||||
// if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
|
||||
if ((endAt && theDate.isAfter(endAt)) || (dueAt && theDate.isAfter(dueAt)))
|
||||
classes += 'long-overdue';
|
||||
else if (theDate.isBefore(now, 'minute')) classes += 'almost-due';
|
||||
else if (theDate.isAfter(now)) classes += '';
|
||||
else classes += 'current';
|
||||
return classes;
|
||||
}
|
||||
@@ -363,6 +271,33 @@ class CardEndDate extends CardDate {
|
||||
}
|
||||
CardEndDate.register('cardEndDate');
|
||||
|
||||
class CardCustomFieldDate extends CardDate {
|
||||
template() {
|
||||
return 'dateCustomField';
|
||||
}
|
||||
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
self.autorun(() => {
|
||||
self.date.set(moment(self.data().value));
|
||||
});
|
||||
}
|
||||
|
||||
classes() {
|
||||
return 'customfield-date';
|
||||
}
|
||||
|
||||
showTitle() {
|
||||
return '';
|
||||
}
|
||||
|
||||
events() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
CardCustomFieldDate.register('cardCustomFieldDate');
|
||||
|
||||
(class extends CardReceivedDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
@@ -387,6 +322,12 @@ CardEndDate.register('cardEndDate');
|
||||
}
|
||||
}.register('minicardEndDate'));
|
||||
|
||||
(class extends CardCustomFieldDate {
|
||||
showDate() {
|
||||
return this.date.get().format('l');
|
||||
}
|
||||
}.register('minicardCustomFieldDate'));
|
||||
|
||||
class VoteEndDate extends CardDate {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
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
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
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
|
||||
@@ -57,3 +57,7 @@
|
||||
-webkit-font-smoothing: antialiased
|
||||
margin-right: 0.3em
|
||||
|
||||
.customfield-date
|
||||
display: block
|
||||
border-radius: 4px
|
||||
padding: 1px 3px
|
||||
@@ -3,5 +3,5 @@ template(name="descriptionForm")
|
||||
.new-description.js-new-description(
|
||||
class="{{#if descriptionFormIsOpen}}is-open{{/if}}")
|
||||
form.js-new-description-form
|
||||
+editor(class="js-new-description-input" data-meteor-emoji-large="true" autofocus="autofocus")
|
||||
+editor(class="js-new-description-input" autofocus="autofocus")
|
||||
| {{getUnsavedValue 'cardDescription' _id getDescription}}
|
||||
@@ -3,6 +3,7 @@ const descriptionFormIsOpen = new ReactiveVar(false);
|
||||
BlazeComponent.extendComponent({
|
||||
onDestroyed() {
|
||||
descriptionFormIsOpen.set(false);
|
||||
$('.note-popover').hide();
|
||||
},
|
||||
|
||||
descriptionFormIsOpen() {
|
||||
|
||||
@@ -8,11 +8,10 @@ template(name="cardDetails")
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details
|
||||
if currentUser.isBoardMember
|
||||
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
|
||||
input.inline-input(type="text" id="cardURL_copy" value="{{ absoluteUrl }}")
|
||||
input.inline-input(type="text" id="cardURL_copy" value="{{ originRelativeUrl }}")
|
||||
a.fa.fa-link.card-copy-button.js-copy-link(
|
||||
class="fa-link"
|
||||
title="{{_ 'copy-card-link-to-clipboard'}}"
|
||||
value="{{ absoluteUrl }}"
|
||||
)
|
||||
if isMiniScreen
|
||||
a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details
|
||||
@@ -111,15 +110,24 @@ template(name="cardDetails")
|
||||
a.card-label.add-label.js-end-date
|
||||
i.fa.fa-plus
|
||||
|
||||
hr
|
||||
if currentBoard.allowsCreator
|
||||
.card-details-item.card-details-item-creator
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-user
|
||||
| {{_ 'creator'}}
|
||||
|
||||
+userAvatar(userId=userId noRemove=true)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
|
||||
//.card-details-items
|
||||
if currentBoard.allowsMembers
|
||||
hr
|
||||
.card-details-item.card-details-item-members
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-users
|
||||
| {{_ 'members'}}
|
||||
each getMembers
|
||||
+userAvatar(userId=this cardId=../_id)
|
||||
each userId in getMembers
|
||||
+userAvatar(userId=userId cardId=_id)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
@@ -132,8 +140,8 @@ template(name="cardDetails")
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-user
|
||||
| {{_ 'assignee'}}
|
||||
each getAssignees
|
||||
+userAvatarAssignee(userId=this cardId=../_id)
|
||||
each userId in getAssignees
|
||||
+userAvatar(userId=userId cardId=_id assignee=true)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
if canModifyCard
|
||||
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
|
||||
@@ -489,23 +497,6 @@ template(name="cardAssigneesPopup")
|
||||
if currentUser.isCardAssignee
|
||||
i.fa.fa-check
|
||||
|
||||
template(name="userAvatarAssignee")
|
||||
a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})")
|
||||
if userData.profile.avatarUrl
|
||||
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
|
||||
else
|
||||
+userAvatarAssigneeInitials(userId=userData._id)
|
||||
|
||||
if showStatus
|
||||
span.assignee-presence-status(class=presenceStatusClassName)
|
||||
span.member-type(class=memberType)
|
||||
|
||||
unless isSandstorm
|
||||
if showEdit
|
||||
if $eq currentUser._id userData._id
|
||||
a.edit-avatar.js-change-avatar
|
||||
i.fa.fa-pencil
|
||||
|
||||
template(name="cardAssigneePopup")
|
||||
.board-assignee-menu
|
||||
.mini-profile-info
|
||||
@@ -523,17 +514,13 @@ template(name="cardAssigneePopup")
|
||||
with currentUser
|
||||
li: a.js-edit-profile {{_ 'edit-profile'}}
|
||||
|
||||
template(name="userAvatarAssigneeInitials")
|
||||
svg.avatar.avatar-assignee-initials(viewBox="0 0 {{viewPortWidth}} 15")
|
||||
text(x="50%" y="13" text-anchor="middle")= initials
|
||||
|
||||
template(name="cardMorePopup")
|
||||
p.quiet
|
||||
span.clearfix
|
||||
span {{_ 'link-card'}}
|
||||
= ' '
|
||||
i.fa.colorful(class="{{#if board.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
|
||||
input.inline-input(type="text" id="cardURL" readonly value="{{ absoluteUrl }}" autofocus="autofocus")
|
||||
input.inline-input(type="text" id="cardURL" readonly value="{{ originRelativeUrl }}" autofocus="autofocus")
|
||||
button.js-copy-card-link-to-clipboard(class="btn" id="clipboard") {{_ 'copy-card-link-to-clipboard'}}
|
||||
span.clearfix
|
||||
br
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
import Cards from '/models/cards';
|
||||
import Boards from '/models/boards';
|
||||
import Checklists from '/models/checklists';
|
||||
import Integrations from '/models/integrations';
|
||||
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';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndexData } = Utils;
|
||||
|
||||
let cardColors;
|
||||
Meteor.startup(() => {
|
||||
cardColors = Cards.simpleSchema()._schema.color.allowedValues;
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
mixins() {
|
||||
return [Mixins.InfiniteScrolling];
|
||||
@@ -114,7 +121,7 @@ BlazeComponent.extendComponent({
|
||||
if (card) {
|
||||
const board = Boards.findOne(card.boardId);
|
||||
if (board) {
|
||||
result = FlowRouter.url('card', {
|
||||
result = FlowRouter.path('card', {
|
||||
boardId: card.boardId,
|
||||
slug: board.slug,
|
||||
cardId: card._id,
|
||||
@@ -160,9 +167,7 @@ BlazeComponent.extendComponent({
|
||||
integration,
|
||||
'CardSelected',
|
||||
params,
|
||||
() => {
|
||||
return;
|
||||
},
|
||||
() => {},
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -290,7 +295,9 @@ BlazeComponent.extendComponent({
|
||||
Utils.goBoardId(this.data().boardId);
|
||||
},
|
||||
'click .js-copy-link'() {
|
||||
StringToCopyElement = document.getElementById('cardURL_copy');
|
||||
const StringToCopyElement = document.getElementById('cardURL_copy');
|
||||
StringToCopyElement.value =
|
||||
window.location.origin + window.location.pathname;
|
||||
StringToCopyElement.select();
|
||||
if (document.execCommand('copy')) {
|
||||
StringToCopyElement.blur();
|
||||
@@ -405,122 +412,6 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('cardDetails');
|
||||
|
||||
Template.cardDetails.helpers({
|
||||
userData() {
|
||||
// We need to handle a special case for the search results provided by the
|
||||
// `matteodem:easy-search` package. Since these results gets published in a
|
||||
// separate collection, and not in the standard Meteor.Users collection as
|
||||
// expected, we use a component parameter ("property") to distinguish the
|
||||
// two cases.
|
||||
const userCollection = this.esSearch ? ESSearchResults : Users;
|
||||
return userCollection.findOne(this.userId, {
|
||||
fields: {
|
||||
profile: 1,
|
||||
username: 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
receivedSelected() {
|
||||
if (this.getReceived().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
startSelected() {
|
||||
if (this.getStart().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
endSelected() {
|
||||
if (this.getEnd().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
dueSelected() {
|
||||
if (this.getDue().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
memberSelected() {
|
||||
if (this.getMembers().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
labelSelected() {
|
||||
if (this.getLabels().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
assigneeSelected() {
|
||||
if (this.getAssignees().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
requestBySelected() {
|
||||
if (this.getRequestBy().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
assigneeBySelected() {
|
||||
if (this.getAssigneeBy().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
memberType() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return user && user.isBoardAdmin() ? 'admin' : 'normal';
|
||||
},
|
||||
|
||||
presenceStatusClassName() {
|
||||
const user = Users.findOne(this.userId);
|
||||
const userPresence = presences.findOne({ userId: this.userId });
|
||||
if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
|
||||
else if (!userPresence) return 'disconnected';
|
||||
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
|
||||
return 'active';
|
||||
else return 'idle';
|
||||
},
|
||||
});
|
||||
|
||||
Template.userAvatarAssigneeInitials.helpers({
|
||||
initials() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return user && user.getInitials();
|
||||
},
|
||||
|
||||
viewPortWidth() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return ((user && user.getInitials().length) || 1) * 12;
|
||||
},
|
||||
});
|
||||
|
||||
// We extends the normal InlinedForm component to support UnsavedEdits draft
|
||||
// feature.
|
||||
(class extends InlinedForm {
|
||||
@@ -695,7 +586,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find(
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
@@ -705,7 +596,6 @@ BlazeComponent.extendComponent({
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
swimlanes() {
|
||||
@@ -734,7 +624,7 @@ Template.copyCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
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];
|
||||
@@ -799,7 +689,7 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
cursor = Cards.find({ parentId: oldId });
|
||||
const cursor = Cards.find({ parentId: oldId });
|
||||
cursor.forEach(function() {
|
||||
'use strict';
|
||||
const subtask = arguments[0];
|
||||
@@ -825,7 +715,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
colors() {
|
||||
return cardColors.map(color => ({ color, name: '' }));
|
||||
return ALLOWED_COLORS.map(color => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
@@ -869,7 +759,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find(
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
@@ -881,7 +771,6 @@ BlazeComponent.extendComponent({
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
cards() {
|
||||
|
||||
@@ -211,6 +211,7 @@ avatar-radius = 50%
|
||||
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,
|
||||
|
||||
@@ -111,7 +111,7 @@ BlazeComponent.extendComponent({
|
||||
title,
|
||||
checklistId: checklist._id,
|
||||
cardId: checklist.cardId,
|
||||
sort: checklist.itemCount(),
|
||||
sort: Utils.calculateIndexData(checklist.lastItem()).base,
|
||||
});
|
||||
}
|
||||
// We keep the form opened, empty it.
|
||||
|
||||
@@ -44,8 +44,8 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
|
||||
span.fa.checklist-handle
|
||||
padding-right: 20px
|
||||
padding-top: 6px
|
||||
float: right
|
||||
padding-top: 3px
|
||||
float: left
|
||||
|
||||
|
||||
.js-confirm-checklist-delete
|
||||
|
||||
@@ -77,6 +77,14 @@ template(name="minicard")
|
||||
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
|
||||
@@ -85,13 +93,16 @@ template(name="minicard")
|
||||
.minicard-assignees.js-minicard-assignees
|
||||
each getAssignees
|
||||
+userAvatar(userId=this)
|
||||
hr(class="assigneehr" title="{{_ 'assignee'}}")
|
||||
|
||||
if getMembers
|
||||
.minicard-members.js-minicard-members
|
||||
each getMembers
|
||||
+userAvatar(userId=this)
|
||||
|
||||
if showCreator
|
||||
.minicard-creator
|
||||
+userAvatar(userId=this.userId noRemove=true)
|
||||
|
||||
.badges
|
||||
unless currentUser.isNoComments
|
||||
if comments.count
|
||||
@@ -118,3 +129,8 @@ template(name="minicard")
|
||||
.badge(class="{{#if checklistFinished}}is-finished{{/if}}")
|
||||
span.badge-icon.fa.fa-check-square-o
|
||||
span.badge-text.check-list-text {{checklistFinishedCount}}/{{checklistItemCount}}
|
||||
if allSubtasks.count
|
||||
.badge
|
||||
span.badge-icon.fa.fa-sitemap
|
||||
span.badge-text.check-list-text {{subtasksFinishedCount}}/{{allSubtasksCount}}
|
||||
//{{subtasksFinishedCount}}/{{subtasksCount}} does not work because when a subtaks is archived, the count goes down
|
||||
@@ -21,6 +21,34 @@ BlazeComponent.extendComponent({
|
||||
}).format(customFieldTrueValue);
|
||||
},
|
||||
|
||||
formattedStringtemplateCustomFieldValue(definition) {
|
||||
const customField = this.data()
|
||||
.customFieldsWD()
|
||||
.find(f => f._id === definition._id);
|
||||
|
||||
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 ?? '');
|
||||
},
|
||||
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
border-radius: 2px
|
||||
margin-right: 3px
|
||||
margin-bottom: 3px
|
||||
|
||||
|
||||
.minicard-custom-fields
|
||||
display:block;
|
||||
.minicard-custom-field
|
||||
@@ -163,19 +163,18 @@
|
||||
line-height: 12px
|
||||
|
||||
.minicard-members,
|
||||
.minicard-assignees
|
||||
.minicard-assignees,
|
||||
.minicard-creator
|
||||
float: right
|
||||
margin-left: 5px
|
||||
|
||||
.assigneehr
|
||||
border-top: 1px solid red
|
||||
margin-bottom: 4px
|
||||
margin-bottom: 4px
|
||||
|
||||
.member
|
||||
float: right
|
||||
border-radius: 50%
|
||||
height: 28px
|
||||
width: @height
|
||||
margin-bottom: 4px
|
||||
|
||||
.assignee
|
||||
float: right
|
||||
@@ -186,6 +185,12 @@
|
||||
+ .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
|
||||
|
||||
@@ -1,23 +1,44 @@
|
||||
template(name="resultCard")
|
||||
.result-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||
a.minicard-wrapper.card-title(href=originRelativeUrl)
|
||||
+minicard(this)
|
||||
//= card.title
|
||||
ul.result-card-context-list
|
||||
li.result-card-context(title="{{_ 'board'}}")
|
||||
+viewer
|
||||
= getBoard.title
|
||||
.result-card-block-wrapper
|
||||
if boardId
|
||||
+viewer
|
||||
= getBoard.title
|
||||
else
|
||||
.broken-cards-null
|
||||
| NULL
|
||||
if getBoard.archived
|
||||
i.fa.fa-archive
|
||||
li.result-card-context.result-card-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.result-card-context(title="{{_ 'swimlane'}}")
|
||||
+viewer
|
||||
= getSwimlane.title
|
||||
.result-card-block-wrapper
|
||||
if swimlaneId
|
||||
+viewer
|
||||
= getSwimlane.title
|
||||
else
|
||||
.broken-cards-null
|
||||
| NULL
|
||||
if getSwimlane.archived
|
||||
i.fa.fa-archive
|
||||
li.result-card-context.result-card-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.result-card-context(title="{{_ 'list'}}")
|
||||
+viewer
|
||||
= getList.title
|
||||
.result-card-block-wrapper
|
||||
if listId
|
||||
+viewer
|
||||
= getList.title
|
||||
else
|
||||
.broken-cards-null
|
||||
| NULL
|
||||
if getList.archived
|
||||
i.fa.fa-archive
|
||||
@@ -19,3 +19,6 @@
|
||||
|
||||
.result-card-context-list
|
||||
margin-bottom: 0.7rem
|
||||
|
||||
.result-card-block-wrapper
|
||||
display: inline-block
|
||||
@@ -242,11 +242,11 @@ textarea
|
||||
margin: 3px 4px
|
||||
|
||||
// Material Design checkboxes
|
||||
[type="checkbox"]:not(:checked),
|
||||
[type="checkbox"]:checked
|
||||
position: absolute
|
||||
left: -9999px
|
||||
visibility: hidden
|
||||
[type="checkbox"]:not(:checked),
|
||||
[type="checkbox"]:checked
|
||||
position: absolute
|
||||
left: -9999px
|
||||
visibility: hidden
|
||||
|
||||
.materialCheckBox
|
||||
position: relative
|
||||
|
||||
@@ -13,38 +13,43 @@ template(name="import")
|
||||
template(name="importTextarea")
|
||||
form
|
||||
p: label(for='import-textarea') {{_ instruction}} {{_ 'import-board-instruction-about-errors'}}
|
||||
textarea.js-import-json(placeholder="{{_ importPlaceHolder}}" autofocus)
|
||||
textarea.js-import-json(id='import-textarea' placeholder="{{_ importPlaceHolder}}" autofocus)
|
||||
| {{jsonText}}
|
||||
input.primary.wide(type="submit" value="{{_ 'import'}}")
|
||||
|
||||
template(name="importMapMembers")
|
||||
h2 {{_ 'import-map-members'}}
|
||||
.map-members
|
||||
p {{_ 'import-members-map'}}
|
||||
.mapping-list
|
||||
each members
|
||||
a.mapping-item.js-select-member(class="{{#if wekanId}}filled{{/if}}")
|
||||
.profile-source
|
||||
.full-name= fullName
|
||||
.username
|
||||
| ({{username}})
|
||||
.wekan
|
||||
if wekanId
|
||||
+userAvatar(userId=wekanId)
|
||||
else
|
||||
a.member.add-member
|
||||
i.fa.fa-plus
|
||||
//-
|
||||
Due to the way the flewbox layout is working, we need to set some
|
||||
invisible items so that the last row items have a consistent width.
|
||||
See http://jsfiddle.net/Ln4h3c4n/ for an minimal example of the issue.
|
||||
.mapping-item.ghost-item
|
||||
.mapping-item.ghost-item
|
||||
.mapping-item.ghost-item
|
||||
.mapping-item.ghost-item
|
||||
.mapping-item.ghost-item
|
||||
form
|
||||
input.primary.wide(type="submit" value="{{_ 'done'}}")
|
||||
if usersLoaded.get
|
||||
.map-members
|
||||
p {{_ 'import-members-map'}}
|
||||
p.import-members-map-note
|
||||
| {{_ 'import-members-map-note' }}
|
||||
.mapping-list
|
||||
each members
|
||||
a.mapping-item.js-select-member(class="{{#if wekanId}}filled{{/if}}")
|
||||
.profile-source
|
||||
.full-name= fullName
|
||||
.username
|
||||
| ({{username}})
|
||||
.wekan
|
||||
if wekanId
|
||||
+userAvatar(userId=wekanId)
|
||||
else
|
||||
a.member.add-member
|
||||
i.fa.fa-plus
|
||||
//-
|
||||
Due to the way the flewbox layout is working, we need to set some
|
||||
invisible items so that the last row items have a consistent width.
|
||||
See http://jsfiddle.net/Ln4h3c4n/ for an minimal example of the issue.
|
||||
.mapping-item.ghost-item
|
||||
.mapping-item.ghost-item
|
||||
.mapping-item.ghost-item
|
||||
.mapping-item.ghost-item
|
||||
.mapping-item.ghost-item
|
||||
form
|
||||
input.primary.wide(type="submit" value="{{_ 'done'}}")
|
||||
else
|
||||
+spinner
|
||||
|
||||
template(name="importMapMembersAddPopup")
|
||||
.select-member
|
||||
|
||||
@@ -150,14 +150,42 @@ BlazeComponent.extendComponent({
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.usersLoaded = new ReactiveVar(false);
|
||||
|
||||
this.autorun(() => {
|
||||
this.parentComponent()
|
||||
.membersToMap.get()
|
||||
.forEach(({ wekanId }) => {
|
||||
if (wekanId) {
|
||||
this.subscribe('user-miniprofile', wekanId);
|
||||
const handle = this.subscribe(
|
||||
'user-miniprofile',
|
||||
this.members().map(member => {
|
||||
return member.username;
|
||||
}),
|
||||
);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
if (
|
||||
handle.ready() &&
|
||||
!this.usersLoaded.get() &&
|
||||
this.members().length
|
||||
) {
|
||||
this._refreshMembers(
|
||||
this.members().map(member => {
|
||||
if (!member.wekanId) {
|
||||
let user = Users.findOne({ username: member.username });
|
||||
if (!user) {
|
||||
user = Users.findOne({ importUsernames: member.username });
|
||||
}
|
||||
if (user) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('found username:', user.username);
|
||||
member.wekanId = user._id;
|
||||
}
|
||||
}
|
||||
return member;
|
||||
}),
|
||||
);
|
||||
}
|
||||
this.usersLoaded.set(handle.ready());
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
Vendored
+4
@@ -47,3 +47,7 @@
|
||||
|
||||
a.show-mapping
|
||||
text-decoration underline
|
||||
|
||||
.import-members-map-note
|
||||
font-size: 90%
|
||||
font-weight: bold
|
||||
@@ -180,7 +180,8 @@
|
||||
border-bottom: 1px solid darken(white, 20%)
|
||||
|
||||
.list
|
||||
display: block
|
||||
display: contents
|
||||
flex-basis: auto
|
||||
width: 100%
|
||||
border-left: 0px
|
||||
&:first-child
|
||||
|
||||
@@ -5,7 +5,7 @@ template(name="listBody")
|
||||
+inlinedForm(autoclose=false position="top")
|
||||
+addCardForm(listId=_id position="top")
|
||||
each (cardsWithLimit (idOrNull ../../_id))
|
||||
a.minicard-wrapper.js-minicard(href=absoluteUrl
|
||||
a.minicard-wrapper.js-minicard(href=originRelativeUrl
|
||||
class="{{#if cardIsSelected}}is-selected{{/if}}"
|
||||
class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
|
||||
if MultiSelection.isActive
|
||||
|
||||
@@ -168,13 +168,16 @@ BlazeComponent.extendComponent({
|
||||
|
||||
cardsWithLimit(swimlaneId) {
|
||||
const limit = this.cardlimit.get();
|
||||
const defaultSort = { sort: 1 };
|
||||
const sortBy = Session.get('sortBy') ? Session.get('sortBy') : defaultSort;
|
||||
const selector = {
|
||||
listId: this.currentData()._id,
|
||||
archived: false,
|
||||
};
|
||||
if (swimlaneId) selector.swimlaneId = swimlaneId;
|
||||
return Cards.find(Filter.mongoSelector(selector), {
|
||||
sort: ['sort'],
|
||||
// sort: ['sort'],
|
||||
sort: sortBy,
|
||||
limit,
|
||||
});
|
||||
},
|
||||
@@ -239,7 +242,7 @@ BlazeComponent.extendComponent({
|
||||
.customFields()
|
||||
.fetch(),
|
||||
function(field) {
|
||||
if (field.automaticallyOnCard)
|
||||
if (field.automaticallyOnCard || field.alwaysOnCard)
|
||||
arr.push({ _id: field._id, value: null });
|
||||
},
|
||||
);
|
||||
@@ -675,12 +678,18 @@ BlazeComponent.extendComponent({
|
||||
element.type = 'swimlane';
|
||||
_id = element.copy(this.boardId);
|
||||
} else if (this.isBoardTemplateSearch) {
|
||||
board = Boards.findOne(element.linkedId);
|
||||
board.sort = Boards.find({ archived: false }).count();
|
||||
board.type = 'board';
|
||||
board.title = element.title;
|
||||
delete board.slug;
|
||||
_id = board.copy();
|
||||
Meteor.call(
|
||||
'copyBoard',
|
||||
element.linkedId,
|
||||
{
|
||||
sort: Boards.find({ archived: false }).count(),
|
||||
type: 'board',
|
||||
title: element.title,
|
||||
},
|
||||
(err, data) => {
|
||||
_id = data;
|
||||
},
|
||||
);
|
||||
}
|
||||
Popup.close();
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ template(name="listHeader")
|
||||
= title
|
||||
if wipLimit.enabled
|
||||
| (
|
||||
span(class="{{#if reachedWipLimit}}highlight{{/if}}") {{cards.count}}
|
||||
span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.count}}
|
||||
|/#{wipLimit.value})
|
||||
|
||||
if showCardsCountForList cards.count
|
||||
|
||||
@@ -72,9 +72,17 @@ BlazeComponent.extendComponent({
|
||||
);
|
||||
},
|
||||
|
||||
exceededWipLimit() {
|
||||
const list = Template.currentData();
|
||||
return (
|
||||
list.getWipLimit('enabled') &&
|
||||
list.getWipLimit('value') < list.cards().count()
|
||||
);
|
||||
},
|
||||
|
||||
showCardsCountForList(count) {
|
||||
const limit = this.limitToShowCardsCount();
|
||||
return limit > 0 && count > limit;
|
||||
return limit >= 0 && count >= limit;
|
||||
},
|
||||
|
||||
events() {
|
||||
|
||||
@@ -3,39 +3,15 @@ template(name="brokenCardsHeaderBar")
|
||||
| {{_ 'broken-cards'}}
|
||||
|
||||
template(name="brokenCards")
|
||||
.wrapper
|
||||
.broken-cards-wrapper
|
||||
each card in brokenCardsList
|
||||
.broken-cards-card-wrapper
|
||||
.broken-cards-card-title
|
||||
= card.title
|
||||
ul.broken-cards-context-list
|
||||
li.broken-cards-context(title="{{_ 'board'}}")
|
||||
if card.boardId
|
||||
+viewer
|
||||
= card.getBoard.title
|
||||
else
|
||||
.broken-cards-null
|
||||
| NULL
|
||||
li.broken-cards-context.broken-cards-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.broken-cards-context(title="{{_ 'swimlane'}}")
|
||||
if card.swimlaneId
|
||||
+viewer
|
||||
= card.getSwimlane.title
|
||||
else
|
||||
.broken-cards-null
|
||||
| NULL
|
||||
li.broken-cards-context
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.broken-cards-context(title="{{_ 'list'}}")
|
||||
if card.listId
|
||||
+viewer
|
||||
= card.getList.title
|
||||
else
|
||||
.broken-cards-null
|
||||
| NULL
|
||||
if currentUser
|
||||
if searching.get
|
||||
+spinner
|
||||
else if hasResults.get
|
||||
.global-search-results-list-wrapper
|
||||
if hasQueryErrors.get
|
||||
div
|
||||
each msg in errorMessages
|
||||
span.global-search-error-messages
|
||||
= msg
|
||||
else
|
||||
+resultsPaged(this)
|
||||
@@ -1,3 +1,5 @@
|
||||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
|
||||
BlazeComponent.extendComponent({}).register('brokenCardsHeaderBar');
|
||||
|
||||
Template.brokenCards.helpers({
|
||||
@@ -6,23 +8,11 @@ Template.brokenCards.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
class BrokenCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('brokenCards');
|
||||
},
|
||||
super.onCreated();
|
||||
|
||||
brokenCardsList() {
|
||||
const selector = {
|
||||
$or: [
|
||||
{ boardId: { $in: [null, ''] } },
|
||||
{ swimlaneId: { $in: [null, ''] } },
|
||||
{ listId: { $in: [null, ''] } },
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
|
||||
],
|
||||
};
|
||||
|
||||
return Cards.find(selector);
|
||||
},
|
||||
}).register('brokenCards');
|
||||
Meteor.subscribe('brokenCards', this.sessionId);
|
||||
}
|
||||
}
|
||||
BrokenCardsComponent.register('brokenCards');
|
||||
@@ -22,33 +22,17 @@ template(name="dueCardsModalTitle")
|
||||
|
||||
template(name="dueCards")
|
||||
if currentUser
|
||||
if isPageReady.get
|
||||
.wrapper
|
||||
.due-cards-dueat-list-wrapper
|
||||
each card in dueCardsList
|
||||
.due-cards-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||
+minicard(card)
|
||||
ul.due-cards-context-list
|
||||
li.due-cards-context(title="{{_ 'board'}}")
|
||||
+viewer
|
||||
= card.getBoard.title
|
||||
li.due-cards-context.due-cards-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.due-cards-context(title="{{_ 'swimlane'}}")
|
||||
+viewer
|
||||
= card.getSwimlane.title
|
||||
li.due-cards-context
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.due-cards-context(title="{{_ 'list'}}")
|
||||
+viewer
|
||||
= card.getList.title
|
||||
else
|
||||
if searching.get
|
||||
+spinner
|
||||
else if hasResults.get
|
||||
.global-search-results-list-wrapper
|
||||
if hasQueryErrors.get
|
||||
div
|
||||
each msg in errorMessages
|
||||
span.global-search-error-messages
|
||||
= msg
|
||||
else
|
||||
+resultsPaged(this)
|
||||
|
||||
template(name="dueCardsViewChangePopup")
|
||||
if currentUser
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
const subManager = new SubsManager();
|
||||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
import {
|
||||
OPERATOR_HAS,
|
||||
OPERATOR_SORT,
|
||||
OPERATOR_USER,
|
||||
ORDER_ASCENDING,
|
||||
PREDICATE_DUE_AT,
|
||||
} from '../../../config/search-const';
|
||||
import { QueryParams } from '../../../config/query-classes';
|
||||
|
||||
// const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
dueCardsView() {
|
||||
@@ -40,106 +50,51 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('dueCardsViewChangePopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
class DueCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
this.isPageReady = new ReactiveVar(false);
|
||||
super.onCreated();
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe(
|
||||
'dueCards',
|
||||
Utils.dueCardsView() === 'all',
|
||||
);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isPageReady.set(handle.ready());
|
||||
});
|
||||
});
|
||||
const queryParams = new QueryParams();
|
||||
queryParams.addPredicate(OPERATOR_HAS, {
|
||||
field: PREDICATE_DUE_AT,
|
||||
exists: true,
|
||||
});
|
||||
Meteor.subscribe('setting');
|
||||
},
|
||||
// queryParams[OPERATOR_LIMIT] = 5;
|
||||
queryParams.addPredicate(OPERATOR_SORT, {
|
||||
name: PREDICATE_DUE_AT,
|
||||
order: ORDER_ASCENDING,
|
||||
});
|
||||
|
||||
if (Utils.dueCardsView() !== 'all') {
|
||||
queryParams.addPredicate(OPERATOR_USER, Meteor.user().username);
|
||||
}
|
||||
|
||||
this.runGlobalSearch(queryParams);
|
||||
}
|
||||
|
||||
dueCardsView() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('sort:', Utils.dueCardsView());
|
||||
//console.log('sort:', Utils.dueCardsView());
|
||||
return Utils.dueCardsView();
|
||||
},
|
||||
}
|
||||
|
||||
sortByBoard() {
|
||||
return this.dueCardsView() === 'board';
|
||||
},
|
||||
}
|
||||
|
||||
dueCardsList() {
|
||||
const allUsers = Utils.dueCardsView() === 'all';
|
||||
|
||||
const user = Meteor.user();
|
||||
|
||||
const archivedBoards = [];
|
||||
Boards.find({ archived: true }).forEach(board => {
|
||||
archivedBoards.push(board._id);
|
||||
});
|
||||
|
||||
const permiitedBoards = [];
|
||||
let selector = {
|
||||
archived: false,
|
||||
};
|
||||
// for every user including admin allow her to see cards only from public boards
|
||||
// or those where she is a member
|
||||
//if (!user.isAdmin) {
|
||||
selector.$or = [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
|
||||
];
|
||||
//}
|
||||
Boards.find(selector).forEach(board => {
|
||||
permiitedBoards.push(board._id);
|
||||
});
|
||||
|
||||
const archivedSwimlanes = [];
|
||||
Swimlanes.find({ archived: true }).forEach(swimlane => {
|
||||
archivedSwimlanes.push(swimlane._id);
|
||||
});
|
||||
|
||||
const archivedLists = [];
|
||||
Lists.find({ archived: true }).forEach(list => {
|
||||
archivedLists.push(list._id);
|
||||
});
|
||||
|
||||
selector = {
|
||||
archived: false,
|
||||
boardId: {
|
||||
$nin: archivedBoards,
|
||||
$in: permiitedBoards,
|
||||
},
|
||||
swimlaneId: { $nin: archivedSwimlanes },
|
||||
listId: { $nin: archivedLists },
|
||||
dueAt: { $ne: null },
|
||||
endAt: null,
|
||||
};
|
||||
|
||||
if (!allUsers) {
|
||||
selector.$or = [{ members: user._id }, { assignees: user._id }];
|
||||
const results = this.getResults();
|
||||
console.log('results:', results);
|
||||
const cards = [];
|
||||
if (results) {
|
||||
results.forEach(card => {
|
||||
cards.push(card);
|
||||
});
|
||||
}
|
||||
|
||||
const cards = [];
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cards selector:', selector);
|
||||
Cards.find(selector).forEach(card => {
|
||||
cards.push(card);
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log(
|
||||
// 'board:',
|
||||
// card.board(),
|
||||
// 'swimlane:',
|
||||
// card.swimlane(),
|
||||
// 'list:',
|
||||
// card.list(),
|
||||
// );
|
||||
});
|
||||
|
||||
cards.sort((a, b) => {
|
||||
const x = a.dueAt === null ? Date('2100-12-31') : a.dueAt;
|
||||
const y = b.dueAt === null ? Date('2100-12-31') : b.dueAt;
|
||||
const x = a.dueAt === null ? new Date('2100-12-31') : a.dueAt;
|
||||
const y = b.dueAt === null ? new Date('2100-12-31') : b.dueAt;
|
||||
|
||||
if (x > y) return 1;
|
||||
else if (x < y) return -1;
|
||||
@@ -148,7 +103,9 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cards:', cards);
|
||||
console.log('cards:', cards);
|
||||
return cards;
|
||||
},
|
||||
}).register('dueCards');
|
||||
}
|
||||
}
|
||||
|
||||
DueCardsComponent.register('dueCards');
|
||||
@@ -1,69 +1,4 @@
|
||||
.due-cards-board-wrapper
|
||||
border-radius: 8px
|
||||
//padding: 0.5rem
|
||||
min-width: 400px
|
||||
border-width: 8px
|
||||
border-color: grey
|
||||
border-style: solid
|
||||
margin-bottom: 2rem
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
|
||||
.due-cards-board-title
|
||||
font-size: 1.4rem
|
||||
font-weight: bold
|
||||
padding: 0.5rem
|
||||
background-color: grey
|
||||
color: white
|
||||
|
||||
.due-cards-swimlane-title
|
||||
font-size: 1.1rem
|
||||
font-weight: bold
|
||||
padding: 0.5rem
|
||||
padding-bottom: 0.4rem
|
||||
margin-top: 0
|
||||
margin-bottom: 0.5rem
|
||||
//border-top: black 1px solid
|
||||
//border-bottom: black 1px solid
|
||||
text-align: center
|
||||
|
||||
.swimlane-default-color
|
||||
background-color: lightgrey
|
||||
|
||||
.due-cards-list-title
|
||||
font-weight: bold
|
||||
font-size: 1.1rem
|
||||
//padding-bottom: 0
|
||||
//margin-bottom: 0
|
||||
text-align: center
|
||||
margin-bottom: 0.7rem
|
||||
|
||||
.due-cards-list-wrapper
|
||||
margin: 1rem
|
||||
border-radius: 5px
|
||||
padding: 1.5rem
|
||||
padding-top: 0.75rem
|
||||
display: inline-block
|
||||
min-width: 250px
|
||||
max-width: 350px
|
||||
|
||||
.due-cards-card-wrapper
|
||||
margin-top: 0
|
||||
margin-bottom: 10px
|
||||
|
||||
.due-cards-dueat-list-wrapper
|
||||
max-width: 500px
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
|
||||
.due-cards-field-name
|
||||
font-weight: bold
|
||||
|
||||
.due-cards-context
|
||||
display: inline-block
|
||||
|
||||
.due-cards-context-separator
|
||||
font-weight: bold
|
||||
|
||||
.due-cards-context-list
|
||||
margin-bottom: 0.7rem
|
||||
Executable → Regular
+30
-11
@@ -49,7 +49,7 @@ Template.editor.onRendered(() => {
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
//['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
//['insert', ['link', 'picture']], // modal popup has issue somehow :(
|
||||
['insert', ['link']], //, 'picture']], // modal popup has issue somehow :(
|
||||
['view', ['fullscreen', 'help']],
|
||||
];
|
||||
const cleanPastedHTML = function(input) {
|
||||
@@ -145,6 +145,7 @@ Template.editor.onRendered(() => {
|
||||
const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
|
||||
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
|
||||
const insertImage = src => {
|
||||
// process all image upload types to the description/comment window
|
||||
const img = document.createElement('img');
|
||||
img.src = src;
|
||||
img.setAttribute('width', '100%');
|
||||
@@ -210,7 +211,16 @@ Template.editor.onRendered(() => {
|
||||
}
|
||||
}
|
||||
},
|
||||
onPaste() {
|
||||
onPaste(e) {
|
||||
var clipboardData = e.clipboardData;
|
||||
var pastedData = clipboardData.getData('Text');
|
||||
|
||||
//if pasted data is an image, exit
|
||||
if (!pastedData.length) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// clear up unwanted tag info when user pasted in text
|
||||
const thisNote = this;
|
||||
const updatePastedText = function(object) {
|
||||
@@ -219,7 +229,7 @@ Template.editor.onRendered(() => {
|
||||
// (and multiplies by pasting more) by changing paste "p" to "br".
|
||||
// Fixes https://github.com/wekan/wekan/2890 .
|
||||
// == Fix Start ==
|
||||
someNote.execCommand('defaultParagraphSeparator', false, 'br');
|
||||
//someNote.execCommand('defaultParagraphSeparator', false, 'br');
|
||||
// == Fix End ==
|
||||
const original = someNote.summernote('code');
|
||||
const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
|
||||
@@ -234,17 +244,17 @@ Template.editor.onRendered(() => {
|
||||
},
|
||||
},
|
||||
dialogsInBody: true,
|
||||
disableDragAndDrop: true,
|
||||
spellCheck: true,
|
||||
disableGrammar: false,
|
||||
disableDragAndDrop: false,
|
||||
toolbar,
|
||||
popover: {
|
||||
image: [
|
||||
[
|
||||
'image',
|
||||
['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone'],
|
||||
],
|
||||
['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
|
||||
['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||
['remove', ['removeMedia']],
|
||||
],
|
||||
link: [['link', ['linkDialogShow', 'unlink']]],
|
||||
table: [
|
||||
['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
|
||||
['delete', ['deleteRow', 'deleteCol', 'deleteTable']],
|
||||
@@ -263,10 +273,12 @@ Template.editor.onRendered(() => {
|
||||
}
|
||||
});
|
||||
|
||||
import sanitizeXss from 'xss';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
// Additional safeAttrValue function to allow for other specific protocols
|
||||
// See https://github.com/leizongmin/js-xss/issues/52#issuecomment-241354114
|
||||
|
||||
/*
|
||||
function mySafeAttrValue(tag, name, value, cssFilter) {
|
||||
// only when the tag is 'a' and attribute is 'href'
|
||||
// then use your custom function
|
||||
@@ -278,6 +290,8 @@ function mySafeAttrValue(tag, name, value, cssFilter) {
|
||||
/^aodroplink:/gi.test(value) ||
|
||||
/^onenote:/gi.test(value) ||
|
||||
/^file:/gi.test(value) ||
|
||||
/^abasurl:/gi.test(value) ||
|
||||
/^conisio:/gi.test(value) ||
|
||||
/^mailspring:/gi.test(value)
|
||||
) {
|
||||
return value;
|
||||
@@ -290,6 +304,7 @@ function mySafeAttrValue(tag, name, value, cssFilter) {
|
||||
return sanitizeXss.safeAttrValue(tag, name, value, cssFilter);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// XXX I believe we should compute a HTML rendered field on the server that
|
||||
// would handle markdown and user mentions. We can simply have two
|
||||
@@ -305,7 +320,9 @@ Blaze.Template.registerHelper(
|
||||
let content = Blaze.toHTML(view.templateContentBlock);
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (!currentBoard)
|
||||
return HTML.Raw(sanitizeXss(content, { safeAttrValue: mySafeAttrValue }));
|
||||
return HTML.Raw(
|
||||
DOMPurify.sanitize(content, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
);
|
||||
const knowedUsers = currentBoard.members.map(member => {
|
||||
const u = Users.findOne(member.userId);
|
||||
if (u) {
|
||||
@@ -349,7 +366,9 @@ Blaze.Template.registerHelper(
|
||||
content = content.replace(fullMention, Blaze.toHTML(link));
|
||||
}
|
||||
|
||||
return HTML.Raw(sanitizeXss(content, { safeAttrValue: mySafeAttrValue }));
|
||||
return HTML.Raw(
|
||||
DOMPurify.sanitize(content, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -10,74 +10,77 @@ template(name="globalSearchModalTitle")
|
||||
i.fa.fa-keyboard-o
|
||||
| {{_ 'globalSearch-title'}}
|
||||
|
||||
template(name="resultsPaged")
|
||||
if resultsHeading.get
|
||||
h1
|
||||
= resultsHeading.get
|
||||
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
|
||||
each card in results.get
|
||||
+resultCard(card)
|
||||
table.global-search-footer
|
||||
tr
|
||||
td.global-search-previous-page
|
||||
if hasPreviousPage.get
|
||||
button.js-previous-page
|
||||
| {{_ 'previous-page' }}
|
||||
td.global-search-next-page(align="right")
|
||||
if hasNextPage.get
|
||||
button.js-next-page
|
||||
| {{_ 'next-page' }}
|
||||
|
||||
template(name="globalSearch")
|
||||
if currentUser
|
||||
.wrapper
|
||||
form.global-search-instructions.js-search-query-form
|
||||
input.global-search-query-input(type="text" name="searchQuery" placeholder="{{_ 'search-example'}}" autofocus dir="auto")
|
||||
form.global-search-page.js-search-query-form
|
||||
input.global-search-query-input(
|
||||
style="{# if hasResults.get #}display: inline-block;{#/if#}"
|
||||
id="global-search-input"
|
||||
type="text"
|
||||
name="searchQuery"
|
||||
placeholder="{{_ 'search-example'}}"
|
||||
value="{{ query.get }}"
|
||||
autofocus dir="auto"
|
||||
)
|
||||
a.js-new-search.fa.fa-eraser
|
||||
if searching.get
|
||||
+spinner
|
||||
else if hasResults.get
|
||||
.global-search-dueat-list-wrapper
|
||||
h1
|
||||
if $eq resultsCount.get 0
|
||||
| {{_ 'no-cards-found' }}
|
||||
else if $eq resultsCount.get 1
|
||||
| {{_ 'one-card-found' }}
|
||||
else if $eq resultsCount.get totalHits.get
|
||||
| {{_ 'n-cards-found' resultsCount.get }}
|
||||
else
|
||||
| {{_ 'n-n-of-n-cards-found' 1 resultsCount.get totalHits.get }}
|
||||
if queryErrors.get
|
||||
div
|
||||
.global-search-results-list-wrapper
|
||||
if hasQueryErrors.get
|
||||
ul
|
||||
each msg in errorMessages
|
||||
span.global-search-error-messages
|
||||
| {{_ msg.tag msg.value }}
|
||||
each card in results
|
||||
a.minicard-wrapper(href=card.absoluteUrl)
|
||||
+resultCard(card)
|
||||
li.global-search-error-messages
|
||||
= msg
|
||||
+resultsPaged(this)
|
||||
else if serverError.get
|
||||
.global-search-page
|
||||
.global-search-help
|
||||
h1 {{_ 'server-error' }}
|
||||
+viewer
|
||||
| {{_ 'server-error-troubleshooting' }}
|
||||
else
|
||||
.global-search-instructions
|
||||
h1 Search Operators
|
||||
+viewer
|
||||
= 'Searches can include operators to refine the search. Operators are specified by writing the operator'
|
||||
= 'name and value separated by a colon. For example, an operator specification of `list:Blocked` would limit the search'
|
||||
= 'to cards that are contained in a list named *Blocked*. If the value contains spaces or special characters'
|
||||
= 'it must be enclosed in quotation marks (e.g. `list:"To Review"`).\n'
|
||||
= 'Available operators are:\n'
|
||||
= '* `board:title` - cards in boards matching the specified title\n'
|
||||
= '* `list:title` - cards in lists matching the specified title\n'
|
||||
= '* `swimlane:title` - cards in swimlanes matching the specified title\n'
|
||||
= '* `label:color` - cards that have a label matching the given color\n'
|
||||
= '* `label:name` - cards that have a label matching the given name\n'
|
||||
= '* `user:username` - cards where the specified user is a member or assignee\n'
|
||||
= '* `@username` - shorthand for `user:username`\n'
|
||||
= '* `#label` - shorthand for `label:color-or-name`\n'
|
||||
= '## Notes\n'
|
||||
= '* Multiple operators may be specified.\n'
|
||||
= '* Similar operators are *OR*ed together. Cards that match any of the conditions will be returned.\n'
|
||||
= ' `list:Available list:Blocked` would return cards contained in any list named *Blocked* or *Available*.\n'
|
||||
= '* Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned.\n'
|
||||
= '`list:Available label:red` returns only cards in the list *Available* with a *red* label.\n'
|
||||
= '* Text searches are case insensitive.\n'
|
||||
|
||||
template(name="globalSearchViewChangePopup")
|
||||
if currentUser
|
||||
ul.pop-over-list
|
||||
li
|
||||
with "globalSearchViewChange-choice-me"
|
||||
a.js-global-search-view-me
|
||||
i.fa.fa-user.colorful
|
||||
| {{_ 'globalSearchViewChange-choice-me'}}
|
||||
if $eq Utils.globalSearchView "me"
|
||||
i.fa.fa-check
|
||||
li
|
||||
with "globalSearchViewChange-choice-all"
|
||||
a.js-global-search-view-all
|
||||
i.fa.fa-users.colorful
|
||||
| {{_ 'globalSearchViewChange-choice-all'}}
|
||||
span.sub-name
|
||||
+viewer
|
||||
| {{_ 'globalSearchViewChange-choice-all-description' }}
|
||||
if $eq Utils.globalSearchView "all"
|
||||
i.fa.fa-check
|
||||
.global-search-page
|
||||
.global-search-help
|
||||
h2 {{_ 'boards' }}
|
||||
.lists-wrapper
|
||||
each title in myBoardNames.get
|
||||
span.card-label.list-title.js-board-title
|
||||
= title
|
||||
h2 {{_ 'lists' }}
|
||||
.lists-wrapper
|
||||
each title in myLists.get
|
||||
span.card-label.list-title.js-list-title
|
||||
= title
|
||||
h2 {{_ 'label-colors' }}
|
||||
.palette-colors: each label in labelColors
|
||||
span.card-label.palette-color.js-label-color(class="card-label-{{label.color}}")
|
||||
= label.name
|
||||
if myLabelNames.get.length
|
||||
h2 {{_ 'label-names' }}
|
||||
.lists-wrapper
|
||||
each name in myLabelNames.get
|
||||
span.card-label.list-title.js-label-name
|
||||
= name
|
||||
.global-search-instructions
|
||||
+viewer
|
||||
= searchInstructions
|
||||
@@ -1,4 +1,8 @@
|
||||
const subManager = new SubsManager();
|
||||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
import Boards from '../../../models/boards';
|
||||
import { Query, QueryErrors } from '../../../config/query-classes';
|
||||
|
||||
// const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
@@ -16,186 +20,246 @@ Template.globalSearch.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-due-cards-view-me'() {
|
||||
Utils.setDueCardsView('me');
|
||||
Popup.close();
|
||||
},
|
||||
|
||||
'click .js-due-cards-view-all'() {
|
||||
Utils.setDueCardsView('all');
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('globalSearchViewChangePopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
this.isPageReady = new ReactiveVar(true);
|
||||
this.searching = new ReactiveVar(false);
|
||||
this.hasResults = new ReactiveVar(false);
|
||||
this.query = new ReactiveVar('');
|
||||
super.onCreated();
|
||||
this.myLists = new ReactiveVar([]);
|
||||
this.myLabelNames = new ReactiveVar([]);
|
||||
this.myBoardNames = new ReactiveVar([]);
|
||||
this.parsingErrors = new QueryErrors();
|
||||
this.queryParams = null;
|
||||
this.resultsCount = new ReactiveVar(0);
|
||||
this.totalHits = new ReactiveVar(0);
|
||||
this.queryErrors = new ReactiveVar(null);
|
||||
Meteor.subscribe('setting');
|
||||
},
|
||||
|
||||
results() {
|
||||
if (this.queryParams) {
|
||||
const results = Cards.globalSearch(this.queryParams);
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('user:', Meteor.user());
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('user:', Meteor.user().sessionData);
|
||||
// console.log('errors:', results.errors);
|
||||
this.totalHits.set(Meteor.user().sessionData.totalHits);
|
||||
this.resultsCount.set(results.cards.count());
|
||||
this.queryErrors.set(results.errors);
|
||||
return results.cards;
|
||||
Meteor.call('myLists', (err, data) => {
|
||||
if (!err) {
|
||||
this.myLists.set(data);
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.call('myLabelNames', (err, data) => {
|
||||
if (!err) {
|
||||
this.myLabelNames.set(data);
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.call('myBoardNames', (err, data) => {
|
||||
if (!err) {
|
||||
this.myBoardNames.set(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onRendered() {
|
||||
Meteor.subscribe('setting');
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('lang:', TAPi18n.getLanguage());
|
||||
|
||||
if (Session.get('globalQuery')) {
|
||||
this.searchAllBoards(Session.get('globalQuery'));
|
||||
}
|
||||
this.resultsCount.set(0);
|
||||
return [];
|
||||
},
|
||||
}
|
||||
|
||||
resetSearch() {
|
||||
super.resetSearch();
|
||||
this.parsingErrors = new QueryErrors();
|
||||
}
|
||||
|
||||
errorMessages() {
|
||||
const errors = this.queryErrors.get();
|
||||
const messages = [];
|
||||
if (this.parsingErrors.hasErrors()) {
|
||||
return this.parsingErrors.errorMessages();
|
||||
}
|
||||
return this.queryErrorMessages();
|
||||
}
|
||||
|
||||
errors.notFound.boards.forEach(board => {
|
||||
messages.push({ tag: 'board-title-not-found', value: board });
|
||||
});
|
||||
errors.notFound.swimlanes.forEach(swim => {
|
||||
messages.push({ tag: 'swimlane-title-not-found', value: swim });
|
||||
});
|
||||
errors.notFound.lists.forEach(list => {
|
||||
messages.push({ tag: 'list-title-not-found', value: list });
|
||||
});
|
||||
errors.notFound.users.forEach(user => {
|
||||
messages.push({ tag: 'user-username-not-found', value: user });
|
||||
parsingErrorMessages() {
|
||||
this.parsingErrors.errorMessages();
|
||||
}
|
||||
|
||||
searchAllBoards(queryText) {
|
||||
queryText = queryText.trim();
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('queryText:', queryText);
|
||||
|
||||
this.query.set(queryText);
|
||||
|
||||
this.resetSearch();
|
||||
|
||||
if (!queryText) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.searching.set(true);
|
||||
|
||||
const query = new Query();
|
||||
query.buildParams(queryText);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('params:', query.getParams());
|
||||
|
||||
this.queryParams = query.getQueryParams().getParams();
|
||||
|
||||
if (query.hasErrors()) {
|
||||
this.searching.set(false);
|
||||
this.queryErrors = query.errors();
|
||||
this.hasResults.set(true);
|
||||
this.hasQueryErrors.set(true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.runGlobalSearch(query.getQueryParams());
|
||||
}
|
||||
|
||||
searchInstructions() {
|
||||
const tags = {
|
||||
operator_board: TAPi18n.__('operator-board'),
|
||||
operator_list: TAPi18n.__('operator-list'),
|
||||
operator_swimlane: TAPi18n.__('operator-swimlane'),
|
||||
operator_comment: TAPi18n.__('operator-comment'),
|
||||
operator_label: TAPi18n.__('operator-label'),
|
||||
operator_label_abbrev: TAPi18n.__('operator-label-abbrev'),
|
||||
operator_user: TAPi18n.__('operator-user'),
|
||||
operator_user_abbrev: TAPi18n.__('operator-user-abbrev'),
|
||||
operator_member: TAPi18n.__('operator-member'),
|
||||
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
|
||||
operator_assignee: TAPi18n.__('operator-assignee'),
|
||||
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
|
||||
operator_creator: TAPi18n.__('operator-creator'),
|
||||
operator_due: TAPi18n.__('operator-due'),
|
||||
operator_created: TAPi18n.__('operator-created'),
|
||||
operator_modified: TAPi18n.__('operator-modified'),
|
||||
operator_status: TAPi18n.__('operator-status'),
|
||||
operator_has: TAPi18n.__('operator-has'),
|
||||
operator_sort: TAPi18n.__('operator-sort'),
|
||||
operator_limit: TAPi18n.__('operator-limit'),
|
||||
predicate_overdue: TAPi18n.__('predicate-overdue'),
|
||||
predicate_archived: TAPi18n.__('predicate-archived'),
|
||||
predicate_all: TAPi18n.__('predicate-all'),
|
||||
predicate_ended: TAPi18n.__('predicate-ended'),
|
||||
predicate_week: TAPi18n.__('predicate-week'),
|
||||
predicate_month: TAPi18n.__('predicate-month'),
|
||||
predicate_quarter: TAPi18n.__('predicate-quarter'),
|
||||
predicate_year: TAPi18n.__('predicate-year'),
|
||||
predicate_attachment: TAPi18n.__('predicate-attachment'),
|
||||
predicate_description: TAPi18n.__('predicate-description'),
|
||||
predicate_checklist: TAPi18n.__('predicate-checklist'),
|
||||
predicate_public: TAPi18n.__('predicate-public'),
|
||||
predicate_private: TAPi18n.__('predicate-private'),
|
||||
predicate_due: TAPi18n.__('predicate-due'),
|
||||
predicate_created: TAPi18n.__('predicate-created'),
|
||||
predicate_modified: TAPi18n.__('predicate-modified'),
|
||||
predicate_start: TAPi18n.__('predicate-start'),
|
||||
predicate_end: TAPi18n.__('predicate-end'),
|
||||
predicate_assignee: TAPi18n.__('predicate-assignee'),
|
||||
predicate_member: TAPi18n.__('predicate-member'),
|
||||
};
|
||||
|
||||
let text = '';
|
||||
[
|
||||
['# ', 'globalSearch-instructions-heading'],
|
||||
['\n', 'globalSearch-instructions-description'],
|
||||
['\n\n', 'globalSearch-instructions-operators'],
|
||||
['\n* ', 'globalSearch-instructions-operator-board'],
|
||||
['\n* ', 'globalSearch-instructions-operator-list'],
|
||||
['\n* ', 'globalSearch-instructions-operator-swimlane'],
|
||||
['\n* ', 'globalSearch-instructions-operator-comment'],
|
||||
['\n* ', 'globalSearch-instructions-operator-label'],
|
||||
['\n* ', 'globalSearch-instructions-operator-hash'],
|
||||
['\n* ', 'globalSearch-instructions-operator-user'],
|
||||
['\n* ', 'globalSearch-instructions-operator-at'],
|
||||
['\n* ', 'globalSearch-instructions-operator-member'],
|
||||
['\n* ', 'globalSearch-instructions-operator-assignee'],
|
||||
['\n* ', 'globalSearch-instructions-operator-creator'],
|
||||
['\n* ', 'globalSearch-instructions-operator-due'],
|
||||
['\n* ', 'globalSearch-instructions-operator-created'],
|
||||
['\n* ', 'globalSearch-instructions-operator-modified'],
|
||||
['\n* ', 'globalSearch-instructions-operator-status'],
|
||||
['\n * ', 'globalSearch-instructions-status-archived'],
|
||||
['\n * ', 'globalSearch-instructions-status-public'],
|
||||
['\n * ', 'globalSearch-instructions-status-private'],
|
||||
['\n * ', 'globalSearch-instructions-status-all'],
|
||||
['\n * ', 'globalSearch-instructions-status-ended'],
|
||||
['\n* ', 'globalSearch-instructions-operator-has'],
|
||||
['\n* ', 'globalSearch-instructions-operator-sort'],
|
||||
['\n* ', 'globalSearch-instructions-operator-limit'],
|
||||
['\n## ', 'heading-notes'],
|
||||
['\n* ', 'globalSearch-instructions-notes-1'],
|
||||
['\n* ', 'globalSearch-instructions-notes-2'],
|
||||
['\n* ', 'globalSearch-instructions-notes-3'],
|
||||
['\n* ', 'globalSearch-instructions-notes-3-2'],
|
||||
['\n* ', 'globalSearch-instructions-notes-4'],
|
||||
['\n* ', 'globalSearch-instructions-notes-5'],
|
||||
].forEach(([prefix, instruction]) => {
|
||||
text += `${prefix}${TAPi18n.__(instruction, tags)}`;
|
||||
});
|
||||
|
||||
return messages;
|
||||
},
|
||||
return text;
|
||||
}
|
||||
|
||||
labelColors() {
|
||||
return Boards.simpleSchema()._schema['labels.$.color'].allowedValues.map(
|
||||
color => {
|
||||
return { color, name: TAPi18n.__(`color-${color}`) };
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
return super.events().concat([
|
||||
{
|
||||
'submit .js-search-query-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.query.set(evt.target.searchQuery.value);
|
||||
this.queryErrors.set(null);
|
||||
|
||||
if (!this.query.get()) {
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.searching.set(true);
|
||||
this.searchAllBoards(evt.target.searchQuery.value);
|
||||
},
|
||||
'click .js-label-color'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
this.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-label')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-board-title'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
this.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-board')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-list-title'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
this.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-list')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-label-name'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
this.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-label')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-new-search'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
input.value = '';
|
||||
this.query.set('');
|
||||
this.hasResults.set(false);
|
||||
|
||||
let query = this.query.get();
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('query:', query);
|
||||
|
||||
const reOperator1 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<value>\w+)(\s+|$)/;
|
||||
const reOperator2 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<quote>["']*)(?<value>.*?)\k<quote>(\s+|$)/;
|
||||
const reText = /^(?<text>\S+)(\s+|$)/;
|
||||
const reQuotedText = /^(?<quote>["'])(?<text>\w+)\k<quote>(\s+|$)/;
|
||||
|
||||
const operatorMap = {};
|
||||
operatorMap[TAPi18n.__('operator-board')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-board-abbrev')] = 'boards';
|
||||
operatorMap[TAPi18n.__('operator-swimlane')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-swimlane-abbrev')] = 'swimlanes';
|
||||
operatorMap[TAPi18n.__('operator-list')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-list-abbrev')] = 'lists';
|
||||
operatorMap[TAPi18n.__('operator-label')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-label-abbrev')] = 'labels';
|
||||
operatorMap[TAPi18n.__('operator-user')] = 'users';
|
||||
operatorMap[TAPi18n.__('operator-user-abbrev')] = 'users';
|
||||
operatorMap[TAPi18n.__('operator-is')] = 'is';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('operatorMap:', operatorMap);
|
||||
const params = {
|
||||
boards: [],
|
||||
swimlanes: [],
|
||||
lists: [],
|
||||
users: [],
|
||||
labels: [],
|
||||
is: [],
|
||||
};
|
||||
|
||||
let text = '';
|
||||
while (query) {
|
||||
m = query.match(reOperator1);
|
||||
if (!m) {
|
||||
m = query.match(reOperator2);
|
||||
if (m) {
|
||||
query = query.replace(reOperator2, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reOperator1, '');
|
||||
}
|
||||
if (m) {
|
||||
let op;
|
||||
if (m.groups.operator) {
|
||||
op = m.groups.operator.toLowerCase();
|
||||
} else {
|
||||
op = m.groups.abbrev;
|
||||
}
|
||||
if (op in operatorMap) {
|
||||
params[operatorMap[op]].push(m.groups.value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m = query.match(reQuotedText);
|
||||
if (!m) {
|
||||
m = query.match(reText);
|
||||
if (m) {
|
||||
query = query.replace(reText, '');
|
||||
}
|
||||
} else {
|
||||
query = query.replace(reQuotedText, '');
|
||||
}
|
||||
if (m) {
|
||||
text += (text ? ' ' : '') + m.groups.text;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('text:', text);
|
||||
params.text = text;
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('params:', params);
|
||||
|
||||
this.queryParams = params;
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe('globalSearch', params);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('ready:', handle.ready());
|
||||
if (handle.ready()) {
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('globalSearch');
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
GlobalSearchComponent.register('globalSearch');
|
||||
@@ -51,7 +51,7 @@
|
||||
margin-top: 0
|
||||
margin-bottom: 10px
|
||||
|
||||
.global-search-dueat-list-wrapper
|
||||
.global-search-results-list-wrapper
|
||||
max-width: 500px
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
@@ -71,13 +71,19 @@
|
||||
.global-search-error-messages
|
||||
color: darkred
|
||||
|
||||
.global-search-instructions
|
||||
.global-search-page
|
||||
width: 40%
|
||||
min-width: 400px
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
line-height: 150%
|
||||
|
||||
.global-search-page h1
|
||||
margin-top: 2rem;
|
||||
|
||||
.global-search-page h2
|
||||
margin-top: 1rem;
|
||||
|
||||
.global-search-query-input
|
||||
width: 90% !important
|
||||
margin-right: auto
|
||||
@@ -91,7 +97,25 @@
|
||||
font-style: italic
|
||||
|
||||
code
|
||||
color: white
|
||||
background-color: grey
|
||||
color: black
|
||||
background-color: lightgrey
|
||||
padding: 0.1rem !important
|
||||
font-size: 0.7rem !important
|
||||
font-size: 0.8rem !important
|
||||
|
||||
.list-title
|
||||
background-color: darkgray
|
||||
|
||||
.global-search-footer
|
||||
border: none
|
||||
width: 100%
|
||||
|
||||
.global-search-next-page
|
||||
border: none
|
||||
text-align: right;
|
||||
|
||||
.global-search-previous-page
|
||||
border: none
|
||||
text-align: left;
|
||||
|
||||
.global-search-instructions li
|
||||
margin-bottom: 0.3rem
|
||||
@@ -7,16 +7,21 @@ template(name="header")
|
||||
if currentUser
|
||||
#header-quick-access(class=currentBoard.colorClass)
|
||||
if isMiniScreen
|
||||
ul
|
||||
li
|
||||
a(href="{{pathFor 'home'}}")
|
||||
span.fa.fa-home
|
||||
span
|
||||
a(href="{{pathFor 'home'}}")
|
||||
span.fa.fa-home
|
||||
|
||||
ul.header-quick-access-list
|
||||
if currentList
|
||||
each currentBoard.lists
|
||||
li(class="{{#if $.Session.equals 'currentList' _id}}current{{/if}}")
|
||||
a.js-select-list
|
||||
= title
|
||||
else
|
||||
each currentUser.starredBoards
|
||||
li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
|
||||
a(href="{{pathFor 'board' id=_id slug=slug}}")
|
||||
= title
|
||||
#header-new-board-icon
|
||||
else
|
||||
//-
|
||||
@@ -32,23 +37,21 @@ template(name="header")
|
||||
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0")
|
||||
unless currentSetting.customTopLeftCornerLogoImageUrl
|
||||
img(src="{{pathFor '/logo-header.png'}}" alt="")
|
||||
ul
|
||||
li
|
||||
a(href="{{pathFor 'home'}}")
|
||||
span.fa.fa-home
|
||||
| {{_ 'all-boards'}}
|
||||
li.separator -
|
||||
span.allBoards
|
||||
a(href="{{pathFor 'home'}}")
|
||||
span.fa.fa-home
|
||||
| {{_ 'all-boards'}}
|
||||
ul.header-quick-access-list
|
||||
//li
|
||||
// a(href="{{pathFor 'public'}}")
|
||||
// span.fa.fa-globe
|
||||
// | {{_ 'public'}}
|
||||
each currentUser.starredBoards
|
||||
li.separator -
|
||||
li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
|
||||
a(href="{{pathFor 'board' id=_id slug=slug}}")
|
||||
= title
|
||||
else
|
||||
li.current {{_ 'quick-access-description'}}
|
||||
li.current.empty {{_ 'quick-access-description'}}
|
||||
|
||||
// Next line is used only for spacing at header,
|
||||
// there is no visible clickable icon.
|
||||
|
||||
@@ -100,6 +100,10 @@
|
||||
font-size: 12px
|
||||
display: flex
|
||||
z-index: 21
|
||||
padding: 10px 0px
|
||||
|
||||
.allBoards
|
||||
padding: 0 0 0 10px
|
||||
|
||||
#header-user-bar,
|
||||
#header-new-board-icon,
|
||||
@@ -112,21 +116,31 @@
|
||||
a:hover, a.is-active
|
||||
color: white
|
||||
|
||||
ul
|
||||
ul.header-quick-access-list
|
||||
transition: opacity 0.2s
|
||||
margin: 4px 0 0 5px
|
||||
overflow: hidden
|
||||
overflow-x: auto
|
||||
overflow-y: hidden
|
||||
white-space: nowrap
|
||||
padding: 10px
|
||||
margin: -10px
|
||||
|
||||
li
|
||||
display: block
|
||||
float: left
|
||||
display: inline
|
||||
width: auto
|
||||
color: darken(white, 15%)
|
||||
padding: 2px 5px 0
|
||||
padding: 12px 0px
|
||||
margin: -10px 0px
|
||||
|
||||
a
|
||||
padding: 12px 10px
|
||||
margin: -10px 0px
|
||||
|
||||
&.current
|
||||
color: darken(white, 5%)
|
||||
|
||||
&.current.empty
|
||||
padding: 12px 10px 12px 10px
|
||||
|
||||
&:first-child .fa-home,&:nth-child(3) .fa-globe
|
||||
margin-right: 5px
|
||||
|
||||
@@ -186,37 +200,32 @@
|
||||
#header-quick-access
|
||||
transition: background-color 0.4s
|
||||
width: 100%
|
||||
padding: 10px 0px
|
||||
z-index: 30
|
||||
|
||||
ul
|
||||
width: calc(100% - 60px)
|
||||
overflow: ellipsis
|
||||
padding: 10px
|
||||
margin: -10px
|
||||
margin-right: 10px
|
||||
|
||||
li
|
||||
height: 100%
|
||||
padding: 12px 0px
|
||||
margin: -10px 0px
|
||||
|
||||
a
|
||||
height: 100%
|
||||
padding: 12px 10px
|
||||
margin: -10px 0px
|
||||
|
||||
.fa-home
|
||||
span
|
||||
.fa-home
|
||||
font-size: 26px
|
||||
margin-top: -2px
|
||||
margin-right: 10px
|
||||
margin-left: 10px
|
||||
|
||||
#header-new-board-icon
|
||||
display: none
|
||||
|
||||
#header-user-bar
|
||||
position: absolute
|
||||
right: 0px
|
||||
padding: 10px
|
||||
margin: -10px 0 -10px -10px
|
||||
margin: -8px 0 -10px -10px
|
||||
|
||||
.announcement .viewer
|
||||
display: inline-block
|
||||
|
||||
@@ -74,20 +74,50 @@ Template.userFormsLayout.helpers({
|
||||
let name = lang.name;
|
||||
if (lang.name === 'br') {
|
||||
name = 'Brezhoneg';
|
||||
} else if (lang.name === 'es-PY') {
|
||||
name = 'Español de Paraguayo';
|
||||
} else if (lang.name === 'ar-EG') {
|
||||
// ar-EG = Arabic (Egypt), simply Masri (مَصرى, [ˈmɑsˤɾi], Egyptian, Masr refers to Cairo)
|
||||
name = 'مَصرى';
|
||||
} else if (lang.name === 'de-CH') {
|
||||
name = 'Deutsch (Schweiz)';
|
||||
} else if (lang.name === 'fa-IR') {
|
||||
// fa-IR = Persian (Iran)
|
||||
name = 'فارسی/پارسی (ایران)';
|
||||
} else if (lang.name === 'fr-BE') {
|
||||
name = 'Français (Belgique)';
|
||||
} else if (lang.name === 'fr-CA') {
|
||||
name = 'Français (Canada)';
|
||||
} else if (lang.name === 'fr-CA') {
|
||||
name = 'Français (Canada)';
|
||||
} else if (lang.name === 'ig') {
|
||||
name = 'Igbo';
|
||||
} else if (lang.name === 'lv') {
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'latviešu valoda') {
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'Español') {
|
||||
name = 'español';
|
||||
} else if (lang.name === 'es_419') {
|
||||
name = 'español de América Latina';
|
||||
} else if (lang.name === 'es-419') {
|
||||
name = 'español de América Latina';
|
||||
} else if (lang.name === 'Español de América Latina') {
|
||||
name = 'español de América Latina';
|
||||
} else if (lang.name === 'es-LA') {
|
||||
name = 'español de América Latina';
|
||||
} else if (lang.name === 'Español de Argentina') {
|
||||
name = 'español de Argentina';
|
||||
} else if (lang.name === 'Español de Chile') {
|
||||
name = 'español de Chile';
|
||||
} else if (lang.name === 'Español de Colombia') {
|
||||
name = 'español de Colombia';
|
||||
} else if (lang.name === 'Español de México') {
|
||||
name = 'español de México';
|
||||
} else if (lang.name === 'es-PY') {
|
||||
name = 'español de Paraguayo';
|
||||
} else if (lang.name === 'Español de Paraguayo') {
|
||||
name = 'español de Paraguayo';
|
||||
} else if (lang.name === 'Español de Perú') {
|
||||
name = 'español de Perú';
|
||||
} else if (lang.name === 'Español de Puerto Rico') {
|
||||
name = 'español de Puerto Rico';
|
||||
} else if (lang.name === 'oc') {
|
||||
name = 'Occitan';
|
||||
} else if (lang.name === 'st') {
|
||||
|
||||
@@ -6,16 +6,6 @@ template(name="myCardsHeaderBar")
|
||||
i.fa.fa-list
|
||||
| {{_ 'my-cards'}}
|
||||
|
||||
.board-header-btns.left
|
||||
a.board-header-btn.js-toggle-my-cards-choose-sort(title="{{_ 'myCardsSortChange-title'}}")
|
||||
i.fa.fa-caret-down
|
||||
if $eq myCardsSort 'board'
|
||||
i.fa.fa-th-large
|
||||
| {{_ 'myCardsSortChange-choice-board'}}
|
||||
if $eq myCardsSort 'dueAt'
|
||||
i.fa.fa-calendar
|
||||
| {{_ 'myCardsSortChange-choice-dueat'}}
|
||||
|
||||
template(name="myCardsModalTitle")
|
||||
if currentUser
|
||||
h2
|
||||
@@ -24,69 +14,26 @@ template(name="myCardsModalTitle")
|
||||
|
||||
template(name="myCards")
|
||||
if currentUser
|
||||
if isPageReady.get
|
||||
if searching.get
|
||||
+spinner
|
||||
else
|
||||
.wrapper
|
||||
if $eq myCardsSort 'board'
|
||||
each board in myCardsList
|
||||
.my-cards-board-wrapper
|
||||
.my-cards-board-title
|
||||
each board in myCardsList
|
||||
.my-cards-board-wrapper
|
||||
.my-cards-board-title(class=board.colorClass, id="header")
|
||||
a(href=board.originRelativeUrl)
|
||||
+viewer
|
||||
= board.title
|
||||
each swimlane in board.mySwimlanes
|
||||
.my-cards-swimlane-title(class="{{#if swimlane.colorClass}}{{ swimlane.colorClass }}{{else}}swimlane-default-color{{/if}}")
|
||||
+viewer
|
||||
= swimlane.title
|
||||
each list in swimlane.myLists
|
||||
.my-cards-list-wrapper
|
||||
.my-cards-list-title(class=list.colorClass)
|
||||
+viewer
|
||||
= list.title
|
||||
each card in list.myCards
|
||||
.my-cards-card-wrapper
|
||||
a.minicard-wrapper(href=card.absoluteUrl)
|
||||
+minicard(card)
|
||||
else
|
||||
.my-cards-dueat-list-wrapper
|
||||
each card in myDueCardsList
|
||||
.my-cards-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||
+minicard(card)
|
||||
ul.my-cards-context-list
|
||||
li.my-cards-context(title="{{_ 'board'}}")
|
||||
each swimlane in board.mySwimlanes
|
||||
.my-cards-swimlane-title(class="{{#if swimlane.colorClass}}{{ swimlane.colorClass }}{{else}}swimlane-default-color{{/if}}")
|
||||
+viewer
|
||||
= swimlane.title
|
||||
each list in swimlane.myLists
|
||||
.my-cards-list-wrapper
|
||||
.my-cards-list-title(class=list.colorClass)
|
||||
+viewer
|
||||
= card.getBoard.title
|
||||
li.my-cards-context.my-cards-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.my-cards-context(title="{{_ 'swimlane'}}")
|
||||
+viewer
|
||||
= card.getSwimlane.title
|
||||
li.my-cards-context
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.my-cards-context(title="{{_ 'list'}}")
|
||||
+viewer
|
||||
= card.getList.title
|
||||
else
|
||||
+spinner
|
||||
|
||||
template(name="myCardsSortChangePopup")
|
||||
if currentUser
|
||||
ul.pop-over-list
|
||||
li
|
||||
with "my-cards-sort-board"
|
||||
a.js-my-cards-sort-board
|
||||
i.fa.fa-th-large.colorful
|
||||
| {{_ 'myCardsSortChange-choice-board'}}
|
||||
if $eq Utils.myCardsSort "board"
|
||||
i.fa.fa-check
|
||||
hr
|
||||
li
|
||||
with "my-cards-sort-dueat"
|
||||
a.js-my-cards-sort-dueat
|
||||
i.fa.fa-calendar.colorful
|
||||
| {{_ 'myCardsSortChange-choice-dueat'}}
|
||||
if $eq Utils.myCardsSort "dueAt"
|
||||
i.fa.fa-check
|
||||
= list.title
|
||||
each card in list.myCards
|
||||
.my-cards-card-wrapper
|
||||
a.minicard-wrapper(href=card.originRelativeUrl)
|
||||
+minicard(card)
|
||||
@@ -1,4 +1,4 @@
|
||||
const subManager = new SubsManager();
|
||||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
myCardsSort() {
|
||||
@@ -24,227 +24,115 @@ Template.myCards.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-my-cards-sort-board'() {
|
||||
Utils.setMyCardsSort('board');
|
||||
Popup.close();
|
||||
},
|
||||
|
||||
'click .js-my-cards-sort-dueat'() {
|
||||
Utils.setMyCardsSort('dueAt');
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('myCardsSortChangePopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
class MyCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
this.isPageReady = new ReactiveVar(false);
|
||||
super.onCreated();
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe('myCards');
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isPageReady.set(handle.ready());
|
||||
});
|
||||
});
|
||||
});
|
||||
this.runGlobalSearch(null);
|
||||
Meteor.subscribe('setting');
|
||||
},
|
||||
}
|
||||
|
||||
myCardsSort() {
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('sort:', Utils.myCardsSort());
|
||||
return Utils.myCardsSort();
|
||||
},
|
||||
|
||||
sortByBoard() {
|
||||
return this.myCardsSort() === 'board';
|
||||
},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
getSubscription(queryParams) {
|
||||
return Meteor.subscribe(
|
||||
'myCards',
|
||||
this.sessionId,
|
||||
this.subscriptionCallbacks,
|
||||
);
|
||||
}
|
||||
|
||||
myCardsList() {
|
||||
const userId = Meteor.userId();
|
||||
const boards = [];
|
||||
let board = null;
|
||||
let swimlane = null;
|
||||
let list = null;
|
||||
|
||||
const cursor = Cards.find(
|
||||
{
|
||||
$or: [{ members: userId }, { assignees: userId }],
|
||||
archived: false,
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
boardId: 1,
|
||||
swimlaneId: 1,
|
||||
listId: 1,
|
||||
sort: 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
const cursor = this.getResults();
|
||||
|
||||
let newBoard = false;
|
||||
let newSwimlane = false;
|
||||
let newList = false;
|
||||
|
||||
cursor.forEach(card => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('card:', card.title);
|
||||
if (list === null || card.listId !== list._id) {
|
||||
if (cursor) {
|
||||
cursor.forEach(card => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new list');
|
||||
list = card.getList();
|
||||
if (list.archived) {
|
||||
list = null;
|
||||
return;
|
||||
}
|
||||
list.myCards = [card];
|
||||
newList = true;
|
||||
}
|
||||
if (swimlane === null || card.swimlaneId !== swimlane._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new swimlane');
|
||||
swimlane = card.getSwimlane();
|
||||
if (swimlane.archived) {
|
||||
// console.log('card:', card.title);
|
||||
if (board === null || card.boardId !== board._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new board');
|
||||
board = card.getBoard();
|
||||
if (board.archived) {
|
||||
board = null;
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('board:', b, b._id, b.title);
|
||||
boards.push(board);
|
||||
board.mySwimlanes = [];
|
||||
swimlane = null;
|
||||
return;
|
||||
list = null;
|
||||
}
|
||||
|
||||
if (swimlane === null || card.swimlaneId !== swimlane._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new swimlane');
|
||||
swimlane = card.getSwimlane();
|
||||
if (swimlane.archived) {
|
||||
swimlane = null;
|
||||
return;
|
||||
}
|
||||
board.mySwimlanes.push(swimlane);
|
||||
swimlane.myLists = [];
|
||||
list = null;
|
||||
}
|
||||
|
||||
if (list === null || card.listId !== list._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new list');
|
||||
list = card.getList();
|
||||
if (list.archived) {
|
||||
list = null;
|
||||
return;
|
||||
}
|
||||
swimlane.myLists.push(list);
|
||||
list.myCards = [];
|
||||
}
|
||||
swimlane.myLists = [list];
|
||||
newSwimlane = true;
|
||||
}
|
||||
if (board === null || card.boardId !== board._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new board');
|
||||
board = card.getBoard();
|
||||
if (board.archived) {
|
||||
board = null;
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('board:', b, b._id, b.title);
|
||||
board.mySwimlanes = [swimlane];
|
||||
newBoard = true;
|
||||
}
|
||||
|
||||
if (newBoard) {
|
||||
boards.push(board);
|
||||
} else if (newSwimlane) {
|
||||
board.mySwimlanes.push(swimlane);
|
||||
} else if (newList) {
|
||||
swimlane.myLists.push(list);
|
||||
} else {
|
||||
list.myCards.push(card);
|
||||
}
|
||||
});
|
||||
|
||||
newBoard = false;
|
||||
newSwimlane = false;
|
||||
newList = false;
|
||||
});
|
||||
|
||||
// sort the data structure
|
||||
boards.forEach(board => {
|
||||
board.mySwimlanes.forEach(swimlane => {
|
||||
swimlane.myLists.forEach(list => {
|
||||
list.myCards.sort((a, b) => {
|
||||
// sort the data structure
|
||||
boards.forEach(board => {
|
||||
board.mySwimlanes.forEach(swimlane => {
|
||||
swimlane.myLists.forEach(list => {
|
||||
list.myCards.sort((a, b) => {
|
||||
return a.sort - b.sort;
|
||||
});
|
||||
});
|
||||
swimlane.myLists.sort((a, b) => {
|
||||
return a.sort - b.sort;
|
||||
});
|
||||
});
|
||||
swimlane.myLists.sort((a, b) => {
|
||||
board.mySwimlanes.sort((a, b) => {
|
||||
return a.sort - b.sort;
|
||||
});
|
||||
});
|
||||
board.mySwimlanes.sort((a, b) => {
|
||||
return a.sort - b.sort;
|
||||
|
||||
boards.sort((a, b) => {
|
||||
let x = a.sort;
|
||||
let y = b.sort;
|
||||
|
||||
// show the template board last
|
||||
if (a.type === 'template-container') {
|
||||
x = 99999999;
|
||||
} else if (b.type === 'template-container') {
|
||||
y = 99999999;
|
||||
}
|
||||
return x - y;
|
||||
});
|
||||
});
|
||||
|
||||
boards.sort((a, b) => {
|
||||
let x = a.sort;
|
||||
let y = b.sort;
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('boards:', boards);
|
||||
return boards;
|
||||
}
|
||||
|
||||
// show the template board last
|
||||
if (a.type === 'template-container') {
|
||||
x = 99999999;
|
||||
} else if (b.type === 'template-container') {
|
||||
y = 99999999;
|
||||
}
|
||||
return x - y;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('boards:', boards);
|
||||
return boards;
|
||||
},
|
||||
|
||||
myDueCardsList() {
|
||||
const userId = Meteor.userId();
|
||||
|
||||
const cursor = Cards.find(
|
||||
{
|
||||
$or: [{ members: userId }, { assignees: userId }],
|
||||
archived: false,
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
dueAt: -1,
|
||||
boardId: 1,
|
||||
swimlaneId: 1,
|
||||
listId: 1,
|
||||
sort: 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cursor:', cursor);
|
||||
|
||||
const cards = [];
|
||||
cursor.forEach(card => {
|
||||
if (
|
||||
!card.getBoard().archived &&
|
||||
!card.getSwimlane().archived &&
|
||||
!card.getList().archived
|
||||
) {
|
||||
cards.push(card);
|
||||
}
|
||||
});
|
||||
|
||||
cards.sort((a, b) => {
|
||||
const x = a.dueAt === null ? Date('2100-12-31') : a.dueAt;
|
||||
const y = b.dueAt === null ? Date('2100-12-31') : b.dueAt;
|
||||
|
||||
if (x > y) return 1;
|
||||
else if (x < y) return -1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cursor:', cards);
|
||||
return cards;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
// 'click .js-my-card'(evt) {
|
||||
// const card = this.currentData().card;
|
||||
// // eslint-disable-next-line no-console
|
||||
// console.log('currentData():', this.currentData());
|
||||
// // eslint-disable-next-line no-console
|
||||
// console.log('card:', card);
|
||||
// if (card) {
|
||||
// Utils.goCardId(card._id);
|
||||
// }
|
||||
// evt.preventDefault();
|
||||
// },
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('myCards');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
MyCardsComponent.register('myCards');
|
||||
@@ -1,14 +1,14 @@
|
||||
.my-cards-board-wrapper
|
||||
border-radius: 8px
|
||||
border-radius: 0 0 4px 4px;
|
||||
//padding: 0.5rem
|
||||
min-width: 400px
|
||||
border-width: 8px
|
||||
border-color: grey
|
||||
border-style: solid
|
||||
margin-bottom: 2rem
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
|
||||
border-width: 2px
|
||||
border-style: solid
|
||||
border-color: #a2a2a2
|
||||
|
||||
.my-cards-board-title
|
||||
font-size: 1.4rem
|
||||
font-weight: bold
|
||||
@@ -41,9 +41,9 @@
|
||||
.my-cards-list-wrapper
|
||||
margin: 1rem
|
||||
border-radius: 5px
|
||||
padding: 1.5rem
|
||||
padding-top: 0.75rem
|
||||
display: inline-block
|
||||
//padding: 1.5rem
|
||||
//padding-top: 0.75rem
|
||||
display: inline-grid
|
||||
min-width: 250px
|
||||
max-width: 350px
|
||||
|
||||
@@ -55,15 +55,3 @@
|
||||
max-width: 500px
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
|
||||
.my-cards-field-name
|
||||
font-weight: bold
|
||||
|
||||
.my-cards-context
|
||||
display: inline-block
|
||||
|
||||
.my-cards-context-separator
|
||||
font-weight: bold
|
||||
|
||||
.my-cards-context-list
|
||||
margin-bottom: 0.7rem
|
||||
@@ -5,7 +5,7 @@
|
||||
.notification
|
||||
display: flex
|
||||
float: none
|
||||
padding: 12px 8px 8px
|
||||
padding: 15px 8px 8px
|
||||
color: black
|
||||
border-bottom: 1px solid #dbdbdb
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
.read-status
|
||||
width: 30px
|
||||
padding: 0px 10px 0px 0px
|
||||
|
||||
input
|
||||
width: 24px
|
||||
@@ -28,13 +29,16 @@
|
||||
color: #bbb
|
||||
|
||||
.details
|
||||
width: calc(100% - 30px)
|
||||
|
||||
.activity
|
||||
display: flex
|
||||
a.member
|
||||
margin: 0px 0px 0px 0px
|
||||
padding: 0px
|
||||
|
||||
svg
|
||||
padding: 3px
|
||||
|
||||
.activity-desc
|
||||
width: 100%;
|
||||
margin: 0px 0px 0px 5px
|
||||
|
||||
.activity-comment
|
||||
display: block
|
||||
@@ -55,3 +59,14 @@
|
||||
.remove
|
||||
a:hover
|
||||
color #eb4646 !important
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
#notifications-drawer
|
||||
.notification
|
||||
height: auto
|
||||
|
||||
.details
|
||||
.activity
|
||||
a.member
|
||||
height: 36px
|
||||
width: 36px
|
||||
@@ -2,7 +2,7 @@ belize = #2980b9
|
||||
|
||||
section#notifications-drawer
|
||||
position: fixed
|
||||
top: 28px
|
||||
top: 48px
|
||||
right: 0
|
||||
width: 400px
|
||||
background-color: #fafafa
|
||||
@@ -17,7 +17,7 @@ section#notifications-drawer
|
||||
|
||||
.header
|
||||
position: fixed
|
||||
top 28px
|
||||
top 48px
|
||||
right 0
|
||||
width calc(400px - 32px)
|
||||
padding: 8px 16px
|
||||
@@ -63,7 +63,7 @@ section#notifications-drawer
|
||||
|
||||
ul.notifications
|
||||
display: block
|
||||
padding: 0px 16px
|
||||
padding: 0px 16px 0px 16px
|
||||
margin: 0
|
||||
height: calc(100vh - 102px)
|
||||
height: calc(100vh - 122px)
|
||||
overflow-y: scroll
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,111 @@
|
||||
template(name="adminReports")
|
||||
.setting-content
|
||||
unless currentUser.isAdmin
|
||||
| {{_ 'error-notAuthorized'}}
|
||||
else
|
||||
.content-body
|
||||
.side-menu
|
||||
ul
|
||||
li
|
||||
a.js-report-broken(data-id="report-broken")
|
||||
i.fa.fa-chain-broken
|
||||
| {{_ 'broken-cards'}}
|
||||
|
||||
li
|
||||
a.js-report-files(data-id="report-orphaned-files")
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'orphanedFilesReportTitle'}}
|
||||
|
||||
li
|
||||
a.js-report-files(data-id="report-files")
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'filesReportTitle'}}
|
||||
|
||||
li
|
||||
a.js-report-rules(data-id="report-rules")
|
||||
i.fa.fa-magic
|
||||
| {{_ 'rulesReportTitle'}}
|
||||
|
||||
.main-body
|
||||
if loading.get
|
||||
+spinner
|
||||
else if showBrokenCardsReport.get
|
||||
+brokenCardsReport
|
||||
else if showFilesReport.get
|
||||
+filesReport
|
||||
else if showOrphanedFilesReport.get
|
||||
+orphanedFilesReport
|
||||
else if showRulesReport.get
|
||||
+rulesReport
|
||||
|
||||
|
||||
template(name="brokenCardsReport")
|
||||
.global-search-results-list-wrapper
|
||||
h1 {{_ 'broken-cards'}}
|
||||
if resultsCount
|
||||
+resultsPaged(this)
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
template(name="rulesReport")
|
||||
h1 {{_ 'rulesReportTitle'}}
|
||||
if resultsCount
|
||||
table.table
|
||||
tr
|
||||
th Rule Title
|
||||
th Board Title
|
||||
th actionType
|
||||
th activityType
|
||||
|
||||
each rule in rows
|
||||
tr
|
||||
td {{ rule.title }}
|
||||
td {{ rule.boardTitle }}
|
||||
td {{ rule.action.actionType }}
|
||||
td {{ rule.trigger.activityType }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
template(name="filesReport")
|
||||
h1 {{_ 'filesReportTitle'}}
|
||||
if resultsCount
|
||||
table.table
|
||||
tr
|
||||
th Filename
|
||||
th.right Size (kB)
|
||||
th MIME Type
|
||||
th.center Usage
|
||||
th MD5 Sum
|
||||
th ID
|
||||
|
||||
each att in attachmentFiles
|
||||
tr
|
||||
td {{ att.filename }}
|
||||
td.right {{fileSize att.length }}
|
||||
td {{ att.contentType }}
|
||||
td.center {{usageCount att._id.toHexString }}
|
||||
td {{ att.md5 }}
|
||||
td {{ att._id.toHexString }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
template(name="orphanedFilesReport")
|
||||
h1 {{_ 'orphanedFilesReportTitle'}}
|
||||
if resultsCount
|
||||
table.table
|
||||
tr
|
||||
th Filename
|
||||
th.right Size (kB)
|
||||
th MIME Type
|
||||
th MD5 Sum
|
||||
th ID
|
||||
|
||||
each att in attachmentFiles
|
||||
tr
|
||||
td {{ att.filename }}
|
||||
td.right {{fileSize att.length }}
|
||||
td {{ att.contentType }}
|
||||
td {{ att.md5 }}
|
||||
td {{ att._id.toHexString }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { AttachmentStorage } from '/models/attachments';
|
||||
import { CardSearchPagedComponent } from '/client/lib/cardSearch';
|
||||
import SessionData from '/models/usersessiondata';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
subscription: null,
|
||||
showFilesReport: new ReactiveVar(false),
|
||||
showBrokenCardsReport: new ReactiveVar(false),
|
||||
showOrphanedFilesReport: new ReactiveVar(false),
|
||||
showRulesReport: new ReactiveVar(false),
|
||||
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click a.js-report-broken': this.switchMenu,
|
||||
'click a.js-report-files': this.switchMenu,
|
||||
'click a.js-report-orphaned-files': this.switchMenu,
|
||||
'click a.js-report-rules': this.switchMenu,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
switchMenu(event) {
|
||||
const target = $(event.target);
|
||||
if (!target.hasClass('active')) {
|
||||
this.loading.set(true);
|
||||
this.showFilesReport.set(false);
|
||||
this.showBrokenCardsReport.set(false);
|
||||
this.showOrphanedFilesReport.set(false);
|
||||
if (this.subscription) {
|
||||
this.subscription.stop();
|
||||
}
|
||||
|
||||
$('.side-menu li.active').removeClass('active');
|
||||
target.parent().addClass('active');
|
||||
const targetID = target.data('id');
|
||||
|
||||
if ('report-broken' === targetID) {
|
||||
this.showBrokenCardsReport.set(true);
|
||||
this.subscription = Meteor.subscribe(
|
||||
'brokenCards',
|
||||
SessionData.getSessionId(),
|
||||
() => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
);
|
||||
} else if ('report-files' === targetID) {
|
||||
this.showFilesReport.set(true);
|
||||
this.subscription = Meteor.subscribe('attachmentsList', () => {
|
||||
this.loading.set(false);
|
||||
});
|
||||
} else if ('report-orphaned-files' === targetID) {
|
||||
this.showOrphanedFilesReport.set(true);
|
||||
this.subscription = Meteor.subscribe('orphanedAttachments', () => {
|
||||
this.loading.set(false);
|
||||
});
|
||||
} else if ('report-rules' === targetID) {
|
||||
this.subscription = Meteor.subscribe('rulesReport', () => {
|
||||
this.showRulesReport.set(true);
|
||||
this.loading.set(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
}).register('adminReports');
|
||||
|
||||
Template.filesReport.helpers({
|
||||
attachmentFiles() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('attachments:', AttachmentStorage.find());
|
||||
// console.log('attachments.count:', AttachmentStorage.find().count());
|
||||
return AttachmentStorage.find();
|
||||
},
|
||||
|
||||
rulesReport() {
|
||||
const rules = [];
|
||||
|
||||
Rules.find().forEach(rule => {
|
||||
rules.push({
|
||||
_id: rule._id,
|
||||
title: rule.title,
|
||||
boardId: rule.boardId,
|
||||
boardTitle: rule.board().title,
|
||||
action: rule.action().fetch(),
|
||||
trigger: rule.trigger().fetch(),
|
||||
});
|
||||
});
|
||||
|
||||
return rules;
|
||||
},
|
||||
|
||||
resultsCount() {
|
||||
return AttachmentStorage.find().count();
|
||||
},
|
||||
|
||||
fileSize(size) {
|
||||
return Math.round(size / 1024);
|
||||
},
|
||||
|
||||
usageCount(key) {
|
||||
return Attachments.find({ 'copies.attachments.key': key }).count();
|
||||
},
|
||||
});
|
||||
|
||||
Template.orphanedFilesReport.helpers({
|
||||
attachmentFiles() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('attachments:', AttachmentStorage.find());
|
||||
// console.log('attachments.count:', AttachmentStorage.find().count());
|
||||
return AttachmentStorage.find();
|
||||
},
|
||||
|
||||
resultsCount() {
|
||||
return AttachmentStorage.find().count();
|
||||
},
|
||||
|
||||
fileSize(size) {
|
||||
return Math.round(size / 1024);
|
||||
},
|
||||
});
|
||||
|
||||
Template.rulesReport.helpers({
|
||||
rows() {
|
||||
const rules = [];
|
||||
|
||||
Rules.find().forEach(rule => {
|
||||
rules.push({
|
||||
_id: rule._id,
|
||||
title: rule.title,
|
||||
boardId: rule.boardId,
|
||||
boardTitle: rule.board().title,
|
||||
action: rule.action(),
|
||||
trigger: rule.trigger(),
|
||||
});
|
||||
});
|
||||
|
||||
console.log('rows:', rules);
|
||||
return rules;
|
||||
},
|
||||
|
||||
resultsCount() {
|
||||
return Rules.find().count();
|
||||
},
|
||||
});
|
||||
|
||||
class BrokenCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
}
|
||||
}
|
||||
BrokenCardsComponent.register('brokenCardsReport');
|
||||
@@ -74,7 +74,6 @@ template(name="orgGeneral")
|
||||
th {{_ 'description'}}
|
||||
th {{_ 'shortName'}}
|
||||
th {{_ 'website'}}
|
||||
th {{_ 'teams'}}
|
||||
th {{_ 'createdAt'}}
|
||||
th {{_ 'active'}}
|
||||
th
|
||||
@@ -110,6 +109,7 @@ template(name="peopleGeneral")
|
||||
th {{_ 'createdAt'}}
|
||||
th {{_ 'active'}}
|
||||
th {{_ 'authentication-method'}}
|
||||
th {{_ 'import-usernames'}}
|
||||
th
|
||||
+newUserRow
|
||||
each user in peopleList
|
||||
@@ -117,41 +117,37 @@ template(name="peopleGeneral")
|
||||
|
||||
template(name="newOrgRow")
|
||||
a.new-org
|
||||
i.fa.fa-edit
|
||||
i.fa.fa-plus-square
|
||||
| {{_ 'new'}}
|
||||
|
||||
template(name="newTeamRow")
|
||||
a.new-team
|
||||
i.fa.fa-edit
|
||||
i.fa.fa-plus-square
|
||||
| {{_ 'new'}}
|
||||
|
||||
template(name="newUserRow")
|
||||
a.new-user
|
||||
i.fa.fa-edit
|
||||
i.fa.fa-plus-square
|
||||
| {{_ 'new'}}
|
||||
|
||||
template(name="orgRow")
|
||||
tr
|
||||
if orgData.loginDisabled
|
||||
td <s>{{ orgData.displayName }}</s>
|
||||
td <s>{{ orgData.orgDisplayName }}</s>
|
||||
else
|
||||
td {{ orgData.displayName }}
|
||||
td {{ orgData.orgDisplayName }}
|
||||
if orgData.loginDisabled
|
||||
td <s>{{ orgData.orgDesc }}</s>
|
||||
else
|
||||
td {{ orgData.desc }}
|
||||
td {{ orgData.orgDesc }}
|
||||
if orgData.loginDisabled
|
||||
td <s>{{ orgData.name }}</s>
|
||||
td <s>{{ orgData.orgName }}</s>
|
||||
else
|
||||
td {{ orgData.name }}
|
||||
td {{ orgData.orgName }}
|
||||
if orgData.loginDisabled
|
||||
td <s>{{ orgData.website }}</s>
|
||||
td <s>{{ orgData.orgWebsite }}</s>
|
||||
else
|
||||
td {{ orgData.website }}
|
||||
if orgData.loginDisabled
|
||||
td <s>{{ orgData.teams }}</s>
|
||||
else
|
||||
td {{ orgData.teams }}
|
||||
td {{ orgData.orgWebsite }}
|
||||
if orgData.loginDisabled
|
||||
td <s>{{ moment orgData.createdAt 'LLL' }}</s>
|
||||
else
|
||||
@@ -171,21 +167,21 @@ template(name="orgRow")
|
||||
template(name="teamRow")
|
||||
tr
|
||||
if teamData.loginDisabled
|
||||
td <s>{{ teamData.displayName }}</s>
|
||||
td <s>{{ teamData.teamDisplayName }}</s>
|
||||
else
|
||||
td {{ teamData.displayName }}
|
||||
td {{ teamData.teamDisplayName }}
|
||||
if teamData.loginDisabled
|
||||
td <s>{{ teamData.desc }}</s>
|
||||
td <s>{{ teamData.teamDesc }}</s>
|
||||
else
|
||||
td {{ teamData.desc }}
|
||||
td {{ teamData.teamDesc }}
|
||||
if teamData.loginDisabled
|
||||
td <s>{{ teamData.dame }}</s>
|
||||
td <s>{{ teamData.teamName }}</s>
|
||||
else
|
||||
td {{ teamData.name }}
|
||||
td {{ teamData.teamName }}
|
||||
if teamData.loginDisabled
|
||||
td <s>{{ teamData.website }}</s>
|
||||
td <s>{{ teamData.teamWebsite }}</s>
|
||||
else
|
||||
td {{ teamData.website }}
|
||||
td {{ teamData.teamWebsite }}
|
||||
if orgData.loginDisabled
|
||||
td <s>{{ moment teamData.createdAt 'LLL' }}</s>
|
||||
else
|
||||
@@ -257,6 +253,10 @@ template(name="peopleRow")
|
||||
td <s>{{_ userData.authenticationMethod }}</s>
|
||||
else
|
||||
td {{_ userData.authenticationMethod }}
|
||||
if userData.loginDisabled
|
||||
td <s>{{ userData.importUsernamesString }}</s>
|
||||
else
|
||||
td {{ userData.importUsernamesString }}
|
||||
td
|
||||
a.edit-user
|
||||
i.fa.fa-edit
|
||||
@@ -268,19 +268,19 @@ template(name="editOrgPopup")
|
||||
form
|
||||
label.hide.orgId(type="text" value=org._id)
|
||||
label
|
||||
| {{_ 'orgDisplayName'}}
|
||||
| {{_ 'displayName'}}
|
||||
input.js-orgDisplayName(type="text" value=org.displayName required)
|
||||
span.error.hide.orgname-taken
|
||||
| {{_ 'error-orgname-taken'}}
|
||||
label
|
||||
| {{_ 'orgDesc'}}
|
||||
input.js-orgDesc(type="text" value=org.desc required)
|
||||
| {{_ 'description'}}
|
||||
input.js-orgDesc(type="text" value=org.orgDesc required)
|
||||
label
|
||||
| {{_ 'orgName'}}
|
||||
input.js-orgName(type="text" value=org.name required)
|
||||
| {{_ 'shortName'}}
|
||||
input.js-orgShortName(type="text" value=org.orgShortName required)
|
||||
label
|
||||
| {{_ 'orgWebsite'}}
|
||||
input.js-orgWebsite(type="text" value=org.website required)
|
||||
| {{_ 'website'}}
|
||||
input.js-orgWebsite(type="text" value=org.orgWebsite required)
|
||||
label
|
||||
| {{_ 'active'}}
|
||||
select.select-active.js-org-isactive
|
||||
@@ -295,18 +295,18 @@ template(name="editTeamPopup")
|
||||
label.hide.teamId(type="text" value=team._id)
|
||||
label
|
||||
| {{_ 'displayName'}}
|
||||
input.js-teamDisplayName(type="text" value=team.displayName required)
|
||||
input.js-teamDisplayName(type="text" value=team.teamDisplayName required)
|
||||
span.error.hide.teamname-taken
|
||||
| {{_ 'error-teamname-taken'}}
|
||||
label
|
||||
| {{_ 'description'}}
|
||||
input.js-orgDesc(type="text" value=org.desc required)
|
||||
input.js-teamDesc(type="text" value=team.teamDesc required)
|
||||
label
|
||||
| {{_ 'name'}}
|
||||
input.js-orgName(type="text" value=org.name required)
|
||||
| {{_ 'shortName'}}
|
||||
input.js-teamShortName(type="text" value=team.teamShortName required)
|
||||
label
|
||||
| {{_ 'website'}}
|
||||
input.js-orgWebsite(type="text" value=org.website required)
|
||||
input.js-teamWebsite(type="text" value=team.teamWebsite required)
|
||||
label
|
||||
| {{_ 'active'}}
|
||||
select.select-active.js-team-isactive
|
||||
@@ -332,7 +332,7 @@ template(name="editUserPopup")
|
||||
input.js-profile-fullname(type="text" value=user.profile.fullname required)
|
||||
label
|
||||
| {{_ 'initials'}}
|
||||
input.js-profile-initials(type="text" value=user.profile.initials)
|
||||
input.js-profile-initials(type="text" value=user.profile.initials required)
|
||||
label
|
||||
| {{_ 'admin'}}
|
||||
select.select-role.js-profile-isadmin
|
||||
@@ -346,6 +346,9 @@ template(name="editUserPopup")
|
||||
input.js-profile-email(type="email" value="{{user.emails.[0].address}}" readonly)
|
||||
else
|
||||
input.js-profile-email(type="email" value="{{user.emails.[0].address}}" required)
|
||||
label
|
||||
| {{_ 'import-usernames'}}
|
||||
input.js-import-usernames(type="text" value=user.importUsernames)
|
||||
label
|
||||
| {{_ 'verified'}}
|
||||
select.select-verified.js-profile-email-verified
|
||||
@@ -376,22 +379,19 @@ template(name="newOrgPopup")
|
||||
//label.hide.userId(type="text" value=user._id)
|
||||
label
|
||||
| {{_ 'displayName'}}
|
||||
input.js-displayName(type="text" value="" required)
|
||||
input.js-orgDisplayName(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'description'}}
|
||||
input.js-desc(type="text" value="" required)
|
||||
input.js-orgDesc(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'shortName'}}
|
||||
input.js-name(type="text" value="")
|
||||
label
|
||||
| {{_ 'teams'}}
|
||||
input.js-teams(type="text" value="")
|
||||
input.js-orgName(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'website'}}
|
||||
input.js-website(type="text" value="")
|
||||
input.js-orgWebsite(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'active'}}
|
||||
select.select-active.js-profile-isactive
|
||||
select.select-active.js-org-isactive
|
||||
option(value="false" selected="selected") {{_ 'yes'}}
|
||||
option(value="true") {{_ 'no'}}
|
||||
hr
|
||||
@@ -409,13 +409,13 @@ template(name="newTeamPopup")
|
||||
input.js-teamDesc(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'shortName'}}
|
||||
input.js-teamName(type="text" value="")
|
||||
input.js-teamName(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'website'}}
|
||||
input.js-teamWebsite(type="text" value="")
|
||||
input.js-teamWebsite(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'active'}}
|
||||
select.select-active.js-profile-isactive
|
||||
select.select-active.js-team-isactive
|
||||
option(value="false" selected="selected") {{_ 'yes'}}
|
||||
option(value="true") {{_ 'no'}}
|
||||
hr
|
||||
@@ -436,6 +436,9 @@ template(name="newUserPopup")
|
||||
// input.js-profile-username(type="text" value=user.username readonly)
|
||||
//else
|
||||
input.js-profile-username(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'initials'}}
|
||||
input.js-profile-initials(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'email'}}
|
||||
span.error.hide.email-taken
|
||||
@@ -444,6 +447,9 @@ template(name="newUserPopup")
|
||||
// input.js-profile-email(type="email" value="{{user.emails.[0].address}}" readonly)
|
||||
//else
|
||||
input.js-profile-email(type="email" value="" required)
|
||||
label
|
||||
| {{_ 'import-usernames'}}
|
||||
input.js-import-usernames(type="text" value="")
|
||||
label
|
||||
| {{_ 'admin'}}
|
||||
select.select-role.js-profile-isadmin
|
||||
|
||||
@@ -143,7 +143,7 @@ BlazeComponent.extendComponent({
|
||||
const orgs = Org.find(this.findOrgsOptions.get(), {
|
||||
fields: { _id: true },
|
||||
});
|
||||
this.numberOrgs.set(org.count(false));
|
||||
this.numberOrgs.set(orgs.count(false));
|
||||
return orgs;
|
||||
},
|
||||
teamList() {
|
||||
@@ -404,6 +404,88 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('newUserRow');
|
||||
|
||||
Template.editOrgPopup.events({
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const org = Orgs.findOne(this.orgId);
|
||||
|
||||
const orgDisplayName = templateInstance
|
||||
.find('.js-orgDisplayName')
|
||||
.value.trim();
|
||||
const orgDesc = templateInstance.find('.js-orgDesc').value.trim();
|
||||
const orgShortName = templateInstance.find('.js-orgShortName').value.trim();
|
||||
const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim();
|
||||
const orgIsActive = templateInstance.find('.js-org-isactive').value.trim();
|
||||
|
||||
const isChangeOrgDisplayName = orgDisplayName !== org.orgDisplayName;
|
||||
const isChangeOrgDesc = orgDesc !== org.orgDesc;
|
||||
const isChangeOrgShortName = orgShortName !== org.orgShortName;
|
||||
const isChangeOrgWebsite = orgWebsite !== org.orgWebsite;
|
||||
const isChangeOrgIsActive = orgIsActive !== org.orgIsActive;
|
||||
|
||||
if (isChangeOrgDisplayName) {
|
||||
Meteor.call('setOrgDisplayName', org, orgDisplayName);
|
||||
}
|
||||
|
||||
if (isChangeOrgDesc) {
|
||||
Meteor.call('setOrgDesc', org, orgDesc);
|
||||
}
|
||||
|
||||
if (isChangeOrgShortName) {
|
||||
Meteor.call('setOrgShortName', org, orgShortName);
|
||||
}
|
||||
|
||||
if (isChangeOrgIsActive) {
|
||||
Meteor.call('setOrgIsActive', org, orgIsActive);
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
|
||||
Template.editTeamPopup.events({
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const team = Teams.findOne(this.teamId);
|
||||
|
||||
const teamDisplayName = templateInstance
|
||||
.find('.js-teamDisplayName')
|
||||
.value.trim();
|
||||
const teamDesc = templateInstance.find('.js-teamDesc').value.trim();
|
||||
const teamShortName = templateInstance
|
||||
.find('.js-teamShortName')
|
||||
.value.trim();
|
||||
const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim();
|
||||
const teamIsActive = templateInstance
|
||||
.find('.js-team-isactive')
|
||||
.value.trim();
|
||||
|
||||
const isChangeTeamDisplayName = teamDisplayName !== team.teamDisplayName;
|
||||
const isChangeTeamDesc = teamDesc !== team.teamDesc;
|
||||
const isChangeTeamShortName = teamShortName !== team.teamShortName;
|
||||
const isChangeTeamWebsite = teamWebsite !== team.teamWebsite;
|
||||
const isChangeTeamIsActive = teamIsActive !== team.teamIsActive;
|
||||
|
||||
if (isChangeTeamDisplayName) {
|
||||
Meteor.call('setTeamDisplayName', team, teamDisplayName);
|
||||
}
|
||||
|
||||
if (isChangeTeamDesc) {
|
||||
Meteor.call('setTeamDesc', team, teamDesc);
|
||||
}
|
||||
|
||||
if (isChangeTeamShortName) {
|
||||
Meteor.call('setTeamShortName', team, teamShortName);
|
||||
}
|
||||
|
||||
if (isChangeTeamIsActive) {
|
||||
Meteor.call('setTeamIsActive', team, teamIsActive);
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
|
||||
Template.editUserPopup.events({
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
@@ -421,6 +503,9 @@ Template.editUserPopup.events({
|
||||
const authentication = templateInstance
|
||||
.find('.js-authenticationMethod')
|
||||
.value.trim();
|
||||
const importUsernames = templateInstance
|
||||
.find('.js-import-usernames')
|
||||
.value.trim();
|
||||
|
||||
const isChangePassword = password.length > 0;
|
||||
const isChangeUserName = username !== user.username;
|
||||
@@ -441,6 +526,7 @@ Template.editUserPopup.events({
|
||||
isAdmin: isAdmin === 'true',
|
||||
loginDisabled: isActive === 'true',
|
||||
authenticationMethod: authentication,
|
||||
importUsernames: Users.parseImportUsernames(importUsernames),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -516,39 +602,48 @@ Template.editUserPopup.events({
|
||||
Template.newOrgPopup.events({
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const displayName = templateInstance.find('.js-displayName').value.trim();
|
||||
const desc = templateInstance.find('.js-desc').value.trim();
|
||||
const name = templateInstance.find('.js-name').value.trim();
|
||||
const teams = templateInstance.find('.js-teams').value.trim();
|
||||
const website = templateInstance.find('.js-website').value.trim();
|
||||
const isActive = templateInstance.find('.js-profile-isactive').value.trim();
|
||||
const orgDisplayName = templateInstance
|
||||
.find('.js-orgDisplayName')
|
||||
.value.trim();
|
||||
const orgDesc = templateInstance.find('.js-orgDesc').value.trim();
|
||||
const orgShortName = templateInstance.find('.js-orgShortName').value.trim();
|
||||
const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim();
|
||||
const orgIsActive = templateInstance.find('.js-org-isactive').value.trim();
|
||||
|
||||
Meteor.call(
|
||||
'setCreateOrg',
|
||||
displayName,
|
||||
desc,
|
||||
name,
|
||||
teams,
|
||||
website,
|
||||
isActive,
|
||||
email.toLowerCase(),
|
||||
function(error) {
|
||||
const nameMessageElement = templateInstance.$('.name-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'name-already-taken') {
|
||||
nameMessageElement.show();
|
||||
emailMessageElement.hide();
|
||||
} else if (errorElement === 'email-already-taken') {
|
||||
usernameMessageElement.hide();
|
||||
emailMessageElement.show();
|
||||
}
|
||||
} else {
|
||||
usernameMessageElement.hide();
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
}
|
||||
},
|
||||
orgDisplayName,
|
||||
orgDesc,
|
||||
orgShortName,
|
||||
orgWebsite,
|
||||
orgIsActive,
|
||||
);
|
||||
Popup.close();
|
||||
},
|
||||
});
|
||||
|
||||
Template.newTeamPopup.events({
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const teamDisplayName = templateInstance
|
||||
.find('.js-teamDisplayName')
|
||||
.value.trim();
|
||||
const teamDesc = templateInstance.find('.js-teamDesc').value.trim();
|
||||
const teamShortName = templateInstance
|
||||
.find('.js-teamShortName')
|
||||
.value.trim();
|
||||
const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim();
|
||||
const teamIsActive = templateInstance
|
||||
.find('.js-team-isactive')
|
||||
.value.trim();
|
||||
|
||||
Meteor.call(
|
||||
'setCreateTeam',
|
||||
teamDisplayName,
|
||||
teamDesc,
|
||||
teamShortName,
|
||||
teamWebsite,
|
||||
teamIsActive,
|
||||
);
|
||||
Popup.close();
|
||||
},
|
||||
@@ -559,19 +654,25 @@ Template.newUserPopup.events({
|
||||
event.preventDefault();
|
||||
const fullname = templateInstance.find('.js-profile-fullname').value.trim();
|
||||
const username = templateInstance.find('.js-profile-username').value.trim();
|
||||
const initials = templateInstance.find('.js-profile-initials').value.trim();
|
||||
const password = templateInstance.find('.js-profile-password').value;
|
||||
const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
|
||||
const isActive = templateInstance.find('.js-profile-isactive').value.trim();
|
||||
const email = templateInstance.find('.js-profile-email').value.trim();
|
||||
const importUsernames = Users.parseImportUsernames(
|
||||
templateInstance.find('.js-import-usernames').value,
|
||||
);
|
||||
|
||||
Meteor.call(
|
||||
'setCreateUser',
|
||||
fullname,
|
||||
username,
|
||||
initials,
|
||||
password,
|
||||
isAdmin,
|
||||
isActive,
|
||||
email.toLowerCase(),
|
||||
importUsernames,
|
||||
function(error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
const emailMessageElement = templateInstance.$('.email-taken');
|
||||
|
||||
@@ -13,10 +13,11 @@ template(name="setting")
|
||||
a.js-setting-menu(data-id="registration-setting")
|
||||
i.fa.fa-sign-in
|
||||
| {{_ 'registration'}}
|
||||
li
|
||||
a.js-setting-menu(data-id="email-setting")
|
||||
i.fa.fa-envelope
|
||||
| {{_ 'email'}}
|
||||
unless isSandstorm
|
||||
li
|
||||
a.js-setting-menu(data-id="email-setting")
|
||||
i.fa.fa-envelope
|
||||
| {{_ 'email'}}
|
||||
li
|
||||
a.js-setting-menu(data-id="account-setting")
|
||||
i.fa.fa-users
|
||||
@@ -39,7 +40,8 @@ template(name="setting")
|
||||
else if generalSetting.get
|
||||
+general
|
||||
else if emailSetting.get
|
||||
+email
|
||||
unless isSandstorm
|
||||
+email
|
||||
else if accountSetting.get
|
||||
+accountSettings
|
||||
else if announcementSetting.get
|
||||
@@ -80,45 +82,48 @@ template(name="general")
|
||||
|
||||
template(name='email')
|
||||
ul#email-setting.setting-detail
|
||||
li.smtp-form
|
||||
.title {{_ 'smtp-host'}}
|
||||
.description {{_ 'smtp-host-description'}}
|
||||
.form-group
|
||||
input.wekan-form-control#mail-server-host(type="text", placeholder="smtp.domain.com" value="{{currentSetting.mailServer.host}}")
|
||||
li.smtp-form
|
||||
.title {{_ 'smtp-port'}}
|
||||
.description {{_ 'smtp-port-description'}}
|
||||
.form-group
|
||||
input.wekan-form-control#mail-server-port(type="text", placeholder="25" value="{{currentSetting.mailServer.port}}")
|
||||
li.smtp-form
|
||||
.title {{_ 'smtp-username'}}
|
||||
.form-group
|
||||
input.wekan-form-control#mail-server-username(type="text", placeholder="{{_ 'username'}}" value="{{currentSetting.mailServer.username}}")
|
||||
li.smtp-form
|
||||
.title {{_ 'smtp-password'}}
|
||||
.form-group
|
||||
input.wekan-form-control#mail-server-password(type="password", placeholder="{{_ 'password'}}" value="{{currentSetting.mailServer.password}}")
|
||||
li.smtp-form
|
||||
.title {{_ 'smtp-tls'}}
|
||||
.form-group
|
||||
a.flex.js-toggle-tls
|
||||
.materialCheckBox#mail-server-tls(class="{{#if currentSetting.mailServer.enableTLS}}is-checked{{/if}}")
|
||||
|
||||
span {{_ 'smtp-tls-description'}}
|
||||
|
||||
li.smtp-form
|
||||
.title {{_ 'send-from'}}
|
||||
.form-group
|
||||
input.wekan-form-control#mail-server-from(type="email", placeholder="no-reply@domain.com" value="{{currentSetting.mailServer.from}}")
|
||||
|
||||
li
|
||||
button.js-save.primary {{_ 'save'}}
|
||||
//if isSandstorm
|
||||
// li.smtp-form
|
||||
// .title {{_ 'smtp-host'}}
|
||||
// .description {{_ 'smtp-host-description'}}
|
||||
// .form-group
|
||||
// input.wekan-form-control#mail-server-host(type="text", placeholder="smtp.domain.com" value="{{currentSetting.mailServer.host}}")
|
||||
// li.smtp-form
|
||||
// .title {{_ 'smtp-port'}}
|
||||
// .description {{_ 'smtp-port-description'}}
|
||||
// .form-group
|
||||
// input.wekan-form-control#mail-server-port(type="text", placeholder="25" value="{{currentSetting.mailServer.port}}")
|
||||
// li.smtp-form
|
||||
// .title {{_ 'smtp-username'}}
|
||||
// .form-group
|
||||
// input.wekan-form-control#mail-server-username(type="text", placeholder="{{_ 'username'}}" value="{{currentSetting.mailServer.username}}")
|
||||
// li.smtp-form
|
||||
// .title {{_ 'smtp-password'}}
|
||||
// .form-group
|
||||
// input.wekan-form-control#mail-server-password(type="password", placeholder="{{_ 'password'}}" value="")
|
||||
// li.smtp-form
|
||||
// .title {{_ 'smtp-tls'}}
|
||||
// .form-group
|
||||
// a.flex.js-toggle-tls
|
||||
// .materialCheckBox#mail-server-tls(class="{{#if currentSetting.mailServer.enableTLS}}is-checked{{/if}}")
|
||||
//
|
||||
// span {{_ 'smtp-tls-description'}}
|
||||
//
|
||||
// li.smtp-form
|
||||
// .title {{_ 'send-from'}}
|
||||
// .form-group
|
||||
// input.wekan-form-control#mail-server-from(type="email", placeholder="no-reply@domain.com" value="{{currentSetting.mailServer.from}}")
|
||||
//
|
||||
// li
|
||||
// button.js-save.primary {{_ 'save'}}
|
||||
|
||||
li
|
||||
button.js-send-smtp-test-email.primary {{_ 'send-smtp-test'}}
|
||||
|
||||
template(name='accountSettings')
|
||||
ul#account-setting.setting-detail
|
||||
li
|
||||
button.js-all-hide-system-messages.primary {{_ 'hide-system-messages-of-all-users'}}
|
||||
li.accounts-form
|
||||
.title {{_ 'accounts-allowEmailChange'}}
|
||||
.form-group.flex
|
||||
@@ -126,23 +131,18 @@ template(name='accountSettings')
|
||||
span {{_ 'yes'}}
|
||||
input.wekan-form-control#accounts-allowEmailChange(type="radio" name="allowEmailChange" value="false" checked="{{#unless allowEmailChange}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
li
|
||||
li.accounts-form
|
||||
.title {{_ 'accounts-allowUserNameChange'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#accounts-allowUserNameChange(type="radio" name="allowUserNameChange" value="true" checked="{{#if allowUserNameChange}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
input.wekan-form-control#accounts-allowUserNameChange(type="radio" name="allowUserNameChange" value="false" checked="{{#unless allowUserNameChange}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
li
|
||||
li.accounts-form
|
||||
.title {{_ 'accounts-allowUserDelete'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#accounts-allowUserDelete(type="radio" name="allowUserDelete" value="true" checked="{{#if allowUserDelete}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
input.wekan-form-control#accounts-allowUserDelete(type="radio" name="allowUserDelete" value="false" checked="{{#unless allowUserDelete}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
li
|
||||
button.js-accounts-save.primary {{_ 'save'}}
|
||||
|
||||
template(name='announcementSettings')
|
||||
@@ -208,6 +208,10 @@ template(name='layoutSettings')
|
||||
.title {{_ 'custom-top-left-corner-logo-height'}}
|
||||
.form-group
|
||||
input.wekan-form-control#custom-top-left-corner-logo-height(type="text", placeholder="" value="{{currentSetting.customTopLeftCornerLogoHeight}}")
|
||||
li.layout-form
|
||||
.title {{_ 'automatic-linked-url-schemes'}}
|
||||
.form-group
|
||||
textarea#automatic-linked-url-schemes.wekan-form-control= currentSetting.automaticLinkedUrlSchemes
|
||||
li
|
||||
button.js-save-layout.primary {{_ 'save'}}
|
||||
|
||||
|
||||
@@ -176,6 +176,9 @@ BlazeComponent.extendComponent({
|
||||
const textBelowCustomLoginLogo = $('#text-below-custom-login-logo')
|
||||
.val()
|
||||
.trim();
|
||||
const automaticLinkedUrlSchemes = $('#automatic-linked-url-schemes')
|
||||
.val()
|
||||
.trim();
|
||||
const customTopLeftCornerLogoImageUrl = $(
|
||||
'#custom-top-left-corner-logo-image-url',
|
||||
)
|
||||
@@ -209,6 +212,7 @@ BlazeComponent.extendComponent({
|
||||
customTopLeftCornerLogoHeight,
|
||||
displayAuthenticationMethod,
|
||||
defaultAuthenticationMethod,
|
||||
automaticLinkedUrlSchemes,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -270,7 +274,6 @@ BlazeComponent.extendComponent({
|
||||
$set: { booleanValue: allowUserDelete },
|
||||
});
|
||||
},
|
||||
|
||||
allowEmailChange() {
|
||||
return AccountSettings.findOne('accounts-allowEmailChange').booleanValue;
|
||||
},
|
||||
@@ -280,12 +283,31 @@ BlazeComponent.extendComponent({
|
||||
allowUserDelete() {
|
||||
return AccountSettings.findOne('accounts-allowUserDelete').booleanValue;
|
||||
},
|
||||
allHideSystemMessages() {
|
||||
Meteor.call('setAllUsersHideSystemMessages', (err, ret) => {
|
||||
if (!err && ret) {
|
||||
if (ret === true) {
|
||||
const message = `${TAPi18n.__(
|
||||
'now-system-messages-of-all-users-are-hidden',
|
||||
)}`;
|
||||
alert(message);
|
||||
}
|
||||
} else {
|
||||
const reason = err.reason || '';
|
||||
const message = `${TAPi18n.__(err.error)}\n${reason}`;
|
||||
alert(message);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click button.js-accounts-save': this.saveAccountsChange,
|
||||
},
|
||||
{
|
||||
'click button.js-all-hide-system-messages': this.allHideSystemMessages,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('accountSettings');
|
||||
|
||||
@@ -12,6 +12,10 @@ template(name="settingHeaderBar")
|
||||
i.fa(class="fa-users")
|
||||
span {{_ 'people'}}
|
||||
|
||||
a.setting-header-btn.informations(href="{{pathFor 'admin-reports'}}")
|
||||
i.fa(class="fa-list")
|
||||
span {{_ 'reports'}}
|
||||
|
||||
a.setting-header-btn.informations(href="{{pathFor 'information'}}")
|
||||
i.fa(class="fa-info-circle")
|
||||
span {{_ 'info'}}
|
||||
|
||||
@@ -105,6 +105,14 @@ template(name="boardCardSettingsPopup")
|
||||
span
|
||||
i.fa.fa-users
|
||||
| {{_ 'members'}}
|
||||
|
||||
div.check-div
|
||||
a.flex.js-field-has-creator(class="{{#if allowsCreator}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsCreator}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-user
|
||||
| {{_ 'creator'}}
|
||||
|
||||
div.check-div
|
||||
a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
||||
@@ -324,6 +332,10 @@ template(name="exportBoard")
|
||||
a.download-json-link(href="{{exportUrl}}", download="{{exportJsonFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
| {{_ 'export-board-json'}}
|
||||
li
|
||||
a(href="{{exportUrlExcel}}", download="{{exportFilenameExcel}}")
|
||||
i.fa.fa-share-alt
|
||||
| {{_ 'export-board-excel'}}
|
||||
li
|
||||
a(href="{{exportCsvUrl}}", download="{{exportCsvFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
|
||||
@@ -166,16 +166,16 @@ Template.memberPopup.helpers({
|
||||
const noComments = currentBoard.hasNoComments(this.userId);
|
||||
const worker = currentBoard.hasWorker(this.userId);
|
||||
if (commentOnly) {
|
||||
return TAPi18n.__('comment-only').toLowerCase();
|
||||
return TAPi18n.__('comment-only');
|
||||
} else if (noComments) {
|
||||
return TAPi18n.__('no-comments').toLowerCase();
|
||||
return TAPi18n.__('no-comments');
|
||||
} else if (worker) {
|
||||
return TAPi18n.__('worker').toLowerCase();
|
||||
return TAPi18n.__('worker');
|
||||
} else {
|
||||
return TAPi18n.__(type).toLowerCase();
|
||||
return TAPi18n.__(type);
|
||||
}
|
||||
} else {
|
||||
return TAPi18n.__(type).toLowerCase();
|
||||
return TAPi18n.__(type);
|
||||
}
|
||||
},
|
||||
isInvited() {
|
||||
@@ -435,6 +435,23 @@ BlazeComponent.extendComponent({
|
||||
};
|
||||
return FlowRouter.path('/api/boards/:boardId/export', params, queryParams);
|
||||
},
|
||||
exportUrlExcel() {
|
||||
const params = {
|
||||
boardId: Session.get('currentBoard'),
|
||||
};
|
||||
const queryParams = {
|
||||
authToken: Accounts._storedLoginToken(),
|
||||
};
|
||||
return FlowRouter.path(
|
||||
'/api/boards/:boardId/exportExcel',
|
||||
params,
|
||||
queryParams,
|
||||
);
|
||||
},
|
||||
exportFilenameExcel() {
|
||||
const boardId = Session.get('currentBoard');
|
||||
return `export-board-excel-${boardId}.xlsx`;
|
||||
},
|
||||
exportCsvUrl() {
|
||||
const params = {
|
||||
boardId: Session.get('currentBoard'),
|
||||
@@ -713,6 +730,14 @@ BlazeComponent.extendComponent({
|
||||
return this.currentBoard.allowsSubtasks;
|
||||
},
|
||||
|
||||
allowsCreator() {
|
||||
return (
|
||||
this.currentBoard.allowsCreator === null ||
|
||||
this.currentBoard.allowsCreator === undefined ||
|
||||
this.currentBoard.allowsCreator
|
||||
);
|
||||
},
|
||||
|
||||
allowsMembers() {
|
||||
return this.currentBoard.allowsMembers;
|
||||
},
|
||||
@@ -872,6 +897,19 @@ BlazeComponent.extendComponent({
|
||||
this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-creator'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsCreator = !this.currentBoard.allowsCreator;
|
||||
this.currentBoard.setAllowsCreator(this.currentBoard.allowsCreator);
|
||||
$(`.js-field-has-creator ${MCB}`).toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsCreator,
|
||||
);
|
||||
$('.js-field-has-creator').toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsCreator,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-members'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
|
||||
|
||||
@@ -14,6 +14,11 @@ template(name="archivesSidebar")
|
||||
if currentUser.isBoardMember
|
||||
unless isWorker
|
||||
p.quiet
|
||||
if this.archivedAt
|
||||
| {{_ 'archived-at' }}
|
||||
|
|
||||
| {{ moment this.archivedAt 'LLL' }}
|
||||
br
|
||||
a.js-restore-card {{_ 'restore'}}
|
||||
if currentUser.isBoardAdmin
|
||||
| -
|
||||
@@ -37,6 +42,11 @@ template(name="archivesSidebar")
|
||||
if currentUser.isBoardMember
|
||||
unless isWorker
|
||||
p.quiet
|
||||
if this.archivedAt
|
||||
| {{_ 'archived-at' }}
|
||||
|
|
||||
| {{ moment this.archivedAt 'LLL' }}
|
||||
br
|
||||
a.js-restore-list {{_ 'restore'}}
|
||||
if currentUser.isBoardAdmin
|
||||
| -
|
||||
@@ -58,6 +68,11 @@ template(name="archivesSidebar")
|
||||
if currentUser.isBoardMember
|
||||
unless isWorker
|
||||
p.quiet
|
||||
if this.archivedAt
|
||||
| {{_ 'archived-at' }}
|
||||
|
|
||||
| {{ moment this.archivedAt 'LLL' }}
|
||||
br
|
||||
a.js-restore-swimlane {{_ 'restore'}}
|
||||
if currentUser.isBoardAdmin
|
||||
| -
|
||||
|
||||
@@ -31,24 +31,39 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
archivedCards() {
|
||||
return Cards.find({
|
||||
archived: true,
|
||||
boardId: Session.get('currentBoard'),
|
||||
});
|
||||
return Cards.find(
|
||||
{
|
||||
archived: true,
|
||||
boardId: Session.get('currentBoard'),
|
||||
},
|
||||
{
|
||||
sort: { archivedAt: -1, modifiedAt: -1 },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
archivedLists() {
|
||||
return Lists.find({
|
||||
archived: true,
|
||||
boardId: Session.get('currentBoard'),
|
||||
});
|
||||
return Lists.find(
|
||||
{
|
||||
archived: true,
|
||||
boardId: Session.get('currentBoard'),
|
||||
},
|
||||
{
|
||||
sort: { archivedAt: -1, modifiedAt: -1 },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
archivedSwimlanes() {
|
||||
return Swimlanes.find({
|
||||
archived: true,
|
||||
boardId: Session.get('currentBoard'),
|
||||
});
|
||||
return Swimlanes.find(
|
||||
{
|
||||
archived: true,
|
||||
boardId: Session.get('currentBoard'),
|
||||
},
|
||||
{
|
||||
sort: { archivedAt: -1, modifiedAt: -1 },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
cardIsInArchivedList() {
|
||||
|
||||
@@ -50,6 +50,15 @@ template(name="createCustomFieldPopup")
|
||||
each dropdownItems.get
|
||||
input.js-dropdown-item(type="text" value=name placeholder="")
|
||||
input.js-dropdown-item.last(type="text" value="" placeholder="{{_ 'custom-field-dropdown-options-placeholder'}}")
|
||||
|
||||
div.js-field-settings.js-field-settings-stringtemplate(class="{{#if isTypeNotSelected 'stringtemplate'}}hide{{/if}}")
|
||||
label
|
||||
| {{_ 'custom-field-stringtemplate-format'}}
|
||||
input.js-field-stringtemplate-format(type="text" value=getStringtemplateFormat)
|
||||
label
|
||||
| {{_ 'custom-field-stringtemplate-separator'}}
|
||||
input.js-field-stringtemplate-separator(type="text" value=getStringtemplateSeparator)
|
||||
|
||||
a.flex.js-field-show-on-card(class="{{#if showOnCard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if showOnCard}}is-checked{{/if}}")
|
||||
|
||||
@@ -59,6 +68,10 @@ template(name="createCustomFieldPopup")
|
||||
|
||||
span {{_ 'automatically-field-on-card'}}
|
||||
|
||||
a.flex.js-field-always-on-card(class="{{#if alwaysOnCard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if alwaysOnCard}}is-checked{{/if}}")
|
||||
span {{_ 'always-field-on-card'}}
|
||||
|
||||
a.flex.js-field-showLabel-on-card(class="{{#if showLabelOnMiniCard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if showLabelOnMiniCard}}is-checked{{/if}}")
|
||||
|
||||
|
||||
@@ -16,7 +16,15 @@ BlazeComponent.extendComponent({
|
||||
}).register('customFieldsSidebar');
|
||||
|
||||
const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
_types: ['text', 'number', 'date', 'dropdown', 'currency'],
|
||||
_types: [
|
||||
'text',
|
||||
'number',
|
||||
'date',
|
||||
'dropdown',
|
||||
'currency',
|
||||
'checkbox',
|
||||
'stringtemplate',
|
||||
],
|
||||
|
||||
_currencyList: [
|
||||
{
|
||||
@@ -77,6 +85,18 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
? this.data().settings.dropdownItems
|
||||
: [],
|
||||
);
|
||||
|
||||
this.stringtemplateFormat = new ReactiveVar(
|
||||
this.data().settings && this.data().settings.stringtemplateFormat
|
||||
? this.data().settings.stringtemplateFormat
|
||||
: '',
|
||||
);
|
||||
|
||||
this.stringtemplateSeparator = new ReactiveVar(
|
||||
this.data().settings && this.data().settings.stringtemplateSeparator
|
||||
? this.data().settings.stringtemplateSeparator
|
||||
: '',
|
||||
);
|
||||
},
|
||||
|
||||
types() {
|
||||
@@ -121,6 +141,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
return items;
|
||||
},
|
||||
|
||||
getStringtemplateFormat() {
|
||||
return this.stringtemplateFormat.get();
|
||||
},
|
||||
|
||||
getStringtemplateSeparator() {
|
||||
return this.stringtemplateSeparator.get();
|
||||
},
|
||||
|
||||
getSettings() {
|
||||
const settings = {};
|
||||
switch (this.type.get()) {
|
||||
@@ -136,6 +164,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
settings.dropdownItems = dropdownItems;
|
||||
break;
|
||||
}
|
||||
case 'stringtemplate': {
|
||||
const stringtemplateFormat = this.stringtemplateFormat.get();
|
||||
settings.stringtemplateFormat = stringtemplateFormat;
|
||||
|
||||
const stringtemplateSeparator = this.stringtemplateSeparator.get();
|
||||
settings.stringtemplateSeparator = stringtemplateSeparator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
},
|
||||
@@ -158,6 +194,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
evt.target.value = '';
|
||||
}
|
||||
},
|
||||
'input .js-field-stringtemplate-format'(evt) {
|
||||
const value = evt.target.value;
|
||||
this.stringtemplateFormat.set(value);
|
||||
},
|
||||
'input .js-field-stringtemplate-separator'(evt) {
|
||||
const value = evt.target.value;
|
||||
this.stringtemplateSeparator.set(value);
|
||||
},
|
||||
'click .js-field-show-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if (!$target.hasClass('js-field-show-on-card')) {
|
||||
@@ -174,6 +218,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .js-field-always-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if (!$target.hasClass('js-field-always-on-card')) {
|
||||
$target = $target.parent();
|
||||
}
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .js-field-showLabel-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if (!$target.hasClass('js-field-showLabel-on-card')) {
|
||||
@@ -194,6 +246,8 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
this.find('.js-field-showLabel-on-card.is-checked') !== null,
|
||||
automaticallyOnCard:
|
||||
this.find('.js-field-automatically-on-card.is-checked') !== null,
|
||||
alwaysOnCard:
|
||||
this.find('.js-field-always-on-card.is-checked') !== null,
|
||||
};
|
||||
|
||||
// insert or update
|
||||
|
||||
@@ -72,6 +72,43 @@ template(name="filterSidebar")
|
||||
| (<span class="username">{{ username }}</span>)
|
||||
if Filter.assignees.isSelected _id
|
||||
i.fa.fa-check
|
||||
|
||||
hr
|
||||
h3
|
||||
i.fa.fa-list-alt
|
||||
| {{_ 'filter-dates-label' }}
|
||||
ul.sidebar-list
|
||||
li(class="{{#if Filter.dueAt.isSelected 'noDate'}}active{{/if}}")
|
||||
a.name.js-toggle-no-due-date-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-no-due-date' }}
|
||||
if Filter.dueAt.isSelected 'noDate'
|
||||
i.fa.fa-check
|
||||
li(class="{{#if Filter.dueAt.isSelected 'past'}}active{{/if}}")
|
||||
a.name.js-toggle-overdue-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-overdue' }}
|
||||
if Filter.dueAt.isSelected 'past'
|
||||
i.fa.fa-check
|
||||
li(class="{{#if Filter.dueAt.isSelected 'today'}}active{{/if}}")
|
||||
a.name.js-toggle-due-today-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-due-today' }}
|
||||
if Filter.dueAt.isSelected 'today'
|
||||
i.fa.fa-check
|
||||
li(class="{{#if Filter.dueAt.isSelected 'tomorrow'}}active{{/if}}")
|
||||
a.name.js-toggle-due-tomorrow-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-due-tomorrow' }}
|
||||
if Filter.dueAt.isSelected 'tomorrow'
|
||||
i.fa.fa-check
|
||||
li(class="{{#if Filter.dueAt.isSelected 'week'}}active{{/if}}")
|
||||
a.name.js-toggle-due-this-week-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-due-this-week' }}
|
||||
if Filter.dueAt.isSelected 'week'
|
||||
i.fa.fa-check
|
||||
|
||||
hr
|
||||
h3
|
||||
i.fa.fa-list-alt
|
||||
|
||||
@@ -23,6 +23,31 @@ BlazeComponent.extendComponent({
|
||||
Filter.assignees.toggle(this.currentData()._id);
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-no-due-date-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.noDate();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-overdue-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.past();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-due-today-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.today();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-due-tomorrow-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.tomorrow();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-due-this-week-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.thisWeek();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-archive-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.archive.toggle(this.currentData()._id);
|
||||
@@ -166,7 +191,7 @@ Template.disambiguateMultiMemberPopup.events({
|
||||
});
|
||||
|
||||
Template.moveSelectionPopup.events({
|
||||
'click .js-select-list'(event) {
|
||||
'click .js-select-list'() {
|
||||
// Move the minicard to the end of the target list
|
||||
mutateSelectedCards('moveToEndOfList', { listId: this._id });
|
||||
EscapeActions.executeUpTo('multiselection');
|
||||
|
||||
@@ -4,9 +4,9 @@ template(name="searchSidebar")
|
||||
.list-body
|
||||
.minilists.clearfix.js-minilists
|
||||
each (lists)
|
||||
a.minilist-wrapper.js-minilist(href=absoluteUrl)
|
||||
a.minilist-wrapper.js-minilist(href=originRelativeUrl)
|
||||
+minilist(this)
|
||||
.minicards.clearfix.js-minicards
|
||||
each (results)
|
||||
a.minicard-wrapper.js-minicard(href=absoluteUrl)
|
||||
a.minicard-wrapper.js-minicard(href=originRelativeUrl)
|
||||
+minicard(this)
|
||||
@@ -39,6 +39,10 @@ template(name="swimlaneActionPopup")
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-close-swimlane {{_ 'archive-swimlane'}}
|
||||
ul.pop-over-list
|
||||
li: a.js-copy-swimlane {{_ 'copy-swimlane'}}
|
||||
ul.pop-over-list
|
||||
li: a.js-move-swimlane {{_ 'move-swimlane'}}
|
||||
|
||||
template(name="swimlaneAddPopup")
|
||||
unless currentUser.isCommentOnly
|
||||
|
||||
@@ -54,9 +54,11 @@ Template.swimlaneActionPopup.events({
|
||||
this.archive();
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-move-swimlane': Popup.open('moveSwimlane'),
|
||||
'click .js-copy-swimlane': Popup.open('copySwimlane'),
|
||||
});
|
||||
|
||||
Template.swimlaneActionPopup.helpers({
|
||||
Template.swimlaneActionPopup.events({
|
||||
isCommentOnly() {
|
||||
return Meteor.user().isCommentOnly();
|
||||
},
|
||||
|
||||
@@ -61,3 +61,23 @@ template(name="addListForm")
|
||||
a.open-list-composer.js-open-inlined-form
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-list'}}
|
||||
|
||||
template(name="moveSwimlanePopup")
|
||||
unless currentUser.isWorker
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards(autofocus)
|
||||
each toBoard in toBoards
|
||||
option(value="{{toBoard._id}}") {{toBoard.title}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
|
||||
template(name="copySwimlanePopup")
|
||||
unless currentUser.isWorker
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards(autofocus)
|
||||
each toBoard in toBoards
|
||||
option(value="{{toBoard._id}}" selected="{{#if $eq toBoard.title board.title}}1{{/if}}") {{toBoard.title}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
@@ -85,14 +85,14 @@ function initSortable(boardComponent, $listsDom) {
|
||||
},
|
||||
});
|
||||
|
||||
function userIsMember() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
}
|
||||
//function userIsMember() {
|
||||
// return (
|
||||
// Meteor.user() &&
|
||||
// Meteor.user().isBoardMember() &&
|
||||
// !Meteor.user().isCommentOnly() &&
|
||||
// !Meteor.user().isWorker()
|
||||
// );
|
||||
//}
|
||||
|
||||
boardComponent.autorun(() => {
|
||||
let showDesktopDragHandles = false;
|
||||
@@ -323,3 +323,55 @@ BlazeComponent.extendComponent({
|
||||
initSortable(boardComponent, $listsDom);
|
||||
},
|
||||
}).register('listsGroup');
|
||||
|
||||
class MoveSwimlaneComponent extends BlazeComponent {
|
||||
serverMethod = 'moveSwimlane';
|
||||
|
||||
onCreated() {
|
||||
this.currentSwimlane = this.currentData();
|
||||
}
|
||||
|
||||
board() {
|
||||
return Boards.findOne(Session.get('currentBoard'));
|
||||
}
|
||||
|
||||
toBoardsSelector() {
|
||||
return {
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
type: 'board',
|
||||
_id: { $ne: this.board()._id },
|
||||
};
|
||||
}
|
||||
|
||||
toBoards() {
|
||||
return Boards.find(this.toBoardsSelector(), { sort: { title: 1 } });
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-done'() {
|
||||
// const swimlane = Swimlanes.findOne(this.currentSwimlane._id);
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
let boardId;
|
||||
if (bSelect) {
|
||||
boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
Meteor.call(this.serverMethod, this.currentSwimlane._id, boardId);
|
||||
}
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
MoveSwimlaneComponent.register('moveSwimlanePopup');
|
||||
|
||||
(class extends MoveSwimlaneComponent {
|
||||
serverMethod = 'copySwimlane';
|
||||
toBoardsSelector() {
|
||||
const selector = super.toBoardsSelector();
|
||||
delete selector._id;
|
||||
return selector;
|
||||
}
|
||||
}.register('copySwimlanePopup'));
|
||||
@@ -1,5 +1,5 @@
|
||||
template(name="userAvatar")
|
||||
a.member.js-member(title="{{userData.profile.fullname}} ({{userData.username}}) {{_ memberType}}")
|
||||
a.member(class="js-{{#if assignee}}assignee{{else}}member{{/if}}" title="{{userData.profile.fullname}} ({{userData.username}}) {{_ memberType}}")
|
||||
if userData.profile.avatarUrl
|
||||
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
|
||||
else
|
||||
@@ -72,9 +72,10 @@ template(name="cardMemberPopup")
|
||||
h3= user.profile.fullname
|
||||
p.quiet @{{ user.username }}
|
||||
ul.pop-over-list
|
||||
if currentUser.isNotCommentOnly
|
||||
if currentUser.isNotWorker
|
||||
li: a.js-remove-member {{_ 'remove-member-from-card'}}
|
||||
unless noRemove
|
||||
if currentUser.isNotCommentOnly
|
||||
if currentUser.isNotWorker
|
||||
li: a.js-remove-member {{_ 'remove-member-from-card'}}
|
||||
|
||||
if $eq currentUser._id user._id
|
||||
with currentUser
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import Cards from '/models/cards';
|
||||
import Avatars from '/models/avatars';
|
||||
import Users from '/models/users';
|
||||
|
||||
Template.userAvatar.helpers({
|
||||
userData() {
|
||||
// We need to handle a special case for the search results provided by the
|
||||
@@ -30,10 +34,6 @@ Template.userAvatar.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
Template.userAvatar.events({
|
||||
'click .js-change-avatar': Popup.open('changeAvatar'),
|
||||
});
|
||||
|
||||
Template.userAvatarInitials.helpers({
|
||||
initials() {
|
||||
const user = Users.findOne(this.userId);
|
||||
|
||||
@@ -9,7 +9,6 @@ avatar-radius = 50%
|
||||
float: left
|
||||
height: 30px
|
||||
width: @height
|
||||
margin: 0 4px 4px 0
|
||||
cursor: pointer
|
||||
user-select: none
|
||||
z-index: 1
|
||||
|
||||
@@ -25,10 +25,6 @@ template(name="memberMenuPopup")
|
||||
a.js-global-search(href="{{pathFor 'global-search'}}")
|
||||
i.fa.fa-search
|
||||
| {{_ 'globalSearch-title'}}
|
||||
li
|
||||
a.js-broken-cards(href="{{pathFor 'broken-cards'}}")
|
||||
i.fa.fa-chain-broken
|
||||
| {{_ 'broken-cards'}}
|
||||
li
|
||||
a(href="{{pathFor 'home'}}")
|
||||
span.fa.fa-home
|
||||
@@ -143,7 +139,7 @@ template(name="changeSettingsPopup")
|
||||
label.bold.clear
|
||||
i.fa.fa-sort-numeric-asc
|
||||
| {{_ 'show-cards-minimum-count'}}
|
||||
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
|
||||
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="-1")
|
||||
label.bold.clear
|
||||
i.fa.fa-calendar
|
||||
| {{_ 'start-day-of-week'}}
|
||||
|
||||
@@ -168,10 +168,34 @@ Template.changeLanguagePopup.helpers({
|
||||
let name = lang.name;
|
||||
if (lang.name === 'br') {
|
||||
name = 'Brezhoneg';
|
||||
} else if (lang.name === 'ar-EG') {
|
||||
// ar-EG = Arabic (Egypt), simply Masri (مَصرى, [ˈmɑsˤɾi], Egyptian, Masr refers to Cairo)
|
||||
name = 'مَصرى';
|
||||
} else if (lang.name === 'fa-IR') {
|
||||
// fa-IR = Persian (Iran)
|
||||
name = 'فارسی/پارسی (ایران)';
|
||||
} else if (lang.name === 'de-CH') {
|
||||
name = 'Deutsch (Schweiz)';
|
||||
} else if (lang.name === 'fr-BE') {
|
||||
name = 'Français (Belgique)';
|
||||
} else if (lang.name === 'fr-CA') {
|
||||
name = 'Français (Canada)';
|
||||
} else if (lang.name === 'ig') {
|
||||
name = 'Igbo';
|
||||
} else if (lang.name === 'lv') {
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'latviešu valoda') {
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'Español') {
|
||||
name = 'español';
|
||||
} else if (lang.name === 'es_419') {
|
||||
name = 'español de América Latina';
|
||||
} else if (lang.name === 'es-419') {
|
||||
name = 'español de América Latina';
|
||||
} else if (lang.name === 'Español de América Latina') {
|
||||
name = 'español de América Latina';
|
||||
} else if (lang.name === 'es-LA') {
|
||||
name = 'español de América Latina';
|
||||
} else if (lang.name === 'Español de Argentina') {
|
||||
name = 'español de Argentina';
|
||||
} else if (lang.name === 'Español de Chile') {
|
||||
@@ -182,12 +206,16 @@ Template.changeLanguagePopup.helpers({
|
||||
name = 'español de México';
|
||||
} else if (lang.name === 'es-PY') {
|
||||
name = 'español de Paraguayo';
|
||||
} else if (lang.name === 'Español de Paraguayo') {
|
||||
name = 'español de Paraguayo';
|
||||
} else if (lang.name === 'Español de Perú') {
|
||||
name = 'español de Perú';
|
||||
} else if (lang.name === 'Español de Puerto Rico') {
|
||||
name = 'español de Puerto Rico';
|
||||
} else if (lang.name === 'oc') {
|
||||
name = 'Occitan';
|
||||
} else if (lang.name === 'st') {
|
||||
name = 'Sãotomense';
|
||||
} else if (lang.name === '繁体中文(台湾)') {
|
||||
name = '繁體中文(台灣)';
|
||||
}
|
||||
@@ -213,6 +241,7 @@ Template.changeLanguagePopup.events({
|
||||
'profile.language': this.tag,
|
||||
},
|
||||
});
|
||||
TAPi18n.setLanguage(this.tag);
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
@@ -270,6 +299,13 @@ Template.changeSettingsPopup.helpers({
|
||||
});
|
||||
|
||||
Template.changeSettingsPopup.events({
|
||||
'keypress/paste #show-cards-count-at'() {
|
||||
let keyCode = event.keyCode;
|
||||
let charCode = String.fromCharCode(keyCode);
|
||||
let regex = new RegExp('[-0-9]');
|
||||
let ret = regex.test(charCode);
|
||||
return ret;
|
||||
},
|
||||
'click .js-toggle-desktop-drag-handles'() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
@@ -292,7 +328,7 @@ Template.changeSettingsPopup.events({
|
||||
},
|
||||
'click .js-apply-user-settings'(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const minLimit = parseInt(
|
||||
let minLimit = parseInt(
|
||||
templateInstance.$('#show-cards-count-at').val(),
|
||||
10,
|
||||
);
|
||||
@@ -301,6 +337,9 @@ Template.changeSettingsPopup.events({
|
||||
10,
|
||||
);
|
||||
const currentUser = Meteor.user();
|
||||
if (isNaN(minLimit) || minLimit < -1) {
|
||||
minLimit = -1;
|
||||
}
|
||||
if (!isNaN(minLimit)) {
|
||||
if (currentUser) {
|
||||
Meteor.call('changeLimitToShowCardsCount', minLimit);
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
import Cards from '../../models/cards';
|
||||
import SessionData from '../../models/usersessiondata';
|
||||
|
||||
export class CardSearchPagedComponent extends BlazeComponent {
|
||||
onCreated() {
|
||||
this.searching = new ReactiveVar(false);
|
||||
this.hasResults = new ReactiveVar(false);
|
||||
this.hasQueryErrors = new ReactiveVar(false);
|
||||
this.query = new ReactiveVar('');
|
||||
this.resultsHeading = new ReactiveVar('');
|
||||
this.searchLink = new ReactiveVar(null);
|
||||
this.results = new ReactiveVar([]);
|
||||
this.hasNextPage = new ReactiveVar(false);
|
||||
this.hasPreviousPage = new ReactiveVar(false);
|
||||
this.resultsCount = 0;
|
||||
this.totalHits = 0;
|
||||
this.queryErrors = null;
|
||||
this.resultsPerPage = 25;
|
||||
this.sessionId = SessionData.getSessionId();
|
||||
this.subscriptionHandle = null;
|
||||
this.serverError = new ReactiveVar(false);
|
||||
|
||||
const that = this;
|
||||
this.subscriptionCallbacks = {
|
||||
onReady() {
|
||||
that.getResults();
|
||||
that.searching.set(false);
|
||||
that.hasResults.set(true);
|
||||
that.serverError.set(false);
|
||||
},
|
||||
onError(error) {
|
||||
that.searching.set(false);
|
||||
that.hasResults.set(false);
|
||||
that.serverError.set(true);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error.reason:', error.reason);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error.message:', error.message);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error.stack:', error.stack);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
resetSearch() {
|
||||
this.searching.set(false);
|
||||
this.results.set([]);
|
||||
this.hasResults.set(false);
|
||||
this.hasQueryErrors.set(false);
|
||||
this.resultsHeading.set('');
|
||||
this.serverError.set(false);
|
||||
this.resultsCount = 0;
|
||||
this.totalHits = 0;
|
||||
this.queryErrors = null;
|
||||
}
|
||||
|
||||
getSessionData(sessionId) {
|
||||
return SessionData.findOne({
|
||||
sessionId: sessionId ? sessionId : SessionData.getSessionId(),
|
||||
});
|
||||
}
|
||||
|
||||
getResults() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('getting results');
|
||||
const sessionData = this.getSessionData();
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('session data:', sessionData);
|
||||
const cards = [];
|
||||
sessionData.cards.forEach(cardId => {
|
||||
cards.push(Cards.findOne({ _id: cardId }));
|
||||
});
|
||||
this.queryErrors = sessionData.errors;
|
||||
if (this.queryErrors.length) {
|
||||
// console.log('queryErrors:', this.queryErrorMessages());
|
||||
this.hasQueryErrors.set(true);
|
||||
// return null;
|
||||
}
|
||||
|
||||
if (cards) {
|
||||
this.totalHits = sessionData.totalHits;
|
||||
this.resultsCount = cards.length;
|
||||
this.resultsStart = sessionData.lastHit - this.resultsCount + 1;
|
||||
this.resultsEnd = sessionData.lastHit;
|
||||
this.resultsHeading.set(this.getResultsHeading());
|
||||
this.results.set(cards);
|
||||
this.hasNextPage.set(sessionData.lastHit < sessionData.totalHits);
|
||||
this.hasPreviousPage.set(
|
||||
sessionData.lastHit - sessionData.resultsCount > 0,
|
||||
);
|
||||
return cards;
|
||||
}
|
||||
|
||||
this.resultsCount = 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
stopSubscription() {
|
||||
if (this.subscriptionHandle) {
|
||||
this.subscriptionHandle.stop();
|
||||
}
|
||||
}
|
||||
|
||||
getSubscription(queryParams) {
|
||||
return Meteor.subscribe(
|
||||
'globalSearch',
|
||||
this.sessionId,
|
||||
queryParams.params,
|
||||
queryParams.text,
|
||||
this.subscriptionCallbacks,
|
||||
);
|
||||
}
|
||||
|
||||
runGlobalSearch(queryParams) {
|
||||
this.searching.set(true);
|
||||
this.stopSubscription();
|
||||
this.subscriptionHandle = this.getSubscription(queryParams);
|
||||
}
|
||||
|
||||
queryErrorMessages() {
|
||||
const messages = [];
|
||||
|
||||
this.queryErrors.forEach(err => {
|
||||
let value = err.color ? TAPi18n.__(`color-${err.value}`) : err.value;
|
||||
if (!value) {
|
||||
value = err.value;
|
||||
}
|
||||
messages.push(TAPi18n.__(err.tag, value));
|
||||
});
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
nextPage() {
|
||||
this.searching.set(true);
|
||||
this.stopSubscription();
|
||||
this.subscriptionHandle = Meteor.subscribe(
|
||||
'nextPage',
|
||||
this.sessionId,
|
||||
this.subscriptionCallbacks,
|
||||
);
|
||||
}
|
||||
|
||||
previousPage() {
|
||||
this.searching.set(true);
|
||||
this.stopSubscription();
|
||||
this.subscriptionHandle = Meteor.subscribe(
|
||||
'previousPage',
|
||||
this.sessionId,
|
||||
this.subscriptionCallbacks,
|
||||
);
|
||||
}
|
||||
|
||||
getResultsHeading() {
|
||||
if (this.resultsCount === 0) {
|
||||
return TAPi18n.__('no-cards-found');
|
||||
} else if (this.resultsCount === 1) {
|
||||
return TAPi18n.__('one-card-found');
|
||||
} else if (this.resultsCount === this.totalHits) {
|
||||
return TAPi18n.__('n-cards-found', this.resultsCount);
|
||||
}
|
||||
|
||||
return TAPi18n.__('n-n-of-n-cards-found', {
|
||||
start: this.resultsStart,
|
||||
end: this.resultsEnd,
|
||||
total: this.totalHits,
|
||||
});
|
||||
}
|
||||
|
||||
getSearchHref() {
|
||||
const baseUrl = window.location.href.replace(/([?#].*$|\s*$)/, '');
|
||||
return `${baseUrl}?q=${encodeURIComponent(this.query.get())}`;
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-next-page'(evt) {
|
||||
evt.preventDefault();
|
||||
this.nextPage();
|
||||
},
|
||||
'click .js-previous-page'(evt) {
|
||||
evt.preventDefault();
|
||||
this.previousPage();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
+43
-19
@@ -1,14 +1,22 @@
|
||||
DatePicker = BlazeComponent.extendComponent({
|
||||
// Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours
|
||||
function adjustedTimeFormat() {
|
||||
return moment
|
||||
.localeData()
|
||||
.longDateFormat('LT')
|
||||
.replace(/HH/i, 'H');
|
||||
}
|
||||
|
||||
export class DatePicker extends BlazeComponent {
|
||||
template() {
|
||||
return 'datepicker';
|
||||
},
|
||||
}
|
||||
|
||||
onCreated(defaultTime = '1970-01-01 08:00:00') {
|
||||
this.error = new ReactiveVar('');
|
||||
this.card = this.data();
|
||||
this.date = new ReactiveVar(moment.invalid());
|
||||
this.defaultTime = defaultTime;
|
||||
},
|
||||
}
|
||||
|
||||
startDayOfWeek() {
|
||||
const currentUser = Meteor.user();
|
||||
@@ -17,7 +25,7 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
onRendered() {
|
||||
const $picker = this.$('.js-datepicker')
|
||||
@@ -34,7 +42,7 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
this.error.set('');
|
||||
const timeInput = this.find('#time');
|
||||
timeInput.focus();
|
||||
if (!timeInput.value) {
|
||||
if (!timeInput.value && this.defaultTime) {
|
||||
const currentHour = evt.date.getHours();
|
||||
const defaultMoment = moment(
|
||||
currentHour > 0 ? evt.date : this.defaultTime,
|
||||
@@ -47,22 +55,22 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
if (this.date.get().isValid()) {
|
||||
$picker.datepicker('update', this.date.get().toDate());
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
showDate() {
|
||||
if (this.date.get().isValid()) return this.date.get().format('L');
|
||||
return '';
|
||||
},
|
||||
}
|
||||
showTime() {
|
||||
if (this.date.get().isValid()) return this.date.get().format('LT');
|
||||
return '';
|
||||
},
|
||||
}
|
||||
dateFormat() {
|
||||
return moment.localeData().longDateFormat('L');
|
||||
},
|
||||
}
|
||||
timeFormat() {
|
||||
return moment.localeData().longDateFormat('LT');
|
||||
},
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
@@ -77,7 +85,11 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
},
|
||||
'keyup .js-time-field'() {
|
||||
// parse for localized time format in strict mode
|
||||
const dateMoment = moment(this.find('#time').value, 'LT', true);
|
||||
const dateMoment = moment(
|
||||
this.find('#time').value,
|
||||
adjustedTimeFormat(),
|
||||
true,
|
||||
);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
}
|
||||
@@ -89,16 +101,28 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
const time =
|
||||
evt.target.time.value ||
|
||||
moment(new Date().setHours(12, 0, 0)).format('LT');
|
||||
|
||||
const newTime = moment(time, adjustedTimeFormat(), true);
|
||||
const newDate = moment(evt.target.date.value, 'L', true);
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
const newDate = moment(dateString, 'L LT', true);
|
||||
if (newDate.isValid()) {
|
||||
this._storeDate(newDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
const newCompleteDate = moment(
|
||||
dateString,
|
||||
`L ${adjustedTimeFormat()}`,
|
||||
true,
|
||||
);
|
||||
if (!newTime.isValid()) {
|
||||
this.error.set('invalid-time');
|
||||
evt.target.time.focus();
|
||||
}
|
||||
if (!newDate.isValid()) {
|
||||
this.error.set('invalid-date');
|
||||
evt.target.date.focus();
|
||||
}
|
||||
if (newCompleteDate.isValid()) {
|
||||
this._storeDate(newCompleteDate.toDate());
|
||||
Popup.close();
|
||||
} else if (!this.error) {
|
||||
this.error.set('invalid');
|
||||
}
|
||||
},
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
@@ -107,5 +131,5 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,154 @@ function showFilterSidebar() {
|
||||
Sidebar.setView('filter');
|
||||
}
|
||||
|
||||
class DateFilter {
|
||||
constructor() {
|
||||
this._dep = new Tracker.Dependency();
|
||||
this.subField = ''; // Prevent name mangling in Filter
|
||||
this._filter = null;
|
||||
this._filterState = null;
|
||||
}
|
||||
|
||||
_updateState(state) {
|
||||
this._filterState = state;
|
||||
showFilterSidebar();
|
||||
this._dep.changed();
|
||||
}
|
||||
|
||||
// past builds a filter for all dates before now
|
||||
past() {
|
||||
if (this._filterState == 'past') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this._filter = { $lte: moment().toDate() };
|
||||
this._updateState('past');
|
||||
}
|
||||
|
||||
// today is a convenience method for calling relativeDay with 0
|
||||
today() {
|
||||
if (this._filterState == 'today') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this.relativeDay(0);
|
||||
this._updateState('today');
|
||||
}
|
||||
|
||||
// tomorrow is a convenience method for calling relativeDay with 1
|
||||
tomorrow() {
|
||||
if (this._filterState == 'tomorrow') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this.relativeDay(1);
|
||||
this._updateState('tomorrow');
|
||||
}
|
||||
|
||||
// thisWeek is a convenience method for calling relativeWeek with 1
|
||||
thisWeek() {
|
||||
this.relativeWeek(1);
|
||||
}
|
||||
|
||||
// relativeDay builds a filter starting from now and including all
|
||||
// days up to today +/- offset.
|
||||
relativeDay(offset) {
|
||||
if (this._filterState == 'day') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
var startDay = moment()
|
||||
.startOf('day')
|
||||
.toDate(),
|
||||
endDay = moment()
|
||||
.endOf('day')
|
||||
.add(offset, 'day')
|
||||
.toDate();
|
||||
|
||||
if (offset >= 0) {
|
||||
this._filter = { $gte: startDay, $lte: endDay };
|
||||
} else {
|
||||
this._filter = { $lte: startDay, $gte: endDay };
|
||||
}
|
||||
|
||||
this._updateState('day');
|
||||
}
|
||||
|
||||
// relativeWeek builds a filter starting from today and including all
|
||||
// weeks up to today +/- offset. This considers the user's preferred
|
||||
// start of week day (as defined by Meteor).
|
||||
relativeWeek(offset) {
|
||||
if (this._filterState == 'week') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// getStartDayOfWeek returns the offset from Sunday of the user's
|
||||
// preferred starting day of the week. This date should be added
|
||||
// to the moment start of week to get the real start of week date.
|
||||
// The default is 1, meaning Monday.
|
||||
const currentUser = Meteor.user();
|
||||
const weekStartDay = currentUser ? currentUser.getStartDayOfWeek() : 1;
|
||||
|
||||
// Moments are mutable so they must be cloned before modification
|
||||
var thisWeekStart = moment()
|
||||
.startOf('day')
|
||||
.startOf('week')
|
||||
.add(weekStartDay, 'days');
|
||||
var thisWeekEnd = thisWeekStart
|
||||
.clone()
|
||||
.add(offset, 'week')
|
||||
.endOf('day');
|
||||
var startDate = thisWeekStart.toDate();
|
||||
var endDate = thisWeekEnd.toDate();
|
||||
|
||||
if (offset >= 0) {
|
||||
this._filter = { $gte: startDate, $lte: endDate };
|
||||
} else {
|
||||
this._filter = { $lte: startDate, $gte: endDate };
|
||||
}
|
||||
|
||||
this._updateState('week');
|
||||
}
|
||||
|
||||
// noDate builds a filter for items where date is not set
|
||||
noDate() {
|
||||
if (this._filterState == 'noDate') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this._filter = null;
|
||||
this._updateState('noDate');
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._filter = null;
|
||||
this._filterState = null;
|
||||
this._dep.changed();
|
||||
}
|
||||
|
||||
isSelected(val) {
|
||||
this._dep.depend();
|
||||
return this._filterState == val;
|
||||
}
|
||||
|
||||
_isActive() {
|
||||
this._dep.depend();
|
||||
return this._filterState !== null;
|
||||
}
|
||||
|
||||
_getMongoSelector() {
|
||||
this._dep.depend();
|
||||
return this._filter;
|
||||
}
|
||||
|
||||
_getEmptySelector() {
|
||||
this._dep.depend();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Use a "set" filter for a field that is a set of documents uniquely
|
||||
// identified. For instance `{ labels: ['labelA', 'labelC', 'labelD'] }`.
|
||||
// use "subField" for searching inside object Fields.
|
||||
@@ -462,6 +610,7 @@ Filter = {
|
||||
assignees: new SetFilter(),
|
||||
archive: new SetFilter(),
|
||||
hideEmpty: new SetFilter(),
|
||||
dueAt: new DateFilter(),
|
||||
customFields: new SetFilter('_id'),
|
||||
advanced: new AdvancedFilter(),
|
||||
lists: new AdvancedFilter(), // we need the ability to filter list by name as well
|
||||
@@ -472,6 +621,7 @@ Filter = {
|
||||
'assignees',
|
||||
'archive',
|
||||
'hideEmpty',
|
||||
'dueAt',
|
||||
'customFields',
|
||||
],
|
||||
|
||||
|
||||
+17
-17
@@ -4,24 +4,24 @@
|
||||
|
||||
Meteor.startup(() => {
|
||||
TAPi18n.conf.i18n_files_route = Meteor._relativeToSiteRootUrl('/tap-i18n');
|
||||
Tracker.autorun(() => {
|
||||
const currentUser = Meteor.user();
|
||||
let language;
|
||||
if (currentUser) {
|
||||
language = currentUser.profile && currentUser.profile.language;
|
||||
}
|
||||
const currentUser = Meteor.user();
|
||||
let language;
|
||||
if (currentUser) {
|
||||
language = currentUser.profile && currentUser.profile.language;
|
||||
}
|
||||
|
||||
if (!language) {
|
||||
if (navigator.languages) {
|
||||
language = navigator.languages[0];
|
||||
} else {
|
||||
language = navigator.language || navigator.userLanguage;
|
||||
}
|
||||
if (!language) {
|
||||
if (navigator.languages) {
|
||||
language = navigator.languages[0];
|
||||
} else {
|
||||
language = navigator.language || navigator.userLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
if (language) {
|
||||
TAPi18n.setLanguage(language);
|
||||
T9n.setLanguage(language);
|
||||
}
|
||||
});
|
||||
if (language) {
|
||||
TAPi18n.setLanguage(language);
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('language set!');
|
||||
T9n.setLanguage(language);
|
||||
}
|
||||
});
|
||||
Loaded 100 of 534 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user