Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57ddd82ef1 | ||
|
|
79f7ec2715 | ||
|
|
ab4c3bd2fc | ||
|
|
bd3a7e1068 | ||
|
|
aa33ead7b2 | ||
|
|
94cf2a80a5 | ||
|
|
2ee959a08e | ||
|
|
54b7591ca0 | ||
|
|
b3f0392e7d | ||
|
|
77e7350e96 | ||
|
|
9c87572f90 | ||
|
|
65765f6c2f | ||
|
|
785b312009 | ||
|
|
1d9a710e3b | ||
|
|
909bf811d1 | ||
|
|
90abe01286 | ||
|
|
2c13d74c5f | ||
|
|
5d975c5da4 | ||
|
|
35c1057527 | ||
|
|
3912181857 | ||
|
|
b9a01eb5d6 | ||
|
|
3027f0acb0 | ||
|
|
609c2a5ddb | ||
|
|
f76d648012 | ||
|
|
203bf92a0a | ||
|
|
3e8cc9e905 | ||
|
|
b9f32c1739 | ||
|
|
7e1c65f616 | ||
|
|
447ae93476 | ||
|
|
6df40f41ac | ||
|
|
3e0ef3d070 | ||
|
|
33a17a9a1b | ||
|
|
aed4bfb7f4 | ||
|
|
78f7c3e5bd | ||
|
|
d764047c1f | ||
|
|
65015ea7c7 | ||
|
|
6ef2d9cb9d | ||
|
|
c39fdab11d | ||
|
|
547261abb6 | ||
|
|
7696b7b769 | ||
|
|
585635494a | ||
|
|
a36e80099a | ||
|
|
f5f8a4f6fb | ||
|
|
e207bff91f | ||
|
|
a2c7c230ef | ||
|
|
83a12a9a26 | ||
|
|
096fe130f6 | ||
|
|
12d22f92b2 | ||
|
|
017628e2a6 | ||
|
|
332b9f5816 | ||
|
|
09a96c1118 | ||
|
|
82750ee8a2 | ||
|
|
4e89f27768 | ||
|
|
6f60235a3f | ||
|
|
42be851d64 | ||
|
|
6c32c210f8 | ||
|
|
aca665ae4e | ||
|
|
09bee30610 | ||
|
|
87d53f6f93 | ||
|
|
98cf7ec715 | ||
|
|
12e50d93df | ||
|
|
dfcdb1994b | ||
|
|
30a5b87370 | ||
|
|
465e2b1b6a | ||
|
|
1ae52bbdb1 | ||
|
|
d0024d397a | ||
|
|
0b2cdc4d56 | ||
|
|
92af6f71d9 | ||
|
|
8879b8498f | ||
|
|
ad7e0e0bf9 | ||
|
|
edb7398295 | ||
|
|
cfca28e25a | ||
|
|
a811e22f95 | ||
|
|
fbb2e6e261 | ||
|
|
c44b99d515 | ||
|
|
0f283e6cdf | ||
|
|
b7e8f796a3 | ||
|
|
d63507dd97 | ||
|
|
e1168b5107 | ||
|
|
cecb4b8336 | ||
|
|
5bbbf9d0e6 | ||
|
|
29775eef9a | ||
|
|
3f3e645c96 | ||
|
|
ebece3f961 | ||
|
|
4951d409db | ||
|
|
cf90511f3c | ||
|
|
4ec0e63d99 | ||
|
|
08843a9dd6 | ||
|
|
b3f229dc86 | ||
|
|
18b74e7a61 | ||
|
|
c60961cbb1 | ||
|
|
3338fc366d | ||
|
|
79cbf25b73 | ||
|
|
7963118032 | ||
|
|
35e6c938dc | ||
|
|
c632bc4555 | ||
|
|
7a34bc3eb0 | ||
|
|
6e0d41834c | ||
|
|
4bcbaf9113 | ||
|
|
5448a15691 | ||
|
|
c63feb3ff3 | ||
|
|
fb4d95672e | ||
|
|
bfb1658abe | ||
|
|
79e2c9175f | ||
|
|
b9c2d49a1c | ||
|
|
a25eb4ded6 | ||
|
|
3851de2774 | ||
|
|
0783b733b0 | ||
|
|
bb17feaa17 | ||
|
|
7623c8dcb7 | ||
|
|
1d7eb8a03b | ||
|
|
c5712f5ae0 | ||
|
|
00e5808a4c | ||
|
|
0a591564fd | ||
|
|
8448068d22 | ||
|
|
9fa36c3991 | ||
|
|
d949753d54 | ||
|
|
f6d2b08025 | ||
|
|
8a446de3e9 | ||
|
|
0196f46094 | ||
|
|
bd83b3bc8e | ||
|
|
89347abf53 | ||
|
|
a27f8ecfa9 | ||
|
|
75ca4920c5 | ||
|
|
324be07b85 | ||
|
|
282a5b30f9 | ||
|
|
b24acefa6f | ||
|
|
751b519167 | ||
|
|
330aec2bdd | ||
|
|
d5635c3a89 | ||
|
|
1e86d67bfe | ||
|
|
8ac9353c53 | ||
|
|
1557970170 | ||
|
|
508bbb37ce | ||
|
|
a90fc396a9 | ||
|
|
96627540da | ||
|
|
db6ebe0470 | ||
|
|
559251eb0d | ||
|
|
23396d1bd6 | ||
|
|
9d92a79a28 | ||
|
|
90a64a7403 | ||
|
|
f709cc332c | ||
|
|
c925a27870 | ||
|
|
d31403a918 | ||
|
|
f9051d768c | ||
|
|
06eb2adeaa | ||
|
|
12af3a5d10 | ||
|
|
8bf94d6ac6 | ||
|
|
15af5d2c2e | ||
|
|
60c5c5c723 | ||
|
|
f972da7442 | ||
|
|
948537cb75 | ||
|
|
ec5d0d00f5 | ||
|
|
0e7c454013 | ||
|
|
6e8e581ceb | ||
|
|
2ec435af41 | ||
|
|
5aa38c2e40 | ||
|
|
0ff3952a8b | ||
|
|
e32d2daa45 | ||
|
|
57780801aa | ||
|
|
ecdfc68170 | ||
|
|
3b29678ef1 | ||
|
|
6c54b38cdf | ||
|
|
dfcabc5a36 | ||
|
|
5c8cf2ebbd | ||
|
|
a9d41217bd | ||
|
|
75f44a821c | ||
|
|
0ab16764d1 | ||
|
|
b6e7e03c95 | ||
|
|
718c1a393d | ||
|
|
50f3316088 | ||
|
|
2727651897 | ||
|
|
e72646a4d4 | ||
|
|
8ef8d546c5 | ||
|
|
a8b2d7a6bd | ||
|
|
0a1074ca6e | ||
|
|
019d7c92c3 | ||
|
|
cb653e03f2 | ||
|
|
a6c8833f65 | ||
|
|
f00b39d154 | ||
|
|
39597e04ac | ||
|
|
1610eff0e9 | ||
|
|
c944f9fdff | ||
|
|
9b4d4c5953 | ||
|
|
a8f09011e9 | ||
|
|
2e1675969a | ||
|
|
a71973cc80 | ||
|
|
608ec63899 | ||
|
|
52bdba8e85 | ||
|
|
f5b11b15ce | ||
|
|
3359559ad2 | ||
|
|
b4aa464473 | ||
|
|
dbe31a86b7 | ||
|
|
bd283ff4e2 | ||
|
|
c020ce8895 | ||
|
|
f3562525db | ||
|
|
cc89c35c8d | ||
|
|
2457084db2 | ||
|
|
508a2d7103 | ||
|
|
3d7fef67d8 | ||
|
|
d63d445b7d | ||
|
|
8d3fe29c68 | ||
|
|
ad3ecbfd96 | ||
|
|
51ec43e7bc | ||
|
|
88dfc57e7e | ||
|
|
7409c366f6 | ||
|
|
b99b2a6d66 | ||
|
|
fa21b00bb6 | ||
|
|
f061b5e12a | ||
|
|
8ca642edc8 | ||
|
|
c3909edc5e | ||
|
|
f4cf09d394 | ||
|
|
6aa4f9fb86 | ||
|
|
a31545824e | ||
|
|
a94cfd5b91 | ||
|
|
6d6d51310e | ||
|
|
c9cf2971de | ||
|
|
54c481e3d8 | ||
|
|
8b4fbd266d | ||
|
|
3c5241b007 | ||
|
|
42a1f94931 | ||
|
|
cae6f38b80 | ||
|
|
25722e5e9d | ||
|
|
1d339ec17e | ||
|
|
ec534c8cca | ||
|
|
cab285c34d | ||
|
|
975993dd6d | ||
|
|
43084eb664 | ||
|
|
0e770ac3b3 | ||
|
|
206abc8c68 | ||
|
|
62b13cc343 | ||
|
|
7822dd3b75 | ||
|
|
a214cd64b6 | ||
|
|
8839a8ea39 | ||
|
|
a4169f3da7 | ||
|
|
19d59b3ab4 | ||
|
|
27b2e2384c | ||
|
|
49d2cbc2c1 | ||
|
|
bf2b3b40cd | ||
|
|
1541c6ca23 | ||
|
|
42c1ff94e2 | ||
|
|
53aa5ebc17 | ||
|
|
0f43818757 | ||
|
|
f3c5b1eb07 | ||
|
|
517d1bb7fa | ||
|
|
6ee0919ead | ||
|
|
13eb1a58e0 | ||
|
|
08f9c09484 | ||
|
|
3f3abe26ee | ||
|
|
e31c5b6d42 | ||
|
|
7bf77fa7cb | ||
|
|
a2662f1366 | ||
|
|
116cf6aa21 | ||
|
|
79bb6d0c55 | ||
|
|
c157ecec38 | ||
|
|
28893cf637 | ||
|
|
73ffc99f37 | ||
|
|
35802c7c62 | ||
|
|
bc44fdf89c | ||
|
|
030c3dd587 | ||
|
|
498be2759e | ||
|
|
649d00733a | ||
|
|
8a7a4f3033 | ||
|
|
b6ce81dc79 | ||
|
|
490c417cee | ||
|
|
2dfeb3924b | ||
|
|
bff96f6ae2 | ||
|
|
4801e26b2f | ||
|
|
027997f7b3 | ||
|
|
76aaefbde1 | ||
|
|
a01bc0f325 | ||
|
|
f6f4f5660e | ||
|
|
956925889a | ||
|
|
0bd49326d8 | ||
|
|
64c4e8eca8 | ||
|
|
55fc342f6d | ||
|
|
3ae0d96760 | ||
|
|
fc1e55cc19 | ||
|
|
58fc810a43 | ||
|
|
3c477e9783 | ||
|
|
f2247ed86d | ||
|
|
0d9a9d1ed8 | ||
|
|
ca5a579e22 | ||
|
|
53d97a593f | ||
|
|
3641a8f389 | ||
|
|
1dea1bd09d | ||
|
|
3db265d57f | ||
|
|
38a32824ea | ||
|
|
b0c94f7ff2 | ||
|
|
2415aa3d2b | ||
|
|
1af1844f37 | ||
|
|
75bb247c2b | ||
|
|
5a1b517733 | ||
|
|
0fbc8754f7 | ||
|
|
aaca60b676 | ||
|
|
a929d1e3a4 | ||
|
|
4a7fa7c396 | ||
|
|
45674806d7 | ||
|
|
8905bc7dfe | ||
|
|
dd6aec1ba9 | ||
|
|
aca5fa4d0a | ||
|
|
0d1f45b8a2 | ||
|
|
3f9ae57144 | ||
|
|
45c2f1007b | ||
|
|
83adaa3074 | ||
|
|
1951b279c0 | ||
|
|
50406ad683 | ||
|
|
95740eaa01 | ||
|
|
08f2c1c735 | ||
|
|
32459ec405 | ||
|
|
c7ac0cbce1 | ||
|
|
711fb5754e | ||
|
|
24fffd8bfa | ||
|
|
52556aff68 | ||
|
|
c76959ff25 | ||
|
|
a141871e60 | ||
|
|
5e639a7c2d | ||
|
|
f9654d17d5 | ||
|
|
c89f3ba3da | ||
|
|
1dc5ae444e | ||
|
|
5db67e47a8 | ||
|
|
3877e5feb9 | ||
|
|
7456186265 | ||
|
|
952f7feb49 | ||
|
|
3983d750f3 | ||
|
|
b4f2a8ab5b | ||
|
|
e8a4554789 | ||
|
|
de3bc9cb4d | ||
|
|
e09f028cd1 | ||
|
|
ea9f597685 | ||
|
|
e538fb9514 | ||
|
|
32fed3d048 | ||
|
|
c6b6d1aa40 | ||
|
|
b9615ccbad | ||
|
|
0032a91f66 | ||
|
|
88f6076422 | ||
|
|
8e11ecf799 | ||
|
|
d04c5c9846 | ||
|
|
e46a66904f | ||
|
|
d038cb4a2d | ||
|
|
78e68c7fea | ||
|
|
700f345576 | ||
|
|
22067198ab | ||
|
|
21d6dfd790 | ||
|
|
e663ce038a | ||
|
|
367b7778fd | ||
|
|
989e0a5326 | ||
|
|
13940f0663 | ||
|
|
09e87f207c | ||
|
|
f9884b7782 | ||
|
|
cca5f81c65 | ||
|
|
c863428aa2 | ||
|
|
2104b853ef | ||
|
|
00a56f6aaa | ||
|
|
67e7a75b9c | ||
|
|
52375df783 | ||
|
|
77fad0e7c6 | ||
|
|
802341384e | ||
|
|
057ac4031e | ||
|
|
ea5d0999c4 | ||
|
|
7b21650003 | ||
|
|
1cfaddff9c | ||
|
|
7690d91771 | ||
|
|
6b7b66801b | ||
|
|
d671071758 | ||
|
|
e9c1c620eb | ||
|
|
b17a8757fb | ||
|
|
2b04cef50b | ||
|
|
16060108b4 | ||
|
|
0fb2f7fdd6 | ||
|
|
c002cf759e | ||
|
|
5fee0b05e0 | ||
|
|
48bc176bdf | ||
|
|
ec56dc73c4 | ||
|
|
83e9d681d7 | ||
|
|
e0b9e80698 | ||
|
|
449c02c42a | ||
|
|
ce89ff4833 | ||
|
|
1961e22cbd | ||
|
|
b95ee7cf4d | ||
|
|
14b2291590 | ||
|
|
954ec05d41 | ||
|
|
4c25b40b2d | ||
|
|
1792e3d884 | ||
|
|
3c35a6400b | ||
|
|
2a4ba31a9e | ||
|
|
99a8afd6c3 | ||
|
|
cc9ff4c8db | ||
|
|
ed4189cc5e | ||
|
|
6d66e60830 | ||
|
|
930f0a7f82 | ||
|
|
89347e2a56 | ||
|
|
09a08cd074 | ||
|
|
c344474d9e | ||
|
|
d13d169768 | ||
|
|
e3929973d1 | ||
|
|
9b428150a4 | ||
|
|
8ba565042f | ||
|
|
6df03294bd | ||
|
|
4a40ac3ced | ||
|
|
2f34d65164 | ||
|
|
7c68d6a710 | ||
|
|
55fb7a8f27 | ||
|
|
bb9fb1187e | ||
|
|
f0170c576c | ||
|
|
02caaafc52 | ||
|
|
168b17ac4e | ||
|
|
e3214c874e | ||
|
|
91444cce9b | ||
|
|
c952aaadec | ||
|
|
77ab5c1690 | ||
|
|
37857935f3 | ||
|
|
e5c7650fc8 | ||
|
|
f42e0fe507 | ||
|
|
58dbd78997 | ||
|
|
04b995e77f | ||
|
|
82aa3fb7ee | ||
|
|
ec0e88ad2e | ||
|
|
cac28fde4d | ||
|
|
e65a8c9017 | ||
|
|
4a26f80fb1 | ||
|
|
bca8a72a0f | ||
|
|
13be8160d5 | ||
|
|
8efea6c4e2 | ||
|
|
36bb5e099e | ||
|
|
b86a24d97a | ||
|
|
3febe80329 | ||
|
|
b11a2103a4 | ||
|
|
4a9d881e47 | ||
|
|
8e5c6af612 | ||
|
|
ca588a4b54 | ||
|
|
258032acb0 | ||
|
|
2c9c9c4356 | ||
|
|
88d4e8a4d9 | ||
|
|
4e2a8735bc | ||
|
|
b72d24f6d0 | ||
|
|
1f2fb2ccce | ||
|
|
5e2b423ef8 | ||
|
|
79e2001708 | ||
|
|
2ea96518ad | ||
|
|
2666001226 | ||
|
|
1bd30bc121 | ||
|
|
d6d8144476 | ||
|
|
68e7818c1d | ||
|
|
e0e5f4514b | ||
|
|
c2942fa269 | ||
|
|
6f31240c74 | ||
|
|
5124265142 | ||
|
|
f0bd5eaca9 | ||
|
|
8589f85907 | ||
|
|
66d739b3b1 | ||
|
|
2206e34ab6 | ||
|
|
cc1ca4a1bb | ||
|
|
c8b1ef8ea6 | ||
|
|
f836faad48 | ||
|
|
7e27b916aa | ||
|
|
ae9963c1f7 | ||
|
|
2b4f4c8d4d | ||
|
|
68104fe578 | ||
|
|
6f2c46a747 | ||
|
|
e7e1f1b667 | ||
|
|
7dc20616a7 | ||
|
|
b5b323000a | ||
|
|
19d204c7a8 | ||
|
|
672c279b0a | ||
|
|
669e31ea64 | ||
|
|
4ab72170e3 | ||
|
|
9ca39696b3 | ||
|
|
7c36279308 | ||
|
|
89e5b3a979 | ||
|
|
58782c9a89 | ||
|
|
c3824dcbd2 | ||
|
|
cead2bc00f | ||
|
|
0d5380466a | ||
|
|
58f6acbc48 | ||
|
|
db73fb4e37 | ||
|
|
a3d4c3d948 | ||
|
|
f9dc5c6f80 | ||
|
|
96b763fb89 | ||
|
|
98a75305e7 | ||
|
|
0d8739327e | ||
|
|
7dc4d9bb71 | ||
|
|
c814221185 | ||
|
|
f2f6b57944 | ||
|
|
f50c2af914 | ||
|
|
5b7442436d | ||
|
|
9cebee7347 | ||
|
|
f311359373 | ||
|
|
31b9970c04 | ||
|
|
baf0edc51e | ||
|
|
69a2a76b2e | ||
|
|
7fd894f33d | ||
|
|
3f366844e2 | ||
|
|
4156b6f932 | ||
|
|
ed801f7082 | ||
|
|
cedd10e067 | ||
|
|
86b756b6bc | ||
|
|
259a12acf9 | ||
|
|
902f86d363 | ||
|
|
d5fc75e345 | ||
|
|
5650bd829a | ||
|
|
f9ad46180e | ||
|
|
6e534d7543 | ||
|
|
9aa56a8e42 | ||
|
|
4ed67832d2 | ||
|
|
3409de9ed1 | ||
|
|
e73a44d15b | ||
|
|
5cf30f05fd | ||
|
|
63117e87e7 | ||
|
|
a8e520e7f0 | ||
|
|
e14bb16d60 | ||
|
|
b704d58f0f | ||
|
|
246e9a7764 | ||
|
|
1c7b63f583 | ||
|
|
e6476319bc | ||
|
|
c954934b33 | ||
|
|
a72a6886f6 | ||
|
|
a601ba542a | ||
|
|
f22f470ba4 | ||
|
|
3faeeac126 | ||
|
|
40be1d416b | ||
|
|
9ef82458b7 | ||
|
|
da99e363cd | ||
|
|
3110bb48fd | ||
|
|
08e2f2f273 | ||
|
|
6636d12215 | ||
|
|
5f243d173a | ||
|
|
5ea8d3a4c6 | ||
|
|
dad3fd22ec | ||
|
|
f381402e8f | ||
|
|
f010235ff2 | ||
|
|
09f6dcbde1 | ||
|
|
224b8e3492 | ||
|
|
28612bbc8a | ||
|
|
dfe4f7bb56 | ||
|
|
3f28c0dd22 | ||
|
|
959dd1a95a | ||
|
|
d5297cd1df | ||
|
|
95404f43b7 | ||
|
|
9838af5e56 | ||
|
|
dab656f8ca | ||
|
|
802eeb1e6b | ||
|
|
98dd28818a | ||
|
|
f705373cc4 | ||
|
|
5bc60a8dab | ||
|
|
1c8f783767 | ||
|
|
24d3075c85 | ||
|
|
c461adff11 | ||
|
|
b172f7207d | ||
|
|
ef31fffa40 | ||
|
|
426632a307 | ||
|
|
2c708b6df6 | ||
|
|
b555d0ccc8 | ||
|
|
e1724c8a12 | ||
|
|
cd35574c0b | ||
|
|
d016cbcffc | ||
|
|
7e4b672888 | ||
|
|
06d9abf6f6 | ||
|
|
a1a6dae5c6 | ||
|
|
c5f40f1be9 | ||
|
|
4358cb5e57 | ||
|
|
a591bf05e0 | ||
|
|
568b7538ad | ||
|
|
bbd7765432 | ||
|
|
558024785f | ||
|
|
76043cc5b6 | ||
|
|
583fca1814 | ||
|
|
806201b8cb | ||
|
|
24c89aeb64 | ||
|
|
8f0d88d9e0 | ||
|
|
e92d8dc127 | ||
|
|
8a99e8f796 | ||
|
|
c0246c6834 | ||
|
|
42ed92f346 | ||
|
|
df0bd90fae | ||
|
|
3453f94b43 | ||
|
|
ca537e266d | ||
|
|
8f3c948614 | ||
|
|
059743ad34 | ||
|
|
33ccf68e17 | ||
|
|
b6f22712f9 | ||
|
|
9e413b3961 | ||
|
|
5dd318a481 | ||
|
|
1783257c0e | ||
|
|
966852c212 | ||
|
|
07d6c1e514 | ||
|
|
09be915ea5 | ||
|
|
abd3c95dfa | ||
|
|
3fa61ac23c | ||
|
|
00bbc26698 | ||
|
|
3004f54b5d | ||
|
|
ef5b888779 | ||
|
|
835e33bf09 | ||
|
|
8ba3a05648 | ||
|
|
73ae73d4c3 | ||
|
|
76175a711a | ||
|
|
574acd6744 | ||
|
|
9e0e052bba | ||
|
|
ffafb30b9b | ||
|
|
9c4e402e6c | ||
|
|
f6f9dbb5e3 | ||
|
|
7beced1e68 | ||
|
|
c8921b77f0 | ||
|
|
482ce0a265 | ||
|
|
c570405d02 | ||
|
|
35ed465e10 | ||
|
|
747bc4c088 | ||
|
|
7c156927e4 | ||
|
|
13f0c77fb6 | ||
|
|
82d9f77eb3 | ||
|
|
151699ceb9 | ||
|
|
cc5d5bdd01 | ||
|
|
f43dadc068 | ||
|
|
b24694bea3 | ||
|
|
f4b31aa771 | ||
|
|
3cd5d00b0b | ||
|
|
7112584139 | ||
|
|
639b9c8f56 | ||
|
|
791ebc6406 | ||
|
|
059134d3d0 | ||
|
|
3b0616c150 | ||
|
|
ad5c4fdca2 | ||
|
|
b219b8fb65 | ||
|
|
73a170a388 | ||
|
|
e55e9c73b9 | ||
|
|
71ed5d3427 | ||
|
|
8b7bdfe0c7 | ||
|
|
ed6018fad1 | ||
|
|
b5f4be36d4 | ||
|
|
5c0a46b006 | ||
|
|
54ed46447a | ||
|
|
84fe3c2f40 | ||
|
|
e30329a18c | ||
|
|
22b40aab5d | ||
|
|
c3c8b1eb70 | ||
|
|
ce82ee8d0d | ||
|
|
04f0967b18 | ||
|
|
f8ef2e33de | ||
|
|
20a8ac7c98 | ||
|
|
37e9236bde | ||
|
|
fc11ff13c5 | ||
|
|
c92586ee64 | ||
|
|
38e2308d94 | ||
|
|
03044250a3 | ||
|
|
ebe7ec40e4 | ||
|
|
412626052b | ||
|
|
2a0af154c6 | ||
|
|
87debbd26b | ||
|
|
10e88fccd6 | ||
|
|
cc781c0082 | ||
|
|
5198ee997c | ||
|
|
4843d6cdc1 | ||
|
|
8ca0b2d9c3 | ||
|
|
cf589cb792 | ||
|
|
eda05e0157 | ||
|
|
4ae8a0308e | ||
|
|
88c6ce1c7e | ||
|
|
b714e2fca0 | ||
|
|
ce3a931c59 | ||
|
|
69f5d90e91 | ||
|
|
1a6ab68752 | ||
|
|
f13001e53a | ||
|
|
824b537db0 | ||
|
|
42e4bc3105 | ||
|
|
ebcedcffec | ||
|
|
b8cc84261f | ||
|
|
4e2475502a | ||
|
|
7dd9bcce2b | ||
|
|
620628dca3 | ||
|
|
5ee7d52e8c | ||
|
|
26004b1f73 | ||
|
|
59c42bc1a3 | ||
|
|
c8a7d539f3 | ||
|
|
98c86bcc0f | ||
|
|
1b138e14c3 | ||
|
|
a54e52d34b | ||
|
|
67218fb61d | ||
|
|
4eb0085243 | ||
|
|
38a8fcb69a | ||
|
|
64592d734c | ||
|
|
abddc8a864 | ||
|
|
09b45aca4f | ||
|
|
f624211620 | ||
|
|
c19cde53d6 | ||
|
|
cf8694fcd1 | ||
|
|
28d640afb4 | ||
|
|
7b432078a0 | ||
|
|
19703fed31 | ||
|
|
b01e127cc3 | ||
|
|
f0c2d81352 | ||
|
|
67896adefc | ||
|
|
d1b29b6e61 | ||
|
|
ef93aad659 | ||
|
|
616b433409 | ||
|
|
515d96eef0 | ||
|
|
85963cc58d | ||
|
|
e15611a0ea | ||
|
|
f89a2ab91d | ||
|
|
18d3fb75ac | ||
|
|
f5bc2b08f9 | ||
|
|
80e5916394 | ||
|
|
48c47f9911 | ||
|
|
c3f5849f11 | ||
|
|
c73346eda0 | ||
|
|
d976455a12 | ||
|
|
f10f80f655 | ||
|
|
b680d03da2 | ||
|
|
9590fec3fc | ||
|
|
e9fc5537e4 | ||
|
|
8ae47cb2f8 | ||
|
|
2fefaf1b5b | ||
|
|
5554cd7567 | ||
|
|
442ea7595a | ||
|
|
0e6d83c085 | ||
|
|
3199da4b3d | ||
|
|
23c2a2bc22 | ||
|
|
554bec4f1e | ||
|
|
4d6e6a85e0 | ||
|
|
fe887b7b5e | ||
|
|
d954b97aac | ||
|
|
374e673107 | ||
|
|
68c0d3fbe5 | ||
|
|
335f0451e0 | ||
|
|
b5a071f51e | ||
|
|
805458a763 | ||
|
|
4f8e711a5f | ||
|
|
06397e9e11 | ||
|
|
94be762f68 | ||
|
|
ccb48d0711 | ||
|
|
b6014ca992 | ||
|
|
02c5f2e3cf | ||
|
|
84a228fc1a | ||
|
|
356fddc182 | ||
|
|
c4853b9aa7 | ||
|
|
d6cb460186 | ||
|
|
a0f9b81113 | ||
|
|
15b9497a72 | ||
|
|
93be112a94 | ||
|
|
5a9a5df448 | ||
|
|
75383fe477 | ||
|
|
3f0109e810 | ||
|
|
c21db857fb | ||
|
|
e3a0a480ed | ||
|
|
3917d8c4a3 | ||
|
|
6a3b8a668b | ||
|
|
edfa9783c3 | ||
|
|
b4c9c1df9a | ||
|
|
85353d9f90 | ||
|
|
905f0316b6 | ||
|
|
b3961609e1 | ||
|
|
8ad4a1aa96 | ||
|
|
3fd1f4f5ec | ||
|
|
31ca78c17b | ||
|
|
a628c0fc03 | ||
|
|
3cc10c77f9 | ||
|
|
a727f8a62c | ||
|
|
f324286911 | ||
|
|
7f7d06be65 | ||
|
|
c9f1278651 | ||
|
|
dd69966def | ||
|
|
2f2a039e3c | ||
|
|
80db4cd645 | ||
|
|
ed9338608e | ||
|
|
fef0c0e490 | ||
|
|
e0e703dc72 | ||
|
|
8aca32179e | ||
|
|
ab823795cf | ||
|
|
d90ab03bbe | ||
|
|
bf053e0018 | ||
|
|
46c911bb2a | ||
|
|
2bc8df9188 | ||
|
|
6367b18f4f | ||
|
|
a307bfa1ec | ||
|
|
cfb3caf504 | ||
|
|
daf524238b | ||
|
|
5435604448 | ||
|
|
64500f1f73 | ||
|
|
d5ee0b567a | ||
|
|
5ec4988efe | ||
|
|
e41072dcdf | ||
|
|
126ddda45d | ||
|
|
bec460f77e | ||
|
|
fd56d4c63c | ||
|
|
d9cefb84b8 | ||
|
|
3013114c1f | ||
|
|
095dfdb24f | ||
|
|
a032586aaf | ||
|
|
d16c141821 | ||
|
|
dcf75f0bcd | ||
|
|
282a9e3097 | ||
|
|
0e4b121209 | ||
|
|
3e469c5a44 | ||
|
|
e760a4140b | ||
|
|
2048975e92 | ||
|
|
cf5312ed2e | ||
|
|
8f33ab3487 | ||
|
|
d4d6a5f96d | ||
|
|
a55992276d | ||
|
|
6405c35bc0 | ||
|
|
bd3d7ec9c9 | ||
|
|
cd51ad7508 | ||
|
|
bd5bb2a8a9 | ||
|
|
9bc6366993 | ||
|
|
b6c7737f73 | ||
|
|
6a9d738348 | ||
|
|
6e2f84673e | ||
|
|
63201e992c | ||
|
|
f512047ac6 | ||
|
|
c4293ecd95 | ||
|
|
f047c6da29 | ||
|
|
59874d16b9 | ||
|
|
026236edc9 | ||
|
|
e91e68c48c | ||
|
|
d840cb3be7 | ||
|
|
2b1d2222cc | ||
|
|
b6fdbbd2bb | ||
|
|
0c54c1540c | ||
|
|
fae5f6d64a | ||
|
|
17c4c44426 | ||
|
|
67835e3421 | ||
|
|
832bd6187c | ||
|
|
6507d2be71 | ||
|
|
21785a9322 | ||
|
|
fc387d0ddf | ||
|
|
27e749abb8 | ||
|
|
83143ee570 | ||
|
|
d4252f3056 | ||
|
|
1dd97ebd49 | ||
|
|
bfa15d4719 | ||
|
|
4b22f29826 | ||
|
|
0f14bd3358 | ||
|
|
f7d5d65c0c | ||
|
|
86fbfd3c50 | ||
|
|
cfde9d5a61 | ||
|
|
a9932823c4 | ||
|
|
67b99a88fc | ||
|
|
455ec8c3d8 | ||
|
|
42ece21fa1 | ||
|
|
663ecab51e | ||
|
|
dbae718f4d | ||
|
|
52e76c5496 | ||
|
|
71b6cd630a | ||
|
|
2c36fe3d45 | ||
|
|
28b14777c2 | ||
|
|
4e97a5351a | ||
|
|
2ab5009de7 | ||
|
|
65668e91fe | ||
|
|
d6098dbedf | ||
|
|
fe6b94e539 | ||
|
|
00e60ff855 | ||
|
|
c4882707e9 | ||
|
|
402528d97e | ||
|
|
4e25e335a9 | ||
|
|
972981a579 | ||
|
|
ce5abf8299 | ||
|
|
8bb31861a7 | ||
|
|
7d2e40ee47 | ||
|
|
f0de48a3c4 | ||
|
|
799e66c078 | ||
|
|
55903472aa | ||
|
|
22c7d3b07f | ||
|
|
485737f6b0 | ||
|
|
b756150f76 | ||
|
|
40108d63bb | ||
|
|
8f3103e329 | ||
|
|
9bf1478587 | ||
|
|
91cf377948 | ||
|
|
ff4c8a5d23 | ||
|
|
439f76e48a | ||
|
|
720d4223e2 | ||
|
|
4518e2d23c | ||
|
|
c9a24c38d4 | ||
|
|
b54d17467c | ||
|
|
5c93de8911 | ||
|
|
8323649cf0 | ||
|
|
75fbf00eb7 | ||
|
|
820908ab05 | ||
|
|
4fe168b03b | ||
|
|
4825bcec8d | ||
|
|
0f99f22fa5 | ||
|
|
051d1670b7 | ||
|
|
ef0e1f9089 | ||
|
|
974c177237 | ||
|
|
75de7b119d | ||
|
|
b088763272 | ||
|
|
c45bf4e368 | ||
|
|
af5054e7fb | ||
|
|
9529d71732 | ||
|
|
74ea237c2a | ||
|
|
c8f1d99fe6 | ||
|
|
a22631915b | ||
|
|
05df419a46 | ||
|
|
0f1b57fee4 | ||
|
|
e69a9f17b8 | ||
|
|
de3467a6d5 | ||
|
|
7214f6d49a | ||
|
|
c1bc6d4733 | ||
|
|
3d4f97364c | ||
|
|
2c74240bcb | ||
|
|
90da40fde0 | ||
|
|
fe985e219a | ||
|
|
f5355422ee | ||
|
|
aae7960f25 | ||
|
|
b99f878c98 | ||
|
|
4b61f38cba | ||
|
|
4085a98bcd | ||
|
|
524e3b098f | ||
|
|
dc8595ad68 | ||
|
|
a76067abfa | ||
|
|
6ef10a54bd | ||
|
|
c9e80eaa03 | ||
|
|
e47dcf734a | ||
|
|
4dfcc31506 | ||
|
|
14e5c96f73 | ||
|
|
53e569d285 | ||
|
|
71381dce95 | ||
|
|
0cc63b810c | ||
|
|
7724b0d5bb | ||
|
|
6206eca725 | ||
|
|
069e2c69b2 | ||
|
|
0e902d73a8 | ||
|
|
be72274ca5 | ||
|
|
ab0dacc65a | ||
|
|
cfa0a063ce |
No files matched your search
+102
-94
@@ -1,20 +1,24 @@
|
||||
FROM ubuntu:21.10
|
||||
LABEL maintainer="sgr"
|
||||
FROM ubuntu:24.04
|
||||
LABEL maintainer="wekan"
|
||||
LABEL org.opencontainers.image.ref.name="ubuntu"
|
||||
LABEL org.opencontainers.image.version="24.04"
|
||||
LABEL org.opencontainers.image.source="https://github.com/wekan/wekan"
|
||||
|
||||
# 2022-04-25:
|
||||
# - gyp does not yet work with Ubuntu 22.04 ubuntu:rolling,
|
||||
# so changing to 21.10. https://github.com/wekan/wekan/issues/4488
|
||||
|
||||
ENV BUILD_DEPS="gnupg gosu libarchive-tools wget curl bzip2 g++ build-essential python3 git ca-certificates iproute2"
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV BUILD_DEPS="apt-utils gnupg gosu wget bzip2 g++ iproute2 apt-transport-https libarchive-tools"
|
||||
ENV DEV_DEPS="curl python3 ca-certificates build-essential git"
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v14.21.4 \
|
||||
METEOR_RELEASE=METEOR@2.13 \
|
||||
METEOR_RELEASE=METEOR@2.14 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=latest \
|
||||
NPM_VERSION=6.14.17 \
|
||||
FIBERS_VERSION=4.0.1 \
|
||||
ARCHITECTURE=linux-x64 \
|
||||
SRC_PATH=./ \
|
||||
@@ -28,15 +32,14 @@ ENV \
|
||||
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \
|
||||
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \
|
||||
ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS=90 \
|
||||
RICHER_CARD_COMMENT_EDITOR=false \
|
||||
CARD_OPENED_WEBHOOK_ENABLED=false \
|
||||
ATTACHMENTS_STORE_PATH="" \
|
||||
ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM="" \
|
||||
ATTACHMENTS_UPLOAD_MIME_TYPES="" \
|
||||
ATTACHMENTS_UPLOAD_MAX_SIZE=0 \
|
||||
AVATARS_UPLOAD_EXTERNAL_PROGRAM="" \
|
||||
AVATARS_UPLOAD_MIME_TYPES="" \
|
||||
AVATARS_UPLOAD_MAX_SIZE=0 \
|
||||
RICHER_CARD_COMMENT_EDITOR=false \
|
||||
CARD_OPENED_WEBHOOK_ENABLED=false \
|
||||
MAX_IMAGE_PIXEL="" \
|
||||
IMAGE_COMPRESS_RATIO="" \
|
||||
NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE="" \
|
||||
@@ -48,12 +51,15 @@ ENV \
|
||||
MATOMO_SITE_ID="" \
|
||||
MATOMO_DO_NOT_TRACK=true \
|
||||
MATOMO_WITH_USERNAME=false \
|
||||
METRICS_ALLOWED_IP_ADDRESSES="" \
|
||||
BROWSER_POLICY_ENABLED=true \
|
||||
TRUSTED_URL="" \
|
||||
WEBHOOKS_ATTRIBUTES="" \
|
||||
OAUTH2_ENABLED=false \
|
||||
OIDC_REDIRECTION_ENABLED=false \
|
||||
OAUTH2_CA_CERT="" \
|
||||
OAUTH2_ADFS_ENABLED=false \
|
||||
OAUTH2_B2C_ENABLED=false \
|
||||
OAUTH2_LOGIN_STYLE=redirect \
|
||||
OAUTH2_CLIENT_ID="" \
|
||||
OAUTH2_SECRET="" \
|
||||
@@ -70,6 +76,9 @@ ENV \
|
||||
LDAP_ENABLE=false \
|
||||
LDAP_PORT=389 \
|
||||
LDAP_HOST="" \
|
||||
LDAP_AD_SIMPLE_AUTH="" \
|
||||
LDAP_USER_AUTHENTICATION=false \
|
||||
LDAP_USER_AUTHENTICATION_FIELD=uid \
|
||||
LDAP_BASEDN="" \
|
||||
LDAP_LOGIN_FALLBACK=false \
|
||||
LDAP_RECONNECT=true \
|
||||
@@ -87,8 +96,6 @@ ENV \
|
||||
LDAP_ENCRYPTION=false \
|
||||
LDAP_CA_CERT="" \
|
||||
LDAP_REJECT_UNAUTHORIZED=false \
|
||||
LDAP_USER_AUTHENTICATION=false \
|
||||
LDAP_USER_AUTHENTICATION_FIELD=uid \
|
||||
LDAP_USER_SEARCH_FILTER="" \
|
||||
LDAP_USER_SEARCH_SCOPE="" \
|
||||
LDAP_USER_SEARCH_FIELD="" \
|
||||
@@ -143,69 +150,32 @@ ENV \
|
||||
SAML_IDENTIFIER_FORMAT="" \
|
||||
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
|
||||
SAML_ATTRIBUTES="" \
|
||||
DEFAULT_WAIT_SPINNER="" \
|
||||
ORACLE_OIM_ENABLED=false \
|
||||
WAIT_SPINNER="" \
|
||||
WRITABLE_PATH=/data \
|
||||
S3=""
|
||||
# \
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap:
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
|
||||
#---------------------------------------------
|
||||
# == at docker-compose.yml: AUTOLOGIN WITH OIDC/OAUTH2 ====
|
||||
# https://github.com/wekan/wekan/wiki/autologin
|
||||
#- OIDC_REDIRECTION_ENABLED=true
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# Install OS
|
||||
RUN set -o xtrace \
|
||||
&& useradd --user-group -m --system --home-dir /home/wekan wekan \
|
||||
&& apt-get update \
|
||||
&& apt-get install --assume-yes --no-install-recommends apt-utils apt-transport-https ca-certificates 2>&1 \
|
||||
&& apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
|
||||
|
||||
# OLD:
|
||||
# && curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-$ARCHITECTURE.tar.xz" \
|
||||
# && curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/SHASUMS256.txt.asc" \
|
||||
|
||||
# Install NodeJS
|
||||
RUN set -o xtrace \
|
||||
&& cd /tmp \
|
||||
&& curl -fsSLO --compressed "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.xz" \
|
||||
&& curl -fsSLO --compressed "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt" \
|
||||
&& grep " node-$NODE_VERSION-$ARCHITECTURE.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
|
||||
&& tar -xJf "node-$NODE_VERSION-$ARCHITECTURE.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
|
||||
&& rm "node-$NODE_VERSION-$ARCHITECTURE.tar.xz" SHASUMS256.txt \
|
||||
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
|
||||
&& mkdir -p /usr/local/lib/node_modules/fibers/.node-gyp /root/.node-gyp/${NODE_VERSION} /home/wekan/.config \
|
||||
&& npm install -g npm@${NPM_VERSION} \
|
||||
&& chown wekan:wekan --recursive /home/wekan/.config
|
||||
|
||||
ENV DEBIAN_FRONTEND=dialog
|
||||
|
||||
USER wekan
|
||||
|
||||
# Install Meteor
|
||||
RUN set -o xtrace \
|
||||
&& cd /home/wekan \
|
||||
&& curl https://install.meteor.com/?release=$METEOR_VERSION --output /home/wekan/install-meteor.sh \
|
||||
# Replace tar with bsdtar in the install script; https://github.com/jshimko/meteor-launchpad/issues/39
|
||||
&& sed --in-place "s/tar -xzf.*/bsdtar -xf \"\$TARBALL_FILE\" -C \"\$INSTALL_TMPDIR\"/g" /home/wekan/install-meteor.sh \
|
||||
&& sed --in-place 's/VERBOSITY="--silent"/VERBOSITY="--progress-bar"/' /home/wekan/install-meteor.sh \
|
||||
&& printf "\n[-] Installing Meteor $METEOR_VERSION...\n\n" \
|
||||
&& sh /home/wekan/install-meteor.sh
|
||||
|
||||
ENV PATH=$PATH:/home/wekan/.meteor/
|
||||
|
||||
USER root
|
||||
|
||||
RUN echo "export PATH=$PATH" >> /etc/environment
|
||||
|
||||
USER wekan
|
||||
RUN <<EOR
|
||||
echo "export PATH=$PATH" >> /etc/environment
|
||||
EOR
|
||||
|
||||
# Copy source dir
|
||||
RUN set -o xtrace \
|
||||
&& mkdir -p /home/wekan/app/.meteor \
|
||||
&& mkdir -p /home/wekan/app/packages
|
||||
RUN <<EOR
|
||||
set -o xtrace
|
||||
|
||||
mkdir -p /home/wekan/app/.meteor
|
||||
mkdir -p /home/wekan/app/packages
|
||||
EOR
|
||||
|
||||
COPY \
|
||||
.meteor/.finished-upgraders \
|
||||
@@ -230,44 +200,83 @@ COPY \
|
||||
packages \
|
||||
/home/wekan/app/packages/
|
||||
|
||||
USER root
|
||||
# Install OS
|
||||
RUN <<EOR
|
||||
set -o xtrace
|
||||
|
||||
RUN set -o xtrace \
|
||||
&& chown -R wekan:wekan /home/wekan/app /home/wekan/.meteor
|
||||
# Add non-root user wekan
|
||||
useradd --user-group --system --home-dir /home/wekan wekan
|
||||
# OS dependencies
|
||||
apt-get update --assume-yes
|
||||
apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS} ${DEV_DEPS}
|
||||
|
||||
USER wekan
|
||||
# Meteor installer doesn't work with the default tar binary, so using bsdtar while installing.
|
||||
# https://github.com/coreos/bugs/issues/1095#issuecomment-350574389
|
||||
cp $(which tar) $(which tar)~
|
||||
ln -sf $(which bsdtar) $(which tar)
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
# Build app
|
||||
cd /home/wekan/app && \
|
||||
/home/wekan/.meteor/meteor add standard-minifier-js && \
|
||||
/home/wekan/.meteor/meteor npm install && \
|
||||
/home/wekan/.meteor/meteor build --directory /home/wekan/app_build
|
||||
# Install NodeJS
|
||||
cd /tmp
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
chmod u+w package.json npm-shrinkwrap.json && \
|
||||
npm install && \
|
||||
cd node_modules/fibers && \
|
||||
node build.js
|
||||
# Download nodejs
|
||||
wget "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz"
|
||||
wget "https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt"
|
||||
|
||||
# Verify nodejs authenticity
|
||||
grep "node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz" "SHASUMS256.txt" | shasum -a 256 -c -
|
||||
rm -f "SHASUMS256.txt"
|
||||
|
||||
# Install Node
|
||||
tar xzf "node-$NODE_VERSION-$ARCHITECTURE.tar.gz" -C /usr/local --strip-components=1 --no-same-owner
|
||||
rm "node-$NODE_VERSION-$ARCHITECTURE.tar.gz" "SHASUMS256.txt"
|
||||
ln -s "/usr/local/bin/node" "/usr/local/bin/nodejs"
|
||||
mkdir -p "/opt/nodejs/lib/node_modules/fibers/.node-gyp" "/root/.node-gyp/${NODE_VERSION} /home/wekan/.config"
|
||||
|
||||
# Install node dependencies
|
||||
npm install -g npm@${NPM_VERSION}
|
||||
chown --recursive wekan:wekan /home/wekan/.config
|
||||
|
||||
# Install Meteor
|
||||
cd /home/wekan
|
||||
chown --recursive wekan:wekan /home/wekan
|
||||
echo "Starting meteor ${METEOR_RELEASE} installation... \n"
|
||||
gosu wekan:wekan curl https://install.meteor.com/ | /bin/sh
|
||||
mv /root/.meteor /home/wekan/
|
||||
chown --recursive wekan:wekan /home/wekan/.meteor
|
||||
|
||||
# sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js
|
||||
cd /home/wekan/.meteor
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor -- help
|
||||
|
||||
# Build app (Development)
|
||||
cd /home/wekan/app
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor add standard-minifier-js
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor npm install
|
||||
|
||||
# Put back the original tar
|
||||
mv $(which tar)~ $(which tar)
|
||||
|
||||
USER root
|
||||
# Cleanup
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
apt-get clean -y && \
|
||||
apt-get autoremove -y && \
|
||||
rm -Rf /tmp/* && \
|
||||
rm -Rf /home/wekan/app_build && \
|
||||
rm -Rf /var/cache/apt /var/lib/apt/lists && \
|
||||
rm -Rf /var/lib/apt/lists/*
|
||||
apt-get remove --purge --assume-yes ${BUILD_DEPS}
|
||||
apt-get install --assume-yes --no-install-recommends build-essential
|
||||
apt-get autoremove --assume-yes
|
||||
apt-get clean --assume-yes
|
||||
rm -Rf /tmp/*
|
||||
rm -Rf /var/lib/apt/lists/*
|
||||
rm -Rf /var/cache/apt
|
||||
rm -Rf /var/lib/apt/lists
|
||||
rm -Rf /home/wekan/app_build
|
||||
|
||||
mkdir /data
|
||||
chown wekan --recursive /data
|
||||
EOR
|
||||
|
||||
USER wekan
|
||||
|
||||
ENV PORT=3000
|
||||
EXPOSE $PORT
|
||||
|
||||
STOPSIGNAL SIGKILL
|
||||
WORKDIR /home/wekan/app
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
@@ -277,7 +286,6 @@ WORKDIR /home/wekan/app
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------------
|
||||
#TODO:
|
||||
#CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 /build/main.js"]
|
||||
#
|
||||
|
||||
CMD ["/home/wekan/.meteor/meteor", "run", "--verbose", "--settings", "settings.json"]
|
||||
+34
-14
@@ -1,18 +1,26 @@
|
||||
## Issue
|
||||
<!--
|
||||
**[PLEASE UPGRADE](https://github.com/wekan/wekan/wiki/Backup)** to the newest WeKan ® before reporting an issue.
|
||||
|
||||
Please report these issues elsewhere:
|
||||
|
||||
- SECURITY ISSUES, PGP EMAIL: https://github.com/wekan/wekan/blob/main/SECURITY.md
|
||||
- UCS: https://github.com/wekan/univention/issues
|
||||
|
||||
If WeKan Snap is slow, try this: https://github.com/wekan/wekan/wiki/Cron
|
||||
|
||||
**[PLEASE UPGRADE](https://github.com/wekan/wekan/wiki/Backup)** to the newest
|
||||
WeKan ® before reporting an issue, if possible.
|
||||
|
||||
Please search existing Open and Closed issues, most questions have already been answered.
|
||||
|
||||
If you can not login for any reason: https://github.com/wekan/wekan/wiki/Forgot-Password
|
||||
Email settings, only SMTP MAIL_URL and MAIL_FROM are in use: https://github.com/wekan/wekan/wiki/Troubleshooting-Mail
|
||||
|
||||
The following types of issues should be reported separately:
|
||||
- SECURITY ISSUES: https://github.com/wekan/wekan/blob/main/SECURITY.md
|
||||
- UCS: https://github.com/wekan/univention/issues
|
||||
-->
|
||||
Email settings, only SMTP MAIL_URL and MAIL_FROM are in use:
|
||||
https://github.com/wekan/wekan/wiki/Troubleshooting-Mail
|
||||
|
||||
### Server Setup Information
|
||||
<!-- Please anonymize info, and do not any of your Wekan board URLs, passwords, API tokens etc to this public issue. -->
|
||||
|
||||
Please anonymize info, and do not any of your Wekan board URLs, passwords,
|
||||
API tokens etc to this public issue.
|
||||
|
||||
* Did you test in newest Wekan?:
|
||||
* Did you configure root-url correctly so Wekan cards open correctly (see https://github.com/wekan/wekan/wiki/Settings)?
|
||||
* Operating System:
|
||||
@@ -23,13 +31,25 @@ The following types of issues should be reported separately:
|
||||
* What webbrowser version are you using (Wekan should work on all modern browsers that support Javascript)?
|
||||
|
||||
### Problem description
|
||||
<!-- Add a recorded animated gif (e.g. with https://github.com/phw/peek) about how it works currently, and screenshot mockups how it should work. -->
|
||||
|
||||
Add a recorded animated gif (e.g. with https://github.com/phw/peek) about
|
||||
how it works currently, and screenshot mockups how it should work.
|
||||
|
||||
|
||||
#### Reproduction Steps
|
||||
|
||||
#### Logs
|
||||
<!-- Check Right Click>Inspect>Console in you browser - generally Chrome shows more detailed info than Firefox. -->
|
||||
|
||||
<!-- Please anonymize logs.
|
||||
|
||||
#### Logs
|
||||
|
||||
Check Right Click / Inspect / Console in you browser - generally Chromium
|
||||
based browsers show more detailed info than Firefox based browsers.
|
||||
|
||||
Please anonymize logs.
|
||||
|
||||
Snap: sudo snap logs wekan.wekan
|
||||
|
||||
Docker: sudo docker logs wekan-app
|
||||
If logs are very long, attach them in .zip file -->
|
||||
|
||||
If logs are very long, attach them in .zip file
|
||||
|
||||
@@ -11,4 +11,4 @@ jobs:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v4
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v3
|
||||
uses: actions/dependency-review-action@v4
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -48,14 +48,14 @@ jobs:
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934
|
||||
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09
|
||||
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
@@ -25,6 +25,6 @@ jobs:
|
||||
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||
|
||||
- name: Run chart-releaser
|
||||
uses: helm/chart-releaser-action@v1.5.0
|
||||
uses: helm/chart-releaser-action@v1.6.0
|
||||
env:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
# CACHING
|
||||
- name: Install Meteor
|
||||
id: cache-meteor-install
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.meteor
|
||||
key: v1-meteor-${{ hashFiles('.meteor/versions') }}
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
|
||||
- name: Cache NPM dependencies
|
||||
id: cache-meteor-npm
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: v1-npm-${{ hashFiles('package-lock.json') }}
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
|
||||
- name: Cache Meteor build
|
||||
id: cache-meteor-build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.meteor/local/resolver-result-cache.json
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
v1-meteor_build_cache-
|
||||
|
||||
- name: Setup meteor
|
||||
uses: meteorengineer/setup-meteor@v1
|
||||
uses: meteorengineer/setup-meteor@v2
|
||||
with:
|
||||
meteor-release: '2.2'
|
||||
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
run: sh ./test-wekan.sh -cv
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-folder
|
||||
path: .coverage/
|
||||
@@ -150,14 +150,14 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download coverage
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: coverage-folder
|
||||
path: .coverage/
|
||||
|
||||
|
||||
- name: Coverage Report
|
||||
uses: VeryGoodOpenSource/very_good_coverage@v2.1.0
|
||||
uses: VeryGoodOpenSource/very_good_coverage@v3.0.0
|
||||
with:
|
||||
path: ".coverage/lcov.info"
|
||||
min_coverage: 1 # TODO add tests and increase to 95!
|
||||
+6
-6
@@ -6,7 +6,7 @@
|
||||
meteor-base@1.5.1
|
||||
|
||||
# Build system
|
||||
ecmascript@0.16.7
|
||||
ecmascript@0.16.8
|
||||
standard-minifier-js@2.8.1
|
||||
mquandalle:jade
|
||||
coffeescript@2.4.1!
|
||||
@@ -20,11 +20,11 @@ cottz:publish-relations
|
||||
dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
easy:search
|
||||
mongo@1.16.7
|
||||
mongo@1.16.8
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
accounts-password@2.3.4
|
||||
accounts-password@2.4.0
|
||||
useraccounts:core
|
||||
useraccounts:flow-routing
|
||||
useraccounts:unstyled
|
||||
@@ -42,7 +42,7 @@ jquery@3.0.0!
|
||||
random@1.2.1
|
||||
reactive-dict@1.3.1
|
||||
session@1.2.1
|
||||
tracker@1.3.2
|
||||
tracker@1.3.3
|
||||
underscore@1.0.13
|
||||
arillo:flow-router-helpers
|
||||
audit-argument-checks@1.0.7
|
||||
@@ -83,14 +83,14 @@ matb33:collection-hooks
|
||||
simple:json-routes
|
||||
kadira:flow-router
|
||||
spacebars
|
||||
service-configuration@1.3.1
|
||||
service-configuration@1.3.2
|
||||
communitypackages:picker
|
||||
minifier-css@1.6.4
|
||||
blaze
|
||||
kadira:blaze-layout
|
||||
peerlibrary:blaze-components
|
||||
ejson@1.1.3
|
||||
logging@1.3.2
|
||||
logging@1.3.3
|
||||
wekan-fullcalendar
|
||||
momentjs:moment@2.29.3
|
||||
wekan-fontawesome
|
||||
+1
-1
@@ -1 +1 @@
|
||||
METEOR@2.13.3
|
||||
METEOR@2.14
|
||||
+24
-23
@@ -1,6 +1,6 @@
|
||||
accounts-base@2.2.8
|
||||
accounts-oauth@1.4.2
|
||||
accounts-password@2.3.4
|
||||
accounts-base@2.2.10
|
||||
accounts-oauth@1.4.3
|
||||
accounts-password@2.4.0
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
@@ -10,13 +10,13 @@ allow-deny@1.1.1
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
audit-argument-checks@1.0.7
|
||||
autoupdate@1.8.0
|
||||
babel-compiler@7.10.4
|
||||
babel-compiler@7.10.5
|
||||
babel-runtime@1.5.1
|
||||
base64@1.0.12
|
||||
binary-heap@1.0.11
|
||||
blaze@2.7.1
|
||||
blaze-tools@1.1.3
|
||||
boilerplate-generator@1.7.1
|
||||
boilerplate-generator@1.7.2
|
||||
caching-compiler@1.2.2
|
||||
caching-html-compiler@1.2.1
|
||||
callback-hook@1.5.1
|
||||
@@ -29,22 +29,22 @@ dburles:collection-helpers@1.1.0
|
||||
ddp@1.4.1
|
||||
ddp-client@2.6.1
|
||||
ddp-common@1.4.0
|
||||
ddp-rate-limiter@1.2.0
|
||||
ddp-server@2.6.2
|
||||
ddp-rate-limiter@1.2.1
|
||||
ddp-server@2.7.0
|
||||
deps@1.0.12
|
||||
diff-sequence@1.1.2
|
||||
dynamic-import@0.7.3
|
||||
easy:search@2.2.1
|
||||
easysearch:components@2.2.2
|
||||
easysearch:core@2.2.2
|
||||
ecmascript@0.16.7
|
||||
ecmascript@0.16.8
|
||||
ecmascript-runtime@0.8.1
|
||||
ecmascript-runtime-client@0.12.1
|
||||
ecmascript-runtime-server@0.11.0
|
||||
ejson@1.1.3
|
||||
email@2.2.5
|
||||
es5-shim@4.8.0
|
||||
fetch@0.1.3
|
||||
fetch@0.1.4
|
||||
geojson-utils@1.0.11
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.1.3
|
||||
@@ -60,10 +60,10 @@ kadira:flow-router@2.12.1
|
||||
konecty:mongo-counter@0.0.5_3
|
||||
lmieulet:meteor-coverage@1.1.4
|
||||
localstorage@1.2.0
|
||||
logging@1.3.2
|
||||
logging@1.3.3
|
||||
matb33:collection-hooks@1.3.0
|
||||
mdg:validation-error@0.5.1
|
||||
meteor@1.11.3
|
||||
meteor@1.11.5
|
||||
meteor-autosize@5.0.1
|
||||
meteor-base@1.5.1
|
||||
meteorhacks:aggregate@1.3.0
|
||||
@@ -77,11 +77,11 @@ minifier-css@1.6.4
|
||||
minifier-js@2.7.5
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.9.3
|
||||
modern-browsers@0.1.9
|
||||
modules@0.19.0
|
||||
modern-browsers@0.1.10
|
||||
modules@0.20.0
|
||||
modules-runtime@0.13.1
|
||||
momentjs:moment@2.29.3
|
||||
mongo@1.16.7
|
||||
mongo@1.16.8
|
||||
mongo-decimal@0.1.3
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.8
|
||||
@@ -94,8 +94,8 @@ mquandalle:jade-compiler@0.4.5
|
||||
mquandalle:jquery-textcomplete@0.8.0_1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
msavin:usercache@1.8.0
|
||||
npm-mongo@4.16.0
|
||||
oauth@2.2.0
|
||||
npm-mongo@4.17.2
|
||||
oauth@2.2.1
|
||||
oauth2@1.3.2
|
||||
observe-sequence@1.0.21
|
||||
ongoworks:speakingurl@1.1.0
|
||||
@@ -111,19 +111,19 @@ peerlibrary:blaze-components@0.23.0
|
||||
peerlibrary:computed-field@0.10.0
|
||||
peerlibrary:data-lookup@0.3.0
|
||||
peerlibrary:reactive-field@0.6.0
|
||||
percolate:synced-cron@1.3.2
|
||||
percolate:synced-cron@1.5.2
|
||||
promise@0.12.2
|
||||
raix:eventemitter@0.1.3
|
||||
raix:handlebar-helpers@0.2.5
|
||||
random@1.2.1
|
||||
rate-limit@1.1.1
|
||||
react-fast-refresh@0.2.7
|
||||
react-fast-refresh@0.2.8
|
||||
reactive-dict@1.3.1
|
||||
reactive-var@1.0.12
|
||||
reload@1.3.1
|
||||
retry@1.1.0
|
||||
routepolicy@1.1.1
|
||||
service-configuration@1.3.1
|
||||
service-configuration@1.3.3
|
||||
session@1.2.1
|
||||
sha@1.0.9
|
||||
shell-server@0.5.0
|
||||
@@ -132,7 +132,7 @@ simple:json-routes@2.3.1
|
||||
simple:rest-accounts-password@1.2.2
|
||||
simple:rest-bearer-token-parser@1.1.1
|
||||
simple:rest-json-error-handler@1.1.1
|
||||
socket-stream-client@0.5.1
|
||||
socket-stream-client@0.5.2
|
||||
spacebars@1.4.1
|
||||
spacebars-compiler@1.3.1
|
||||
standard-minifier-js@2.8.1
|
||||
@@ -141,14 +141,15 @@ templating@1.4.1
|
||||
templating-compiler@1.4.1
|
||||
templating-runtime@1.5.0
|
||||
templating-tools@1.2.2
|
||||
tracker@1.3.2
|
||||
tracker@1.3.3
|
||||
typescript@4.9.5
|
||||
ui@1.0.13
|
||||
underscore@1.0.13
|
||||
url@1.3.2
|
||||
useraccounts:core@1.16.2
|
||||
useraccounts:flow-routing@1.15.0
|
||||
useraccounts:unstyled@1.14.2
|
||||
webapp@1.13.5
|
||||
webapp@1.13.6
|
||||
webapp-hashing@1.1.1
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-lockout@1.0.0
|
||||
@@ -162,4 +163,4 @@ wekan-markdown@1.0.9
|
||||
wekan-oidc@1.0.12
|
||||
yasaricli:slugify@0.0.7
|
||||
zimme:active-route@2.3.2
|
||||
zodern:types@1.0.9
|
||||
zodern:types@1.0.10
|
||||
Vendored
+48
-36
@@ -1,45 +1,57 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Meteor: Chrome",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Meteor: Node",
|
||||
"runtimeExecutable": "meteor",
|
||||
"runtimeArgs": [
|
||||
"--port=4000",
|
||||
"--exclude-archs=web.browser.legacy,web.cordova",
|
||||
"--raw-logs"
|
||||
],
|
||||
"env": {
|
||||
"WRITABLE_PATH": "/tmp/uploads",
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Meteor: Node",
|
||||
"runtimeExecutable": "/home/wekan/.meteor/meteor",
|
||||
"runtimeArgs": ["run", "--inspect-brk=9229"],
|
||||
"outputCapture": "std",
|
||||
"port": 9229,
|
||||
"timeout": 60000
|
||||
"outputCapture": "std",
|
||||
"restart": true,
|
||||
"timeout": 60000
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Meteor: Chrome",
|
||||
"url": "http://localhost:4000",
|
||||
"sourceMapPathOverrides": {
|
||||
"meteor://💻app/*": "${workspaceFolder}/*"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Test: Node",
|
||||
"runtimeExecutable": "meteor",
|
||||
"runtimeArgs": [
|
||||
"test",
|
||||
"--inspect-brk=9229",
|
||||
"--port=4040",
|
||||
"--exclude-archs=web.browser.legacy,web.cordova",
|
||||
"--driver-package=meteortesting:mocha",
|
||||
"--settings=settings.json"
|
||||
],
|
||||
"outputCapture": "std",
|
||||
"port": 9229,
|
||||
"timeout": 60000
|
||||
}
|
||||
"userDataDir": "${env:HOME}/.vscode/chrome"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Test: Node",
|
||||
"runtimeExecutable": "meteor",
|
||||
"runtimeArgs": [
|
||||
"test",
|
||||
"--port=4040",
|
||||
"--exclude-archs=web.browser.legacy,web.cordova",
|
||||
"--driver-package=meteortesting:mocha",
|
||||
"--settings=settings.json",
|
||||
"--raw-logs"
|
||||
],
|
||||
"env": {
|
||||
"TEST_WATCH": "1"
|
||||
},
|
||||
"outputCapture": "std",
|
||||
"timeout": 60000
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Meteor: All",
|
||||
"configurations": ["Meteor: Node", "Meteor: Chrome"]
|
||||
}
|
||||
{
|
||||
"name": "Meteor: All",
|
||||
"configurations": ["Meteor: Node", "Meteor: Chrome"]
|
||||
}
|
||||
]
|
||||
}
|
||||
+1151
-14
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,22 @@
|
||||
# Code of Conduct
|
||||
|
||||
For all code at WeKan GitHub Organization https://github.com/wekan
|
||||
|
||||
- All code in pull requests need to have permission already to add it to WeKan with MIT license, and will become MIT license.
|
||||
- All code xet7 add is MIT license.
|
||||
- For any dependencies, permissive licenses like https://copyfree.org are preferred
|
||||
- For anything currently that is non-permissive (like GPL, AGPL, SSPL), those will be replaced with permissive-licensed alternatives
|
||||
|
||||
# Reporting about violations or something else
|
||||
|
||||
## Private reports
|
||||
|
||||
- Email support@wekan.team
|
||||
- Security issues: [SECURITY.md](SECURITY.md)
|
||||
- License violations
|
||||
- Anything private, sensitive or negative
|
||||
|
||||
## Public
|
||||
|
||||
- Feature Requests and Bug Reports https://github.com/wekan/wekan/issues
|
||||
- Anything happy, positive, encouraging, helping, at friendly WeKan Global FOSS Community
|
||||
+28
-14
@@ -1,3 +1,23 @@
|
||||
## About money
|
||||
|
||||
Not paid:
|
||||
|
||||
- Money is not paid for these, everyone uses their own time at their own cost:
|
||||
- Security reports, see [SECURITY.md](SECURITY.md)
|
||||
- Pull requests
|
||||
- xet7 checking pull requests
|
||||
- Public Community Support
|
||||
- https://github.com/wekan/wekan/issues
|
||||
|
||||
Paid by customers of WeKan Team:
|
||||
|
||||
- Commercial Support at https://wekan.team/commercial-support/
|
||||
- Support
|
||||
- Private Chat
|
||||
- Features
|
||||
- Fixes
|
||||
- Hosting
|
||||
|
||||
## Contributing Security related
|
||||
|
||||
For responsible security disclosure, please follow this process:
|
||||
@@ -7,13 +27,9 @@ CVE Hall of Fame is at https://wekan.github.io/hall-of-fame/
|
||||
|
||||
## Contributing to Documentation Wiki
|
||||
|
||||
Please clone wiki:
|
||||
```
|
||||
git clone https://github.com/wekan/wekan.wiki
|
||||
```
|
||||
Edit .md files, and add changed files in .zip attachment
|
||||
directly to comment of new issue at
|
||||
https://github.com/wekan/wekan/issues
|
||||
Fork WeKan repo https://github.com/wekan/wekan ,
|
||||
edit `docs` directory content at GitHub web interface,
|
||||
and click send PR.
|
||||
|
||||
## Contributing code
|
||||
|
||||
@@ -42,7 +58,7 @@ About 300 persons have contributed to WeKan, stats at:
|
||||
|
||||
https://www.openhub.net/p/wekan
|
||||
|
||||
WeKan maintainer xet7 checks PR for typos etc before accepting to WeKan,
|
||||
WeKan maintainer xet7 reviews PR for typos etc before accepting to WeKan,
|
||||
so that WeKan code will still work OK.
|
||||
|
||||
## Contributing translations
|
||||
@@ -62,12 +78,10 @@ new release.
|
||||
|
||||
## About WeKan Organization https://github.com/wekan
|
||||
|
||||
xet7 rarely adds any new members to GitHub Organization,
|
||||
because xet7 prefers to check PRs.
|
||||
Only xet7 has write access to WeKan Organization.
|
||||
|
||||
For some repos (other than https://github.com/wekan/wekan ),
|
||||
some contributors have direct commit access.
|
||||
xet7 reviews all PRs before merging.
|
||||
|
||||
Some contributors are mentioned at this outdated page:
|
||||
There has been over 300 contributors to WeKan, newest stats at:
|
||||
|
||||
https://github.com/wekan/wekan/wiki/Team
|
||||
https://www.openhub.net/p/wekan
|
||||
+99
-141
@@ -1,35 +1,76 @@
|
||||
FROM --platform=linux/amd64 ubuntu:23.04 as wekan
|
||||
FROM scratch AS downloads
|
||||
|
||||
# Download NodeJS bundle
|
||||
ADD https://static.meteor.com/dev-bundle-node-os/v14.21.4/node-v14.21.4-linux-x64.tar.gz node.tar.gz
|
||||
|
||||
# Download Meteor bundle
|
||||
ADD https://static.meteor.com/packages-bootstrap/2.16/meteor-bootstrap-os.linux.x86_64.tar.gz meteor.tar.gz
|
||||
|
||||
FROM ubuntu:24.04 AS build
|
||||
|
||||
# Install node from official archive
|
||||
RUN --mount=type=bind,from=downloads,source=node.tar.gz,target=node.tar.gz \
|
||||
tar xzf node.tar.gz --strip-components=1 --keep-old-files --no-same-owner
|
||||
|
||||
# Install meteor from official archive
|
||||
RUN --mount=type=bind,from=downloads,source=meteor.tar.gz,target=meteor.tar.gz \
|
||||
tar xzf meteor.tar.gz -C $HOME --no-same-owner
|
||||
|
||||
# Update the npm version
|
||||
RUN npm install -g npm@6.14.17
|
||||
|
||||
# Install several build dependencies
|
||||
RUN apt update && \
|
||||
apt install --yes --no-install-suggests --no-install-recommends ssh git g++ curl ca-certificates && \
|
||||
rm -r /var/lib/apt/lists
|
||||
|
||||
# Change to temporary working directory
|
||||
WORKDIR /workdir
|
||||
|
||||
# Copy package.json and package-lock.json to install application dependencies
|
||||
COPY package.json .
|
||||
COPY package-lock.json .
|
||||
|
||||
# Install build dependencies
|
||||
RUN $HOME/.meteor/meteor npm install --production
|
||||
|
||||
# Copy meteor application configurations
|
||||
COPY .meteor .meteor
|
||||
|
||||
# Copy application sources
|
||||
COPY packages packages
|
||||
COPY imports imports
|
||||
COPY config config
|
||||
COPY models models
|
||||
COPY public public
|
||||
COPY server server
|
||||
COPY client client
|
||||
|
||||
# Build the application
|
||||
RUN $HOME/.meteor/meteor build --directory /build --allow-superuser
|
||||
|
||||
# Enter server bundle directory
|
||||
WORKDIR /build/bundle/programs/server
|
||||
|
||||
# Install server dependencies
|
||||
RUN $HOME/.meteor/meteor npm install --production
|
||||
|
||||
# Enter installation directory of fibers
|
||||
WORKDIR /build/bundle/programs/server/node_modules/fibers
|
||||
|
||||
# Build fibers integrations
|
||||
RUN node build.js
|
||||
|
||||
FROM ubuntu:24.04 AS wekan
|
||||
|
||||
# Add metadata to image
|
||||
LABEL maintainer="wekan"
|
||||
LABEL org.opencontainers.image.ref.name="ubuntu"
|
||||
LABEL org.opencontainers.image.version="24.04"
|
||||
LABEL org.opencontainers.image.source="https://github.com/wekan/wekan"
|
||||
|
||||
# 2022-09-04:
|
||||
# - above "--platform=linux/amd64 ubuntu:22.04 as wekan" is needed to build Dockerfile
|
||||
# correctly on Mac M1 etc, to not get this error:
|
||||
# https://stackoverflow.com/questions/71040681/qemu-x86-64-could-not-open-lib64-ld-linux-x86-64-so-2-no-such-file-or-direc
|
||||
|
||||
# 2022-04-25:
|
||||
# - gyp does not yet work with Ubuntu 22.04 ubuntu:rolling,
|
||||
# so changing to 21.10. https://github.com/wekan/wekan/issues/4488
|
||||
|
||||
# 2021-09-18:
|
||||
# - Above Ubuntu base image copied from Docker Hub ubuntu:hirsute-20210825
|
||||
# to Quay to avoid Docker Hub rate limits.
|
||||
|
||||
# Set the environment variables (defaults where required)
|
||||
# DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
# ENV BUILD_DEPS="paxctl"
|
||||
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=v14.21.4 \
|
||||
METEOR_RELEASE=METEOR@2.13.3 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
NPM_VERSION=9.8.1 \
|
||||
FIBERS_VERSION=4.0.1 \
|
||||
ARCHITECTURE=linux-x64 \
|
||||
SRC_PATH=./ \
|
||||
WITH_API=true \
|
||||
# Set default environment variables
|
||||
ENV WITH_API=true \
|
||||
RESULTS_PER_PAGE="" \
|
||||
DEFAULT_BOARD_ID="" \
|
||||
ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \
|
||||
@@ -66,6 +107,7 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
OIDC_REDIRECTION_ENABLED=false \
|
||||
OAUTH2_CA_CERT="" \
|
||||
OAUTH2_ADFS_ENABLED=false \
|
||||
OAUTH2_B2C_ENABLED=false \
|
||||
OAUTH2_LOGIN_STYLE=redirect \
|
||||
OAUTH2_CLIENT_ID="" \
|
||||
OAUTH2_SECRET="" \
|
||||
@@ -161,120 +203,36 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
WRITABLE_PATH=/data \
|
||||
S3=""
|
||||
|
||||
# NODE_OPTIONS="--max_old_space_size=4096" \
|
||||
|
||||
#---------------------------------------------
|
||||
# == at docker-compose.yml: AUTOLOGIN WITH OIDC/OAUTH2 ====
|
||||
# https://github.com/wekan/wekan/wiki/autologin
|
||||
#- OIDC_REDIRECTION_ENABLED=true
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# Copy the app to the image
|
||||
COPY ${SRC_PATH} /home/wekan/app
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
# Add non-root user wekan
|
||||
useradd --user-group --system --home-dir /home/wekan wekan && \
|
||||
\
|
||||
# OS dependencies
|
||||
apt-get update -y && apt-get install -y --no-install-recommends ${BUILD_DEPS} && \
|
||||
\
|
||||
# Meteor installer doesn't work with the default tar binary, so using bsdtar while installing.
|
||||
# https://github.com/coreos/bugs/issues/1095#issuecomment-350574389
|
||||
cp $(which tar) $(which tar)~ && \
|
||||
ln -sf $(which bsdtar) $(which tar) && \
|
||||
\
|
||||
# Download nodejs
|
||||
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
wget https://github.com/wekan/node-v14-esm/releases/download/${NODE_VERSION}/SHASUMS256.txt && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
#wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \
|
||||
#---------------------------------------------------------------------------------------------
|
||||
\
|
||||
# Verify nodejs authenticity
|
||||
grep node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz SHASUMS256.txt | shasum -a 256 -c - && \
|
||||
rm -f SHASUMS256.txt && \
|
||||
#grep ${NODE_VERSION}-${ARCHITECTURE}.tar.gz SHASUMS256.txt.asc | shasum -a 256 -c - && \
|
||||
#rm -f SHASUMS256.txt.asc && \
|
||||
\
|
||||
# Install Node
|
||||
tar xvzf node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
rm node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \
|
||||
mv node-${NODE_VERSION}-${ARCHITECTURE} /opt/nodejs && \
|
||||
ln -s /opt/nodejs/bin/node /usr/bin/node && \
|
||||
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
|
||||
mkdir -p /opt/nodejs/lib/node_modules/fibers/.node-gyp /root/.node-gyp/${NODE_VERSION} /home/wekan/.config && \
|
||||
chown wekan --recursive /home/wekan/.config && \
|
||||
\
|
||||
#DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
|
||||
#paxctl -mC `which node` && \
|
||||
\
|
||||
# Install Node dependencies. Python path for node-gyp.
|
||||
#npm install -g npm@${NPM_VERSION} && \
|
||||
\
|
||||
# Change user to wekan and install meteor
|
||||
cd /home/wekan/ && \
|
||||
chown wekan --recursive /home/wekan && \
|
||||
echo "Starting meteor ${METEOR_RELEASE} installation... \n" && \
|
||||
gosu wekan:wekan curl https://install.meteor.com/ | /bin/sh && \
|
||||
mv /root/.meteor /home/wekan/ && \
|
||||
chown wekan --recursive /home/wekan/.meteor && \
|
||||
\
|
||||
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \
|
||||
cd /home/wekan/.meteor && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor -- help; \
|
||||
\
|
||||
# Build app
|
||||
cd /home/wekan/app && \
|
||||
mkdir -p /home/wekan/.npm && \
|
||||
chown wekan --recursive /home/wekan/.npm /home/wekan/.config /home/wekan/.meteor && \
|
||||
chmod u+w *.json && \
|
||||
gosu wekan:wekan meteor npm install && \
|
||||
gosu wekan:wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build && \
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
chmod u+w *.json && \
|
||||
gosu wekan:wekan meteor npm install && \
|
||||
cd node_modules/fibers && \
|
||||
node build.js && \
|
||||
cd ../.. && \
|
||||
# Remove legacy webbroser bundle, so that Wekan works also at Android Firefox, iOS Safari, etc.
|
||||
rm -rf /home/wekan/app_build/bundle/programs/web.browser.legacy && \
|
||||
mv /home/wekan/app_build/bundle /build && \
|
||||
\
|
||||
# Put back the original tar
|
||||
mv $(which tar)~ $(which tar) && \
|
||||
\
|
||||
# Cleanup
|
||||
apt-get remove --purge -y ${BUILD_DEPS} && \
|
||||
apt-get autoremove -y && \
|
||||
npm uninstall -g api2html &&\
|
||||
rm -R /tmp/* && \
|
||||
rm -R /var/lib/apt/lists/* && \
|
||||
rm -R /home/wekan/.meteor && \
|
||||
rm -R /home/wekan/app && \
|
||||
rm -R /home/wekan/app_build && \
|
||||
# Install node and application sources from build stage
|
||||
RUN --mount=type=bind,from=downloads,source=node.tar.gz,target=node.tar.gz \
|
||||
--mount=type=bind,from=build,source=/build,target=/build \
|
||||
# Install NodeJS
|
||||
tar xzf node.tar.gz --strip-components=1 --keep-old-files --no-same-owner && \
|
||||
# Add the wekan user
|
||||
useradd --uid 999 --user-group --system wekan && \
|
||||
# Copy wekan bundle
|
||||
cp -r /build/bundle /wekan && \
|
||||
# Remove the web.browser.legacy platform
|
||||
rm -r /wekan/programs/web.browser.legacy && \
|
||||
# Change ownership of wekan directory
|
||||
chown wekan:wekan -R /wekan && \
|
||||
# Create the data directory
|
||||
mkdir /data && \
|
||||
chown wekan --recursive /data
|
||||
#cat /home/wekan/python/esprima-python/files.txt | xargs rm -R && \
|
||||
#rm -R /home/wekan/python
|
||||
#rm /home/wekan/install_meteor.sh
|
||||
# Change ownership of data directory
|
||||
chown wekan:root -R /data
|
||||
|
||||
ENV PORT=8080
|
||||
EXPOSE $PORT
|
||||
# Change to the wekan user
|
||||
USER wekan
|
||||
|
||||
STOPSIGNAL SIGKILL
|
||||
# Set the port to listen on
|
||||
ENV PORT=8080
|
||||
EXPOSE ${PORT}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap:
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------------
|
||||
#
|
||||
# CMD ["node", "/build/main.js"]
|
||||
# Define the stop signal for node
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
#CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 /build/main.js"]
|
||||
CMD ["bash", "-c", "ulimit -s 65500; exec node /build/main.js"]
|
||||
# Declare a volume on /data
|
||||
VOLUME /data
|
||||
|
||||
# Set startup command
|
||||
CMD ["node", "/wekan/main.js"]
|
||||
@@ -88,5 +88,6 @@ RUN \
|
||||
EXPOSE $PORT
|
||||
USER wekan
|
||||
|
||||
# CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 --max-old-space-size=8192 /home/wekan/bundle/main.js"]
|
||||
CMD ["bash", "-c", "ulimit -s 65500; exec node /home/wekan/bundle/main.js"]
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Future
|
||||
|
||||
## Moved Import/Export/Sync issues to Big Picture Roadmap wiki page
|
||||
|
||||
This change is limited to only Import/Export/Sync issues, while those are In Progress of being fixed.
|
||||
|
||||
2023-11-21 xet7 closed 261 issues that are linked at https://github.com/wekan/wekan/wiki/Sync ,
|
||||
that is Roadmap of Import/Export/Sync in WeKan. It means, that those issues progress will be
|
||||
updated at that wiki page, when xet7 and other WeKan contributors fix those.
|
||||
Many of those issues are In Progress of being fixed and added.
|
||||
|
||||
## Platform Updates
|
||||
|
||||
Issues related to platforms are being closed, because only list of working platforms is mentioned now
|
||||
at WeKan website https://wekan.github.io Install section and at [ChangeLog](https://github.com/wekan/wekan/blob/main/CHANGELOG.md)
|
||||
where is this new text:
|
||||
|
||||
> Newest WeKan at amd64 platforms: Linux bundle, Snap Candidate, Docker, Kubernetes. Fixing other platforms In Progress.
|
||||
|
||||
Platform support changes often, because:
|
||||
|
||||
- There are many dependencies, that update or break or change often
|
||||
- Node.js segfaults at some CPU/OS
|
||||
- Some platforms have build errors
|
||||
|
||||
Roadmap is to update all existing platforms, and add more platforms.
|
||||
|
||||
Upcoming platform upgrades:
|
||||
|
||||
- Fix migrations, so that newest WeKan can be released to Snap Stable. (Currently newest is at Snap Candidate).
|
||||
|
||||
## WeKan features
|
||||
|
||||
Most Meteor WeKan features are listed here:
|
||||
|
||||
https://github.com/wekan/wekan/wiki/Deep-Dive-Into-WeKan
|
||||
|
||||
Remaining features and all changes are listed here:
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/CHANGELOG.md
|
||||
@@ -0,0 +1,10 @@
|
||||
# Governance
|
||||
|
||||
Anyone can send pull request to https://github.com/wekan/wekan/wiki/pulls ,
|
||||
if there is permission to add code to WeKan with MIT license.
|
||||
|
||||
As maintainer, xet7 checks all pull requests and merges them.
|
||||
|
||||
Only xet7 has write access to repo https://github.com/wekan/wekan
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2019 The Wekan Team
|
||||
Copyright (c) 2014-2024 The Wekan Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -2,23 +2,34 @@
|
||||
|
||||
# WeKan ® - Open Source kanban
|
||||
|
||||
## Downloads
|
||||
|
||||
https://wekan.github.io / Install WeKan ® Server
|
||||
|
||||
## Docker Containers
|
||||
|
||||
- [GitHub](https://github.com/wekan/wekan/pkgs/container/wekan)
|
||||
- [Quay](https://quay.io/repository/wekan/wekan)
|
||||
- [Docker Hub](https://hub.docker.com/r/wekanteam/wekan)
|
||||
|
||||
Other platforms and compatible software versions at https://wekan.github.io Download section.
|
||||
docker-compose.yml at https://github.com/wekan/wekan/blob/main/docker-compose.yml
|
||||
|
||||
## Standards
|
||||
|
||||
- [WeKan and Standard for Public Code](https://wekan.github.io/standard-for-public-code/) assessment was made at 2023-11.
|
||||
Currently Wekan meets 8 out of 16 criteria out of the box.
|
||||
Some others could be met with small changes.
|
||||
|
||||
## Code stats
|
||||
|
||||
- [CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4619)
|
||||
- [Code Climate](https://codeclimate.com/github/wekan/wekan)
|
||||
- [Open Hub](https://www.openhub.net/p/wekan)
|
||||
- [OSS Insight](https://ossinsight.io/analyze/wekan/wekan)
|
||||
- [CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4619)
|
||||
|
||||
## [Translate WeKan ® at Transifex](https://app.transifex.com/wekan/)
|
||||
|
||||
Translations to non-English languages are accepted only at [Transifex](https://app.transifex.com/wekan/) using webbrowser.
|
||||
Translations to non-English languages are accepted only at [Transifex](https://app.transifex.com/wekan/wekan) using webbrowser.
|
||||
New English strings of new features can be added as PRs to master branch file wekan/imports/i18n/data/en.i18n.json .
|
||||
|
||||
## [WeKan ® feature requests and bugs](https://github.com/wekan/wekan/issues)
|
||||
@@ -109,7 +120,7 @@ with [Meteor](https://www.meteor.com).
|
||||
|
||||
[platforms]: https://github.com/wekan/wekan/wiki/Platforms
|
||||
[dev_docs]: https://github.com/wekan/wekan/wiki/Developer-Documentation
|
||||
[screenshot_wekan]: https://wekan.github.io/wekan-markdown.png
|
||||
[screenshot_wekan]: https://wekan.github.io/wekan-dark-mode.png
|
||||
[features]: https://github.com/wekan/wekan/wiki/Features
|
||||
[roadmap_wekan]: https://boards.wekan.team/b/D2SzJKZDS4Z48yeQH/wekan-open-source-kanban-board-with-mit-license
|
||||
[wekan_issues]: https://github.com/wekan/wekan/issues
|
||||
|
||||
+126
-30
@@ -1,6 +1,7 @@
|
||||
About money, see [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
Security is very important to us. If you discover any issue regarding security, please disclose
|
||||
the information responsibly by sending an email to support (at) wekan.team using
|
||||
[this PGP public key](support-at-wekan.team_pgp-publickey.asc) and not by
|
||||
the information responsibly by sending an email to support@wekan.team and not by
|
||||
creating a GitHub issue. We will respond swiftly to fix verifiable security issues.
|
||||
|
||||
We thank you with a place at our hall of fame page, that is
|
||||
@@ -29,7 +30,7 @@ added to the Wekan Hall of Fame.
|
||||
## Which domains are in scope?
|
||||
|
||||
No public domains, because all those are donated to Wekan Open Source project,
|
||||
and we don't have any permissions to do security scans on those donated servers
|
||||
and we don't have any permissions to do security scans on those donated servers.
|
||||
|
||||
Please don't perform research that could impact other users. Secondly, please keep
|
||||
the reports short and succinct. If we fail to understand the logics of your bug, we will tell you.
|
||||
@@ -48,31 +49,132 @@ like Snap and Docker have their own specific sandboxing etc features.
|
||||
|
||||
Standalone Wekan by default does not load any files from Internet, like fonts, CSS, etc.
|
||||
This also means all Standalone Wekan functionality works in offline local networks.
|
||||
Wekan is used by companies that have [thousands of users](https://github.com/wekan/wekan/wiki/AWS) and at healthcare.
|
||||
WeKan is used at most countries of the world https://snapcraft.io/wekan
|
||||
and by by companies that have 30k users.
|
||||
|
||||
Wekan uses xss package for input fields like cards, as you can see from
|
||||
[package.json](https://github.com/wekan/wekan/blob/main/package.json). Other used versions can be seen from
|
||||
[Meteor versions file](https://github.com/wekan/wekan/blob/main/.meteor/versions).
|
||||
Forms can include markdown links, html, image tags etc like you see at https://wekan.github.io .
|
||||
It's possible to add attachments to cards, and markdown/html links to files.
|
||||
- Wekan private board attachments are not accessible without logging in.
|
||||
- There is feature to set board public, so that board is visible without logging in in readonly mode, with realtime updates.
|
||||
- Admin Panel has feature to disable all public boards, so all boards are private.
|
||||
|
||||
Wekan attachments are not accessible without logging in. Import from Trello works by copying
|
||||
Trello export JSON to Wekan Trello import page, and in Trello JSON file there is direct links to all publicly
|
||||
accessible Trello attachment files, that Standalone Wekan downloads directly to Wekan MongoDB database in
|
||||
[CollectionFS](https://github.com/wekan/wekan/pull/875) format. When Wekan board is exported in
|
||||
Wekan JSON format, all board attachments are included in Wekan JSON file as base64 encoded text.
|
||||
That Wekan JSON format file can be imported to Sandstorm Wekan with all the attachments, when we get
|
||||
latest Wekan version working on Sandstorm, only couple of bugs are left before that. In Sandstorm it's not
|
||||
possible yet to import from Trello with attachments, because Wekan does not implement Sandstorm-compatible
|
||||
access to outside of Wekan grain.
|
||||
## SSL/TLS
|
||||
|
||||
Standalone Wekan only has password auth currently, there is work in progress to add
|
||||
[oauth2](https://github.com/wekan/wekan/pull/1578), [Openid](https://github.com/wekan/wekan/issues/538),
|
||||
[LDAP](https://github.com/wekan/wekan/issues/119) etc. If you need more login security for Standalone Wekan now,
|
||||
it's possible add additional [Google Auth proxybouncer](https://github.com/wekan/wekan/wiki/Let's-Encrypt-and-Google-Auth) in front of password auth, and then use Google Authenticator for Google Auth. Standalone Wekan does have [brute force protection with eluck:accounts-lockout and browser-policy clickjacking protection](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v080-2018-04-04-wekan-release). You can also optionally use some [WAF](https://en.wikipedia.org/wiki/Web_application_firewall)
|
||||
like for example [AWS WAF](https://aws.amazon.com/waf/).
|
||||
- SSL/TLS encrypts traffic between webbrowser and webserver.
|
||||
- If you are thinking about TLS MITM, look at https://github.com/caddyserver/caddy/issues/2530
|
||||
- Let's Encrypt TLS requires publicly accessible webserver, that Let's Encrypt TLS validation servers check.
|
||||
- If firewall limits to only allowed IP addresses, you may need non-Let's Encrypt TLS cert.
|
||||
- For On Premise:
|
||||
- https://caddyserver.com/docs/automatic-https#local-https
|
||||
- https://github.com/wekan/wekan/wiki/Caddy-Webserver-Config
|
||||
- https://github.com/wekan/wekan/wiki/Azure
|
||||
- https://github.com/wekan/wekan/wiki/Traefik-and-self-signed-SSL-certs
|
||||
|
||||
[All Wekan Platforms](https://github.com/wekan/wekan/wiki/Platforms)
|
||||
## XSS
|
||||
|
||||
- Dompurify https://www.npmjs.com/package/dompurify
|
||||
- WeKan uses dompurify npm package to filter for XSS at fields like cards, as you can see from
|
||||
[package.json](https://github.com/wekan/wekan/blob/main/package.json). Other used versions can be seen from
|
||||
[Meteor versions file](https://github.com/wekan/wekan/blob/main/.meteor/versions).
|
||||
- Forms can include markdown links, html, image tags etc like you see at https://wekan.github.io .
|
||||
- It's possible to add attachments to cards, and markdown/html links to files.
|
||||
- Dompurify cleans up viewed code, so Javascript in input fields does not execute
|
||||
- https://wekan.github.io/hall-of-fame/fieldbleed/
|
||||
- Reaction in comment is now checked, that it does not have extra added code
|
||||
- https://wekan.github.io/hall-of-fame/reactionbleed/
|
||||
- https://github.com/wekan/wekan/blob/main/packages/markdown/src/template-integration.js#L76
|
||||
|
||||
## QA about PubSub
|
||||
|
||||
Q:
|
||||
|
||||
Hello,
|
||||
I have just seen the Meteor DevTools Evolved extension and was wondering if anyone had asked themselves the question of security.
|
||||
Insofar as all data is shown in the minimongo tab in plain text.
|
||||
How can data be hidden from this extension?
|
||||
|
||||
A:
|
||||
|
||||
## PubSub
|
||||
|
||||
- It is not security issue to show some text or image, that user has permission to see. It is a security issue, if browserside is some text or image that user should not see.
|
||||
- Meteor has browserside minimongo database, made with Javascript, updated with Publish/Subscribe, PubSub.
|
||||
- Publish/Subscribe means, that realtime web framework reads database changes stream, and then immediately updates webpage,
|
||||
like like dashboards, chat, kanban. That is the point in any realtime web framework in any programming language.
|
||||
- Yes, you should check with Meteor DevTools Evolved Chromium/Firefox extension that at minimongo is only text that user has permission to see.
|
||||
- Do checking as logged in user, and logged out user.
|
||||
- Check permissions and sanitize before allowing some change, because someone could modify content of input field,
|
||||
PubSub/websocket data (for example with Burp Suite Community Edition), etc.
|
||||
- If you have REST API, also check that only those that have login token, and have permission, can view or edit text
|
||||
- You should not include any data user is not allowed to see. Not to webpage text, not to websockets/PubSub, etc.
|
||||
- Minimongo should not have password hashes PubSub https://wekan.github.io/hall-of-fame/userbleed/
|
||||
- PubSub uses Websockets, so you need those to be enabled at webserver like Caddy/Nginx/Apache etc, examples of settings
|
||||
at right menu of https://github.com/wekan/wekan/wiki
|
||||
- Clientside https://github.com/wekan/wekan/tree/main/client/components subscribes to
|
||||
PubSub https://github.com/wekan/wekan/tree/main/server/publications or calls meteor methods at https://github.com/wekan/wekan/tree/main/models
|
||||
- For Admin:
|
||||
- You can have input field for password https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
|
||||
- You can save password to database https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
|
||||
- Check that only current user or Admin can change password https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
|
||||
- Note that currentUser uses code like Meteor.user() in .js file
|
||||
- Do not have password hashes in PubSub https://github.com/wekan/wekan/blob/main/server/publications/users.js
|
||||
- Only show Admin Panel to Admin https://github.com/wekan/wekan/blob/main/client/components/settings/settingBody.jade#L3
|
||||
- If there is a lot of data, use pagination https://github.com/wekan/wekan/blob/main/client/components/settings/peopleBody.js
|
||||
- Only have limited amount of data published in PubSub. Limit in MongoDB query in publications how much is published. Too much could make browser too slow.
|
||||
- Use Environment variables for any email etc passwords.
|
||||
- But what if you would like to remove minimongo? And only use Meteor methods for saving? In that case, you don't have realtime updates,
|
||||
and you need to write much more code to load and save data yourself, handle any multi user data saving conflicts yourself,
|
||||
and many Meteor Atmospherejs.com PubSub using packages would not work anymore https://github.com/wekan/we
|
||||
|
||||
## PubSub: Fix that user can not change to Admin
|
||||
|
||||
- With PubSub, there is checking, that someone modifying Websockets content, like permission isAdmin, can not change to Admin.
|
||||
- https://github.com/wekan/wekan/commit/cbad4cf5943d47b916f64b4582f8ca76a9dfd743
|
||||
- https://wekan.github.io/hall-of-fame/adminbleed/
|
||||
|
||||
## Permissions and Roles
|
||||
|
||||
- For any user permissions, it's best to use Meteor package package https://github.com/Meteor-Community-Packages/meteor-roles .
|
||||
- Currently WeKan has custom hardcoded permissions, WeKan does not yet use that meteor-roles package.
|
||||
- Using permissions at WeKan sidebar https://github.com/wekan/wekan/blob/main/client/components/sidebar/sidebar.js#L1854-L1875
|
||||
- List of roles https://github.com/wekan/wekan/wiki/REST-API-Role . Change at board or Admin Panel. Also Organizations/Teams.
|
||||
- Worker role: https://github.com/wekan/wekan/issues/2788
|
||||
- Not implemented yet: Granular Roles https://github.com/wekan/wekan/issues/3022
|
||||
- Check is user logged in, with `if (Meteor.user()) {`
|
||||
- Check is code running at server `if (Meteor.isServer()) {` or client `if Meteor.isClient()) {` .
|
||||
- Here is some authentication code https://github.com/wekan/wekan/blob/main/server/authentication.js
|
||||
|
||||
## Environment variables
|
||||
|
||||
- For any passwords, use environment variables, those are serverside
|
||||
- Do not copy environment variable to public variable that is visible browserside https://github.com/wekan/wekan/blob/main/server/max-size.js
|
||||
|
||||
```
|
||||
Meteor.startup(() => {
|
||||
if (process.env.HEADER_LOGIN_ID) {
|
||||
Meteor.settings.public.attachmentsUploadMaxSize = process.env.ATTACHMENTS_UPLOAD_MAX_SIZE;
|
||||
Meteor.settings.public.attachmentsUploadMimeTypes = process.env.ATTACHMENTS_UPLOAD_MIME_TYPES;
|
||||
Meteor.settings.public.avatarsUploadMaxSize = process.env.AVATARS_UPLOAD_MAX_SIZE;
|
||||
```
|
||||
|
||||
- For serverside, you can set Meteor.settings.variablename, without text public
|
||||
- For WeKan kanban, there is feature for setting board public, it can be viewed by anyone, there is realtime updates. But
|
||||
- Some of those permissions are checked at users.js models at https://github.com/wekan/wekan/tree/main/models
|
||||
- Environment variables are used for email server passwords, etc, at all platforms https://github.com/wekan/wekan/commit/a781c0e7dcfdbe34c1483ee83cec12455b7026f7
|
||||
|
||||
## Escape HTML comment tags so that HTML comments are visible
|
||||
|
||||
- Someone reported, that it is problem that content of HTML comments in edit mode, are not visible at at view mode, so this makes HTML comments visible.
|
||||
- https://github.com/wekan/wekan/commit/167863d95711249e69bb3511175d73b34acbbdb3
|
||||
- https://wekan.github.io/hall-of-fame/invisiblebleed/
|
||||
|
||||
## Attachments: XSS in filename is sanitized
|
||||
|
||||
- https://github.com/wekan/wekan/blob/main/client/components/cards/attachments.js#L303-L312
|
||||
- https://wekan.github.io/hall-of-fame/filebleed/
|
||||
|
||||
## Brute force login protection
|
||||
|
||||
- https://github.com/wekan/wekan/commit/23e5e1e3bd081699ce39ce5887db7e612616014d
|
||||
- https://github.com/wekan/wekan/tree/main/packages/wekan-accounts-lockout
|
||||
|
||||
### Sandstorm Wekan Security
|
||||
|
||||
@@ -105,12 +207,6 @@ a security issue, we'd like to know about it, and also how to fix it:
|
||||
|
||||
Typical already known or "no impact" bugs such as:
|
||||
|
||||
- Brute force password guessing. Currently there is
|
||||
[brute force protection with eluck:accounts-lockout](https://github.com/wekan/wekan/blob/main/CHANGELOG.md#v080-2018-04-04-wekan-release).
|
||||
- Security issues related to that Wekan uses Meteor 1.6.0.1 related packages, and upgrading to newer
|
||||
Meteor 1.6.1 is complicated process that requires lots of changes to many dependency packages.
|
||||
Upgrading [has been tried many times, spending a lot of time](https://github.com/meteor/meteor/issues/9609)
|
||||
but there still is issues. Helping with package upgrades is very welcome.
|
||||
- [Wekan API old tokens not replaced correctly](https://github.com/wekan/wekan/issues/1437)
|
||||
- Missing Cookie flags on non-session cookies or 3rd party cookies
|
||||
- Logout CSRF
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v7.10.0"
|
||||
appVersion: "v7.69.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -37,9 +37,24 @@ If *nix: chmod +x api.py => ./api.py users
|
||||
python3 api.py customfields BOARDID # Custom Fields of BOARDID
|
||||
python3 api.py customfield BOARDID CUSTOMFIELDID # Info of CUSTOMFIELDID
|
||||
python3 api.py addcustomfieldtoboard AUTHORID BOARDID NAME TYPE SETTINGS SHOWONCARD AUTOMATICALLYONCARD SHOWLABELONMINICARD SHOWSUMATTOPOFLIST # Add Custom Field to Board
|
||||
python3 api.py editcustomfield BOARDID LISTID CARDID CUSTOMFIELDID NEWCUSTOMFIELDVALUE
|
||||
python3 api.py editcustomfield BOARDID LISTID CARDID CUSTOMFIELDID NEWCUSTOMFIELDVALUE # Edit Custom Field
|
||||
python3 api.py listattachments BOARDID # List attachments
|
||||
python3 api.py cardsbyswimlane SWIMLANEID LISTID # Retrieve cards list on a swimlane
|
||||
python3 api.py getcard BOARDID LISTID CARDID # Get card info
|
||||
python3 api.py addlabel BOARDID LISTID CARDID LABELID # Add label to a card
|
||||
python3 api.py addcardwithlabel AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION LABELIDS # Add a card and a label
|
||||
python3 api.py editboardtitle BOARDID NEWBOARDTITLE # Edit board title
|
||||
python3 api.py copyboard BOARDID NEWBOARDTITLE # Copy a board
|
||||
python3 api.py createlabel BOARDID LABELCOLOR LABELNAME (Color available: `white`, `green`, `yellow`, `orange`, `red`, `purple`, `blue`, `sky`, `lime`, `pink`, `black`, `silver`, `peachpuff`, `crimson`, `plum`, `darkgreen`, `slateblue`, `magenta`, `gold`, `navy`, `gray`, `saddlebrown`, `paleturquoise`, `mistyrose`, `indigo`) # Create a new label
|
||||
python3 api.py editcardcolor BOARDID LISTID CARDID COLOR (Color available: `white`, `green`, `yellow`, `orange`, `red`, `purple`, `blue`, `sky`, `lime`, `pink`, `black`, `silver`, `peachpuff`, `crimson`, `plum`, `darkgreen`, `slateblue`, `magenta`, `gold`, `navy`, `gray`, `saddlebrown`, `paleturquoise`, `mistyrose`, `indigo`) # Edit card color
|
||||
python3 api.py addchecklist BOARDID CARDID TITLE ITEM1 ITEM2 ITEM3 ITEM4 (You can add multiple items or just one, or also without any item, just TITLE works as well. * If items or Title contains spaces, you should add ' between them.) # Add checklist + item on a card
|
||||
python3 api.py deleteallcards BOARDID SWIMLANEID ( * Be careful will delete ALL CARDS INSIDE the swimlanes automatically in every list * ) # Delete all cards on a swimlane
|
||||
python3 api.py checklistid BOARDID CARDID # Retrieve Checklist ID attached to a card
|
||||
python3 api.py checklistinfo BOARDID CARDID CHECKLISTID # Get checklist info
|
||||
python3 api.py get_list_cards_count BOARDID LISTID # Retrieve how many cards in a list
|
||||
python3 api.py get_board_cards_count BOARDID # Retrieve how many cards in a board
|
||||
|
||||
|
||||
Admin API:
|
||||
python3 api.py users # All users
|
||||
python3 api.py boards # All Public Boards
|
||||
@@ -179,6 +194,52 @@ if arguments == 10:
|
||||
print(body.text)
|
||||
# ------- ADD CUSTOM FIELD TO BOARD END -----------
|
||||
|
||||
if arguments == 8:
|
||||
|
||||
if sys.argv[1] == 'addcardwithlabel':
|
||||
# ------- ADD CARD WITH LABEL START -----------
|
||||
authorid = sys.argv[2]
|
||||
boardid = sys.argv[3]
|
||||
swimlaneid = sys.argv[4]
|
||||
listid = sys.argv[5]
|
||||
cardtitle = sys.argv[6]
|
||||
carddescription = sys.argv[7]
|
||||
labelIds = sys.argv[8] # Aggiunto labelIds
|
||||
|
||||
cardtolist = wekanurl + apiboards + boardid + s + l + s + listid + s + cs
|
||||
# Add card
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
post_data = {
|
||||
'authorId': '{}'.format(authorid),
|
||||
'title': '{}'.format(cardtitle),
|
||||
'description': '{}'.format(carddescription),
|
||||
'swimlaneId': '{}'.format(swimlaneid),
|
||||
'labelIds': labelIds
|
||||
}
|
||||
|
||||
body = requests.post(cardtolist, data=post_data, headers=headers)
|
||||
print(body.text)
|
||||
|
||||
# If ok id card
|
||||
if body.status_code == 200:
|
||||
card_data = body.json()
|
||||
new_card_id = card_data.get('_id')
|
||||
|
||||
# Updating card
|
||||
if new_card_id:
|
||||
edcard = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + new_card_id
|
||||
put_data = {'labelIds': labelIds}
|
||||
body = requests.put(edcard, data=put_data, headers=headers)
|
||||
print("=== EDIT CARD ===\n")
|
||||
body = requests.get(edcard, headers=headers)
|
||||
data2 = body.text.replace('}', "}\n")
|
||||
print(data2)
|
||||
else:
|
||||
print("Error obraining ID.")
|
||||
else:
|
||||
print("Error adding card.")
|
||||
# ------- ADD CARD WITH LABEL END -----------
|
||||
|
||||
if arguments == 7:
|
||||
|
||||
if sys.argv[1] == 'addcard':
|
||||
@@ -237,7 +298,53 @@ if arguments == 6:
|
||||
print(data2)
|
||||
# ------- EDIT CUSTOMFIELD END -----------
|
||||
|
||||
if arguments == 4:
|
||||
if arguments == 5:
|
||||
|
||||
if sys.argv[1] == 'addlabel':
|
||||
|
||||
# ------- EDIT CARD ADD LABEL START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
cardid = sys.argv[4]
|
||||
labelIds = sys.argv[5]
|
||||
edcard = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + cardid
|
||||
print(edcard)
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
put_data = {'labelIds': labelIds}
|
||||
body = requests.put(edcard, data=put_data, headers=headers)
|
||||
print("=== ADD LABEL ===\n")
|
||||
body = requests.get(edcard, headers=headers)
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- EDIT CARD ADD LABEL END -----------
|
||||
|
||||
if sys.argv[1] == 'editcardcolor':
|
||||
# ------- EDIT CARD COLOR START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
cardid = sys.argv[4]
|
||||
newcolor = sys.argv[5]
|
||||
|
||||
valid_colors = ['white', 'green', 'yellow', 'orange', 'red', 'purple', 'blue', 'sky', 'lime', 'pink', 'black',
|
||||
'silver', 'peachpuff', 'crimson', 'plum', 'darkgreen', 'slateblue', 'magenta', 'gold', 'navy',
|
||||
'gray', 'saddlebrown', 'paleturquoise', 'mistyrose', 'indigo']
|
||||
|
||||
if newcolor not in valid_colors:
|
||||
print("Invalid color. Choose a color from the list.")
|
||||
sys.exit(1)
|
||||
|
||||
edcard = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + cardid
|
||||
print(edcard)
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
put_data = {'color': '{}'.format(newcolor)}
|
||||
body = requests.put(edcard, data=put_data, headers=headers)
|
||||
print("=== EDIT CARD COLOR ===\n")
|
||||
body = requests.get(edcard, headers=headers)
|
||||
data2 = body.text.replace('}', "}\n")
|
||||
print(data2)
|
||||
# ------- EDIT CARD COLOR END -----------
|
||||
|
||||
if arguments >= 4:
|
||||
|
||||
if sys.argv[1] == 'newuser':
|
||||
|
||||
@@ -251,9 +358,155 @@ if arguments == 4:
|
||||
print("=== CREATE NEW USER ===\n")
|
||||
print(body.text)
|
||||
# ------- CREATE NEW USER END -----------
|
||||
|
||||
if sys.argv[1] == 'getcard':
|
||||
|
||||
# ------- LIST OF CARD START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
cardid = sys.argv[4]
|
||||
listone = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + cardid
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
print("=== INFO OF ONE LIST ===\n")
|
||||
print("URL:", listone) # Stampa l'URL per debug
|
||||
try:
|
||||
response = requests.get(listone, headers=headers)
|
||||
print("=== RESPONSE ===\n")
|
||||
print("Status Code:", response.status_code) # Stampa il codice di stato per debug
|
||||
|
||||
if response.status_code == 200:
|
||||
data2 = response.text.replace('}', "}\n")
|
||||
print(data2)
|
||||
else:
|
||||
print(f"Error: {response.status_code}")
|
||||
print(f"Response: {response.text}")
|
||||
except Exception as e:
|
||||
print(f"Error in the GET request: {e}")
|
||||
# ------- LISTS OF CARD END -----------
|
||||
|
||||
if sys.argv[1] == 'createlabel':
|
||||
|
||||
# ------- CREATE LABEL START -----------
|
||||
boardid = sys.argv[2]
|
||||
labelcolor = sys.argv[3]
|
||||
labelname = sys.argv[4]
|
||||
label_url = wekanurl + apiboards + boardid + s + 'labels'
|
||||
print(label_url)
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
# Object to send
|
||||
put_data = {'label': {'color': labelcolor, 'name': labelname}}
|
||||
print("URL:", label_url)
|
||||
print("Headers:", headers)
|
||||
print("Data:", put_data)
|
||||
try:
|
||||
response = requests.put(label_url, json=put_data, headers=headers)
|
||||
print("=== CREATE LABELS ===\n")
|
||||
print("Response Status Code:", response.status_code)
|
||||
print("Response Text:", response.text)
|
||||
except Exception as e:
|
||||
print("Error:", e)
|
||||
# ------- CREATE LABEL END -----------
|
||||
|
||||
if sys.argv[1] == 'addchecklist':
|
||||
|
||||
# ------- ADD CHECKLIST START -----------
|
||||
board_id = sys.argv[2]
|
||||
card_id = sys.argv[3]
|
||||
checklist_title = sys.argv[4]
|
||||
|
||||
# Aggiungi la checklist
|
||||
checklist_url = wekanurl + apiboards + board_id + s + cs + s + card_id + '/checklists'
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
data = {'title': checklist_title}
|
||||
|
||||
response = requests.post(checklist_url, data=data, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
result = json.loads(response.text)
|
||||
checklist_id = result.get('_id')
|
||||
|
||||
print(f"Checklist '{checklist_title}' created. ID: {checklist_id}")
|
||||
|
||||
# Aggiungi gli items alla checklist
|
||||
items_to_add = sys.argv[5:]
|
||||
for item_title in items_to_add:
|
||||
checklist_item_url = wekanurl + apiboards + board_id + s + cs + s + card_id + s + 'checklists' + s + checklist_id + '/items'
|
||||
item_data = {'title': item_title}
|
||||
|
||||
item_response = requests.post(checklist_item_url, data=item_data, headers=headers)
|
||||
item_response.raise_for_status()
|
||||
|
||||
item_result = json.loads(item_response.text)
|
||||
checklist_item_id = item_result.get('_id')
|
||||
|
||||
print(f"Item '{item_title}' added. ID: {checklist_item_id}")
|
||||
|
||||
if sys.argv[1] == 'checklistinfo':
|
||||
|
||||
# ------- ADD CHECKLIST START -----------
|
||||
board_id = sys.argv[2]
|
||||
card_id = sys.argv[3]
|
||||
checklist_id = sys.argv[4]
|
||||
checklist_url = wekanurl + apiboards + board_id + s + cs + s + card_id + '/checklists' + s + checklist_id
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
response = requests.get(checklist_url, headers=headers)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
checklist_info = response.json()
|
||||
print("Checklist Info:")
|
||||
print(checklist_info)
|
||||
|
||||
if arguments == 3:
|
||||
|
||||
if sys.argv[1] == 'editboardtitle':
|
||||
|
||||
# ------- EDIT BOARD TITLE START -----------
|
||||
boardid = sys.argv[2]
|
||||
boardtitle = sys.argv[3]
|
||||
edboardtitle = wekanurl + apiboards + boardid + s + 'title'
|
||||
print(edboardtitle)
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
|
||||
post_data = {'title': boardtitle}
|
||||
|
||||
body = requests.put(edboardtitle, json=post_data, headers=headers)
|
||||
print("=== EDIT BOARD TITLE ===\n")
|
||||
#body = requests.get(edboardtitle, headers=headers)
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
if body.status_code == 200:
|
||||
print("Succesfull!")
|
||||
else:
|
||||
print(f"Error: {body.status_code}")
|
||||
print(body.text)
|
||||
|
||||
# ------- EDIT BOARD TITLE END -----------
|
||||
|
||||
if sys.argv[1] == 'copyboard':
|
||||
|
||||
# ------- COPY BOARD START -----------
|
||||
boardid = sys.argv[2]
|
||||
boardtitle = sys.argv[3]
|
||||
edboardcopy = wekanurl + apiboards + boardid + s + 'copy'
|
||||
print(edboardcopy)
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
|
||||
post_data = {'title': boardtitle}
|
||||
|
||||
body = requests.post(edboardcopy, json=post_data, headers=headers)
|
||||
print("=== COPY BOARD ===\n")
|
||||
#body = requests.get(edboardcopy, headers=headers)
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
if body.status_code == 200:
|
||||
print("Succesfull!")
|
||||
else:
|
||||
print(f"Error: {body.status_code}")
|
||||
print(body.text)
|
||||
|
||||
# ------- COPY BOARD END -----------
|
||||
|
||||
if sys.argv[1] == 'createlist':
|
||||
|
||||
# ------- CREATE LIST START -----------
|
||||
@@ -293,6 +546,90 @@ if arguments == 3:
|
||||
print(data2)
|
||||
# ------- INFO OF CUSTOM FIELD END -----------
|
||||
|
||||
if sys.argv[1] == 'cardsbyswimlane':
|
||||
# ------- RETRIEVE CARDS BY SWIMLANE ID START -----------
|
||||
boardid = sys.argv[2]
|
||||
swimlaneid = sys.argv[3]
|
||||
cardsbyswimlane = wekanurl + apiboards + boardid + s + sws + s + swimlaneid + s + cs
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
print("=== CARDS BY SWIMLANE ID ===\n")
|
||||
print("URL:", cardsbyswimlane) # Debug
|
||||
try:
|
||||
body = requests.get(cardsbyswimlane, headers=headers)
|
||||
print("Status Code:", body.status_code) # Debug
|
||||
data = body.text.replace('}', "}\n")
|
||||
print("Data:", data)
|
||||
except Exception as e:
|
||||
print("Error GET:", e)
|
||||
# ------- RETRIEVE CARDS BY SWIMLANE ID END -----------
|
||||
|
||||
if sys.argv[1] == 'deleteallcards':
|
||||
boardid = sys.argv[2]
|
||||
swimlaneid = sys.argv[3]
|
||||
|
||||
# ------- GET SWIMLANE CARDS START -----------
|
||||
get_swimlane_cards_url = wekanurl + apiboards + boardid + s + "swimlanes" + s + swimlaneid + s + "cards"
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
|
||||
try:
|
||||
response = requests.get(get_swimlane_cards_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
cards_data = response.json()
|
||||
|
||||
# Print the details of each card
|
||||
for card in cards_data:
|
||||
# ------- DELETE CARD START -----------
|
||||
delete_card_url = wekanurl + apiboards + boardid + s + "lists" + s + card['listId'] + s + "cards" + s + card['_id']
|
||||
try:
|
||||
response = requests.delete(delete_card_url, headers=headers)
|
||||
if response.status_code == 404:
|
||||
print(f"Card not found: {card['_id']}")
|
||||
else:
|
||||
response.raise_for_status()
|
||||
deleted_card_data = response.json()
|
||||
print(f"Card Deleted Successfully. Card ID: {deleted_card_data['_id']}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error deleting card: {e}")
|
||||
# ------- DELETE CARD END -----------
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error getting swimlane cards: {e}")
|
||||
sys.exit(1)
|
||||
# ------- GET SWIMLANE CARDS END -----------
|
||||
|
||||
if sys.argv[1] == 'get_list_cards_count':
|
||||
# ------- GET LIST CARDS COUNT START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
|
||||
get_list_cards_count_url = wekanurl + apiboards + boardid + s + l + s + listid + s + "cards_count"
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
|
||||
try:
|
||||
response = requests.get(get_list_cards_count_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
print(f"List Cards Count: {data['list_cards_count']}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error: {e}")
|
||||
# ------- GET LIST CARDS COUNT END -----------
|
||||
|
||||
if sys.argv[1] == 'checklistid':
|
||||
|
||||
# ------- ADD CHECKLIST START -----------
|
||||
board_id = sys.argv[2]
|
||||
card_id = sys.argv[3]
|
||||
|
||||
checklist_url = wekanurl + apiboards + board_id + s + cs + s + card_id + '/checklists'
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
response = requests.get(checklist_url, headers=headers)
|
||||
|
||||
response.raise_for_status()
|
||||
checklists = response.json()
|
||||
print("Checklists:")
|
||||
for checklist in checklists:
|
||||
print(checklist)
|
||||
|
||||
if arguments == 2:
|
||||
|
||||
# ------- BOARDS LIST START -----------
|
||||
@@ -364,6 +701,22 @@ if arguments == 2:
|
||||
print(data2)
|
||||
# ------- LISTS OF ATTACHMENTS END -----------
|
||||
|
||||
if sys.argv[1] == 'get_board_cards_count':
|
||||
# ------- GET BOARD CARDS COUNT START -----------
|
||||
boardid = sys.argv[2]
|
||||
|
||||
get_board_cards_count_url = wekanurl + apiboards + boardid + s + "cards_count"
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
|
||||
try:
|
||||
response = requests.get(get_board_cards_count_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
print(f"Board Cards Count: {data['board_cards_count']}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error: {e}")
|
||||
# ------- GET BOARD CARDS COUNT END -----------
|
||||
|
||||
if arguments == 1:
|
||||
|
||||
if sys.argv[1] == 'users':
|
||||
|
||||
@@ -49,43 +49,6 @@
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions {
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
gap: 5px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .open-comment-reaction-popup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
height: 24px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .open-comment-reaction-popup i.fa.fa-smile-o {
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .open-comment-reaction-popup i.fa.fa-plus {
|
||||
font-size: 8px;
|
||||
margin-top: -7px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .reaction {
|
||||
cursor: pointer;
|
||||
border: 1px solid #808080;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .reaction.selected {
|
||||
background-color: #b0c4de;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .reaction:hover {
|
||||
background-color: #b0c4de;
|
||||
}
|
||||
.activities .activity .activity-desc .reactions .reaction .reaction-count {
|
||||
font-size: 12px;
|
||||
}
|
||||
.activities .activity .activity-desc .activity-checklist {
|
||||
display: block;
|
||||
border-radius: 3px;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
template(name="activities")
|
||||
.activities.js-sidebar-activities
|
||||
//- We should use Template.dynamic here but there is a bug with
|
||||
//- blaze-components: https://github.com/peerlibrary/meteor-blaze-components/issues/30
|
||||
if $eq mode "board"
|
||||
+boardActivities
|
||||
else
|
||||
+cardActivities
|
||||
if showActivities
|
||||
.activities.js-sidebar-activities
|
||||
//- We should use Template.dynamic here but there is a bug with
|
||||
//- blaze-components: https://github.com/peerlibrary/meteor-blaze-components/issues/30
|
||||
if $eq mode "board"
|
||||
+boardActivities
|
||||
else
|
||||
+cardActivities
|
||||
|
||||
template(name="boardActivities")
|
||||
each activityData in currentBoard.activities
|
||||
@@ -15,32 +16,6 @@ template(name="cardActivities")
|
||||
each activityData in activities
|
||||
+activity(activity=activityData card=card mode=mode)
|
||||
|
||||
template(name="editOrDeleteComment")
|
||||
= ' - '
|
||||
a.js-open-inlined-form {{_ "edit"}}
|
||||
= ' - '
|
||||
a.js-delete-comment {{_ "delete"}}
|
||||
|
||||
template(name="deleteCommentPopup")
|
||||
p {{_ "comment-delete"}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
||||
template(name="commentReactions")
|
||||
.reactions
|
||||
each reaction in reactions
|
||||
span.reaction(class="{{#if isSelected reaction.userIds}}selected{{/if}}" data-codepoint="#{reaction.reactionCodepoint}" title="{{userNames reaction.userIds}}")
|
||||
span.reaction-codepoint !{reaction.reactionCodepoint}
|
||||
span.reaction-count #{reaction.userIds.length}
|
||||
if (currentUser.isBoardMember)
|
||||
a.open-comment-reaction-popup(title="{{_ 'addReactionPopup-title'}}")
|
||||
i.fa.fa-smile-o
|
||||
i.fa.fa-plus
|
||||
|
||||
template(name="addReactionPopup")
|
||||
.reactions-popup
|
||||
each codepoint in codepoints
|
||||
span.add-comment-reaction(data-codepoint="#{codepoint}") !{codepoint}
|
||||
|
||||
template(name="activity")
|
||||
.activity(data-id=activity._id)
|
||||
+userAvatar(userId=activity.user._id)
|
||||
@@ -130,39 +105,17 @@ template(name="activity")
|
||||
| {{{_ 'activity-checklist-item-removed' (sanitize activity.checklist.title) cardLink}}}.
|
||||
|
||||
//- comment activity ----------------------------------------------------
|
||||
if($eq mode 'card')
|
||||
//- if we are in card mode we display the comment in a way that it
|
||||
//- can be edited by the owner
|
||||
if($eq activity.activityType 'addComment')
|
||||
+inlinedForm(classNames='js-edit-comment')
|
||||
+editor(autofocus=true)
|
||||
= activity.comment.text
|
||||
.edit-controls
|
||||
button.primary(type="submit") {{_ 'edit'}}
|
||||
.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
.activity-comment
|
||||
+viewer
|
||||
= activity.comment.text
|
||||
+commentReactions(reactions=activity.comment.reactions commentId=activity.comment._id)
|
||||
span(title=activity.createdAt).activity-meta {{ moment activity.createdAt }}
|
||||
if($eq currentUser._id activity.comment.userId)
|
||||
+editOrDeleteComment
|
||||
else if currentUser.isBoardAdmin
|
||||
+editOrDeleteComment
|
||||
if($eq activity.activityType 'deleteComment')
|
||||
| {{{_ 'activity-deleteComment' activity.commentId}}}.
|
||||
|
||||
if($eq activity.activityType 'deleteComment')
|
||||
| {{{_ 'activity-deleteComment' activity.commentId}}}.
|
||||
if($eq activity.activityType 'editComment')
|
||||
| {{{_ 'activity-editComment' activity.commentId}}}.
|
||||
|
||||
if($eq activity.activityType 'editComment')
|
||||
| {{{_ 'activity-editComment' activity.commentId}}}.
|
||||
else
|
||||
//- if we are not in card mode we only display a summary of the comment
|
||||
if($eq activity.activityType 'addComment')
|
||||
| {{{_ 'activity-on' cardLink}}}
|
||||
a.activity-comment(href="{{ activity.card.originRelativeUrl }}")
|
||||
+viewer
|
||||
= activity.comment.text
|
||||
if($eq activity.activityType 'addComment')
|
||||
| {{{_ 'activity-on' cardLink}}}
|
||||
a.activity-comment(href="{{ activity.card.originRelativeUrl }}")
|
||||
+viewer
|
||||
= activity.comment.text
|
||||
|
||||
//- date activity ------------------------------------------------
|
||||
if($eq activity.activityType 'a-receivedAt')
|
||||
@@ -208,6 +161,9 @@ template(name="activity")
|
||||
if($eq activity.activityType 'archivedList')
|
||||
| {{_ 'activity-archived' (sanitize listLabel)}}.
|
||||
|
||||
if($eq activity.activityType 'changedListTitle')
|
||||
| {{_ 'activity-changedListTitle' (sanitize listLabel) boardLabelLink}}
|
||||
|
||||
//- member activity ----------------------------------------------------
|
||||
if($eq activity.activityType 'joinMember')
|
||||
if($eq user._id activity.member._id)
|
||||
@@ -243,4 +199,4 @@ template(name="activity")
|
||||
else if(currentData.timeValue)
|
||||
| {{_ activity.activityType currentData.timeValue}}
|
||||
|
||||
span(title=activity.createdAt).activity-meta {{ moment activity.createdAt }}
|
||||
div(title=activity.createdAt).activity-meta {{ moment activity.createdAt }}
|
||||
@@ -13,39 +13,41 @@ BlazeComponent.extendComponent({
|
||||
const sidebar = Sidebar;
|
||||
sidebar && sidebar.callFirstWith(null, 'resetNextPeak');
|
||||
this.autorun(() => {
|
||||
let mode = this.data().mode;
|
||||
const capitalizedMode = Utils.capitalize(mode);
|
||||
let searchId;
|
||||
if (mode === 'linkedcard' || mode === 'linkedboard') {
|
||||
searchId = Utils.getCurrentCard().linkedId;
|
||||
mode = mode.replace('linked', '');
|
||||
} else if (mode === 'card') {
|
||||
searchId = Utils.getCurrentCardId();
|
||||
} else {
|
||||
searchId = Session.get(`current${capitalizedMode}`);
|
||||
}
|
||||
const limit = this.page.get() * activitiesPerPage;
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
const hideSystem = user ? user.hasHiddenSystemMessages() : false;
|
||||
if (searchId === null) return;
|
||||
|
||||
this.subscribe('activities', mode, searchId, limit, hideSystem, () => {
|
||||
this.loadNextPageLocked = false;
|
||||
|
||||
// TODO the guard can be removed as soon as the TODO above is resolved
|
||||
if (!sidebar) return;
|
||||
// If the sibear peak hasn't increased, that mean that there are no more
|
||||
// activities, and we can stop calling new subscriptions.
|
||||
// XXX This is hacky! We need to know excatly and reactively how many
|
||||
// activities there are, we probably want to denormalize this number
|
||||
// dirrectly into card and board documents.
|
||||
const nextPeakBefore = sidebar.callFirstWith(null, 'getNextPeak');
|
||||
sidebar.calculateNextPeak();
|
||||
const nextPeakAfter = sidebar.callFirstWith(null, 'getNextPeak');
|
||||
if (nextPeakBefore === nextPeakAfter) {
|
||||
sidebar.callFirstWith(null, 'resetNextPeak');
|
||||
let mode = this.data()?.mode;
|
||||
if (mode) {
|
||||
const capitalizedMode = Utils.capitalize(mode);
|
||||
let searchId;
|
||||
const showActivities = this.showActivities();
|
||||
if (mode === 'linkedcard' || mode === 'linkedboard') {
|
||||
const currentCard = Utils.getCurrentCard();
|
||||
searchId = currentCard.linkedId;
|
||||
mode = mode.replace('linked', '');
|
||||
} else if (mode === 'card') {
|
||||
searchId = Utils.getCurrentCardId();
|
||||
} else {
|
||||
searchId = Session.get(`current${capitalizedMode}`);
|
||||
}
|
||||
});
|
||||
const limit = this.page.get() * activitiesPerPage;
|
||||
if (searchId === null) return;
|
||||
|
||||
this.subscribe('activities', mode, searchId, limit, showActivities, () => {
|
||||
this.loadNextPageLocked = false;
|
||||
|
||||
// TODO the guard can be removed as soon as the TODO above is resolved
|
||||
if (!sidebar) return;
|
||||
// If the sibear peak hasn't increased, that mean that there are no more
|
||||
// activities, and we can stop calling new subscriptions.
|
||||
// XXX This is hacky! We need to know excatly and reactively how many
|
||||
// activities there are, we probably want to denormalize this number
|
||||
// dirrectly into card and board documents.
|
||||
const nextPeakBefore = sidebar.callFirstWith(null, 'getNextPeak');
|
||||
sidebar.calculateNextPeak();
|
||||
const nextPeakAfter = sidebar.callFirstWith(null, 'getNextPeak');
|
||||
if (nextPeakBefore === nextPeakAfter) {
|
||||
sidebar.callFirstWith(null, 'resetNextPeak');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
loadNextPage() {
|
||||
@@ -54,14 +56,26 @@ BlazeComponent.extendComponent({
|
||||
this.loadNextPageLocked = true;
|
||||
}
|
||||
},
|
||||
}).register('activities');
|
||||
|
||||
Template.activities.helpers({
|
||||
activities() {
|
||||
const ret = this.card.activities();
|
||||
showActivities() {
|
||||
let ret = false;
|
||||
let mode = this.data()?.mode;
|
||||
if (mode) {
|
||||
if (mode === 'linkedcard' || mode === 'linkedboard') {
|
||||
const currentCard = Utils.getCurrentCard();
|
||||
ret = currentCard.showActivities ?? false;
|
||||
} else if (mode === 'card') {
|
||||
ret = this.data()?.card?.showActivities ?? false;
|
||||
} else {
|
||||
ret = Utils.getCurrentBoard().showActivities ?? false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
});
|
||||
activities() {
|
||||
const ret = this.data().card.activities();
|
||||
return ret;
|
||||
},
|
||||
}).register('activities');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
checkItem() {
|
||||
@@ -247,32 +261,6 @@ BlazeComponent.extendComponent({
|
||||
return customField.name;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
// XXX We should use Popup.afterConfirmation here
|
||||
'click .js-delete-comment': Popup.afterConfirm('deleteComment', () => {
|
||||
const commentId = this.data().activity.commentId;
|
||||
CardComments.remove(commentId);
|
||||
Popup.back();
|
||||
}),
|
||||
'submit .js-edit-comment'(evt) {
|
||||
evt.preventDefault();
|
||||
const commentText = this.currentComponent()
|
||||
.getValue()
|
||||
.trim();
|
||||
const commentId = Template.parentData().activity.commentId;
|
||||
if (commentText) {
|
||||
CardComments.update(commentId, {
|
||||
$set: {
|
||||
text: commentText,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('activity');
|
||||
|
||||
Template.activity.helpers({
|
||||
|
||||
@@ -63,3 +63,78 @@
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
.comments {
|
||||
clear: both;
|
||||
}
|
||||
.comments .comment {
|
||||
margin: 0.5px 0;
|
||||
padding: 6px 0;
|
||||
display: flex;
|
||||
}
|
||||
.comments .comment .member {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.comments .comment .comment-member {
|
||||
font-weight: 700;
|
||||
}
|
||||
.comments .comment .comment-desc {
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
margin: 0;
|
||||
margin-left: 3px;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
}
|
||||
.comments .comment .comment-desc .comment-text {
|
||||
display: block;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
text-decoration: none;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
.comments .comment .comment-desc .reactions {
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
gap: 5px;
|
||||
}
|
||||
.comments .comment .comment-desc .reactions .open-comment-reaction-popup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
height: 24px;
|
||||
}
|
||||
.comments .comment .comment-desc .reactions .open-comment-reaction-popup i.fa.fa-smile-o {
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.comments .comment .comment-desc .reactions .open-comment-reaction-popup i.fa.fa-plus {
|
||||
font-size: 8px;
|
||||
margin-top: -7px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
.comments .comment .comment-desc .reactions .reaction {
|
||||
cursor: pointer;
|
||||
border: 1px solid #808080;
|
||||
border-radius: 15px;
|
||||
display: flex;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
.comments .comment .comment-desc .reactions .reaction.selected {
|
||||
background-color: #b0c4de;
|
||||
}
|
||||
.comments .comment .comment-desc .reactions .reaction:hover {
|
||||
background-color: #b0c4de;
|
||||
}
|
||||
.comments .comment .comment-desc .reactions .reaction .reaction-count {
|
||||
font-size: 12px;
|
||||
}
|
||||
.comments .comment .comment-desc .comment-meta {
|
||||
font-size: 0.8em;
|
||||
color: #999;
|
||||
}
|
||||
@@ -7,3 +7,59 @@ template(name="commentForm")
|
||||
| {{getUnsavedValue 'cardComment' currentCard._id}}
|
||||
.add-controls
|
||||
button.primary.confirm.clear.js-add-comment(type="submit") {{_ 'comment'}}
|
||||
|
||||
template(name="comments")
|
||||
.comments
|
||||
each commentData in getComments
|
||||
+comment(commentData)
|
||||
|
||||
template(name="comment")
|
||||
.comment
|
||||
+userAvatar(userId=userId)
|
||||
p.comment-desc
|
||||
span.comment-member
|
||||
+memberName(user=user)
|
||||
|
||||
+inlinedForm(classNames='js-edit-comment')
|
||||
+editor(autofocus=true)
|
||||
= text
|
||||
.edit-controls
|
||||
button.primary(type="submit") {{_ 'edit'}}
|
||||
.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
.comment-text
|
||||
+viewer
|
||||
= text
|
||||
+commentReactions(reactions=reactions commentId=_id)
|
||||
span(title=createdAt).comment-meta {{ moment createdAt }}
|
||||
if($eq currentUser._id userId)
|
||||
+editOrDeleteComment
|
||||
else if currentUser.isBoardAdmin
|
||||
+editOrDeleteComment
|
||||
|
||||
template(name="editOrDeleteComment")
|
||||
= ' - '
|
||||
a.js-open-inlined-form {{_ "edit"}}
|
||||
= ' - '
|
||||
a.js-delete-comment {{_ "delete"}}
|
||||
|
||||
template(name="deleteCommentPopup")
|
||||
p {{_ "comment-delete"}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
||||
template(name="commentReactions")
|
||||
.reactions
|
||||
each reaction in reactions
|
||||
span.reaction(class="{{#if isSelected reaction.userIds}}selected{{/if}}" data-codepoint="#{reaction.reactionCodepoint}" title="{{userNames reaction.userIds}}")
|
||||
span.reaction-codepoint !{reaction.reactionCodepoint}
|
||||
span.reaction-count #{reaction.userIds.length}
|
||||
if (currentUser.isBoardMember)
|
||||
a.open-comment-reaction-popup(title="{{_ 'addReactionPopup-title'}}")
|
||||
i.fa.fa-smile-o
|
||||
i.fa.fa-plus
|
||||
|
||||
template(name="addReactionPopup")
|
||||
.reactions-popup
|
||||
each codepoint in codepoints
|
||||
span.add-comment-reaction(data-codepoint="#{codepoint}") !{codepoint}
|
||||
|
||||
@@ -55,6 +55,41 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('commentForm');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
getComments() {
|
||||
const ret = this.data().comments();
|
||||
return ret;
|
||||
},
|
||||
}).register("comments");
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-delete-comment': Popup.afterConfirm('deleteComment', () => {
|
||||
const commentId = this.data()._id;
|
||||
CardComments.remove(commentId);
|
||||
Popup.back();
|
||||
}),
|
||||
'submit .js-edit-comment'(evt) {
|
||||
evt.preventDefault();
|
||||
const commentText = this.currentComponent()
|
||||
.getValue()
|
||||
.trim();
|
||||
const commentId = this.data()._id;
|
||||
if (commentText) {
|
||||
CardComments.update(commentId, {
|
||||
$set: {
|
||||
text: commentText,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register("comment");
|
||||
|
||||
// XXX This should be a static method of the `commentForm` component
|
||||
function resetCommentInput(input) {
|
||||
input.val(''); // without manually trigger, input event won't be fired
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
transition: margin 0.1s;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.board-wrapper .board-canvas.is-sibling-sidebar-open {
|
||||
margin-right: 248px;
|
||||
}
|
||||
.board-wrapper .board-canvas.overlayed {overflow:hidden}
|
||||
.board-wrapper .board-canvas .board-overlay {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
|
||||
@@ -17,12 +17,10 @@ template(name="boardBody")
|
||||
| {{_ 'tableVisibilityMode-allowPrivateOnly'}}
|
||||
else
|
||||
.board-wrapper(class=currentBoard.colorClass)
|
||||
+sidebar
|
||||
.board-canvas.js-swimlanes(
|
||||
.board-canvas.js-swimlanes.dragscroll(
|
||||
class="{{#if Sidebar.isOpen}}is-sibling-sidebar-open{{/if}}"
|
||||
class="{{#if MultiSelection.isActive}}is-multiselection-active{{/if}}"
|
||||
class="{{#if draggingActive.get}}is-dragging-active{{/if}}"
|
||||
class="{{#if showOverlay.get}}overlayed{{/if}}")
|
||||
class="{{#if draggingActive.get}}is-dragging-active{{/if}}")
|
||||
if showOverlay.get
|
||||
.board-overlay
|
||||
if currentBoard.isTemplatesBoard
|
||||
@@ -37,6 +35,7 @@ template(name="boardBody")
|
||||
+calendarView
|
||||
else
|
||||
+listsGroup(currentBoard)
|
||||
+sidebar
|
||||
|
||||
template(name="calendarView")
|
||||
if isViewCalendar
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
import dragscroll from '@wekanteam/dragscroll';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndex } = Utils;
|
||||
@@ -219,6 +220,7 @@ BlazeComponent.extendComponent({
|
||||
boardComponent.openNewListForm();
|
||||
}
|
||||
|
||||
dragscroll.reset();
|
||||
Utils.setBackgroundImage();
|
||||
},
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -12,8 +12,9 @@ template(name="boardHeaderBar")
|
||||
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
|
||||
if currentUser.isBoardAdmin
|
||||
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'}}")
|
||||
|
||||
@@ -6,37 +6,6 @@ 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'() {
|
||||
Sidebar.setView('customFields');
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-open-archives'() {
|
||||
Sidebar.setView('archives');
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-change-board-color': Popup.open('boardChangeColor'),
|
||||
'click .js-change-language': Popup.open('changeLanguage'),
|
||||
'click .js-archive-board ': Popup.afterConfirm('archiveBoard', function() {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
currentBoard.archive();
|
||||
// XXX We should have some kind of notification on top of the page to
|
||||
// confirm that the board was successfully archived.
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
Popup.back();
|
||||
Boards.remove(currentBoard._id);
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
|
||||
'click .js-card-settings': Popup.open('boardCardSettings'),
|
||||
'click .js-minicard-settings': Popup.open('boardMinicardSettings'),
|
||||
});
|
||||
|
||||
Template.boardChangeTitlePopup.events({
|
||||
submit(event, templateInstance) {
|
||||
|
||||
@@ -151,8 +151,8 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
const currUser = ReactiveCache.getCurrentUser();
|
||||
|
||||
let orgIdsUserBelongs = currUser !== undefined && currUser.teams !== 'undefined' ? currUser.orgIdsUserBelongs() : '';
|
||||
if (orgIdsUserBelongs && orgIdsUserBelongs != '') {
|
||||
let orgIdsUserBelongs = currUser?.orgIdsUserBelongs() || '';
|
||||
if (orgIdsUserBelongs) {
|
||||
let orgsIds = orgIdsUserBelongs.split(',');
|
||||
// for(let i = 0; i < orgsIds.length; i++){
|
||||
// query.$and[2].$or.push({'orgs.orgId': orgsIds[i]});
|
||||
@@ -162,8 +162,8 @@ BlazeComponent.extendComponent({
|
||||
query.$and[2].$or.push({ 'orgs.orgId': { $in: orgsIds } });
|
||||
}
|
||||
|
||||
let teamIdsUserBelongs = currUser !== undefined && currUser.teams !== 'undefined' ? currUser.teamIdsUserBelongs() : '';
|
||||
if (teamIdsUserBelongs && teamIdsUserBelongs != '') {
|
||||
let teamIdsUserBelongs = currUser?.teamIdsUserBelongs() || '';
|
||||
if (teamIdsUserBelongs) {
|
||||
let teamsIds = teamIdsUserBelongs.split(',');
|
||||
// for(let i = 0; i < teamsIds.length; i++){
|
||||
// query.$or[2].$or.push({'teams.teamId': teamsIds[i]});
|
||||
@@ -199,15 +199,12 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boardMembers(boardId) {
|
||||
let boardMembers = [];
|
||||
/* Bug Board icons random dance https://github.com/wekan/wekan/issues/4214
|
||||
const lists = ReactiveCache.getBoard(boardId)
|
||||
let members = lists.members
|
||||
members.forEach(member => {
|
||||
boardMembers.push(member.userId);
|
||||
});
|
||||
*/
|
||||
const boardMembers = lists?.members.map(member => member.userId);
|
||||
return boardMembers;
|
||||
*/
|
||||
return [];
|
||||
},
|
||||
|
||||
isStarred() {
|
||||
|
||||
@@ -51,8 +51,9 @@ template(name="attachmentGallery")
|
||||
|
||||
.attachment-gallery
|
||||
|
||||
a.attachment-item.add-attachment.js-add-attachment
|
||||
i.fa.fa-plus.icon
|
||||
if canModifyCard
|
||||
a.attachment-item.add-attachment.js-add-attachment
|
||||
i.fa.fa-plus.icon
|
||||
|
||||
each attachments
|
||||
|
||||
@@ -116,8 +117,6 @@ template(name="attachmentActionsPopup")
|
||||
| {{_ 'remove-background-image'}}
|
||||
else
|
||||
| {{_ 'add-background-image'}}
|
||||
p.attachment-storage
|
||||
| {{versions.original.storage}}
|
||||
|
||||
if $neq versions.original.storage "fs"
|
||||
a.js-move-storage-fs
|
||||
|
||||
@@ -39,7 +39,7 @@ Template.attachmentGallery.events({
|
||||
'click .js-rename': Popup.open('attachmentRename'),
|
||||
'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete', function() {
|
||||
Attachments.remove(this._id);
|
||||
Popup.back(2);
|
||||
Popup.back();
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -501,7 +501,7 @@ BlazeComponent.extendComponent({
|
||||
if (name === DOMPurify.sanitize(name)) {
|
||||
Meteor.call('renameAttachment', this.data()._id, name);
|
||||
}
|
||||
Popup.back(2);
|
||||
Popup.back();
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
float: left;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin: 0 4px 4px 0;
|
||||
margin: .3vh;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
z-index: 1;
|
||||
@@ -196,6 +196,9 @@
|
||||
margin-right: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
.card-details .card-description i.fa.fa-pencil-square-o {
|
||||
float: right;
|
||||
}
|
||||
.card-details .card-description textarea {
|
||||
min-height: 100px;
|
||||
}
|
||||
@@ -245,10 +248,10 @@
|
||||
padding-top: 10px;
|
||||
}
|
||||
@media screen and (min-width: 801px) {
|
||||
.card-details-maximized {
|
||||
.card-details {
|
||||
top: 97px;
|
||||
left: 0;
|
||||
right:0;
|
||||
left: calc(50% - (600px / 2));
|
||||
width: 600px;
|
||||
bottom: 0;
|
||||
position: fixed;
|
||||
resize: both;
|
||||
@@ -267,6 +270,10 @@
|
||||
box-shadow: 0 0 7px 0 #b3b3b3;
|
||||
transition: flex-basis 0.1s;
|
||||
box-sizing: border-box;
|
||||
top: 97px;
|
||||
left: 0px;
|
||||
height: calc(100% - 100px);
|
||||
width: calc(100% - 20px);
|
||||
float: left;
|
||||
}
|
||||
.card-details-maximized .card-details-left {
|
||||
|
||||
@@ -5,7 +5,7 @@ template(name="cardDetails")
|
||||
|
||||
+attachmentViewer
|
||||
|
||||
section.card-details.js-card-details(class='{{#if cardMaximized}}card-details-maximized{{/if}}' class='{{#if isPopup}}card-details-popup{{/if}}'): .card-details-canvas
|
||||
section.card-details.js-card-details.nodragscroll(class='{{#if cardMaximized}}card-details-maximized{{/if}}' class='{{#if isPopup}}card-details-popup{{/if}}'): .card-details-canvas
|
||||
.card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}')
|
||||
+inlinedForm(classNames="js-card-details-title")
|
||||
+editCardTitleForm
|
||||
@@ -13,11 +13,12 @@ template(name="cardDetails")
|
||||
unless isMiniScreen
|
||||
unless isPopup
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
|
||||
if cardMaximized
|
||||
a.fa.fa-window-minimize.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
|
||||
else
|
||||
a.fa.fa-window-maximize.maximize-card-details.js-maximize-card-details(title="{{_ 'maximize-card'}}")
|
||||
if currentUser.isBoardMember
|
||||
if canModifyCard
|
||||
if cardMaximized
|
||||
a.fa.fa-window-minimize.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
|
||||
else
|
||||
a.fa.fa-window-maximize.maximize-card-details.js-maximize-card-details(title="{{_ 'maximize-card'}}")
|
||||
if canModifyCard
|
||||
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
a.fa.fa-link.card-copy-button.js-copy-link(
|
||||
id="cardURL_copy"
|
||||
@@ -29,7 +30,7 @@ template(name="cardDetails")
|
||||
else
|
||||
unless isPopup
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
|
||||
if currentUser.isBoardMember
|
||||
if canModifyCard
|
||||
a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
a.fa.fa-link.card-copy-mobile-button.js-copy-link(
|
||||
id="cardURL_copy"
|
||||
@@ -524,12 +525,12 @@ template(name="cardDetails")
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
if currentBoard.allowsDescriptionText
|
||||
a.js-open-inlined-form
|
||||
a.js-open-inlined-form(title="{{_ 'edit'}}" value=title)
|
||||
i.fa.fa-pencil-square-o
|
||||
a.js-open-inlined-form(title="{{_ 'edit'}}" value=title)
|
||||
if getDescription
|
||||
+viewer
|
||||
= getDescription
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
if (hasUnsavedValue 'cardDescription' _id)
|
||||
p.quiet
|
||||
| {{_ 'unsaved-description'}}
|
||||
@@ -548,7 +549,7 @@ template(name="cardDetails")
|
||||
.card-checklist-attachmentGallery.card-checklists
|
||||
if currentBoard.allowsChecklists
|
||||
hr
|
||||
+checklists(cardId = _id)
|
||||
+checklists(cardId = _id card = this)
|
||||
if currentBoard.allowsSubtasks
|
||||
hr
|
||||
+subtasks(cardId = _id)
|
||||
@@ -568,25 +569,34 @@ template(name="cardDetails")
|
||||
+attachmentGallery
|
||||
hr
|
||||
|
||||
unless currentUser.isNoComments
|
||||
.comment-title
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-comment-o
|
||||
| {{_ 'comments'}}
|
||||
|
||||
if currentBoard.allowsComments
|
||||
if currentUser.isBoardMember
|
||||
unless currentUser.isNoComments
|
||||
+commentForm
|
||||
+comments
|
||||
hr
|
||||
|
||||
.card-details-right
|
||||
|
||||
unless currentUser.isNoComments
|
||||
.activity-title
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-history
|
||||
| {{ _ 'activity'}}
|
||||
| {{ _ 'activities'}}
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch(title="{{_ 'hide-system-messages'}}")
|
||||
//span.toggle-switch-title
|
||||
if hiddenSystemMessages
|
||||
input.toggle-switch(type="checkbox" id="toggleButton" checked="checked")
|
||||
.material-toggle-switch(title="{{_ 'show-activities'}}")
|
||||
if showActivities
|
||||
input.toggle-switch(type="checkbox" id="toggleShowActivitiesCard" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleButton")
|
||||
label.toggle-label(for="toggleButton")
|
||||
if currentBoard.allowsComments
|
||||
if currentUser.isBoardMember
|
||||
unless currentUser.isNoComments
|
||||
+commentForm
|
||||
input.toggle-switch(type="checkbox" id="toggleShowActivitiesCard")
|
||||
label.toggle-label(for="toggleShowActivitiesCard")
|
||||
|
||||
unless currentUser.isNoComments
|
||||
if isLoaded.get
|
||||
if isLinkedCard
|
||||
|
||||
@@ -63,10 +63,6 @@ BlazeComponent.extendComponent({
|
||||
return card.findWatcher(Meteor.userId());
|
||||
},
|
||||
|
||||
hiddenSystemMessages() {
|
||||
return ReactiveCache.getCurrentUser().hasHiddenSystemMessages();
|
||||
},
|
||||
|
||||
customFieldsGrid() {
|
||||
return ReactiveCache.getCurrentUser().hasCustomFieldsGrid();
|
||||
},
|
||||
@@ -76,66 +72,6 @@ BlazeComponent.extendComponent({
|
||||
return !Utils.getPopupCardId() && ReactiveCache.getCurrentUser().hasCardMaximized();
|
||||
},
|
||||
|
||||
scrollParentContainer() {
|
||||
const cardPanelWidth = 600;
|
||||
const parentComponent = this.parentComponent();
|
||||
/*
|
||||
// Incomplete fix about bug where opening card scrolls to wrong place
|
||||
// https://github.com/wekan/wekan/issues/4572#issuecomment-1184149395
|
||||
// TODO sometimes parentComponent is not available, maybe because it's not
|
||||
// yet created?!
|
||||
//
|
||||
// uncommented again by chrisi51
|
||||
// only with that, the autoscroll feature is working properly
|
||||
// after my fixes, all scrollings where correct
|
||||
*/
|
||||
if (!parentComponent) return;
|
||||
const bodyBoardComponent = parentComponent.parentComponent();
|
||||
|
||||
|
||||
//On Mobile View Parent is Board, Not Board Body. I cant see how this funciton should work then.
|
||||
if (bodyBoardComponent === null) return;
|
||||
const $cardView = this.$(this.firstNode());
|
||||
const $cardContainer = bodyBoardComponent.$('.js-swimlanes');
|
||||
|
||||
/*
|
||||
// Incomplete fix about bug where opening card scrolls to wrong place
|
||||
// https://github.com/wekan/wekan/issues/4572#issuecomment-1184149395
|
||||
// TODO sometimes cardContainer is not available, maybe because it's not yet
|
||||
// created?!
|
||||
if (!$cardContainer) return;
|
||||
*/
|
||||
|
||||
const cardContainerScroll = $cardContainer.scrollLeft();
|
||||
const cardContainerWidth = $cardContainer.width();
|
||||
|
||||
const cardViewStart = $cardView.offset().left;
|
||||
const cardViewEnd = cardViewStart + cardPanelWidth;
|
||||
|
||||
let offset = false;
|
||||
if (cardViewStart < 0) {
|
||||
offset = cardViewStart;
|
||||
} else if (cardViewEnd > cardContainerWidth) {
|
||||
offset = cardViewEnd - cardContainerWidth;
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
bodyBoardComponent.scrollLeft(cardContainerScroll + offset);
|
||||
}
|
||||
|
||||
//Scroll top
|
||||
const cardViewStartTop = $cardView.offset().top;
|
||||
const cardContainerScrollTop = $cardContainer.scrollTop();
|
||||
|
||||
let topOffset = false;
|
||||
if (cardViewStartTop !== 100) {
|
||||
topOffset = cardViewStartTop - 100;
|
||||
}
|
||||
if (topOffset !== false) {
|
||||
bodyBoardComponent.scrollTop(cardContainerScrollTop + topOffset);
|
||||
}
|
||||
},
|
||||
|
||||
presentParentTask() {
|
||||
let result = this.currentBoard.presentParentTask;
|
||||
if (result === null || result === undefined) {
|
||||
@@ -221,12 +157,6 @@ BlazeComponent.extendComponent({
|
||||
//-------------
|
||||
}
|
||||
|
||||
if (!Utils.isMiniScreen()) {
|
||||
Meteor.setTimeout(() => {
|
||||
this.scrollParentContainer();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
const $checklistsDom = this.$('.card-checklist-items');
|
||||
|
||||
$checklistsDom.sortable({
|
||||
@@ -443,8 +373,11 @@ BlazeComponent.extendComponent({
|
||||
Session.set('cardDetailsIsDragging', false);
|
||||
Session.set('cardDetailsIsMouseDown', false);
|
||||
},
|
||||
'click #toggleButton'() {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
'click #toggleShowActivitiesCard'() {
|
||||
this.data().toggleShowActivities();
|
||||
},
|
||||
'click #toggleHideCheckedChecklistItems'() {
|
||||
this.data().toggleHideCheckedChecklistItems();
|
||||
},
|
||||
'click #toggleCustomFieldsGridButton'() {
|
||||
Meteor.call('toggleCustomFieldsGrid');
|
||||
@@ -452,20 +385,10 @@ BlazeComponent.extendComponent({
|
||||
'click .js-maximize-card-details'() {
|
||||
Meteor.call('toggleCardMaximized');
|
||||
autosize($('.card-details'));
|
||||
// if (!Utils.isMiniScreen()) {
|
||||
// Meteor.setTimeout(() => {
|
||||
// this.scrollParentContainer();
|
||||
// }, 500);
|
||||
// }
|
||||
},
|
||||
'click .js-minimize-card-details'() {
|
||||
Meteor.call('toggleCardMaximized');
|
||||
autosize($('.card-details'));
|
||||
if (!Utils.isMiniScreen()) {
|
||||
Meteor.setTimeout(() => {
|
||||
this.scrollParentContainer();
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
'click .js-vote'(e) {
|
||||
const forIt = $(e.target).hasClass('js-vote-positive');
|
||||
@@ -922,13 +845,15 @@ BlazeComponent.extendComponent({
|
||||
'click .js-palette-color'() {
|
||||
this.currentColor.set(this.currentData().color);
|
||||
},
|
||||
'click .js-submit'() {
|
||||
'click .js-submit'(event) {
|
||||
event.preventDefault();
|
||||
this.currentCard.setColor(this.currentColor.get());
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-remove-color'() {
|
||||
'click .js-remove-color'(event) {
|
||||
event.preventDefault();
|
||||
this.currentCard.setColor(null);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -8,20 +8,6 @@ textarea.js-edit-checklist-item {
|
||||
resize: none;
|
||||
height: 34px;
|
||||
}
|
||||
.card-details .text-show-at-minicard {
|
||||
width: 350px;
|
||||
text-align: left;
|
||||
}
|
||||
.minicard .text-show-at-minicard {
|
||||
display: none;
|
||||
}
|
||||
.text-some-space {
|
||||
width: 20px;
|
||||
}
|
||||
.text-hide-checked-items {
|
||||
width: 400px;
|
||||
text-align: left;
|
||||
}
|
||||
.delete-text,
|
||||
.js-delete-checklist-item,
|
||||
.js-convert-checklist-item-to-card {
|
||||
@@ -40,8 +26,6 @@ textarea.js-edit-checklist-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
|
||||
.checklist-progress-bar-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -9,10 +9,19 @@ template(name="checklists")
|
||||
else
|
||||
a.add-checklist-top.js-open-inlined-form(title="{{_ 'add-checklist'}}")
|
||||
i.fa.fa-plus
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch(title="{{_ 'hide-finished-checklist'}}")
|
||||
//span.toggle-switch-title
|
||||
if card.hideFinishedChecklistIfItemsAreHidden
|
||||
input.toggle-switch(type="checkbox" id="toggleHideFinishedChecklist" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleHideFinishedChecklist")
|
||||
label.toggle-label(for="toggleHideFinishedChecklist")
|
||||
|
||||
.card-checklist-items
|
||||
each checklist in checklists
|
||||
+checklistDetail(checklist=checklist)
|
||||
if checklist.showChecklist card.hideFinishedChecklistIfItemsAreHidden
|
||||
+checklistDetail(checklist = checklist card = card)
|
||||
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
|
||||
@@ -22,7 +31,7 @@ template(name="checklists")
|
||||
i.fa.fa-plus
|
||||
|
||||
template(name="checklistDetail")
|
||||
.js-checklist.checklist
|
||||
.js-checklist.checklist.nodragscroll
|
||||
+inlinedForm(classNames="js-edit-checklist-title" checklist = checklist)
|
||||
+editChecklistItemForm(checklist = checklist)
|
||||
else
|
||||
@@ -47,7 +56,7 @@ template(name="checklistDetail")
|
||||
.checklist-progress-text {{finishedPercent}}%
|
||||
.checklist-progress-bar
|
||||
.checklist-progress(style="width:{{finishedPercent}}%")
|
||||
+checklistItems(checklist = checklist)
|
||||
+checklistItems(checklist = checklist card = card)
|
||||
|
||||
template(name="checklistDeletePopup")
|
||||
p {{_ 'confirm-checklist-delete-popup'}}
|
||||
@@ -64,6 +73,12 @@ template(name="addChecklistItemForm")
|
||||
.material-toggle-switch(title="{{_ 'newlineBecomesNewChecklistItem'}}")
|
||||
input.toggle-switch(type="checkbox" id="toggleNewlineBecomesNewChecklistItem")
|
||||
label.toggle-label(for="toggleNewlineBecomesNewChecklistItem")
|
||||
| {{_ 'newLineNewItem'}}
|
||||
if $eq position 'top'
|
||||
.material-toggle-switch(title="{{_ 'newlineBecomesNewChecklistItemOriginOrder'}}")
|
||||
input.toggle-switch(type="checkbox" id="toggleNewlineBecomesNewChecklistItemOriginOrder")
|
||||
label.toggle-label(for="toggleNewlineBecomesNewChecklistItemOriginOrder")
|
||||
| {{_ 'originOrder'}}
|
||||
|
||||
template(name="editChecklistItemForm")
|
||||
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
|
||||
@@ -87,7 +102,7 @@ template(name="checklistItems")
|
||||
if checklist.items.length
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist position="top")
|
||||
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=true)
|
||||
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=true position="top")
|
||||
else
|
||||
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
|
||||
i.fa.fa-plus
|
||||
@@ -96,7 +111,7 @@ template(name="checklistItems")
|
||||
+inlinedForm(classNames="js-edit-checklist-item" item = item checklist = checklist)
|
||||
+editChecklistItemForm(type = 'item' item = item checklist = checklist)
|
||||
else
|
||||
+checklistItemDetail(item = item checklist = checklist)
|
||||
+checklistItemDetail(item = item checklist = checklist card = card)
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist)
|
||||
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=true)
|
||||
@@ -105,7 +120,7 @@ template(name="checklistItems")
|
||||
i.fa.fa-plus
|
||||
|
||||
template(name='checklistItemDetail')
|
||||
.js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if hideCheckedItems}} invisible{{/if}}{{/if}}"
|
||||
.js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if checklist.hideCheckedChecklistItems}} invisible{{/if}}{{/if}}{{#if checklist.hideAllChecklistItems}} is-checked invisible{{/if}}"
|
||||
role="checkbox" aria-checked="{{#if item.isFinished }}true{{else}}false{{/if}}" tabindex="0")
|
||||
if canModifyCard
|
||||
.check-box-container
|
||||
@@ -122,27 +137,6 @@ template(name='checklistItemDetail')
|
||||
= item.title
|
||||
|
||||
template(name="checklistActionsPopup")
|
||||
if currentUser.isBoardMember
|
||||
span.text-show-at-minicard
|
||||
| {{_ 'show-at-minicard'}}
|
||||
.material-toggle-switch(title="{{_ 'show-checklist-at-minicard'}}")
|
||||
if showAtMinicard
|
||||
input.toggle-switch(type="checkbox" id="toggleShowChecklistAtMinicardButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleShowChecklistAtMinicardButton")
|
||||
label.toggle-label(for="toggleShowChecklistAtMinicardButton")
|
||||
hr
|
||||
span.text-hide-checked-items
|
||||
| {{_ 'hide-checked-items'}}
|
||||
.material-toggle-switch(title="{{_ 'hide-checked-items'}}")
|
||||
//span.toggle-switch-title
|
||||
//.check-square-icon.i.fa.fa-check-square-o
|
||||
if hideCheckedItems
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton")
|
||||
label.toggle-label(for="toggleHideCheckedItemsButton")
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-delete-checklist.delete-checklist
|
||||
@@ -154,6 +148,24 @@ template(name="checklistActionsPopup")
|
||||
a.js-copy-checklist.copy-checklist
|
||||
i.fa.fa-copy
|
||||
| {{_ "copyChecklist"}} ...
|
||||
a.js-hide-checked-checklist-items
|
||||
i.fa.fa-eye-slash
|
||||
| {{_ "hideCheckedChecklistItems"}} ...
|
||||
.material-toggle-switch(title="{{_ 'hide-checked-items'}}")
|
||||
if checklist.hideCheckedChecklistItems
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedChecklistItems_{{checklist._id}}" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedChecklistItems_{{checklist._id}}")
|
||||
label.toggle-label(for="toggleHideCheckedChecklistItems_{{checklist._id}}")
|
||||
a.js-hide-all-checklist-items
|
||||
i.fa.fa-ban
|
||||
| {{_ "hideAllChecklistItems"}} ...
|
||||
.material-toggle-switch(title="{{_ 'hideAllChecklistItems'}}")
|
||||
if checklist.hideAllChecklistItems
|
||||
input.toggle-switch(type="checkbox" id="toggleHideAllChecklistItems_{{checklist._id}}" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleHideAllChecklistItems_{{checklist._id}}")
|
||||
label.toggle-label(for="toggleHideAllChecklistItems_{{checklist._id}}")
|
||||
|
||||
template(name="copyChecklistPopup")
|
||||
+copyAndMoveChecklist
|
||||
|
||||
@@ -119,6 +119,7 @@ BlazeComponent.extendComponent({
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-add-checklist-item');
|
||||
const newlineBecomesNewChecklistItem = this.find('input#toggleNewlineBecomesNewChecklistItem');
|
||||
const newlineBecomesNewChecklistItemOriginOrder = this.find('input#toggleNewlineBecomesNewChecklistItemOriginOrder');
|
||||
const title = textarea.value.trim();
|
||||
const checklist = this.currentData().checklist;
|
||||
|
||||
@@ -127,22 +128,28 @@ BlazeComponent.extendComponent({
|
||||
if (newlineBecomesNewChecklistItem.checked) {
|
||||
checklistItems = title.split('\n').map(_value => _value.trim());
|
||||
if (this.currentData().position === 'top') {
|
||||
checklistItems = checklistItems.reverse();
|
||||
if (newlineBecomesNewChecklistItemOriginOrder.checked === false) {
|
||||
checklistItems = checklistItems.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
let addIndex;
|
||||
let sortIndex;
|
||||
if (this.currentData().position === 'top') {
|
||||
sortIndex = Utils.calculateIndexData(null, checklist.firstItem()).base;
|
||||
addIndex = -1;
|
||||
} else {
|
||||
sortIndex = Utils.calculateIndexData(checklist.lastItem(), null).base;
|
||||
addIndex = 1;
|
||||
}
|
||||
for (let checklistItem of checklistItems) {
|
||||
let sortIndex;
|
||||
if (this.currentData().position === 'top') {
|
||||
sortIndex = Utils.calculateIndexData(null, checklist.firstItem()).base;
|
||||
} else {
|
||||
sortIndex = Utils.calculateIndexData(checklist.lastItem(), null).base;
|
||||
}
|
||||
ChecklistItems.insert({
|
||||
title: checklistItem,
|
||||
checklistId: checklist._id,
|
||||
cardId: checklist.cardId,
|
||||
sort: sortIndex,
|
||||
});
|
||||
sortIndex += addIndex;
|
||||
}
|
||||
}
|
||||
// We keep the form opened, empty it.
|
||||
@@ -201,12 +208,8 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
events() {
|
||||
const events = {
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
...events,
|
||||
'click .js-open-checklist-details-menu': Popup.open('checklistActions'),
|
||||
'submit .js-add-checklist': this.addChecklist,
|
||||
'submit .js-edit-checklist-title': this.editChecklist,
|
||||
@@ -217,6 +220,10 @@ BlazeComponent.extendComponent({
|
||||
'focus .js-add-checklist-item': this.focusChecklistItem,
|
||||
// add and delete checklist / checklist-item
|
||||
'click .js-open-inlined-form': this.closeAllInlinedForms,
|
||||
'click #toggleHideFinishedChecklist'(event) {
|
||||
event.preventDefault();
|
||||
this.data().card.toggleHideFinishedChecklist();
|
||||
},
|
||||
keydown: this.pressKey,
|
||||
},
|
||||
];
|
||||
@@ -271,16 +278,6 @@ Template.checklists.helpers({
|
||||
const ret = card.checklists();
|
||||
return ret;
|
||||
},
|
||||
showAtMinicard() {
|
||||
const card = ReactiveCache.getCard(this.cardId);
|
||||
const ret = card.checklists({'showAtMinicard':1});
|
||||
return ret;
|
||||
},
|
||||
hideCheckedItems() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) return currentUser.hasHideCheckedItems();
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -303,26 +300,9 @@ BlazeComponent.extendComponent({
|
||||
}).register('addChecklistItemForm');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
toggleItem() {
|
||||
const checklist = this.currentData().checklist;
|
||||
const item = this.currentData().item;
|
||||
if (checklist && item && item._id) {
|
||||
item.toggleItem();
|
||||
}
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-checklist-item .check-box-container': this.toggleItem,
|
||||
'click #toggleShowChecklistAtMinicardButton'() {
|
||||
const checklist = this.checklist;
|
||||
if (checklist && checklist._id) {
|
||||
Meteor.call('toggleShowChecklistAtMinicard', checklist._id);
|
||||
}
|
||||
},
|
||||
'click #toggleHideCheckedItemsButton'() {
|
||||
Meteor.call('toggleHideCheckedItems');
|
||||
},
|
||||
'click .js-delete-checklist': Popup.afterConfirm('checklistDelete', function () {
|
||||
Popup.back(2);
|
||||
const checklist = this.checklist;
|
||||
@@ -332,6 +312,16 @@ BlazeComponent.extendComponent({
|
||||
}),
|
||||
'click .js-move-checklist': Popup.open('moveChecklist'),
|
||||
'click .js-copy-checklist': Popup.open('copyChecklist'),
|
||||
'click .js-hide-checked-checklist-items'(event) {
|
||||
event.preventDefault();
|
||||
this.data().checklist.toggleHideCheckedChecklistItems();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-hide-all-checklist-items'(event) {
|
||||
event.preventDefault();
|
||||
this.data().checklist.toggleHideAllChecklistItems();
|
||||
Popup.back();
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -357,11 +347,6 @@ BlazeComponent.extendComponent({
|
||||
}).register('editChecklistItemForm');
|
||||
|
||||
Template.checklistItemDetail.helpers({
|
||||
hideCheckedItems() {
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
if (user) return user.hasHideCheckedItems();
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
||||
@@ -37,5 +37,4 @@ template(name="cardLabelsPopup")
|
||||
= name
|
||||
if(isLabelSelected ../_id)
|
||||
i.card-label-selectable-icon.fa.fa-check
|
||||
if currentUser.isBoardAdmin
|
||||
a.quiet-button.full.js-add-label {{_ 'label-create'}}
|
||||
a.quiet-button.full.js-add-label {{_ 'label-create'}}
|
||||
@@ -1,12 +1,3 @@
|
||||
.minicard .checklists-title,
|
||||
.minicard .add-checklist,
|
||||
.minicard .add-checklist-item,
|
||||
.minicard .checklist-details-menu {
|
||||
display: none;
|
||||
}
|
||||
.minicard .checklist-progress-bar-container {
|
||||
width: 190px; /* TODO: Add adjustable width https://github.com/wekan/wekan/pull/4964 */
|
||||
}
|
||||
.minicard-wrapper {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@@ -56,10 +47,12 @@
|
||||
float: right;
|
||||
font-size: 18px;
|
||||
padding-right: 30px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.minicard-details-menu {
|
||||
float: right;
|
||||
font-size: 18px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
@media print {
|
||||
.minicard-details-menu,
|
||||
@@ -99,7 +92,7 @@
|
||||
background-size: contain;
|
||||
height: 145px;
|
||||
user-select: none;
|
||||
margin: -6px -8px 6px -8px;
|
||||
margin: 6px -8px 6px -8px;
|
||||
border-radius: top 2px;
|
||||
}
|
||||
.minicard .minicard-labels {
|
||||
@@ -136,14 +129,6 @@
|
||||
max-width: 100%;
|
||||
margin-right: 4px;
|
||||
}
|
||||
/*
|
||||
.minicard .checklists-title,
|
||||
.minicard .add-checklist,
|
||||
.minicard .add-checklist-item,
|
||||
.minicard .checklist-details-menu {
|
||||
display: none;
|
||||
}
|
||||
*/
|
||||
.minicard .handle {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@@ -173,7 +158,6 @@
|
||||
.minicard .minicard-title .viewer {
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
max-width: 230px;
|
||||
}
|
||||
}
|
||||
.minicard .dates {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
template(name="minicard")
|
||||
.minicard(
|
||||
.minicard.nodragscroll(
|
||||
class="{{#if isLinkedCard}}linked-card{{/if}}"
|
||||
class="{{#if isLinkedBoard}}linked-board{{/if}}"
|
||||
class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}")
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
a.fa.fa-navicon.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
else
|
||||
a.fa.fa-navicon.minicard-details-menu.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
if canModifyCard
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
a.fa.fa-navicon.minicard-details-menu-with-handle.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
else
|
||||
a.fa.fa-navicon.minicard-details-menu.js-open-minicard-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
.dates
|
||||
if getReceived
|
||||
unless getStart
|
||||
@@ -111,12 +112,6 @@ template(name="minicard")
|
||||
+viewer
|
||||
= trueValue
|
||||
|
||||
.card-checklist-attachmentGalleries
|
||||
.card-checklist-attachmentGallery.card-checklists
|
||||
if currentBoard.allowsChecklists
|
||||
//hr
|
||||
//+checklists(cardId=_id showAtMinicard=true)
|
||||
|
||||
if showAssignee
|
||||
if getAssignees
|
||||
.minicard-assignees.js-minicard-assignees
|
||||
@@ -129,12 +124,12 @@ template(name="minicard")
|
||||
each getMembers
|
||||
+userAvatar(userId=this)
|
||||
|
||||
if showCreator
|
||||
if showCreatorOnMinicard
|
||||
.minicard-creator
|
||||
+userAvatar(userId=this.userId noRemove=true)
|
||||
|
||||
.badges
|
||||
unless currentUser.isNoComments
|
||||
if canModifyCard
|
||||
if comments.length
|
||||
.badge(title="{{_ 'card-comments-title' comments.length }}")
|
||||
span.badge-icon.fa.fa-comment-o.badge-comment.badge-text
|
||||
@@ -189,12 +184,11 @@ template(name="editCardSortOrderPopup")
|
||||
|
||||
template(name="minicardDetailsActionsPopup")
|
||||
ul.pop-over-list
|
||||
if currentUser.isBoardAdmin
|
||||
if canModifyCard
|
||||
li
|
||||
a.js-move-card
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ 'moveCardPopup-title'}}
|
||||
unless currentUser.isWorker
|
||||
li
|
||||
a.js-copy-card
|
||||
i.fa.fa-copy
|
||||
|
||||
@@ -37,16 +37,12 @@ BlazeComponent.extendComponent({
|
||||
return ret;
|
||||
},
|
||||
|
||||
showCreator() {
|
||||
showCreatorOnMinicard() {
|
||||
// cache "board" to reduce the mini-mongodb access
|
||||
const board = this.data().board();
|
||||
let ret = false;
|
||||
if (board) {
|
||||
ret =
|
||||
board.allowsCreator === null ||
|
||||
board.allowsCreator === undefined ||
|
||||
board.allowsCreator
|
||||
;
|
||||
ret = board.allowsCreatorOnMinicard ?? false;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
@@ -92,11 +88,6 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .minicard-checklists'() {
|
||||
// Prevents clicking checklist at minicard from opening card details,
|
||||
// while still allowing checking checlist items.
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-linked-link'() {
|
||||
if (this.data().isLinkedCard()) Utils.goCardId(this.data().linkedId);
|
||||
else if (this.data().isLinkedBoard())
|
||||
|
||||
@@ -7,18 +7,14 @@
|
||||
border-left: 1px solid #ccc;
|
||||
padding: 0;
|
||||
float: left;
|
||||
min-width: 100px; /* TODO(mark-i-m): hardcoded? */
|
||||
/*max-width: 270px;*/
|
||||
/* Reverted incomplete change list width: */
|
||||
/* https://github.com/wekan/wekan/issues/4558 */
|
||||
/* Orinal width: 270px. Changes not saved yet: */
|
||||
/*resize: both; - List width and height resizeable */
|
||||
/* overflow: auto; - List width and height resizeable */
|
||||
flex: 1;
|
||||
}
|
||||
[id^="swimlane-"] .list:first-child {
|
||||
min-width: 20px;
|
||||
}
|
||||
.list:first-child {
|
||||
min-width: 20px;
|
||||
margin-left: 5px;
|
||||
border-left: none;
|
||||
flex: none;
|
||||
}
|
||||
.card-details + .list {
|
||||
border-left: none;
|
||||
@@ -37,6 +33,9 @@
|
||||
box-shadow: none;
|
||||
height: 100px;
|
||||
}
|
||||
.list.list-collapsed {
|
||||
flex: none;
|
||||
}
|
||||
.list.list-composer .open-list-composer,
|
||||
.list .list-composer .open-list-composer {
|
||||
color: #8c8c8c;
|
||||
@@ -48,7 +47,7 @@
|
||||
}
|
||||
.list-header-add {
|
||||
flex: 0 0 auto;
|
||||
padding: 20px 12px 4px;
|
||||
padding: 12px;
|
||||
position: relative;
|
||||
min-height: 20px;
|
||||
}
|
||||
@@ -81,6 +80,20 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.list-rotated {
|
||||
width: 10px;
|
||||
height: 250px;
|
||||
margin-top: -90px;
|
||||
margin-left: -110px;
|
||||
margin-right: 0;
|
||||
transform: rotate(90deg);
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.list-header .list-rotated {
|
||||
|
||||
}
|
||||
.list-header .list-header-watch-icon {
|
||||
padding-left: 10px;
|
||||
@@ -99,6 +112,23 @@
|
||||
color: #a6a6a6;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.list-header .list-header-collapse-right {
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.list-header .list-header-collapse-left {
|
||||
color: #a6a6a6;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.list-header .list-header-uncollapse-left {
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.list-header .list-header-uncollapse-right {
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.list-header .list-header-collapse {
|
||||
color: #a6a6a6;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.list-header .highlight {
|
||||
color: #ce1414;
|
||||
}
|
||||
@@ -220,11 +250,11 @@
|
||||
padding: 15px 19px;
|
||||
}
|
||||
.list-header {
|
||||
padding: 0 12px 0px;
|
||||
/*Updated padding values for mobile devices, this should fix text grouping issue*/
|
||||
padding: 20px 0px 20px 0px;
|
||||
border-bottom: 0px solid #e4e4e4;
|
||||
height: 60px;
|
||||
min-height: 30px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.list-header .list-header-left-icon {
|
||||
@@ -297,7 +327,6 @@
|
||||
}
|
||||
.list-header-white {
|
||||
border-bottom: 6px solid #fff;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.list-header-green {
|
||||
border-bottom: 6px solid #3cb500;
|
||||
@@ -330,7 +359,7 @@
|
||||
border-bottom: 6px solid #51e898;
|
||||
}
|
||||
.list-header-silver {
|
||||
border-bottom: 6px solid unset;
|
||||
border-bottom: 6px solid #e4e4e4;
|
||||
}
|
||||
.list-header-peachpuff {
|
||||
border-bottom: 6px solid #ffdab9;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
template(name='list')
|
||||
.list.js-list(id="js-list-{{_id}}"
|
||||
style="width:{{listWidth}}px;")
|
||||
style="{{#unless collapsed}}min-width:{{listWidth}}px;{{/unless}}"
|
||||
class="{{#if collapsed}}list-collapsed{{/if}}")
|
||||
+listHeader
|
||||
+listBody
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
listWidth() {
|
||||
const user = Meteor.user();
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
const list = Template.currentData();
|
||||
return user.getListWidth(list.boardId, list._id);
|
||||
},
|
||||
|
||||
@@ -1,37 +1,38 @@
|
||||
template(name="listBody")
|
||||
.list-body
|
||||
.minicards.clearfix.js-minicards(class="{{#if reachedWipLimit}}js-list-full{{/if}}")
|
||||
if cards.length
|
||||
+inlinedForm(autoclose=false position="top")
|
||||
+addCardForm(listId=_id position="top")
|
||||
ul.sidebar-list
|
||||
each customFieldsSum
|
||||
li
|
||||
+viewer
|
||||
= name
|
||||
if $eq customFieldsSum.type "number"
|
||||
unless collapsed
|
||||
.list-body
|
||||
.minicards.clearfix.js-minicards(class="{{#if reachedWipLimit}}js-list-full{{/if}}")
|
||||
if cards.length
|
||||
+inlinedForm(autoclose=false position="top")
|
||||
+addCardForm(listId=_id position="top")
|
||||
ul.sidebar-list
|
||||
each customFieldsSum
|
||||
li
|
||||
+viewer
|
||||
= value
|
||||
if $eq customFieldsSum.type "currency"
|
||||
+viewer
|
||||
= formattedCurrencyCustomFieldValue(value)
|
||||
each (cardsWithLimit (idOrNull ../../_id))
|
||||
a.minicard-wrapper.js-minicard(href=originRelativeUrl
|
||||
class="{{#if cardIsSelected}}is-selected{{/if}}"
|
||||
class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
|
||||
if MultiSelection.isActive
|
||||
.materialCheckBox.multi-selection-checkbox.js-toggle-multi-selection(
|
||||
class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
|
||||
+minicard(this)
|
||||
if (showSpinner (idOrNull ../../_id))
|
||||
+spinnerList
|
||||
= name
|
||||
if $eq customFieldsSum.type "number"
|
||||
+viewer
|
||||
= value
|
||||
if $eq customFieldsSum.type "currency"
|
||||
+viewer
|
||||
= formattedCurrencyCustomFieldValue(value)
|
||||
each (cardsWithLimit (idOrNull ../../_id))
|
||||
a.minicard-wrapper.js-minicard(href=originRelativeUrl
|
||||
class="{{#if cardIsSelected}}is-selected{{/if}}"
|
||||
class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
|
||||
if MultiSelection.isActive
|
||||
.materialCheckBox.multi-selection-checkbox.js-toggle-multi-selection(
|
||||
class="{{#if MultiSelection.isSelected _id}}is-checked{{/if}}")
|
||||
+minicard(this)
|
||||
if (showSpinner (idOrNull ../../_id))
|
||||
+spinnerList
|
||||
|
||||
if canSeeAddCard
|
||||
+inlinedForm(autoclose=false position="bottom")
|
||||
+addCardForm(listId=_id position="bottom")
|
||||
else
|
||||
a.open-minicard-composer.js-card-composer.js-open-inlined-form(title="{{_ 'add-card-to-bottom-of-list'}}")
|
||||
i.fa.fa-plus
|
||||
if canSeeAddCard
|
||||
+inlinedForm(autoclose=false position="bottom")
|
||||
+addCardForm(listId=_id position="bottom")
|
||||
else
|
||||
a.open-minicard-composer.js-card-composer.js-open-inlined-form(title="{{_ 'add-card-to-bottom-of-list'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
template(name="spinnerList")
|
||||
.sk-spinner.sk-spinner-list(
|
||||
|
||||
@@ -646,6 +646,7 @@ BlazeComponent.extendComponent({
|
||||
if (this.isTemplateSearch) {
|
||||
const boardId = (ReactiveCache.getCurrentUser().profile || {}).templatesBoardId;
|
||||
if (boardId) {
|
||||
subManager.subscribe('board', boardId, false);
|
||||
this.board = ReactiveCache.getBoard(boardId);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
template(name="listHeader")
|
||||
.list-header.js-list-header(
|
||||
.list-header.js-list-header.nodragscroll(
|
||||
class="{{#if limitToShowCardsCount}}list-header-card-count{{/if}}"
|
||||
class=colorClass)
|
||||
+inlinedForm
|
||||
@@ -8,19 +8,40 @@ template(name="listHeader")
|
||||
if isMiniScreen
|
||||
if currentList
|
||||
a.list-header-left-icon.fa.fa-angle-left.js-unselect-list
|
||||
h2.list-header-name(
|
||||
title="{{ moment modifiedAt 'LLL' }}"
|
||||
class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
|
||||
+viewer
|
||||
= title
|
||||
if wipLimit.enabled
|
||||
| (
|
||||
span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.length}}
|
||||
|/#{wipLimit.value})
|
||||
|
||||
if showCardsCountForList cards.length
|
||||
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
|
||||
|
||||
else
|
||||
if collapsed
|
||||
a.js-collapse(title="{{_ 'uncollapse'}}")
|
||||
i.fa.fa-arrow-left.list-header-uncollapse-left
|
||||
i.fa.fa-arrow-right.list-header-uncollapse-right
|
||||
if showCardsCountForList cards.length
|
||||
br
|
||||
span.cardCount {{cardsCount}}
|
||||
if isMiniScreen
|
||||
h2.list-header-name(
|
||||
title="{{ moment modifiedAt 'LLL' }}"
|
||||
class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
|
||||
+viewer
|
||||
= title
|
||||
if wipLimit.enabled
|
||||
| (
|
||||
span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.length}}
|
||||
|/#{wipLimit.value})
|
||||
if showCardsCountForList cards.length
|
||||
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
|
||||
else
|
||||
div(class="{{#if collapsed}}list-rotated{{/if}}")
|
||||
h2.list-header-name(
|
||||
title="{{ moment modifiedAt 'LLL' }}"
|
||||
class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
|
||||
+viewer
|
||||
= title
|
||||
if wipLimit.enabled
|
||||
| (
|
||||
span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.length}}
|
||||
|/#{wipLimit.value})
|
||||
unless collapsed
|
||||
if showCardsCountForList cards.length
|
||||
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.length}}
|
||||
if isMiniScreen
|
||||
if currentList
|
||||
if isWatching
|
||||
@@ -36,16 +57,20 @@ template(name="listHeader")
|
||||
else if currentUser.isBoardMember
|
||||
if isWatching
|
||||
i.list-header-watch-icon.fa.fa-eye
|
||||
div.list-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
//if isBoardAdmin
|
||||
// a.fa.js-list-star.list-header-plus-top(class="fa-star{{#unless starred}}-o{{/unless}}")
|
||||
if canSeeAddCard
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}")
|
||||
a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
|
||||
if currentUser.isBoardAdmin
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
unless collapsed
|
||||
div.list-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
//if isBoardAdmin
|
||||
// a.fa.js-list-star.list-header-plus-top(class="fa-star{{#unless starred}}-o{{/unless}}")
|
||||
if canSeeAddCard
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-top(title="{{_ 'add-card-to-top-of-list'}}")
|
||||
a.js-collapse(title="{{_ 'collapse'}}")
|
||||
i.fa.fa-arrow-right.list-header-collapse-right
|
||||
i.fa.fa-arrow-left.list-header-collapse-left
|
||||
a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
|
||||
if currentUser.isBoardAdmin
|
||||
if isTouchScreenOrShowDesktopDragHandles
|
||||
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
|
||||
template(name="editListTitleForm")
|
||||
.list-composer
|
||||
|
||||
@@ -31,6 +31,17 @@ BlazeComponent.extendComponent({
|
||||
return !status;
|
||||
}
|
||||
},
|
||||
collapsed(check = undefined) {
|
||||
const list = Template.currentData();
|
||||
const status = list.isCollapsed();
|
||||
if (check === undefined) {
|
||||
// just check
|
||||
return status;
|
||||
} else {
|
||||
list.collapse(!status);
|
||||
return !status;
|
||||
}
|
||||
},
|
||||
editTitle(event) {
|
||||
event.preventDefault();
|
||||
const newTitle = this.childComponents('inlinedForm')[0]
|
||||
@@ -104,6 +115,10 @@ BlazeComponent.extendComponent({
|
||||
event.preventDefault();
|
||||
this.starred(!this.starred());
|
||||
},
|
||||
'click .js-collapse'(event) {
|
||||
event.preventDefault();
|
||||
this.collapsed(!this.collapsed());
|
||||
},
|
||||
'click .js-open-list-menu': Popup.open('listAction'),
|
||||
'click .js-add-card.list-header-plus-top'(event) {
|
||||
const listDom = $(event.target).parents(
|
||||
@@ -347,7 +362,7 @@ BlazeComponent.extendComponent({
|
||||
listWidthValue() {
|
||||
const list = Template.currentData();
|
||||
const board = list.boardId;
|
||||
return Meteor.user().getListWidth(board, list._id);
|
||||
return ReactiveCache.getCurrentUser().getListWidth(board, list._id);
|
||||
},
|
||||
|
||||
events() {
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
.new-comment a.fa.fa-brands.fa-markdown,
|
||||
.inlined-form a.fa.fa-brands.fa-markdown {
|
||||
float: right;
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: 60px;
|
||||
}
|
||||
.new-comment a.fa.fa-copy,
|
||||
.inlined-form a.fa.fa-copy {
|
||||
float: right;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
right: 5px;
|
||||
}
|
||||
.js-inlined-form.viewer.btn-sm {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 6px;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
template(name="editor")
|
||||
a.fa.fa-brands.fa-markdown(title="{{_ 'convert-to-markdown'}}")
|
||||
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
textarea.editor(
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
var converter = require('@wekanteam/html-to-markdown');
|
||||
|
||||
const specialHandles = [
|
||||
{userId: 'board_members', username: 'board_members'},
|
||||
@@ -9,6 +11,33 @@ const specialHandleNames = specialHandles.map(m => m.username);
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
// Start: Copy <pre> code https://github.com/wekan/wekan/issues/5149
|
||||
// TODO: Try to make copyPre visible at Card Details after editing or closing editor or Card Details.
|
||||
// - Also this same TODO below at event, if someone gets it working.
|
||||
var copy = function(target) {
|
||||
var textArea = document.createElement('textarea');
|
||||
textArea.setAttribute('style','width:1px;border:0;opacity:0;');
|
||||
document.body.appendChild(textArea);
|
||||
textArea.value = target.innerHTML;
|
||||
textArea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
var pres = document.querySelectorAll(".viewer > pre");
|
||||
pres.forEach(function(pre){
|
||||
var button = document.createElement("a");
|
||||
button.className = "fa fa-copy btn btn-sm right";
|
||||
// TODO: Translate text 'Copy text to clipboard'
|
||||
button.setAttribute('title','Copy text to clipboard');
|
||||
button.innerHTML = '';
|
||||
pre.parentNode.insertBefore(button, pre);
|
||||
button.addEventListener('click', function(e){
|
||||
e.preventDefault();
|
||||
copy(pre.childNodes[0]);
|
||||
})
|
||||
})
|
||||
// End: Copy <pre> code
|
||||
|
||||
const textareaSelector = 'textarea';
|
||||
const mentions = [
|
||||
// User mentions
|
||||
@@ -44,12 +73,14 @@ BlazeComponent.extendComponent({
|
||||
index: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const enableTextarea = function() {
|
||||
const $textarea = this.$(textareaSelector);
|
||||
autosize($textarea);
|
||||
$textarea.escapeableTextComplete(mentions);
|
||||
};
|
||||
if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) {
|
||||
/*
|
||||
if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === true || Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === 'true') {
|
||||
const isSmall = Utils.isMiniScreen();
|
||||
const toolbar = isSmall
|
||||
? [
|
||||
@@ -269,6 +300,8 @@ BlazeComponent.extendComponent({
|
||||
} else {
|
||||
enableTextarea();
|
||||
}
|
||||
*/
|
||||
enableTextarea();
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
@@ -280,6 +313,14 @@ BlazeComponent.extendComponent({
|
||||
const $tooltip = this.$('.copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
'click a.fa.fa-brands.fa-markdown'(event) {
|
||||
const $editor = this.$('textarea.editor');
|
||||
$editor[0].value = converter.convert($editor[0].value);
|
||||
},
|
||||
// TODO: Try to make copyPre visible at Card Details after editing or closing editor or Card Details.
|
||||
//'click .js-close-inlined-form'(event) {
|
||||
// Utils.copyPre();
|
||||
//},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -470,6 +470,7 @@ a:not(.disabled).is-active i.fa {
|
||||
.select-authentication {
|
||||
width: 100%;
|
||||
}
|
||||
.textBelowCustomLoginLogo,
|
||||
.auth-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -37,10 +37,13 @@ template(name="userFormsLayout")
|
||||
else
|
||||
img(src="{{pathFor '/wekan-logo.svg'}}" alt="" width="300" height="auto")
|
||||
br
|
||||
if currentSetting.textBelowCustomLoginLogo
|
||||
+viewer
|
||||
| {{currentSetting.textBelowCustomLoginLogo}}
|
||||
br
|
||||
if currentSetting.textBelowCustomLoginLogo
|
||||
hr
|
||||
section.textBelowCustomLoginLogo
|
||||
+viewer
|
||||
| {{currentSetting.textBelowCustomLoginLogo}}
|
||||
hr
|
||||
section.auth-layout
|
||||
section.auth-dialog
|
||||
if isLoading
|
||||
+loader
|
||||
|
||||
@@ -70,13 +70,14 @@ template(name="myCards")
|
||||
unless isMiniScreen
|
||||
.my-cards-board-badge(class=board.colorClass, id="header")
|
||||
.my-cards-card-title-table
|
||||
a.minicard-wrapper(href=card.originRelativeUrl)
|
||||
| {{card.title}}
|
||||
| {{card.title}}
|
||||
//a.minicard-wrapper(href=card.originRelativeUrl)
|
||||
// | {{card.title}}
|
||||
td
|
||||
| {{list.title}}
|
||||
td
|
||||
a(href=board.originRelativeUrl)
|
||||
| {{board.title}}
|
||||
//a(href=board.originRelativeUrl)
|
||||
td
|
||||
| {{swimlane.title}}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ template(name='notificationsDrawer')
|
||||
a.fa.fa-times-thin.close
|
||||
ul.notifications
|
||||
each transformedProfile.notifications
|
||||
+notification(activityData=activity index=dbIndex read=read)
|
||||
+notification(activityData=activityObj index=dbIndex read=read)
|
||||
if($gt unreadNotifications 0)
|
||||
a.all-read {{_ 'mark-all-as-read'}}
|
||||
if ($and ($.Session.get 'showReadNotifications') ($gt readNotifications 0))
|
||||
|
||||
@@ -58,18 +58,20 @@ template(name="rulesReport")
|
||||
h1 {{_ 'rulesReportTitle'}}
|
||||
if resultsCount
|
||||
table
|
||||
tr
|
||||
th Rule Title
|
||||
th Board Title
|
||||
th actionType
|
||||
th activityType
|
||||
thead
|
||||
tr
|
||||
th Rule Title
|
||||
th Board Title
|
||||
th actionType
|
||||
th activityType
|
||||
|
||||
each rule in results
|
||||
tr
|
||||
td {{ rule.title }}
|
||||
td {{ rule.boardTitle }}
|
||||
td {{ rule.action.actionType }}
|
||||
td {{ rule.trigger.activityType }}
|
||||
tbody
|
||||
tr
|
||||
td {{ rule.title }}
|
||||
td {{ rule.boardTitle }}
|
||||
td {{ rule.action.actionType }}
|
||||
td {{ rule.trigger.activityType }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
@@ -77,22 +79,24 @@ template(name="filesReport")
|
||||
h1 {{_ 'filesReportTitle'}}
|
||||
if resultsCount
|
||||
table
|
||||
tr
|
||||
th Filename
|
||||
th.right Size (kB)
|
||||
th MIME Type
|
||||
th Attachment ID
|
||||
th Board ID
|
||||
th Card ID
|
||||
thead
|
||||
tr
|
||||
th Filename
|
||||
th.right Size (kB)
|
||||
th MIME Type
|
||||
th Attachment ID
|
||||
th Board ID
|
||||
th Card ID
|
||||
|
||||
each att in results
|
||||
tr
|
||||
td {{ att.name }}
|
||||
td.right {{ fileSize att.size }}
|
||||
td {{ att.type }}
|
||||
td {{ att._id }}
|
||||
td {{ att.meta.boardId }}
|
||||
td {{ att.meta.cardId }}
|
||||
tbody
|
||||
tr
|
||||
td {{ att.name }}
|
||||
td.right {{ fileSize att.size }}
|
||||
td {{ att.type }}
|
||||
td {{ att._id }}
|
||||
td {{ att.meta.boardId }}
|
||||
td {{ att.meta.cardId }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
@@ -100,22 +104,24 @@ template(name="cardsReport")
|
||||
h1 {{_ 'cardsReportTitle'}}
|
||||
if resultsCount
|
||||
table.table
|
||||
tr
|
||||
th Card Title
|
||||
th Board
|
||||
th Swimlane
|
||||
th List
|
||||
th Members
|
||||
th Assignees
|
||||
thead
|
||||
tr
|
||||
th Card Title
|
||||
th Board
|
||||
th Swimlane
|
||||
th List
|
||||
th Members
|
||||
th Assignees
|
||||
|
||||
each card in results
|
||||
tr
|
||||
td {{abbreviate card.title }}
|
||||
td {{abbreviate card.board.title }}
|
||||
td {{abbreviate card.swimlane.title }}
|
||||
td {{abbreviate card.list.title }}
|
||||
td {{userNames card.members }}
|
||||
td {{userNames card.assignees }}
|
||||
tbody
|
||||
tr
|
||||
td {{abbreviate card.title }}
|
||||
td {{abbreviate card.board.title }}
|
||||
td {{abbreviate card.swimlane.title }}
|
||||
td {{abbreviate card.list.title }}
|
||||
td {{userNames card.members }}
|
||||
td {{userNames card.assignees }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
@@ -123,22 +129,25 @@ template(name="boardsReport")
|
||||
h1 {{_ 'boardsReportTitle'}}
|
||||
if resultsCount
|
||||
table.table
|
||||
tr
|
||||
th Title
|
||||
th Id
|
||||
th Permission
|
||||
th Archived?
|
||||
th Members
|
||||
th Organizations
|
||||
th Teams
|
||||
|
||||
each board in results
|
||||
thead
|
||||
tr
|
||||
td {{abbreviate board.title }}
|
||||
td {{abbreviate board._id }}
|
||||
td {{ board.permission }}
|
||||
td
|
||||
= yesOrNo(board.archived)
|
||||
td {{userNames board.members }}
|
||||
th Title
|
||||
th Id
|
||||
th Permission
|
||||
th Archived?
|
||||
th Members
|
||||
th Organizations
|
||||
th Teams
|
||||
each board in results
|
||||
tbody
|
||||
tr
|
||||
td {{abbreviate board.title }}
|
||||
td {{abbreviate board._id }}
|
||||
td {{ board.permission }}
|
||||
td
|
||||
= yesOrNo(board.archived)
|
||||
td {{userNames board.members }}
|
||||
td {{orgs board.orgs }}
|
||||
td {{teams board.teams }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
@@ -170,8 +170,27 @@ class AdminReport extends BlazeComponent {
|
||||
.join(", ");
|
||||
return ret;
|
||||
}
|
||||
teams(memberTeams) {
|
||||
const ret = (memberTeams || [])
|
||||
.map(_memberTeam => {
|
||||
const _ret = ReactiveCache.getTeam(_memberTeam.teamId)?.teamDisplayName || _memberTeam.teamId;
|
||||
return _ret;
|
||||
})
|
||||
.join(", ");
|
||||
return ret;
|
||||
}
|
||||
orgs(orgs) {
|
||||
const ret = (orgs || [])
|
||||
.map(_orgs => {
|
||||
const _ret = ReactiveCache.getOrg(_orgs.orgId)?.orgDisplayName || _orgs.orgId;
|
||||
return _ret;
|
||||
})
|
||||
.join(", ");
|
||||
return ret;
|
||||
}
|
||||
}.register('boardsReport'));
|
||||
|
||||
|
||||
(class extends AdminReport {
|
||||
collection = Cards;
|
||||
|
||||
|
||||
@@ -73,22 +73,25 @@ template(name="people")
|
||||
|
||||
template(name="orgGeneral")
|
||||
table
|
||||
tbody
|
||||
thead
|
||||
tr
|
||||
th {{_ 'displayName'}}
|
||||
th {{_ 'description'}}
|
||||
th {{_ 'shortName'}}
|
||||
th {{_ 'autoAddUsersWithDomainName'}}
|
||||
th {{_ 'website'}}
|
||||
th {{_ 'createdAt'}}
|
||||
th {{_ 'active'}}
|
||||
th
|
||||
+newOrgRow
|
||||
tbody
|
||||
tr
|
||||
each org in orgList
|
||||
+orgRow(orgId=org._id)
|
||||
|
||||
template(name="teamGeneral")
|
||||
table
|
||||
tbody
|
||||
thead
|
||||
tr
|
||||
th {{_ 'displayName'}}
|
||||
th {{_ 'description'}}
|
||||
@@ -98,6 +101,8 @@ template(name="teamGeneral")
|
||||
th {{_ 'active'}}
|
||||
th
|
||||
+newTeamRow
|
||||
tbody
|
||||
tr
|
||||
each team in teamList
|
||||
+teamRow(teamId=team._id)
|
||||
|
||||
@@ -105,7 +110,7 @@ template(name="peopleGeneral")
|
||||
#divAddOrRemoveTeamContainer
|
||||
+modifyTeamsUsers
|
||||
table
|
||||
tbody
|
||||
thead
|
||||
tr
|
||||
th
|
||||
+selectAllUser
|
||||
@@ -123,6 +128,8 @@ template(name="peopleGeneral")
|
||||
th {{_ 'teams'}}
|
||||
th
|
||||
+newUserRow
|
||||
tbody
|
||||
tr
|
||||
each user in peopleList
|
||||
+peopleRow(userId=user._id)
|
||||
|
||||
@@ -159,6 +166,10 @@ template(name="orgRow")
|
||||
td {{ orgData.orgShortName }}
|
||||
else
|
||||
td <s>{{ orgData.orgShortName }}</s>
|
||||
if orgData.orgIsActive
|
||||
td {{ orgData.orgAutoAddUsersWithDomainName }}
|
||||
else
|
||||
td <s>{{ orgData.orgAutoAddUsersWithDomainName }}</s>
|
||||
if orgData.orgIsActive
|
||||
td {{ orgData.orgWebsite }}
|
||||
else
|
||||
@@ -307,9 +318,12 @@ template(name="editOrgPopup")
|
||||
label
|
||||
| {{_ 'shortName'}}
|
||||
input.js-orgShortName(type="text" value=org.orgShortName required)
|
||||
label
|
||||
| {{_ 'autoAddUsersWithDomainName'}}
|
||||
input.js-orgAutoAddUsersWithDomainName(type="text" value=org.orgAutoAddUsersWithDomainName)
|
||||
label
|
||||
| {{_ 'website'}}
|
||||
input.js-orgWebsite(type="text" value=org.orgWebsite required)
|
||||
input.js-orgWebsite(type="text" value=org.orgWebsite)
|
||||
label
|
||||
| {{_ 'active'}}
|
||||
select.select-active.js-org-isactive
|
||||
@@ -335,7 +349,7 @@ template(name="editTeamPopup")
|
||||
input.js-teamShortName(type="text" value=team.teamShortName required)
|
||||
label
|
||||
| {{_ 'website'}}
|
||||
input.js-teamWebsite(type="text" value=team.teamWebsite required)
|
||||
input.js-teamWebsite(type="text" value=team.teamWebsite)
|
||||
label
|
||||
| {{_ 'active'}}
|
||||
select.select-active.js-team-isactive
|
||||
@@ -405,7 +419,7 @@ template(name="editUserPopup")
|
||||
each value in orgsDatas
|
||||
option(value="{{value._id}}") {{value.orgDisplayName}}
|
||||
input#jsUserOrgsInPut.js-userOrgs(type="text" value=user.orgsUserBelongs, disabled)
|
||||
input#jsUserOrgIdsInPut.js-userOrgIds.hide(type="text" value=user.orgIdsUserBelongs)
|
||||
input#jsUserOrgIdsInPut.js-userOrgIds.hide(type="hidden" value=user.orgIdsUserBelongs)
|
||||
label
|
||||
| {{_ 'teams'}}
|
||||
i.fa.fa-plus-square#addUserTeam
|
||||
@@ -415,7 +429,7 @@ template(name="editUserPopup")
|
||||
each value in teamsDatas
|
||||
option(value="{{value._id}}") {{_ value.teamDisplayName}}
|
||||
input#jsUserTeamsInPut.js-userteams(type="text" value=user.teamsUserBelongs, disabled)
|
||||
input#jsUserTeamIdsInPut.js-userteamIds.hide(type="text" value=user.teamIdsUserBelongs)
|
||||
input#jsUserTeamIdsInPut.js-userteamIds.hide(type="hidden" value=user.teamIdsUserBelongs)
|
||||
|
||||
hr
|
||||
label
|
||||
@@ -436,6 +450,9 @@ template(name="newOrgPopup")
|
||||
label
|
||||
| {{_ 'shortName'}}
|
||||
input.js-orgShortName(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'autoAddUsersWithDomainName'}}
|
||||
input.js-orgAutoAddUsersWithDomainName(type="text" value="")
|
||||
label
|
||||
| {{_ 'website'}}
|
||||
input.js-orgWebsite(type="text" value="" required)
|
||||
@@ -462,7 +479,7 @@ template(name="newTeamPopup")
|
||||
input.js-teamShortName(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'website'}}
|
||||
input.js-teamWebsite(type="text" value="" required)
|
||||
input.js-teamWebsite(type="text" value="")
|
||||
label
|
||||
| {{_ 'active'}}
|
||||
select.select-active.js-team-isactive
|
||||
@@ -483,9 +500,9 @@ template(name="modifyTeamsUsers")
|
||||
| {{_ 'r-action'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#addAction(type="radio" name="action" value="true" checked="checked")
|
||||
span {{_ 'add'}}
|
||||
label(for=addAction) {{_ 'add'}}
|
||||
input.wekan-form-control#deleteAction(type="radio" name="action" value="false")
|
||||
span {{_ 'delete'}}
|
||||
label(for=deleteAction) {{_ 'delete'}}
|
||||
div.buttonsContainer
|
||||
input.primary.wide#addTeamBtn(type="submit" value="{{_ 'save'}}")
|
||||
input.primary.wide#cancelBtn(type="submit" value="{{_ 'cancel'}}")
|
||||
|
||||
@@ -576,12 +576,14 @@ Template.editOrgPopup.events({
|
||||
.value.trim();
|
||||
const orgDesc = templateInstance.find('.js-orgDesc').value.trim();
|
||||
const orgShortName = templateInstance.find('.js-orgShortName').value.trim();
|
||||
const orgAutoAddUsersWithDomainName = templateInstance.find('.js-orgAutoAddUsersWithDomainName').value.trim();
|
||||
const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim();
|
||||
const orgIsActive = templateInstance.find('.js-org-isactive').value.trim() == 'true';
|
||||
|
||||
const isChangeOrgDisplayName = orgDisplayName !== org.orgDisplayName;
|
||||
const isChangeOrgDesc = orgDesc !== org.orgDesc;
|
||||
const isChangeOrgShortName = orgShortName !== org.orgShortName;
|
||||
const isChangeOrgAutoAddUsersWithDomainName = orgAutoAddUsersWithDomainName !== org.orgAutoAddUsersWithDomainName;
|
||||
const isChangeOrgWebsite = orgWebsite !== org.orgWebsite;
|
||||
const isChangeOrgIsActive = orgIsActive !== org.orgIsActive;
|
||||
|
||||
@@ -589,6 +591,7 @@ Template.editOrgPopup.events({
|
||||
isChangeOrgDisplayName ||
|
||||
isChangeOrgDesc ||
|
||||
isChangeOrgShortName ||
|
||||
isChangeOrgAutoAddUsersWithDomainName ||
|
||||
isChangeOrgWebsite ||
|
||||
isChangeOrgIsActive
|
||||
) {
|
||||
@@ -598,6 +601,7 @@ Template.editOrgPopup.events({
|
||||
orgDisplayName,
|
||||
orgDesc,
|
||||
orgShortName,
|
||||
orgAutoAddUsersWithDomainName,
|
||||
orgWebsite,
|
||||
orgIsActive,
|
||||
);
|
||||
@@ -920,6 +924,7 @@ Template.newOrgPopup.events({
|
||||
.value.trim();
|
||||
const orgDesc = templateInstance.find('.js-orgDesc').value.trim();
|
||||
const orgShortName = templateInstance.find('.js-orgShortName').value.trim();
|
||||
const orgAutoAddUsersWithDomainName = templateInstance.find('.js-orgAutoAddUsersWithDomainName').value.trim();
|
||||
const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim();
|
||||
const orgIsActive =
|
||||
templateInstance.find('.js-org-isactive').value.trim() == 'true';
|
||||
@@ -929,6 +934,7 @@ Template.newOrgPopup.events({
|
||||
orgDisplayName,
|
||||
orgDesc,
|
||||
orgShortName,
|
||||
orgAutoAddUsersWithDomainName,
|
||||
orgWebsite,
|
||||
orgIsActive,
|
||||
);
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
display: -moz-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
.setting-content {
|
||||
color: #727479;
|
||||
background: #dedede;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
.setting-content .content-title {
|
||||
@@ -56,6 +58,8 @@
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.setting-content .content-body .main-body ul li {
|
||||
padding: 0.5rem 0.5rem;
|
||||
|
||||
@@ -137,34 +137,32 @@ template(name='tableVisibilityModeSettings')
|
||||
.title {{_ 'tableVisibilityMode-allowPrivateOnly'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#accounts-allowPrivateOnly(type="radio" name="allowPrivateOnly" value="true" checked="{{#if allowPrivateOnly}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
label {{_ 'yes'}}
|
||||
input.wekan-form-control#accounts-allowPrivateOnly(type="radio" name="allowPrivateOnly" value="false" checked="{{#unless allowPrivateOnly}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
label {{_ 'no'}}
|
||||
button.js-tableVisibilityMode-save.primary {{_ 'save'}}
|
||||
|
||||
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
|
||||
input.wekan-form-control#accounts-allowEmailChange(type="radio" name="allowEmailChange" value="true" checked="{{#if allowEmailChange}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
label {{_ 'yes'}}
|
||||
input.wekan-form-control#accounts-allowEmailChange(type="radio" name="allowEmailChange" value="false" checked="{{#unless allowEmailChange}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
label {{_ 'no'}}
|
||||
.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'}}
|
||||
label {{_ 'yes'}}
|
||||
input.wekan-form-control#accounts-allowUserNameChange(type="radio" name="allowUserNameChange" value="false" checked="{{#unless allowUserNameChange}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
label {{_ 'no'}}
|
||||
.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'}}
|
||||
label {{_ 'yes'}}
|
||||
input.wekan-form-control#accounts-allowUserDelete(type="radio" name="allowUserDelete" value="false" checked="{{#unless allowUserDelete}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
label {{_ 'no'}}
|
||||
button.js-accounts-save.primary {{_ 'save'}}
|
||||
|
||||
template(name='announcementSettings')
|
||||
@@ -185,6 +183,8 @@ template(name='announcementSettings')
|
||||
|
||||
template(name='layoutSettings')
|
||||
ul#layout-setting.setting-detail
|
||||
li
|
||||
button.js-all-boards-hide-activities.primary {{_ 'hide-activities-of-all-boards'}}
|
||||
li.layout-form
|
||||
.title {{_ 'oidc-button-text'}}
|
||||
.form-group
|
||||
@@ -201,9 +201,9 @@ template(name='layoutSettings')
|
||||
.title {{_ 'display-authentication-method'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#display-authentication-method(type="radio" name="displayAuthenticationMethod" value="true" checked="{{#if currentSetting.displayAuthenticationMethod}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
label {{_ 'yes'}}
|
||||
input.wekan-form-control#display-authentication-method(type="radio" name="displayAuthenticationMethod" value="false" checked="{{#unless currentSetting.displayAuthenticationMethod}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
label {{_ 'no'}}
|
||||
li.layout-form
|
||||
.title {{_ 'default-authentication-method'}}
|
||||
+selectAuthenticationMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
|
||||
@@ -218,9 +218,9 @@ template(name='layoutSettings')
|
||||
.title {{_ 'hide-logo'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#hide-logo(type="radio" name="hideLogo" value="true" checked="{{#if currentSetting.hideLogo}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
label {{_ 'yes'}}
|
||||
input.wekan-form-control#hide-logo(type="radio" name="hideLogo" value="false" checked="{{#unless currentSetting.hideLogo}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
label {{_ 'no'}}
|
||||
li.layout-form
|
||||
.title {{_ 'custom-login-logo-image-url'}}
|
||||
.form-group
|
||||
@@ -257,16 +257,16 @@ template(name='layoutSettings')
|
||||
.title {{_ 'hide-card-counter-list'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#hide-card-counter-list(type="radio" name="hideCardCounterList" value="true" checked="{{#if currentSetting.hideCardCounterList}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
label {{_ 'yes'}}
|
||||
input.wekan-form-control#hide-card-counter-list(type="radio" name="hideCardCounterList" value="false" checked="{{#unless currentSetting.hideCardCounterList}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
label {{_ 'no'}}
|
||||
li.layout-form
|
||||
.title {{_ 'hide-board-member-list'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#hide-board-member-list(type="radio" name="hideBoardMemberList" value="true" checked="{{#if currentSetting.hideBoardMemberList}}checked{{/if}}")
|
||||
span {{_ 'yes'}}
|
||||
label {{_ 'yes'}}
|
||||
input.wekan-form-control#hide-board-member-list(type="radio" name="hideBoardMemberList" value="false" checked="{{#unless currentSetting.hideBoardMemberList}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
label {{_ 'no'}}
|
||||
li
|
||||
button.js-save-layout.primary {{_ 'save'}}
|
||||
|
||||
|
||||
@@ -336,12 +336,12 @@ BlazeComponent.extendComponent({
|
||||
allowUserDelete() {
|
||||
return AccountSettings.findOne('accounts-allowUserDelete').booleanValue;
|
||||
},
|
||||
allHideSystemMessages() {
|
||||
Meteor.call('setAllUsersHideSystemMessages', (err, ret) => {
|
||||
allBoardsHideActivities() {
|
||||
Meteor.call('setAllBoardsHideActivities', (err, ret) => {
|
||||
if (!err && ret) {
|
||||
if (ret === true) {
|
||||
const message = `${TAPi18n.__(
|
||||
'now-system-messages-of-all-users-are-hidden',
|
||||
'now-activities-of-all-boards-are-hidden',
|
||||
)}`;
|
||||
alert(message);
|
||||
}
|
||||
@@ -359,7 +359,7 @@ BlazeComponent.extendComponent({
|
||||
'click button.js-accounts-save': this.saveAccountsChange,
|
||||
},
|
||||
{
|
||||
'click button.js-all-hide-system-messages': this.allHideSystemMessages,
|
||||
'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,
|
||||
},
|
||||
];
|
||||
},
|
||||
@@ -376,12 +376,12 @@ BlazeComponent.extendComponent({
|
||||
allowPrivateOnly() {
|
||||
return TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly').booleanValue;
|
||||
},
|
||||
allHideSystemMessages() {
|
||||
Meteor.call('setAllUsersHideSystemMessages', (err, ret) => {
|
||||
allBoardsHideActivities() {
|
||||
Meteor.call('setAllBoardsHideActivities', (err, ret) => {
|
||||
if (!err && ret) {
|
||||
if (ret === true) {
|
||||
const message = `${TAPi18n.__(
|
||||
'now-system-messages-of-all-users-are-hidden',
|
||||
'now-activities-of-all-boards-are-hidden',
|
||||
)}`;
|
||||
alert(message);
|
||||
}
|
||||
@@ -399,7 +399,7 @@ BlazeComponent.extendComponent({
|
||||
'click button.js-tableVisibilityMode-save': this.saveTableVisibilityChange,
|
||||
},
|
||||
{
|
||||
'click button.js-all-hide-system-messages': this.allHideSystemMessages,
|
||||
'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
@@ -34,13 +34,14 @@ template(name="translation")
|
||||
|
||||
template(name="translationGeneral")
|
||||
table
|
||||
tbody
|
||||
thead
|
||||
tr
|
||||
th {{_ 'language'}}
|
||||
th {{_ 'text'}}
|
||||
th {{_ 'translation-text'}}
|
||||
th
|
||||
+newTranslationRow
|
||||
tbody
|
||||
each translation in translationList
|
||||
+translationRow(translationId=translation._id)
|
||||
|
||||
|
||||
@@ -3,34 +3,31 @@
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.sidebar .sidebar-shadow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
.sidebar {
|
||||
background: #f7f7f7;
|
||||
box-shadow: -10px 0px 5px -10px #b3b3b3;
|
||||
z-index: 10;
|
||||
}
|
||||
.sidebar-xmark {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 5px;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
font-size: 25px;
|
||||
padding: 10px;
|
||||
}
|
||||
.sidebar-xmark:hover {
|
||||
background: rgba(0,0,0,0.15);
|
||||
}
|
||||
.sidebar-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 10px 0px 10px;
|
||||
}
|
||||
.sidebar .sidebar-content {
|
||||
padding: 12px;
|
||||
margin-bottom: 1.6em;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 90%;
|
||||
padding: 0 12px;
|
||||
}
|
||||
.sidebar .sidebar-content .hide-btn {
|
||||
display: none;
|
||||
@@ -106,21 +103,19 @@
|
||||
margin-right: 10px;
|
||||
}
|
||||
.sidebar .sidebar-shortcuts {
|
||||
position: absolute;
|
||||
margin-left: 40%;
|
||||
padding: 0;
|
||||
top: 7px;
|
||||
font-size: 0.8em;
|
||||
line-height: 1.6em;
|
||||
color: #999;
|
||||
}
|
||||
.board-sidebar {
|
||||
width: 548px;
|
||||
right: -548px;
|
||||
display: none;
|
||||
width: 30vw;
|
||||
z-index: 100;
|
||||
transition: top 0.1s, right 0.1s, width 0.1s;
|
||||
}
|
||||
.board-sidebar.is-open {
|
||||
right: 0;
|
||||
display: block;
|
||||
}
|
||||
.board-widget h4 {
|
||||
margin: 5px 0;
|
||||
|
||||
@@ -4,17 +4,21 @@ template(name="sidebar")
|
||||
// class="{{#if isTongueHidden}}is-hidden{{/if}}",
|
||||
// title="{{showTongueTitle}}")
|
||||
// i.fa.fa-navicon
|
||||
.sidebar-shadow
|
||||
.sidebar-actions
|
||||
.sidebar-shortcuts
|
||||
a.board-header-btn.js-shortcuts(title="{{_ 'keyboard-shortcuts' }}")
|
||||
i.fa.fa-keyboard-o
|
||||
span {{_ 'keyboard-shortcuts' }}
|
||||
a.sidebar-xmark.js-close-sidebar ✕
|
||||
.sidebar-content.js-board-sidebar-content
|
||||
//a.hide-btn.js-hide-sidebar
|
||||
// i.fa.fa-navicon
|
||||
unless isDefaultView
|
||||
h2
|
||||
a.fa.fa-chevron-left.js-back-home
|
||||
= getViewTitle
|
||||
if isOpen
|
||||
+Template.dynamic(template=getViewTemplate)
|
||||
.sidebar-content.js-board-sidebar-content
|
||||
//a.hide-btn.js-hide-sidebar
|
||||
// i.fa.fa-navicon
|
||||
unless isDefaultView
|
||||
h2
|
||||
a.fa.fa-chevron-left.js-back-home
|
||||
= getViewTitle
|
||||
if isOpen
|
||||
+Template.dynamic(template=getViewTemplate)
|
||||
|
||||
template(name='homeSidebar')
|
||||
hr
|
||||
@@ -28,9 +32,16 @@ template(name='homeSidebar')
|
||||
.materialCheckBox(class="{{#if hiddenMinicardLabelText}}is-checked{{/if}}")
|
||||
hr
|
||||
unless currentUser.isNoComments
|
||||
h3
|
||||
h3.activity-title
|
||||
i.fa.fa-comments-o
|
||||
| {{_ 'activities'}}
|
||||
|
||||
.material-toggle-switch(title="{{_ 'show-activities'}}")
|
||||
if showActivities
|
||||
input.toggle-switch(type="checkbox" id="toggleShowActivitiesBoard" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleShowActivitiesBoard")
|
||||
label.toggle-label(for="toggleShowActivitiesBoard")
|
||||
+activities(mode="board")
|
||||
|
||||
template(name="membersWidget")
|
||||
@@ -40,11 +51,6 @@ template(name="membersWidget")
|
||||
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}")
|
||||
i.board-header-btn-icon.fa.fa-cog
|
||||
| {{_ 'boardMenuPopup-title'}}
|
||||
.board-widget.board-widget-members
|
||||
.sidebar-shortcuts
|
||||
a.board-header-btn.js-shortcuts(title="{{_ 'keyboard-shortcuts' }}")
|
||||
i.fa.fa-keyboard-o
|
||||
span {{_ 'keyboard-shortcuts' }}
|
||||
hr
|
||||
h3
|
||||
i.fa.fa-users
|
||||
@@ -175,7 +181,7 @@ template(name="boardInfoOnMyBoardsPopup")
|
||||
|
||||
template(name="boardCardSettingsPopup")
|
||||
form.board-card-settings
|
||||
h3 {{_ 'show-on-card'}}
|
||||
h3 {{_ 'show-on-card'}}, {{_ 'show-on-minicard'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-receiveddate(class="{{#if allowsReceivedDate}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsReceivedDate}}is-checked{{/if}}")
|
||||
@@ -206,14 +212,18 @@ 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-creator-on-minicard(class="{{#if allowsCreatorOnMinicard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsCreatorOnMinicard}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-user
|
||||
| {{_ 'creator-on-minicard'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
||||
@@ -238,6 +248,12 @@ template(name="boardCardSettingsPopup")
|
||||
span
|
||||
i.fa.fa-sort
|
||||
| {{_ 'card-sorting-by-number'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-card-sorting-by-number-on-minicard(class="{{#if allowsCardSortingByNumberOnMinicard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsCardSortingByNumberOnMinicard}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-sort
|
||||
| {{_ 'card-sorting-by-number-on-minicard'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-card-show-lists(class="{{#if allowsShowLists}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsShowLists}}is-checked{{/if}}")
|
||||
@@ -271,6 +287,12 @@ template(name="boardCardSettingsPopup")
|
||||
i.fa.fa-align-left
|
||||
| {{_ 'description'}}
|
||||
| {{_ 'custom-field-text'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-description-text-on-minicard(class="{{#if allowsDescriptionTextOnMinicard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsDescriptionTextOnMinicard}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-align-left
|
||||
| {{_ 'description-on-minicard'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-checklists(class="{{#if allowsChecklists}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsChecklists}}is-checked{{/if}}")
|
||||
@@ -289,6 +311,19 @@ template(name="boardCardSettingsPopup")
|
||||
span
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'attachments'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-badge-attachment-on-minicard(class="{{#if allowsBadgeAttachmentOnMinicard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsBadgeAttachmentOnMinicard}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'badge-attachment-on-minicard'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-cover-attachment-on-minicard(class="{{#if allowsCoverAttachmentOnMinicard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsCoverAttachmentOnMinicard}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-book
|
||||
i.fa.fa-picture-o
|
||||
| {{_ 'cover-attachment-on-minicard'}}
|
||||
//div.check-div
|
||||
// a.flex.js-field-has-comments(class="{{#if allowsComments}}is-checked{{/if}}")
|
||||
// .materialCheckBox(class="{{#if allowsComments}}is-checked{{/if}}")
|
||||
@@ -302,35 +337,6 @@ template(name="boardCardSettingsPopup")
|
||||
// i.fa.fa-history
|
||||
// | {{_ 'activities'}}
|
||||
|
||||
template(name="boardMinicardSettingsPopup")
|
||||
form.board-minicard-settings
|
||||
h3 {{_ 'show-on-minicard'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-description-text-on-minicard(class="{{#if allowsDescriptionTextOnMinicard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsDescriptionTextOnMinicard}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-align-left
|
||||
| {{_ 'description-on-minicard'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-cover-attachment-on-minicard(class="{{#if allowsCoverAttachmentOnMinicard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsCoverAttachmentOnMinicard}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-book
|
||||
i.fa.fa-picture-o
|
||||
| {{_ 'cover-attachment-on-minicard'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-badge-attachment-on-minicard(class="{{#if allowsBadgeAttachmentOnMinicard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsBadgeAttachmentOnMinicard}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'badge-attachment-on-minicard'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-card-sorting-by-number-on-minicard(class="{{#if allowsCardSortingByNumberOnMinicard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsCardSortingByNumberOnMinicard}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-sort
|
||||
| {{_ 'card-sorting-by-number-on-minicard'}}
|
||||
|
||||
template(name="boardSubtaskSettingsPopup")
|
||||
form.board-subtask-settings
|
||||
h3 {{_ 'show-parent-in-minicard'}}
|
||||
@@ -473,10 +479,6 @@ template(name="boardMenuPopup")
|
||||
a.js-card-settings
|
||||
i.fa.fa-id-card-o
|
||||
| {{_ 'card-settings'}}
|
||||
li
|
||||
a.js-minicard-settings
|
||||
i.fa.fa-id-card-o
|
||||
| {{_ 'minicard-settings'}}
|
||||
li
|
||||
a.js-subtask-settings
|
||||
i.fa.fa-sitemap
|
||||
|
||||
@@ -136,7 +136,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Blaze.registerHelper('Sidebar', () => Sidebar);
|
||||
|
||||
Template.homeSidebar.helpers({
|
||||
BlazeComponent.extendComponent({
|
||||
hiddenMinicardLabelText() {
|
||||
currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
@@ -147,7 +147,20 @@ Template.homeSidebar.helpers({
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
showActivities() {
|
||||
let ret = Utils.getCurrentBoard().showActivities ?? false;
|
||||
return ret;
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click #toggleShowActivitiesBoard'() {
|
||||
Utils.getCurrentBoard().toggleShowActivities();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('homeSidebar');
|
||||
|
||||
Template.boardInfoOnMyBoardsPopup.helpers({
|
||||
hideCardCounterList() {
|
||||
@@ -236,7 +249,6 @@ Template.boardMenuPopup.events({
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
|
||||
'click .js-card-settings': Popup.open('boardCardSettings'),
|
||||
'click .js-minicard-settings': Popup.open('boardMinicardSettings'),
|
||||
'click .js-export-board': Popup.open('exportBoard'),
|
||||
});
|
||||
|
||||
@@ -914,11 +926,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
allowsCreator() {
|
||||
return (
|
||||
this.currentBoard.allowsCreator === null ||
|
||||
this.currentBoard.allowsCreator === undefined ||
|
||||
this.currentBoard.allowsCreator
|
||||
);
|
||||
return this.currentBoard.allowsCreator ?? false;
|
||||
},
|
||||
|
||||
allowsCreatorOnMinicard() {
|
||||
return this.currentBoard.allowsCreatorOnMinicard ?? false;
|
||||
},
|
||||
|
||||
allowsMembers() {
|
||||
@@ -984,6 +996,22 @@ BlazeComponent.extendComponent({
|
||||
);
|
||||
},
|
||||
|
||||
allowsDescriptionTextOnMinicard() {
|
||||
return this.currentBoard.allowsDescriptionTextOnMinicard;
|
||||
},
|
||||
|
||||
allowsCoverAttachmentOnMinicard() {
|
||||
return this.currentBoard.allowsCoverAttachmentOnMinicard;
|
||||
},
|
||||
|
||||
allowsBadgeAttachmentOnMinicard() {
|
||||
return this.currentBoard.allowsBadgeAttachmentOnMinicard;
|
||||
},
|
||||
|
||||
allowsCardSortingByNumberOnMinicard() {
|
||||
return this.currentBoard.allowsCardSortingByNumberOnMinicard;
|
||||
},
|
||||
|
||||
boards() {
|
||||
const ret = ReactiveCache.getBoards(
|
||||
{
|
||||
@@ -1106,6 +1134,19 @@ BlazeComponent.extendComponent({
|
||||
this.currentBoard.allowsCreator,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-creator-on-minicard'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsCreatorOnMinicard = !this.currentBoard.allowsCreatorOnMinicard;
|
||||
this.currentBoard.setAllowsCreatorOnMinicard(this.currentBoard.allowsCreatorOnMinicard);
|
||||
$(`.js-field-has-creator-on-minicard ${MCB}`).toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsCreatorOnMinicard,
|
||||
);
|
||||
$('.js-field-has-creator-on-minicard').toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsCreatorOnMinicard,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-members'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
|
||||
@@ -1241,6 +1282,22 @@ BlazeComponent.extendComponent({
|
||||
this.currentBoard.allowsCardNumber,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-description-text-on-minicard'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsDescriptionTextOnMinicard = !this.currentBoard
|
||||
.allowsDescriptionTextOnMinicard;
|
||||
this.currentBoard.setallowsDescriptionTextOnMinicard(
|
||||
this.currentBoard.allowsDescriptionTextOnMinicard,
|
||||
);
|
||||
$(`.js-field-has-description-text-on-minicard ${MCB}`).toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsDescriptionTextOnMinicard,
|
||||
);
|
||||
$('.js-field-has-description-text-on-minicard').toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsDescriptionTextOnMinicard,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-description-text'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsDescriptionText = !this.currentBoard
|
||||
@@ -1318,74 +1375,6 @@ BlazeComponent.extendComponent({
|
||||
this.currentBoard.allowsActivities,
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('boardCardSettingsPopup');
|
||||
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentBoard = Utils.getCurrentBoard();
|
||||
},
|
||||
|
||||
allowsDescriptionTextOnMinicard() {
|
||||
return this.currentBoard.allowsDescriptionTextOnMinicard;
|
||||
},
|
||||
|
||||
allowsCoverAttachmentOnMinicard() {
|
||||
return this.currentBoard.allowsCoverAttachmentOnMinicard;
|
||||
},
|
||||
|
||||
allowsBadgeAttachmentOnMinicard() {
|
||||
return this.currentBoard.allowsBadgeAttachmentOnMinicard;
|
||||
},
|
||||
|
||||
allowsCardSortingByNumberOnMinicard() {
|
||||
return this.currentBoard.allowsCardSortingByNumberOnMinicard;
|
||||
},
|
||||
|
||||
lists() {
|
||||
return ReactiveCache.getLists(
|
||||
{
|
||||
boardId: this.currentBoard._id,
|
||||
archived: false,
|
||||
},
|
||||
{
|
||||
sort: ['title'],
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
hasLists() {
|
||||
return this.lists().length > 0;
|
||||
},
|
||||
|
||||
isListSelected() {
|
||||
return (
|
||||
this.currentBoard.dateSettingsDefaultBoardId === this.currentData()._id
|
||||
);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-field-has-description-text-on-minicard'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsDescriptionTextOnMinicard = !this.currentBoard
|
||||
.allowsDescriptionTextOnMinicard;
|
||||
this.currentBoard.setallowsDescriptionTextOnMinicard(
|
||||
this.currentBoard.allowsDescriptionTextOnMinicard,
|
||||
);
|
||||
$(`.js-field-has-description-text-on-minicard ${MCB}`).toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsDescriptionTextOnMinicard,
|
||||
);
|
||||
$('.js-field-has-description-text-on-minicard').toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsDescriptionTextOnMinicard,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-cover-attachment-on-minicard'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsCoverAttachmentOnMinicard = !this.currentBoard
|
||||
@@ -1437,7 +1426,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('boardMinicardSettingsPopup');
|
||||
}).register('boardCardSettingsPopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
|
||||
@@ -24,9 +24,17 @@ template(name="swimlaneFixedHeader")
|
||||
| {{isTitleDefault title}}
|
||||
.swimlane-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
if currentUser.isBoardAdmin
|
||||
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon(title="{{_ 'add-swimlane'}}")
|
||||
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon(title="{{_ 'add-swimlane'}}")
|
||||
a.fa.fa-navicon.js-open-swimlane-menu(title="{{_ 'swimlaneActionPopup-title'}}")
|
||||
//// TODO: Collapse Swimlane: make button working, etc.
|
||||
//unless collapsed
|
||||
// a.js-collapse-swimlane(title="{{_ 'collapse'}}")
|
||||
// i.fa.fa-arrow-down.swimlane-header-collapse-down
|
||||
// i.fa.fa-arrow-up.swimlane-header-collapse-up
|
||||
//if collapsed
|
||||
// a.js-collapse-swimlane(title="{{_ 'uncollapse'}}")
|
||||
// i.fa.fa-arrow-up.swimlane-header-collapse-up
|
||||
// i.fa.fa-arrow-down.swimlane-header-collapse-down
|
||||
unless isTouchScreen
|
||||
if isShowDesktopDragHandles
|
||||
a.swimlane-header-handle.handle.fa.fa-arrows.js-swimlane-header-handle
|
||||
|
||||
@@ -18,10 +18,25 @@ BlazeComponent.extendComponent({
|
||||
swimlane.rename(newTitle.trim());
|
||||
}
|
||||
},
|
||||
collapsed(check = undefined) {
|
||||
const swimlane = Template.currentData();
|
||||
const status = swimlane.isCollapsed();
|
||||
if (check === undefined) {
|
||||
// just check
|
||||
return status;
|
||||
} else {
|
||||
swimlane.collapse(!status);
|
||||
return !status;
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-collapse-swimlane'(event) {
|
||||
event.preventDefault();
|
||||
this.collapsed(!this.collapsed());
|
||||
},
|
||||
'click .js-open-swimlane-menu': Popup.open('swimlaneAction'),
|
||||
'click .js-open-add-swimlane-menu': Popup.open('swimlaneAdd'),
|
||||
submit: this.editTitle,
|
||||
@@ -209,7 +224,7 @@ BlazeComponent.extendComponent({
|
||||
swimlaneHeightValue() {
|
||||
const swimlane = this.currentData();
|
||||
const board = swimlane.boardId;
|
||||
return Meteor.user().getSwimlaneHeight(board, swimlane._id);
|
||||
return ReactiveCache.getCurrentUser().getSwimlaneHeight(board, swimlane._id);
|
||||
},
|
||||
|
||||
events() {
|
||||
|
||||
@@ -1,43 +1,3 @@
|
||||
/*
|
||||
// Minimize swimlanes start https://www.w3schools.com/howto/howto_js_accordion.asp
|
||||
|
||||
.accordion
|
||||
cursor: pointer
|
||||
width: 30px
|
||||
height: 20px
|
||||
border: none
|
||||
outline: none
|
||||
font-size: 18px
|
||||
transition: 0.4s
|
||||
padding-top: 0px
|
||||
margin-top: 0px
|
||||
|
||||
.accordion:after
|
||||
// Unicode triagle right:
|
||||
content: '\25B6'
|
||||
color: #777
|
||||
font-weight: bold
|
||||
float: left
|
||||
|
||||
.active:after
|
||||
// Unicode triangle down:
|
||||
content: '\25BC'
|
||||
|
||||
.panel
|
||||
width: 100%
|
||||
max-height: 0
|
||||
overflow: hidden
|
||||
transition: max-height 0.2s ease-out
|
||||
margin: 0px
|
||||
padding: 0px
|
||||
|
||||
// Minimize swimlanes end https://www.w3schools.com/howto/howto_js_accordion.asp
|
||||
*/
|
||||
@media screen and (min-width: 801px) {
|
||||
.swimlane.ui-sortable {
|
||||
width: max-content;
|
||||
}
|
||||
}
|
||||
[class=swimlane] {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
@@ -46,7 +6,30 @@
|
||||
background: #dedede;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: 0;
|
||||
overflow: auto;
|
||||
max-height: 100%;
|
||||
}
|
||||
.swimlane-header-menu .swimlane-header-collapse-down {
|
||||
font-size: 50%;
|
||||
color: #a6a6a6;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 100px;
|
||||
}
|
||||
.swimlane-header-menu .swimlane-header-collapse-up {
|
||||
font-size: 50%;
|
||||
color: #a6a6a6;
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 100px;
|
||||
}
|
||||
.swimlane-header-menu .swimlane-header-uncollapse-up {
|
||||
font-size: 50%;
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.swimlane-header-menu .swimlane-header-uncollapse-down {
|
||||
font-size: 50%;
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.swimlane.placeholder {
|
||||
background-color: rgba(0,0,0,0.2);
|
||||
@@ -170,7 +153,7 @@
|
||||
color: #4d4d4d !important;
|
||||
}
|
||||
.swimlane-silver {
|
||||
background: unset !important;
|
||||
background: #ccc !important;
|
||||
color: #4d4d4d !important;
|
||||
}
|
||||
.swimlane-peachpuff {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
template(name="swimlane")
|
||||
.swimlane
|
||||
.swimlane.nodragscroll
|
||||
+swimlaneHeader
|
||||
unless collapseSwimlane
|
||||
.swimlane.js-lists.js-swimlane(id="swimlane-{{_id}}"
|
||||
.swimlane.js-lists.js-swimlane.dragscroll(id="swimlane-{{_id}}"
|
||||
style="height:{{swimlaneHeight}};")
|
||||
if isMiniScreen
|
||||
if currentListIsInThisSwimlane _id
|
||||
@@ -46,8 +46,8 @@ template(name="listsGroup")
|
||||
|
||||
template(name="addListForm")
|
||||
unless currentUser.isWorker
|
||||
.list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}")
|
||||
if currentUser.isBoardAdmin
|
||||
unless currentUser.isCommentOnly
|
||||
.list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}")
|
||||
.list-header-add
|
||||
+inlinedForm(autoclose=false)
|
||||
input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}"
|
||||
|
||||
@@ -225,7 +225,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
swimlaneHeight() {
|
||||
const user = Meteor.user();
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
const swimlane = Template.currentData();
|
||||
const height = user.getSwimlaneHeight(swimlane.boardId, swimlane._id);
|
||||
return height == -1 ? "auto" : (height + "px");
|
||||
@@ -288,7 +288,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Template.swimlane.helpers({
|
||||
canSeeAddList() {
|
||||
return Meteor.user().isBoardAdmin();
|
||||
return ReactiveCache.getCurrentUser().isBoardAdmin();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
float: left;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin: .3vh;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
z-index: 1;
|
||||
|
||||
@@ -26,9 +26,9 @@ template(name="orgAvatar")
|
||||
template(name="boardOrgRow")
|
||||
tr
|
||||
if orgData.orgIsActive
|
||||
td <s>{{ orgData.orgDisplayName }}</s>
|
||||
else
|
||||
td {{ orgData.orgDisplayName }}
|
||||
else
|
||||
td <s>{{ orgData.orgDisplayName }}</s>
|
||||
td
|
||||
if currentUser.isBoardAdmin
|
||||
a.member.orgOrTeamMember.add-member.js-manage-board-removeOrg(title="{{_ 'remove-from-board'}}")
|
||||
@@ -39,9 +39,9 @@ template(name="boardOrgRow")
|
||||
template(name="boardTeamRow")
|
||||
tr
|
||||
if teamData.teamIsActive
|
||||
td <s>{{ teamData.teamDisplayName }}</s>
|
||||
else
|
||||
td {{ teamData.teamDisplayName }}
|
||||
else
|
||||
td <s>{{ teamData.teamDisplayName }}</s>
|
||||
td
|
||||
if currentUser.isBoardAdmin
|
||||
a.member.orgOrTeamMember.add-member.js-manage-board-removeTeam(title="{{_ 'remove-from-board'}}")
|
||||
|
||||
@@ -153,12 +153,6 @@ template(name="changeLanguagePopup")
|
||||
|
||||
template(name="changeSettingsPopup")
|
||||
ul.pop-over-list
|
||||
//li
|
||||
// a.js-toggle-system-messages
|
||||
// i.fa.fa-comments-o
|
||||
// | {{_ 'hide-system-messages'}}
|
||||
// if hiddenSystemMessages
|
||||
// i.fa.fa-check
|
||||
//li
|
||||
// a.js-toggle-desktop-drag-handles
|
||||
// i.fa.fa-arrows
|
||||
|
||||
@@ -283,16 +283,6 @@ Template.changeLanguagePopup.events({
|
||||
});
|
||||
|
||||
Template.changeSettingsPopup.helpers({
|
||||
hiddenSystemMessages() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).hasHiddenSystemMessages;
|
||||
} else if (window.localStorage.getItem('hasHiddenSystemMessages')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
rescueCardDescription() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
@@ -325,7 +315,7 @@ Template.changeSettingsPopup.helpers({
|
||||
});
|
||||
},
|
||||
startDayOfWeek() {
|
||||
currentUser = Meteor.user();
|
||||
currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
return currentUser.getStartDayOfWeek();
|
||||
} else {
|
||||
@@ -343,7 +333,7 @@ Template.changeSettingsPopup.events({
|
||||
return ret;
|
||||
},
|
||||
'click .js-toggle-desktop-drag-handles'() {
|
||||
currentUser = Meteor.user();
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (currentUser) {
|
||||
Meteor.call('toggleDesktopDragHandles');
|
||||
} else if (window.localStorage.getItem('showDesktopDragHandles')) {
|
||||
@@ -352,16 +342,6 @@ Template.changeSettingsPopup.events({
|
||||
window.localStorage.setItem('showDesktopDragHandles', 'true');
|
||||
}
|
||||
},
|
||||
'click .js-toggle-system-messages'() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
} else if (window.localStorage.getItem('hasHiddenSystemMessages')) {
|
||||
window.localStorage.removeItem('hasHiddenSystemMessages');
|
||||
} else {
|
||||
window.localStorage.setItem('hasHiddenSystemMessages', 'true');
|
||||
}
|
||||
},
|
||||
'click .js-rescue-card-description'() {
|
||||
Meteor.call('toggleRescueCardDescription')
|
||||
},
|
||||
@@ -375,7 +355,7 @@ Template.changeSettingsPopup.events({
|
||||
templateInstance.$('#start-day-of-week').val(),
|
||||
10,
|
||||
);
|
||||
const currentUser = Meteor.user();
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
if (isNaN(minLimit) || minLimit < -1) {
|
||||
minLimit = -1;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ Blaze.registerHelper('isTouchScreenOrShowDesktopDragHandles', () =>
|
||||
Blaze.registerHelper('moment', (...args) => {
|
||||
args.pop(); // hash
|
||||
const [date, format] = args;
|
||||
return moment(date).format(format);
|
||||
return moment(date).format(format ?? 'LLLL');
|
||||
});
|
||||
|
||||
Blaze.registerHelper('canModifyCard', () =>
|
||||
|
||||
@@ -6,10 +6,11 @@ import moment from 'moment/min/moment-with-locales';
|
||||
function adjustedTimeFormat() {
|
||||
return moment
|
||||
.localeData()
|
||||
.longDateFormat('LT')
|
||||
.replace(/HH/i, 'H');
|
||||
.longDateFormat('LT');
|
||||
}
|
||||
|
||||
// .replace(/HH/i, 'H');
|
||||
|
||||
export class DatePicker extends BlazeComponent {
|
||||
template() {
|
||||
return 'datepicker';
|
||||
|
||||
@@ -123,6 +123,7 @@ EscapeActions = {
|
||||
// the shortcut sould work on textarea and inputs as well.
|
||||
Mousetrap.bindGlobal('esc', () => {
|
||||
EscapeActions.executeLowest();
|
||||
Sidebar.hide();
|
||||
});
|
||||
|
||||
// On a left click on the document, we try to exectute one escape action (eg,
|
||||
|
||||
@@ -57,6 +57,9 @@ window.ExportHtml = Popup => {
|
||||
Array.from(
|
||||
document.querySelectorAll('#header-main-bar .board-header-btns'),
|
||||
).forEach(elem => elem.remove());
|
||||
Array.from(
|
||||
document.querySelectorAll('.js-pop-over, .pop-over'),
|
||||
).forEach(elem => elem.remove());
|
||||
Array.from(document.querySelectorAll('.list-composer')).forEach(elem =>
|
||||
elem.remove(),
|
||||
);
|
||||
@@ -152,6 +155,50 @@ window.ExportHtml = Popup => {
|
||||
);
|
||||
};
|
||||
|
||||
const getWebFonts = () => {
|
||||
fontUrls = [];
|
||||
|
||||
for (let sheet of document.styleSheets) {
|
||||
// Get the base URL of the stylesheet
|
||||
let baseUrl = sheet.href ? new URL(sheet.href).origin : window.location.origin;
|
||||
|
||||
try {
|
||||
for (let rule of sheet.cssRules) {
|
||||
if (rule.type === CSSRule.FONT_FACE_RULE) {
|
||||
let src = rule.style.getPropertyValue('src');
|
||||
let urlMatch = src.match(/url\(["']?(.+?)["']?\)/);
|
||||
if (urlMatch) {
|
||||
let fontUrl = urlMatch[1];
|
||||
|
||||
// Resolve the URL relative to the stylesheet's base URL
|
||||
let resolvedUrl = new URL(fontUrl, baseUrl);
|
||||
fontUrls.push(resolvedUrl.href); // Using .href to get the absolute URL
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Access to stylesheet blocked:', e);
|
||||
}
|
||||
}
|
||||
|
||||
return fontUrls;
|
||||
};
|
||||
|
||||
const downloadFonts = async(elements, zip) => {
|
||||
await asyncForEach(elements, async elem => {
|
||||
const response = await fetch(elem);
|
||||
const responseBody = await response.blob();
|
||||
const filename = elem.split('/')
|
||||
.pop()
|
||||
.split('?')
|
||||
.shift()
|
||||
.split('#')
|
||||
.shift();
|
||||
const fileFullPath = `webfonts/${filename}`;
|
||||
zip.file(fileFullPath, responseBody);
|
||||
});
|
||||
}
|
||||
|
||||
const downloadCardCovers = async (elements, zip, boardSlug) => {
|
||||
await asyncForEach(elements, async elem => {
|
||||
const response = await fetch(
|
||||
@@ -194,6 +241,7 @@ window.ExportHtml = Popup => {
|
||||
await downloadStylesheets(getStylesheetList(), zip);
|
||||
await downloadSrcAttached(getSrcAttached(), zip, boardSlug);
|
||||
await downloadCardCovers(getCardCovers(), zip, boardSlug);
|
||||
await downloadFonts(getWebFonts(), zip);
|
||||
|
||||
addBoardHTMLToZip(boardSlug, zip);
|
||||
|
||||
|
||||
@@ -237,7 +237,6 @@ class SetFilter {
|
||||
if (this._indexOfVal(val) === -1) {
|
||||
this._selectedElements.push(val);
|
||||
this._dep.changed();
|
||||
showFilterSidebar();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+77
-2
@@ -3,6 +3,17 @@ import { ReactiveCache } from '/imports/reactiveCache';
|
||||
// XXX There is no reason to define these shortcuts globally, they should be
|
||||
// attached to a template (most of them will go in the `board` template).
|
||||
|
||||
window.addEventListener('keydown', (e) => {
|
||||
// Only handle event if coming from body
|
||||
if (e.target !== document.body) return;
|
||||
|
||||
// Only handle event if it's in another language
|
||||
if (String.fromCharCode(e.which).toLowerCase() === e.key) return;
|
||||
|
||||
// Trigger the corresponding action
|
||||
Mousetrap.trigger(String.fromCharCode(e.which).toLowerCase());
|
||||
});
|
||||
|
||||
function getHoveredCardId() {
|
||||
const card = $('.js-minicard:hover').get(0);
|
||||
if (!card) return null;
|
||||
@@ -33,6 +44,14 @@ Mousetrap.bind('q', () => {
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('a', () => {
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
const currentUserId = Meteor.userId();
|
||||
if (currentBoardId && currentUserId) {
|
||||
Filter.assignees.toggle(currentUserId);
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('x', () => {
|
||||
if (Filter.isActive()) {
|
||||
Filter.reset();
|
||||
@@ -131,6 +150,26 @@ Mousetrap.bind(numArray, (evt, key) => {
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('m', evt => {
|
||||
const cardId = getSelectedCardId();
|
||||
if (!cardId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUserId = Meteor.userId();
|
||||
if (currentUserId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReactiveCache.getCurrentUser().isBoardMember()) {
|
||||
const card = ReactiveCache.getCard(cardId);
|
||||
card.toggleAssignee(currentUserId);
|
||||
// We should prevent scrolling in card when spacebar is clicked
|
||||
// This should do it according to Mousetrap docs, but it doesn't
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('space', evt => {
|
||||
const cardId = getSelectedCardId();
|
||||
if (!cardId) {
|
||||
@@ -151,7 +190,7 @@ Mousetrap.bind('space', evt => {
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('c', evt => {
|
||||
Mousetrap.bind('`', evt => {
|
||||
const cardId = getSelectedCardId();
|
||||
if (!cardId) {
|
||||
return;
|
||||
@@ -171,6 +210,30 @@ Mousetrap.bind('c', evt => {
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('n', evt => {
|
||||
const cardId = getSelectedCardId();
|
||||
if (!cardId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUserId = Meteor.userId();
|
||||
if (currentUserId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Utils.canModifyBoard()) {
|
||||
// Find the current hovered card
|
||||
const card = ReactiveCache.getCard(cardId);
|
||||
|
||||
// Find the button and click it
|
||||
$(`#js-list-${card.listId} .list-body .minicards .open-minicard-composer`).click();
|
||||
|
||||
// We should prevent scrolling in card when spacebar is clicked
|
||||
// This should do it according to Mousetrap docs, but it doesn't
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
Template.keyboardShortcuts.helpers({
|
||||
mapping: [
|
||||
{
|
||||
@@ -181,6 +244,14 @@ Template.keyboardShortcuts.helpers({
|
||||
keys: ['q'],
|
||||
action: 'shortcut-filter-my-cards',
|
||||
},
|
||||
{
|
||||
keys: ['a'],
|
||||
action: 'shortcut-filter-my-assigned-cards',
|
||||
},
|
||||
{
|
||||
keys: ['n'],
|
||||
action: 'add-card-to-bottom-of-list',
|
||||
},
|
||||
{
|
||||
keys: ['f'],
|
||||
action: 'shortcut-toggle-filterbar',
|
||||
@@ -207,10 +278,14 @@ Template.keyboardShortcuts.helpers({
|
||||
},
|
||||
{
|
||||
keys: ['SPACE'],
|
||||
action: 'shortcut-add-self',
|
||||
},
|
||||
{
|
||||
keys: ['n'],
|
||||
action: 'shortcut-assign-self',
|
||||
},
|
||||
{
|
||||
keys: ['c'],
|
||||
keys: ['`'],
|
||||
action: 'archive-card',
|
||||
},
|
||||
{
|
||||
|
||||
+4
-3
@@ -4,10 +4,11 @@ Utils = {
|
||||
setBackgroundImage(url) {
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
if (currentBoard.backgroundImageURL !== undefined) {
|
||||
$(".board-wrapper,.board-wrapper .board-canvas").css({"background":"url(" + currentBoard.backgroundImageURL + ")","background-size":"cover"});
|
||||
$(".board-wrapper").css({"background":"url(" + currentBoard.backgroundImageURL + ")","background-size":"cover"});
|
||||
$(".swimlane,.swimlane .list,.swimlane .list .list-body,.swimlane .list:first-child .list-body").css({"background-color":"transparent"});
|
||||
} else if (currentBoard.color !== undefined) {
|
||||
currentBoard.setColor(currentBoard.color);
|
||||
$(".minicard").css({"opacity": "0.9"});
|
||||
} else if (currentBoard["background-color"]) {
|
||||
currentBoard.setColor(currentBoard["background-color"]);
|
||||
}
|
||||
},
|
||||
/** returns the current board id
|
||||
|
||||
@@ -16,6 +16,8 @@ export const ALLOWED_BOARD_COLORS = [
|
||||
'modern',
|
||||
'moderndark',
|
||||
'exodark',
|
||||
'cleandark',
|
||||
'cleanlight',
|
||||
];
|
||||
export const ALLOWED_COLORS = [
|
||||
'white',
|
||||
|
||||
+20
-13
@@ -1,5 +1,3 @@
|
||||
version: '2'
|
||||
|
||||
# Note: Do not add single quotes '' to variables. Having spaces still works without quotes where required.
|
||||
#---------------------------------------------------------------------------------------------------------
|
||||
# ==== CREATING USERS AND LOGGING IN TO WEKAN ====
|
||||
@@ -12,13 +10,13 @@ version: '2'
|
||||
# NOTE: MongoDB has changed from 3.x to 4.x, in that case you need backup/restore with --noIndexRestore
|
||||
# see https://github.com/wekan/wekan/wiki/Backup
|
||||
# 1) Stop Wekan:
|
||||
# docker-compose stop
|
||||
# docker compose stop
|
||||
# 2) Remove old Wekan app (wekan-app only, not that wekan-db container that has all your data)
|
||||
# docker rm wekan-app
|
||||
# 3) Get newest docker-compose.yml from https://github.com/wekan/wekan to have correct image,
|
||||
# for example: "image: quay.io/wekan/wekan" or version tag "image: quay.io/wekan/wekan:v4.52"
|
||||
# 4) Start Wekan:
|
||||
# docker-compose up -d
|
||||
# docker compose up -d
|
||||
#----------------------------------------------------------------------------------
|
||||
# ==== OPTIONAL: DEDICATED DOCKER USER ====
|
||||
# 1) Optionally create a dedicated user for Wekan, for example:
|
||||
@@ -38,14 +36,14 @@ version: '2'
|
||||
# ----------------------------------------------------------------------------------
|
||||
# ==== USAGE OF THIS docker-compose.yml ====
|
||||
# 1) For seeing does Wekan work, try this and check with your web browser:
|
||||
# docker-compose up
|
||||
# docker compose up
|
||||
# 2) Stop Wekan and start Wekan in background:
|
||||
# docker-compose stop
|
||||
# docker-compose up -d
|
||||
# docker compose stop
|
||||
# docker compose up -d
|
||||
# 3) See running Docker containers:
|
||||
# docker ps
|
||||
# 4) Stop Docker containers:
|
||||
# docker-compose stop
|
||||
# docker compose stop
|
||||
# ----------------------------------------------------------------------------------
|
||||
# ===== INSIDE DOCKER CONTAINERS, AND BACKUP/RESTORE ====
|
||||
# https://github.com/wekan/wekan/wiki/Backup
|
||||
@@ -120,7 +118,10 @@ services:
|
||||
# image: quay.io/wekan/wekan:v6.89
|
||||
# image: wekanteam/wekan:v6.89
|
||||
# b) GitHub Container registry.
|
||||
image: ghcr.io/wekan/wekan:main
|
||||
# b1) Latest release tag:
|
||||
image: ghcr.io/wekan/wekan:latest
|
||||
# b2) Newest git commit automatic build:
|
||||
#image: ghcr.io/wekan/wekan:main
|
||||
# c) Quay:
|
||||
#image: quay.io/wekan/wekan:latest
|
||||
# d) Docker Hub:
|
||||
@@ -139,7 +140,7 @@ services:
|
||||
- wekan-tier
|
||||
#-------------------------------------------------------------------------------------
|
||||
# ==== BUILD wekan-app DOCKER CONTAINER FROM SOURCE, if you uncomment these ====
|
||||
# ==== and use commands: docker-compose up -d --build
|
||||
# ==== and use commands: docker compose up -d --build
|
||||
#build:
|
||||
# context: .
|
||||
# dockerfile: Dockerfile
|
||||
@@ -365,7 +366,7 @@ services:
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OUTGOING WEBHOOKS ====
|
||||
# What to send to Outgoing Webhook, or leave out. If commented out the default values will be: cardId,listId,oldListId,boardId,comment,user,card,commentId,swimlaneId,customerField,customFieldValue
|
||||
#- WEBHOOKS_ATTRIBUTES=cardId,listId,oldListId,boardId,comment,user,card,commentId
|
||||
#- WEBHOOKS_ATTRIBUTES=cardId,listId,oldListId,boardId,comment,user,card,board,list,swimlane,commentId
|
||||
#-----------------------------------------------------------------
|
||||
# ==== Debug OIDC OAuth2 etc ====
|
||||
#- DEBUG=true
|
||||
@@ -388,6 +389,8 @@ services:
|
||||
#- OAUTH2_CA_CERT=ABCD1234
|
||||
# Use OAuth2 ADFS additional changes. Also needs OAUTH2_ENABLED=true setting.
|
||||
#- OAUTH2_ADFS_ENABLED=false
|
||||
# Azure AD B2C. https://github.com/wekan/wekan/issues/5242
|
||||
#- OAUTH2_B2C_ENABLED=false
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
# Application GUID captured during app registration:
|
||||
@@ -439,11 +442,15 @@ services:
|
||||
# OAuth2 login style: popup or redirect.
|
||||
#- OAUTH2_LOGIN_STYLE=redirect
|
||||
#- OAUTH2_CLIENT_ID=<Keycloak create Client ID>
|
||||
#- OAUTH2_SERVER_URL=<Keycloak server name>/auth
|
||||
#- OAUTH2_SERVER_URL=<Keycloak server url - https://keycloak.example.com>
|
||||
#- OAUTH2_AUTH_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/auth
|
||||
#- OAUTH2_USERINFO_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/userinfo
|
||||
#- OAUTH2_TOKEN_ENDPOINT=/realms/<keycloak realm>/protocol/openid-connect/token
|
||||
#- OAUTH2_SECRET=<keycloak client secret>
|
||||
#- OAUTH2_ID_MAP=sub
|
||||
#- OAUTH2_USERNAME_MAP=preferred_username
|
||||
#- OAUTH2_EMAIL_MAP=email
|
||||
#- OAUTH2_FULLNAME_MAP=name
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 DOORKEEPER ====
|
||||
# https://github.com/wekan/wekan/issues/1874
|
||||
@@ -582,7 +589,7 @@ services:
|
||||
# If using LDAPS: LDAP_ENCRYPTION=ssl
|
||||
#- LDAP_ENCRYPTION=false
|
||||
#
|
||||
# The certification for the LDAPS server. Certificate needs to be included in this docker-compose.yml file.
|
||||
# The certification for the LDAPS server. Certificate needs to be included in this docker compose.yml file.
|
||||
#- LDAP_CA_CERT=-----BEGIN CERTIFICATE-----MIIE+G2FIdAgIC...-----END CERTIFICATE-----
|
||||
#
|
||||
# Reject Unauthorized Certificate
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
Wekan provides a python script to ease the call of the REST API from command line interface.
|
||||
|
||||
# Context
|
||||
|
||||
- [API Login to get Bearer token](REST-API#example-call---as-form-data)
|
||||
- [API docs and examples for various programming languages](https://wekan.github.io/api/), there is Boards / Export for exporting board with API
|
||||
- In the right menu, scroll down REST API Docs etc links =====>
|
||||
- Wekan-Gogs integration with Node.js https://github.com/wekan/wekan-gogs
|
||||
|
||||
# Install
|
||||
|
||||
You need python3.
|
||||
|
||||
Windows
|
||||
```
|
||||
choco install python3
|
||||
# REBOOT
|
||||
pip3 install pip --upgrade
|
||||
pip3 install json
|
||||
python3 wekan.py
|
||||
```
|
||||
Debian/Ubuntu
|
||||
```
|
||||
sudo apt-get -y install python3 python3-pip python3-simplejson
|
||||
sudo pip3 install pip --upgrade
|
||||
chmod +x wekan.py
|
||||
./wekan.py
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
Copy the api.py script to you machine. [Newest Wekan Python CLI api.py here](https://raw.githubusercontent.com/wekan/wekan/master/api.py).
|
||||
|
||||
Then, in this script, look for and change:
|
||||
- wekanurl: https://boards.example.com => Your Wekan URL
|
||||
- username (could be username or username@example.com)
|
||||
- Only works with password login admin user. Does not work with LDAP, OAuth2 etc.
|
||||
|
||||
Keep in mind your Wekan credentials are potentially accessible in this file.
|
||||
|
||||
Then call it without any argument to see if everything is all right. You should just get usage examples.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Disclaimer
|
||||
|
||||
This page tries to be as up to date as possible. If you see something wrong here, feel free to update the page and help other people like you, that greatly depends on our APIs. If you don't feel comfortable doing this kind of changes, please contact us by creating an [issue](https://github.com/wekan/wekan/issues/new).
|
||||
|
||||
## Information about boards of user
|
||||
```
|
||||
curl -H "Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf" \
|
||||
http://localhost:3000/api/users/XQMZgynx9M79qTtQc/boards
|
||||
```
|
||||
|
||||
## Add/Remove Board Member and Change Role
|
||||
|
||||
[Add/Remove Board Member and Change Role admin/normal/nocomments/commentonly](REST-API-Role).
|
||||
|
||||
## The admin takes the ownership of ALL boards of the user (archived and not archived) where the user is admin on.
|
||||
|
||||
| URL | Requires Admin Auth | HTTP Method |
|
||||
| :--- | :--- | :--- |
|
||||
| `/api/users/:id` | `yes` | `PUT` |
|
||||
|
||||
```shell
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
-H "Content-type:application/json" \
|
||||
-X PUT \
|
||||
http://localhost:3000/api/users/ztKvBTzCqmyJ77on8 \
|
||||
-d '{ "action": "takeOwnership" }'
|
||||
```
|
||||
|
||||
## Create board
|
||||
|
||||
Required:
|
||||
- "title":"Board title here"
|
||||
- "owner":"ABCDE12345" <= User ID in Wekan. Not username or email.
|
||||
|
||||
Optional, and defaults:
|
||||
- "isAdmin":"true"
|
||||
- "isActive":"true"
|
||||
- "isNoComments":"false"
|
||||
- "isCommentOnly":"false"
|
||||
- "permission":"private" <== Set to "public" if you want public Wekan board
|
||||
- "color":"belize" <== Board color: belize, nephritis, pomegranate, pumpkin, wisteria, midnight.
|
||||
|
||||
<img src="https://wekan.github.io/board-colors.png" width="40%" alt="Wekan logo" />
|
||||
|
||||
Example:
|
||||
```
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards \
|
||||
-d '{"title":"Board title here","owner":"ABCDE12345","permission":"private","color":"nephritis"}'
|
||||
```
|
||||
|
||||
## In Wekan code
|
||||
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](https://github.com/wekan/wekan/blob/main/models/boards.js "Board API code")
|
||||
@@ -0,0 +1,94 @@
|
||||
# Disclaimer
|
||||
|
||||
This page tries to be as up to date as possible. If you see something wrong here, feel free to update the page and help other people like you, that greatly depends on our APIs. If you don't feel comfortable doing this kind of changes, please contact us by creating an [issue](https://github.com/wekan/wekan/issues/new).
|
||||
|
||||
# Retrieve cards by swimlane id
|
||||
|
||||
| API URL / Code Link | Requires Admin Auth | HTTP Method |
|
||||
| :--- | :--- | :--- |
|
||||
| [/api/boards/:boardId/swimlanes/:swimlaneId/cards](https://github.com/wekan/wekan/blob/c115046a7c86b30ab5deb8762d3ef7a5ea3f4f90/models/cards.js#L487) | `yes` | `GET` |
|
||||
|
||||
```shell
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
-X GET \
|
||||
http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/swimlanes/PgTuf6sFJsaxto5dC/cards
|
||||
```
|
||||
## Result example
|
||||
|
||||
```shell
|
||||
{
|
||||
"_id": "AzEeHS7KAGeYZCcak",
|
||||
"title": "Create Auth Code",
|
||||
"description": "Create Auth Code for application.",
|
||||
"listId": "RPRtDTQMKpShpgqoj"
|
||||
},
|
||||
{
|
||||
...
|
||||
```
|
||||
|
||||
# Add Card to List-Board-Swimlane
|
||||
|
||||
| API URL / Code Link | Requires Admin Auth | HTTP Method |
|
||||
| :--- | :--- | :--- |
|
||||
| [/api/boards/:boardId/lists/:listId/cards](https://github.com/wekan/wekan/blob/c115046a7c86b30ab5deb8762d3ef7a5ea3f4f90/models/cards.js#L487) | `yes` | `POST` |
|
||||
|
||||
```shell
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists/PgTuf6sFJsaxto5dC/cards \
|
||||
-d '{ "title": "Card title text", "description": "Card description text", "authorId": "The appropriate existing userId", "swimlaneId": "The destination swimlaneId" }'
|
||||
```
|
||||
## Result example
|
||||
The new card's ID is returned in the format:
|
||||
```json
|
||||
{
|
||||
"_id": "W9m9YxQKT6zZrKzRW"
|
||||
}
|
||||
```
|
||||
|
||||
# Update a card
|
||||
You can change (any of) the card's title, list, and description.
|
||||
|
||||
| API URL / Code Link | Requires Admin Auth | HTTP Method |
|
||||
| :--- | :--- | :--- |
|
||||
| [/api/boards/:boardId/lists/:fromListId/cards/:cardId](https://github.com/wekan/wekan/blob/c115046a7c86b30ab5deb8762d3ef7a5ea3f4f90/models/cards.js#L520) | `yes` | `PUT` |
|
||||
|
||||
```shell
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
-H "Content-type:application/json" \
|
||||
-X PUT \
|
||||
http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists/PgTuf6sFJsaxto5dC/cards/ssrNX9CvXvPxuC5DE \
|
||||
-d '{ "title": "New title text", "listId": "New destination listId", "description": "New description text" }'
|
||||
```
|
||||
## Result example
|
||||
The card's ID is returned in the format:
|
||||
```json
|
||||
{
|
||||
"_id": "W9m9YxQKT6zZrKzRW"
|
||||
}
|
||||
```
|
||||
# Delete a card
|
||||
|
||||
| API URL / Code Link | Requires Admin Auth | HTTP Method |
|
||||
| :--- | :--- | :--- |
|
||||
| [/api/boards/:boardId/lists/:listId/cards/:cardId](https://github.com/wekan/wekan/blob/c115046a7c86b30ab5deb8762d3ef7a5ea3f4f90/models/cards.js#L554) | `yes` | `DELETE` |
|
||||
|
||||
```shell
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
-H "Content-type:application/json" \
|
||||
-X DELETE \
|
||||
http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists/PgTuf6sFJsaxto5dC/cards/ssrNX9CvXvPxuC5DE \
|
||||
-d '{ "authorId": "the appropriate existing userId"}'
|
||||
```
|
||||
## Result example
|
||||
The card's ID is returned in the format:
|
||||
```json
|
||||
{
|
||||
"_id": "W9m9YxQKT6zZrKzRW"
|
||||
}
|
||||
```
|
||||
|
||||
# In Wekan code
|
||||
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](https://github.com/wekan/wekan/blob/main/models/cards.js "Card API code")
|
||||
@@ -0,0 +1,148 @@
|
||||
## In Wekan code
|
||||
|
||||
## 1) Checklists
|
||||
|
||||
wekan/models/checklists.js at bottom:
|
||||
|
||||
```
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/checklists', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramCardId = req.params.cardId;
|
||||
const checklists = Checklists.find({ cardId: paramCardId }).map(function (doc) {
|
||||
return {
|
||||
_id: doc._id,
|
||||
title: doc.title,
|
||||
};
|
||||
});
|
||||
if (checklists) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: checklists,
|
||||
});
|
||||
} else {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 500,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramChecklistId = req.params.checklistId;
|
||||
const paramCardId = req.params.cardId;
|
||||
const checklist = Checklists.findOne({ _id: paramChecklistId, cardId: paramCardId });
|
||||
if (checklist) {
|
||||
checklist.items = ChecklistItems.find({checklistId: checklist._id}).map(function (doc) {
|
||||
return {
|
||||
_id: doc._id,
|
||||
title: doc.title,
|
||||
isFinished: doc.isFinished,
|
||||
};
|
||||
});
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: checklist,
|
||||
});
|
||||
} else {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 500,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
JsonRoutes.add('POST', '/api/boards/:boardId/cards/:cardId/checklists', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
|
||||
const paramCardId = req.params.cardId;
|
||||
const id = Checklists.insert({
|
||||
title: req.body.title,
|
||||
cardId: paramCardId,
|
||||
sort: 0,
|
||||
});
|
||||
if (id) {
|
||||
req.body.items.forEach(function (item, idx) {
|
||||
ChecklistItems.insert({
|
||||
cardId: paramCardId,
|
||||
checklistId: id,
|
||||
title: item.title,
|
||||
sort: idx,
|
||||
});
|
||||
});
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: id,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 400,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
JsonRoutes.add('DELETE', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramChecklistId = req.params.checklistId;
|
||||
Checklists.remove({ _id: paramChecklistId });
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: paramChecklistId,
|
||||
},
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 2) Checklist Items
|
||||
|
||||
wekan/models/checklistItems.js at bottom:
|
||||
|
||||
```
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramItemId = req.params.itemId;
|
||||
const checklistItem = ChecklistItems.findOne({ _id: paramItemId });
|
||||
if (checklistItem) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: checklistItem,
|
||||
});
|
||||
} else {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 500,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
JsonRoutes.add('PUT', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
|
||||
const paramItemId = req.params.itemId;
|
||||
|
||||
if (req.body.hasOwnProperty('isFinished')) {
|
||||
ChecklistItems.direct.update({_id: paramItemId}, {$set: {isFinished: req.body.isFinished}});
|
||||
}
|
||||
if (req.body.hasOwnProperty('title')) {
|
||||
ChecklistItems.direct.update({_id: paramItemId}, {$set: {title: req.body.title}});
|
||||
}
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: paramItemId,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
JsonRoutes.add('DELETE', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramItemId = req.params.itemId;
|
||||
ChecklistItems.direct.remove({ _id: paramItemId });
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: paramItemId,
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,33 @@
|
||||
## Using Meteor with REST API
|
||||
|
||||
### 1) Adding API
|
||||
|
||||
```
|
||||
meteor add simple:json-routes
|
||||
```
|
||||
|
||||
https://atmospherejs.com/simple/json-routes
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/.meteor/packages#L139
|
||||
|
||||
### 2) API code
|
||||
|
||||
At models directory:
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/models/users.js#L2018
|
||||
|
||||
### 3) Login to API as JSON
|
||||
|
||||
https://github.com/wekan/wekan/wiki/REST-API#example-call---as-json
|
||||
|
||||
Also see at wiki right menu about REST API.
|
||||
|
||||
### 4) Use Python3 CLI to login to API and do REST API action
|
||||
|
||||
https://github.com/wekan/wekan/blob/main/api.py
|
||||
|
||||
### 4) API Docs
|
||||
|
||||
https://wekan.github.io/api/
|
||||
|
||||
Building API Docs: https://github.com/wekan/wekan/blob/main/releases/rebuild-docs.sh
|
||||
@@ -0,0 +1,78 @@
|
||||
1) Login as Admin user as Form Data to get Bearer token
|
||||
https://github.com/wekan/wekan/wiki/REST-API#example-call---as-form-data
|
||||
|
||||
2) There needs to be Custom Field added to board
|
||||
https://wekan.github.io/api/v4.42/#wekan-rest-api-customfields
|
||||
|
||||
3) Custom Field at board and card will have same `_id`
|
||||
https://wekan.github.io/api/v4.42/#put_board_list_card
|
||||
|
||||
4) When writing Custom Field value to card, like text field, content type needs to be "application/json" and the string needs to be an array:
|
||||
```
|
||||
-d '{ "customFields" : [ { "_id" : "oZHkpcaxDHnbkbqGo", "value" : "foobar" } ] }'
|
||||
```
|
||||
5) For other types of Custom Fields, you can look at Custom Field structure from MongoDB database with [nosqlbooster](https://nosqlbooster.com/downloads) that can also login through ssh to server Wekan snap MongoDB port 27019
|
||||
|
||||
|
||||
## In Wekan code (old)
|
||||
|
||||
wekan/models/customFields.js , at bottom
|
||||
|
||||
```
|
||||
//CUSTOM FIELD REST API
|
||||
if (Meteor.isServer) {
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramBoardId = req.params.boardId;
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: CustomFields.find({ boardId: paramBoardId }),
|
||||
});
|
||||
});
|
||||
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields/:customFieldId', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramBoardId = req.params.boardId;
|
||||
const paramCustomFieldId = req.params.customFieldId;
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: CustomFields.findOne({ _id: paramCustomFieldId, boardId: paramBoardId }),
|
||||
});
|
||||
});
|
||||
|
||||
JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramBoardId = req.params.boardId;
|
||||
const id = CustomFields.direct.insert({
|
||||
name: req.body.name,
|
||||
type: req.body.type,
|
||||
settings: req.body.settings,
|
||||
showOnCard: req.body.showOnCard,
|
||||
boardId: paramBoardId,
|
||||
});
|
||||
|
||||
const customField = CustomFields.findOne({_id: id, boardId: paramBoardId });
|
||||
customFieldCreation(req.body.authorId, customField);
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
JsonRoutes.add('DELETE', '/api/boards/:boardId/custom-fields/:customFieldId', function (req, res) {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramBoardId = req.params.boardId;
|
||||
const id = req.params.customFieldId;
|
||||
CustomFields.remove({ _id: id, boardId: paramBoardId });
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: id,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,191 @@
|
||||
## In Wekan code
|
||||
|
||||
wekan/models/integrations.js at bottom
|
||||
|
||||
```
|
||||
// Get all integrations in board
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function(req, res) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
Authentication.checkBoardAccess(req.userId, paramBoardId);
|
||||
|
||||
const data = Integrations.find({ boardId: paramBoardId }, { fields: { token: 0 } }).map(function(doc) {
|
||||
return doc;
|
||||
});
|
||||
|
||||
JsonRoutes.sendResult(res, {code: 200, data});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Get a single integration in board
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function(req, res) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
const paramIntId = req.params.intId;
|
||||
Authentication.checkBoardAccess(req.userId, paramBoardId);
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: Integrations.findOne({ _id: paramIntId, boardId: paramBoardId }, { fields: { token: 0 } }),
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Create a new integration
|
||||
JsonRoutes.add('POST', '/api/boards/:boardId/integrations', function(req, res) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
Authentication.checkBoardAccess(req.userId, paramBoardId);
|
||||
|
||||
const id = Integrations.insert({
|
||||
userId: req.userId,
|
||||
boardId: paramBoardId,
|
||||
url: req.body.url,
|
||||
});
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: id,
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Edit integration data
|
||||
JsonRoutes.add('PUT', '/api/boards/:boardId/integrations/:intId', function (req, res) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
const paramIntId = req.params.intId;
|
||||
Authentication.checkBoardAccess(req.userId, paramBoardId);
|
||||
|
||||
if (req.body.hasOwnProperty('enabled')) {
|
||||
const newEnabled = req.body.enabled;
|
||||
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
|
||||
{$set: {enabled: newEnabled}});
|
||||
}
|
||||
if (req.body.hasOwnProperty('title')) {
|
||||
const newTitle = req.body.title;
|
||||
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
|
||||
{$set: {title: newTitle}});
|
||||
}
|
||||
if (req.body.hasOwnProperty('url')) {
|
||||
const newUrl = req.body.url;
|
||||
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
|
||||
{$set: {url: newUrl}});
|
||||
}
|
||||
if (req.body.hasOwnProperty('token')) {
|
||||
const newToken = req.body.token;
|
||||
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
|
||||
{$set: {token: newToken}});
|
||||
}
|
||||
if (req.body.hasOwnProperty('activities')) {
|
||||
const newActivities = req.body.activities;
|
||||
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
|
||||
{$set: {activities: newActivities}});
|
||||
}
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: paramIntId,
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Delete subscribed activities
|
||||
JsonRoutes.add('DELETE', '/api/boards/:boardId/integrations/:intId/activities', function (req, res) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
const paramIntId = req.params.intId;
|
||||
const newActivities = req.body.activities;
|
||||
Authentication.checkBoardAccess(req.userId, paramBoardId);
|
||||
|
||||
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
|
||||
{$pullAll: {activities: newActivities}});
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: Integrations.findOne({_id: paramIntId, boardId: paramBoardId}, { fields: {_id: 1, activities: 1}}),
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add subscribed activities
|
||||
JsonRoutes.add('POST', '/api/boards/:boardId/integrations/:intId/activities', function (req, res) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
const paramIntId = req.params.intId;
|
||||
const newActivities = req.body.activities;
|
||||
Authentication.checkBoardAccess(req.userId, paramBoardId);
|
||||
|
||||
Integrations.direct.update({_id: paramIntId, boardId: paramBoardId},
|
||||
{$addToSet: {activities: { $each: newActivities}}});
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: Integrations.findOne({_id: paramIntId, boardId: paramBoardId}, { fields: {_id: 1, activities: 1}}),
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Delete integration
|
||||
JsonRoutes.add('DELETE', '/api/boards/:boardId/integrations/:intId', function (req, res) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
const paramIntId = req.params.intId;
|
||||
Authentication.checkBoardAccess(req.userId, paramBoardId);
|
||||
|
||||
Integrations.direct.remove({_id: paramIntId, boardId: paramBoardId});
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: paramIntId,
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
## In Wekan code
|
||||
|
||||
wekan/models/lists.js at bottom:
|
||||
```
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/lists', function (req, res) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
Authentication.checkBoardAccess( req.userId, paramBoardId);
|
||||
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: Lists.find({ boardId: paramBoardId, archived: false }).map(function (doc) {
|
||||
return {
|
||||
_id: doc._id,
|
||||
title: doc.title,
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId', function (req, res) {
|
||||
try {
|
||||
const paramBoardId = req.params.boardId;
|
||||
const paramListId = req.params.listId;
|
||||
Authentication.checkBoardAccess( req.userId, paramBoardId);
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: Lists.findOne({ _id: paramListId, boardId: paramBoardId, archived: false }),
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
JsonRoutes.add('POST', '/api/boards/:boardId/lists', function (req, res) {
|
||||
try {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramBoardId = req.params.boardId;
|
||||
const id = Lists.insert({
|
||||
title: req.body.title,
|
||||
boardId: paramBoardId,
|
||||
});
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: id,
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId', function (req, res) {
|
||||
try {
|
||||
Authentication.checkUserId( req.userId);
|
||||
const paramBoardId = req.params.boardId;
|
||||
const paramListId = req.params.listId;
|
||||
Lists.remove({ _id: paramListId, boardId: paramBoardId });
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: {
|
||||
_id: paramListId,
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
JsonRoutes.sendResult(res, {
|
||||
code: 200,
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,82 @@
|
||||
# Change Role at Web UI
|
||||
|
||||
BoardAdmin can change role of user at right sidebar / click avatar / select role like Admin, Normal, etc.
|
||||
|
||||
# Change Role with API
|
||||
|
||||
- https://wekan.github.io/api/
|
||||
- api.py at https://github.com/wekan/wekan
|
||||
- Login to API https://github.com/wekan/wekan/wiki/REST-API#example-call---as-json
|
||||
|
||||
# Disclaimer
|
||||
|
||||
This page tries to be as up to date as possible. If you see something wrong here, feel free to update the page and help other people like you, that greatly depends on our APIs. If you don't feel comfortable doing this kind of changes, please contact us by creating an [issue](https://github.com/wekan/wekan/issues/new).
|
||||
|
||||
## Add New Board Member with Role
|
||||
This example adds with normal role. See examples below for other roles.
|
||||
```
|
||||
curl -H "Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards/BOARD-ID-HERE/members/USER-ID-HERE/add \
|
||||
-d '{"action": "add","isAdmin": "false", "isNoComments":"false", "isCommentOnly": "false", "isWorker": "false" }'
|
||||
```
|
||||
like
|
||||
```
|
||||
/api/boards/{boardid}/members/{MEMBERID}/add Body{ "action" : add, "isadmin" : false ...........
|
||||
```
|
||||
|
||||
## Remove Member from Board
|
||||
```
|
||||
curl -H "Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards/BOARD-ID-HERE/members/USER-ID-HERE/remove \
|
||||
-d '{"action": "remove"}'
|
||||
```
|
||||
|
||||
# Change Role of Existing Board Member
|
||||
|
||||
## Admin
|
||||
```
|
||||
curl -H "Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards/BOARD-ID-HERE/members/USER-ID-HERE \
|
||||
-d '{"isAdmin": "true", "isNoComments":"false", "isCommentOnly": "false", "isWorker": "false"}'
|
||||
```
|
||||
## Normal
|
||||
```
|
||||
curl -H "Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards/BOARD-ID-HERE/members/USER-ID-HERE \
|
||||
-d '{"isAdmin": "false", "isNoComments":"false", "isCommentOnly": "false", "isWorker": "false"}'
|
||||
```
|
||||
## No Comments
|
||||
```
|
||||
curl -H "Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards/BOARD-ID-HERE/members/USER-ID-HERE \
|
||||
-d '{"isAdmin": "false", "isNoComments":"true", "isCommentOnly": "false", "isWorker": "false"}'
|
||||
```
|
||||
## Comment Only
|
||||
```
|
||||
curl -H "Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards/BOARD-ID-HERE/members/USER-ID-HERE \
|
||||
-d '{"isAdmin": "false", "isNoComments":"false", "isCommentOnly": "true", "isWorker": "false"}'
|
||||
```
|
||||
## Worker
|
||||
- Can: move cards, can assign himself to card, can comment.
|
||||
- Cannot: anything else including revoke card, change settings, create/delete/edit cards/tags and so on...
|
||||
```
|
||||
curl -H "Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards/BOARD-ID-HERE/members/USER-ID-HERE \
|
||||
-d '{"isAdmin": "false", "isNoComments":"false", "isCommentOnly": "false", "isWorker": "true"}'
|
||||
```
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Disclaimer
|
||||
|
||||
This page tries to be as up to date as possible. If you see something wrong here, feel free to update the page and help other people like you, that greatly depends on our APIs. If you don't feel comfortable doing this kind of changes, please contact us by creating an [issue](https://github.com/wekan/wekan/issues/new).
|
||||
|
||||
# Retrieve cards by swimlane id
|
||||
|
||||
Please somebody add example by looking this:
|
||||
|
||||
[Issue](https://github.com/wekan/wekan/issues/1934) and [code](https://github.com/wekan/wekan/pull/1944/commits/be42b8d4cbdfa547ca019ab2dc9a590a115cc0e2). Also add to [Cards page](REST-API-Cards)
|
||||
|
||||
# Add Swimlane to Board
|
||||
|
||||
| API URL / Code Link | Requires Admin Auth | HTTP Method |
|
||||
| :--- | :--- | :--- |
|
||||
| [/api/boards/:boardId/swimlanes](https://github.com/wekan/wekan/blob/main/models/swimlanes.js#L223) | `yes` | `POST` |
|
||||
|
||||
```shell
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
-H "Content-type:application/json" \
|
||||
-X POST \
|
||||
http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/swimlanes \
|
||||
-d '{ "title": "Swimlane title text" }'
|
||||
```
|
||||
## Result example
|
||||
The new swimlane's ID is returned in the format:
|
||||
```json
|
||||
{
|
||||
"_id": "W9m9YxQKT6zZrKzRW"
|
||||
}
|
||||
```
|
||||
|
||||
# Delete a swimlane
|
||||
|
||||
| API URL / Code Link | Requires Admin Auth | HTTP Method |
|
||||
| :--- | :--- | :--- |
|
||||
| [/api/boards/:boardId/swimlanes/:swimlaneId](https://github.com/wekan/wekan/blob/main/models/swimlanes.js#L257) | `yes` | `DELETE` |
|
||||
|
||||
```shell
|
||||
curl -H "Authorization: Bearer t7iYB86mXoLfP_XsMegxF41oKT7iiA9lDYiKVtXcctl" \
|
||||
-H "Content-type:application/json" \
|
||||
-X DELETE \
|
||||
http://localhost:3000/api/boards/YRgy7Ku6uLFv2pYwZ/lists/PgTuf6sFJsaxto5dC/cards/ssrNX9CvXvPxuC5DE
|
||||
```
|
||||
## Result example
|
||||
The swimlane's ID is returned in the format:
|
||||
```json
|
||||
{
|
||||
"_id": "W9m9YxQKT6zZrKzRW"
|
||||
}
|
||||
```
|
||||
|
||||
# In Wekan code
|
||||
|
||||
If you believe that code is the best documentation, be our guest: [models/cards.js](https://github.com/wekan/wekan/blob/main/models/swimlanes.js "Swimlane API code")
|
||||
Loaded 100 of 653 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user