Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dadb4f0a1 | ||
|
|
1629e6aae6 | ||
|
|
0775e2a3e5 | ||
|
|
454d0b270d | ||
|
|
3076547cee | ||
|
|
4bcddcc98a | ||
|
|
099085b416 | ||
|
|
9bd6879455 | ||
|
|
00efbf00d3 | ||
|
|
5c214c143d | ||
|
|
3418b97faa | ||
|
|
a484101abf | ||
|
|
2f79432e4e | ||
|
|
f71728ae16 | ||
|
|
e7e4296b02 | ||
|
|
b85db43c47 | ||
|
|
db6fc57515 | ||
|
|
6b404da9f8 | ||
|
|
4d31985eb9 | ||
|
|
0e9aa2eeed | ||
|
|
7d73f2bbba | ||
|
|
9ca8d78514 | ||
|
|
11d8096b8d | ||
|
|
a49a918547 | ||
|
|
4ebd113bf0 | ||
|
|
14df71de5b | ||
|
|
9f459a37b8 | ||
|
|
5487c015db | ||
|
|
f46173077b | ||
|
|
c373013134 | ||
|
|
12c5d2324d | ||
|
|
e6dc20f6c7 | ||
|
|
ea658842a2 | ||
|
|
12f0c85546 | ||
|
|
033a5d8186 | ||
|
|
b769a71af3 | ||
|
|
ad35e7b7bd | ||
|
|
a9e56f2b09 | ||
|
|
5f807b80fe | ||
|
|
4d89eb6083 | ||
|
|
bd2739e78d | ||
|
|
265ee9ab83 | ||
|
|
065485168a | ||
|
|
7d609ef0b1 | ||
|
|
dc7b97c178 | ||
|
|
a5b376e6b5 | ||
|
|
993e99330f | ||
|
|
e563212475 | ||
|
|
cc54867976 | ||
|
|
36250f83b9 | ||
|
|
e715106d65 | ||
|
|
f888b5cb23 | ||
|
|
627d7d9456 | ||
|
|
b62dd5782a | ||
|
|
0460a7d9d3 | ||
|
|
45d53dede4 | ||
|
|
ce3c87f767 | ||
|
|
ceed865e30 | ||
|
|
a9bd5c7496 | ||
|
|
e1730c9912 | ||
|
|
8f1754be50 | ||
|
|
4ace1db9b0 | ||
|
|
eff636ddd6 | ||
|
|
9b7bf1ba28 | ||
|
|
7190057d0c | ||
|
|
bb5b723520 | ||
|
|
a2e6f01469 | ||
|
|
12ee7efc5f | ||
|
|
7c7640e63e | ||
|
|
e668fdc500 | ||
|
|
f02478d57c | ||
|
|
8fc117eb53 | ||
|
|
af598b13e8 | ||
|
|
f00f164078 | ||
|
|
70ac18a7e6 | ||
|
|
e461c4a018 | ||
|
|
186364f227 | ||
|
|
72f9d45bbe | ||
|
|
6392e06a36 | ||
|
|
c879e63fc2 | ||
|
|
290a6dfb6e | ||
|
|
94c82d121c | ||
|
|
3e92d6f6a3 | ||
|
|
3a2b50720e | ||
|
|
8f47e65684 | ||
|
|
95ebf3bda8 | ||
|
|
ea95357ae4 | ||
|
|
7501e255dd | ||
|
|
ff13571c71 | ||
|
|
6501173ce4 | ||
|
|
1d13040305 | ||
|
|
af1439b274 | ||
|
|
32af18b33d | ||
|
|
a48e537b2d | ||
|
|
424bc0e7c5 | ||
|
|
e98821fafc | ||
|
|
2d61fcdef4 | ||
|
|
045160c7bb | ||
|
|
f98b03bee5 | ||
|
|
703baf4ce9 | ||
|
|
1bf1ed79e8 | ||
|
|
a7888f0c66 | ||
|
|
7122f59c51 | ||
|
|
839266903a | ||
|
|
a8e3dc06fe | ||
|
|
7e0bc91a44 | ||
|
|
62efb67d06 | ||
|
|
00fd47105d | ||
|
|
1bee24fa68 | ||
|
|
9a539fb414 | ||
|
|
c0828f2d00 | ||
|
|
90c70aef96 | ||
|
|
dfc736b064 | ||
|
|
af53afb9f8 | ||
|
|
df61e471a1 | ||
|
|
3e364c4f35 | ||
|
|
3fb63b518f | ||
|
|
4bf6b457ae | ||
|
|
71c575d4b5 | ||
|
|
8b2c9cc74f | ||
|
|
fc7c70bdc0 | ||
|
|
dd18fd2477 | ||
|
|
2512ea1715 | ||
|
|
d63fa4aa89 | ||
|
|
ec51f7cced | ||
|
|
3eb834c920 | ||
|
|
ba24e4a775 | ||
|
|
bec25720da | ||
|
|
0db7ba5107 | ||
|
|
5e65dfc272 | ||
|
|
9b56f149e1 | ||
|
|
a15ed97ab3 | ||
|
|
d34918b08b | ||
|
|
e16cc34f6d | ||
|
|
87fd4880c9 | ||
|
|
f325fe8436 | ||
|
|
70558d00a7 | ||
|
|
d86eb74eb0 | ||
|
|
6adedfd77f | ||
|
|
e1a7c8abb7 | ||
|
|
8c8fba3c6c | ||
|
|
a176a58a72 | ||
|
|
1db3d9a081 | ||
|
|
8020aa4b81 | ||
|
|
bbef2aff8c | ||
|
|
7e1755c89f | ||
|
|
66a4562c91 | ||
|
|
6ad4e78fa8 | ||
|
|
6a92a95414 | ||
|
|
1fcc0d2ec2 | ||
|
|
e7051c8ecd | ||
|
|
1670ba31e8 | ||
|
|
ac1f540763 | ||
|
|
61dd3180a4 | ||
|
|
930333d546 | ||
|
|
2967d13968 | ||
|
|
56be68fcb7 | ||
|
|
dbb9ddd7d3 | ||
|
|
879ab282e1 | ||
|
|
d9a7950fa4 | ||
|
|
18e34b7f79 | ||
|
|
4abb26f8bc | ||
|
|
cd6b4529be | ||
|
|
6234fdff18 | ||
|
|
d4c3f32383 | ||
|
|
a72d51d901 | ||
|
|
a66257ec21 | ||
|
|
395452b54f | ||
|
|
e40e42103e | ||
|
|
7651c00d57 | ||
|
|
764dbb71f0 | ||
|
|
83ce43ac52 | ||
|
|
e528474fc5 | ||
|
|
73ff4c7966 | ||
|
|
01d8058a3f | ||
|
|
b69f08889a | ||
|
|
edf3573760 | ||
|
|
021def818f | ||
|
|
f519d277a5 | ||
|
|
218ddf03bc | ||
|
|
31c7539eb9 | ||
|
|
eb8d9955d5 | ||
|
|
7bcaf71bf0 | ||
|
|
7afc4638d2 | ||
|
|
c9b7542a9b | ||
|
|
c59c7bd63d | ||
|
|
574116b058 | ||
|
|
dbcc919768 | ||
|
|
bf37737f44 | ||
|
|
187b31caa5 | ||
|
|
f75ab964be | ||
|
|
bff43c3f92 | ||
|
|
fb3099eba9 | ||
|
|
5d1d90c2ff | ||
|
|
4d3c0c60d0 | ||
|
|
99d6f7a765 | ||
|
|
418b9effa7 | ||
|
|
30131ffc20 | ||
|
|
ac85e00e0a | ||
|
|
8b2e2e9b1f | ||
|
|
724f9d43b4 | ||
|
|
2ce136ee0a | ||
|
|
7495c1bf59 | ||
|
|
ef685703cf | ||
|
|
0218f3266b | ||
|
|
6a97dfb4b9 | ||
|
|
75fdc53746 | ||
|
|
ed3c53df35 | ||
|
|
2efa42b853 | ||
|
|
cf151b5632 | ||
|
|
2241c35e55 | ||
|
|
6f7292db5e | ||
|
|
ce587d23fa | ||
|
|
9979193df5 | ||
|
|
bee9dc8b3e | ||
|
|
31bf4072d3 | ||
|
|
fbbd3d5174 | ||
|
|
65b7d3d50d | ||
|
|
4589c3df15 | ||
|
|
a160b662ef | ||
|
|
e176410b54 | ||
|
|
2ac5f0b2d7 | ||
|
|
33f47414bb | ||
|
|
fb27ce027c | ||
|
|
ae180c7b85 | ||
|
|
95ed3a0ab5 | ||
|
|
dbe0b4922f | ||
|
|
e69114319f | ||
|
|
8db78f8dd4 | ||
|
|
f2c31f877c | ||
|
|
a87a591600 | ||
|
|
67687110bb | ||
|
|
db180fa43e | ||
|
|
aff6e361f0 | ||
|
|
a37856c937 | ||
|
|
7f32de3bec | ||
|
|
c46ec4a155 | ||
|
|
0c38700458 | ||
|
|
942065bb29 | ||
|
|
8aa816149f | ||
|
|
27ee16ea1e | ||
|
|
d6b0b0318d | ||
|
|
faf03ae1ce | ||
|
|
dd3c8394e7 | ||
|
|
c10f3e97bf | ||
|
|
ea5b93f97c | ||
|
|
bd08c1a4f1 | ||
|
|
ea17cc8592 | ||
|
|
d535469163 | ||
|
|
349925ad6b | ||
|
|
b876098dc4 | ||
|
|
dc04beb61f | ||
|
|
04d8aee86a | ||
|
|
8064d203f3 | ||
|
|
2fbf411918 | ||
|
|
1e9695a428 | ||
|
|
443b40cff3 | ||
|
|
f144f8c90a | ||
|
|
998d2f5b44 | ||
|
|
c15c10de97 | ||
|
|
142fa1c73d | ||
|
|
23a403644c | ||
|
|
89f4cdb7e5 | ||
|
|
c650dd2088 | ||
|
|
85d51ec403 | ||
|
|
8804ca84ec | ||
|
|
aa0dee1fba | ||
|
|
27d55a49fb | ||
|
|
6ef612d04e | ||
|
|
42610d9642 | ||
|
|
d9c290deda | ||
|
|
49214747cd | ||
|
|
84d0817894 | ||
|
|
94525a4d3d | ||
|
|
4ded5d2f51 | ||
|
|
7f8f3f8f16 | ||
|
|
45d7be19b6 | ||
|
|
8446640060 | ||
|
|
184290ce98 | ||
|
|
39fb0e4911 | ||
|
|
1f65add7b0 | ||
|
|
241c3ed8ae | ||
|
|
ee2fafb6bc | ||
|
|
fee82f7f7a | ||
|
|
c2340c6508 | ||
|
|
5ad9ee1de6 | ||
|
|
ea6551e567 | ||
|
|
6c9a2319ba | ||
|
|
e305bf019f | ||
|
|
505c2da072 | ||
|
|
9085e52446 | ||
|
|
9ac5e44a29 | ||
|
|
3d8e9c8b09 | ||
|
|
5cf352defb | ||
|
|
371dd7db9f | ||
|
|
051de2ea5e | ||
|
|
73408c73af | ||
|
|
7079b65cc1 | ||
|
|
67676c50ad | ||
|
|
e46d10ea4c | ||
|
|
331a64f067 | ||
|
|
7f06218877 | ||
|
|
fc95698953 | ||
|
|
2f69501c14 | ||
|
|
8d2c5a9bbb | ||
|
|
cec05dae47 | ||
|
|
8e6bd81abe | ||
|
|
eb9d44f5f0 | ||
|
|
3a83d48bef | ||
|
|
01cd1c0fdc | ||
|
|
c9ddad5615 | ||
|
|
9eb6a1d6af | ||
|
|
9b22e6da7a | ||
|
|
66488b834b | ||
|
|
8752225409 | ||
|
|
258b31f04a | ||
|
|
6997dbc6a8 | ||
|
|
1c3f42cec1 | ||
|
|
fc6c4f85f0 | ||
|
|
8ad9f2fbea | ||
|
|
ee5ec45c73 | ||
|
|
710c65a8ae | ||
|
|
f094dfb84f | ||
|
|
b84cc037c1 | ||
|
|
7aaa853d1b | ||
|
|
e32225e18f | ||
|
|
d7f903f3cd | ||
|
|
f0ebfb7f58 | ||
|
|
f660dcaa86 | ||
|
|
daa5de7011 | ||
|
|
2827c0e3ed | ||
|
|
00cd8f140c | ||
|
|
8be1d371cd | ||
|
|
2252a855dd | ||
|
|
c1eb175ab8 | ||
|
|
e7174107e9 | ||
|
|
63c838eee9 | ||
|
|
bd336dddec | ||
|
|
673e5b1987 | ||
|
|
d45bbc2269 | ||
|
|
8884d5b7e0 | ||
|
|
a3b99dd02d | ||
|
|
e7138772f0 | ||
|
|
b8a976d4ad | ||
|
|
ba0fb2d985 | ||
|
|
ebc7741fcb | ||
|
|
d1f8c92a99 | ||
|
|
4036abe3c1 | ||
|
|
23a2e90f5f | ||
|
|
5792b21ed1 | ||
|
|
4a4bfef888 | ||
|
|
a2cefc595e | ||
|
|
892828c02e | ||
|
|
9cf8af0416 | ||
|
|
2767c444b2 | ||
|
|
b5ee0649db | ||
|
|
a7b7bfafb5 | ||
|
|
36d8949831 | ||
|
|
5c5c867aca | ||
|
|
383323c4ea | ||
|
|
96465ac664 | ||
|
|
98dedc697c | ||
|
|
f82fbdb8a2 | ||
|
|
5088c12253 | ||
|
|
611af2d523 | ||
|
|
97ef13c222 | ||
|
|
d7adfc779e | ||
|
|
507cfbb377 | ||
|
|
1af75aa579 | ||
|
|
861b98ea37 | ||
|
|
b09be8614b | ||
|
|
8bc047cec3 | ||
|
|
cb7ec54be2 | ||
|
|
6d3b979d67 | ||
|
|
fe01d35fe6 | ||
|
|
f38e3db93c | ||
|
|
b988ef430c | ||
|
|
4a97e70b8d | ||
|
|
f082128d99 | ||
|
|
7577b246da | ||
|
|
9db14a0c3b | ||
|
|
f3d5b887c8 | ||
|
|
7e21589ee1 | ||
|
|
e1e4ea1205 | ||
|
|
54205b7c5d | ||
|
|
e0b5d1c15e | ||
|
|
79b40cd8a9 | ||
|
|
f6830ff4f3 | ||
|
|
765ac045fe | ||
|
|
b9d98f58cf | ||
|
|
e0978000f8 | ||
|
|
bc186958ab | ||
|
|
a190600934 | ||
|
|
65fc2a4fdd | ||
|
|
d41810aca1 | ||
|
|
b75a7a6424 | ||
|
|
bfb3837ee4 | ||
|
|
72fa10761a | ||
|
|
9c1523c916 | ||
|
|
a786262658 | ||
|
|
6245982025 | ||
|
|
c643cdaad3 | ||
|
|
f0cadf858d | ||
|
|
2566286e86 | ||
|
|
8bf3841e6c | ||
|
|
7a959ef1a2 | ||
|
|
5525247f52 | ||
|
|
7444c11c82 | ||
|
|
6d3ecdea55 | ||
|
|
7db1445d09 | ||
|
|
08ee969445 | ||
|
|
4bfa727e9e | ||
|
|
ed53065a09 | ||
|
|
49fdae6e06 | ||
|
|
b78dfc7149 | ||
|
|
0227b38845 | ||
|
|
7d52016d79 | ||
|
|
d9bc5f80a0 | ||
|
|
f1a0e4754f | ||
|
|
496c791629 | ||
|
|
2b7c5cfcb1 | ||
|
|
f647f4580e | ||
|
|
c62c705755 | ||
|
|
57129eff04 | ||
|
|
3c7ca87625 | ||
|
|
126c9ab853 | ||
|
|
c9071a74bc | ||
|
|
292e43466e | ||
|
|
75af42cd8c | ||
|
|
60c831521e | ||
|
|
9e0de569dc | ||
|
|
6dbb8c8c68 | ||
|
|
ac89276ee9 | ||
|
|
7902221a96 | ||
|
|
47540df916 | ||
|
|
dbef7e7cd3 | ||
|
|
4a19790b11 | ||
|
|
533eb41249 | ||
|
|
f09ea42981 | ||
|
|
6290b3b697 | ||
|
|
92b6b0064a | ||
|
|
8662fd59fb | ||
|
|
16bb65f7c7 | ||
|
|
44c3449ab2 | ||
|
|
ed13a3b95f | ||
|
|
32646fb9be | ||
|
|
38eccdb95b | ||
|
|
6944a20ba4 | ||
|
|
2e8323ca6a | ||
|
|
fae4d0be08 | ||
|
|
b58e1c0102 | ||
|
|
f18658c0d2 | ||
|
|
8054f2b002 | ||
|
|
5178aade75 | ||
|
|
6628ae8b5a | ||
|
|
7ca0fa626c | ||
|
|
0ceff124cc | ||
|
|
37faa41d97 | ||
|
|
fc0630715c | ||
|
|
9e75a70212 | ||
|
|
3d61be028a | ||
|
|
f9c179e9df | ||
|
|
2b77a67c03 | ||
|
|
c6fc172055 | ||
|
|
7aafb5e26e | ||
|
|
02b6df320f | ||
|
|
6d719b8a98 | ||
|
|
b1b12b05b5 | ||
|
|
39260bccb3 | ||
|
|
5754a886ab | ||
|
|
624fdea842 | ||
|
|
1975673152 | ||
|
|
1d523c1ede | ||
|
|
407632eed0 | ||
|
|
7518d93feb | ||
|
|
0fb6b6e519 | ||
|
|
fa9641ba7b | ||
|
|
6d855967fe | ||
|
|
17d90684bb | ||
|
|
d6094c3476 | ||
|
|
4eca4eeddc | ||
|
|
b392e515ca | ||
|
|
f25ed7fcfc | ||
|
|
b7928a9538 | ||
|
|
d523a897f6 | ||
|
|
a67e3754ab | ||
|
|
80319b0038 | ||
|
|
becb81f04a | ||
|
|
f50f876cab | ||
|
|
bd7bbd18c5 | ||
|
|
d8218091c6 | ||
|
|
ba3fa2aaea | ||
|
|
0ab86c22af | ||
|
|
0ed029e706 | ||
|
|
c5a7cf56bc | ||
|
|
9f25938b51 | ||
|
|
e0d92c7f64 | ||
|
|
48ecbf5017 | ||
|
|
8fe2e073c5 | ||
|
|
a8c8970192 | ||
|
|
e8e5616832 | ||
|
|
abe5ca752e | ||
|
|
db84c73337 | ||
|
|
a4ac34d8aa | ||
|
|
e47f26a390 | ||
|
|
1eca7c9996 | ||
|
|
25024fbe6b | ||
|
|
1f6d96c873 | ||
|
|
8ec4e1a7d0 | ||
|
|
f4a3363f6f | ||
|
|
4733afe4eb | ||
|
|
72ddd66495 | ||
|
|
a09e961110 | ||
|
|
49c4dd8b14 | ||
|
|
65f0cab730 | ||
|
|
b72fae24ca | ||
|
|
228c78c248 | ||
|
|
73fdc7ff79 | ||
|
|
cbf91c1869 | ||
|
|
70777cfce8 | ||
|
|
f74fab64b6 | ||
|
|
12839fd929 | ||
|
|
b903750f45 | ||
|
|
f2d2982a76 | ||
|
|
efb24c2d25 | ||
|
|
5fd7407207 | ||
|
|
0a77266f6e | ||
|
|
936d9fe306 | ||
|
|
810c3c81db | ||
|
|
67f614ffb0 | ||
|
|
7f7ef0a914 | ||
|
|
4acf3738ca | ||
|
|
f370f749b9 | ||
|
|
f518971520 | ||
|
|
c5eca7f3ec | ||
|
|
9868b3b6af | ||
|
|
f9c9f2a453 | ||
|
|
206828b440 | ||
|
|
343dcb4995 | ||
|
|
546134f6cd | ||
|
|
ac7ef4d4cd | ||
|
|
eb1d3b6101 | ||
|
|
6151382c07 | ||
|
|
1516be030e | ||
|
|
5eb7ce09b2 | ||
|
|
d791914ddd | ||
|
|
fb09efd1a4 | ||
|
|
2d749e13e0 | ||
|
|
712ae37400 | ||
|
|
92ba5f8874 | ||
|
|
8cf392e1ea | ||
|
|
ea01c5c1b9 | ||
|
|
0991e54c22 | ||
|
|
08a01c6d2c | ||
|
|
95cf54836f | ||
|
|
81f5b6a428 | ||
|
|
f1b49e05eb | ||
|
|
fdd23e2f54 | ||
|
|
f906153b47 | ||
|
|
34fa83a461 | ||
|
|
ddc229dabe | ||
|
|
c4d42d86a2 | ||
|
|
c2bd4d1cee | ||
|
|
4fb4deacf9 | ||
|
|
ce5f2a19a5 | ||
|
|
05a1588bac | ||
|
|
e21363d37e | ||
|
|
d8dad479fd | ||
|
|
d04e9bbabd | ||
|
|
790a82c4b1 | ||
|
|
26826b6120 | ||
|
|
f48f67340e | ||
|
|
cf6713a31c | ||
|
|
30ef5c1bf6 | ||
|
|
1859f58ad9 | ||
|
|
3bc2862ae2 | ||
|
|
3ce1e8220b | ||
|
|
2d9c8740b4 | ||
|
|
502bfcb97d | ||
|
|
2bd23d9525 | ||
|
|
a59a242c1e | ||
|
|
9530f38791 | ||
|
|
c136033c1f | ||
|
|
17a5505eda | ||
|
|
8abea470fb | ||
|
|
1d8ff8398c | ||
|
|
0af41dc19c | ||
|
|
21de4a2ca0 | ||
|
|
ed289451f6 | ||
|
|
045502c9e9 | ||
|
|
405411f97f | ||
|
|
00c3092f44 | ||
|
|
e3d52169d7 | ||
|
|
be5b6245e3 | ||
|
|
705e6d7e3a | ||
|
|
6c83cb5b44 | ||
|
|
b51152fca4 | ||
|
|
344094ec18 | ||
|
|
89a7bb8bc8 | ||
|
|
e762465e09 | ||
|
|
7d42b7eaf0 | ||
|
|
4106aab659 | ||
|
|
df2a2aae1d | ||
|
|
fbcafdbd02 | ||
|
|
7cc35970a8 | ||
|
|
123654008f | ||
|
|
711182ef8a | ||
|
|
46a5eec7d2 | ||
|
|
a42c132306 | ||
|
|
be8a3191d1 | ||
|
|
157f16a1d4 | ||
|
|
803c949c8d | ||
|
|
e64bc38932 | ||
|
|
f1d96fcef8 | ||
|
|
5934b7a4a2 | ||
|
|
245d6dc96d | ||
|
|
1941ee6af5 | ||
|
|
e3292dd562 | ||
|
|
ec33d0b34f | ||
|
|
5add2aaea6 | ||
|
|
50a849fccd | ||
|
|
ef53ade32e | ||
|
|
3ee2156399 | ||
|
|
90cd26e78e | ||
|
|
4c9d10f7a7 | ||
|
|
a686a18809 | ||
|
|
c7ab0403a6 | ||
|
|
f90fb28483 | ||
|
|
fecb9cdb66 | ||
|
|
66df3cc2ee | ||
|
|
d9adce7b67 | ||
|
|
6f39a3cf1c | ||
|
|
f840d26faf | ||
|
|
dfd5acea97 | ||
|
|
5411113544 | ||
|
|
d1eb6f2355 | ||
|
|
aed1e71094 | ||
|
|
46f6d1e989 | ||
|
|
e9c1b2b2c4 | ||
|
|
94f8fb2393 | ||
|
|
2055d916e9 | ||
|
|
70dbd1518d | ||
|
|
3db3a5bad9 | ||
|
|
b8a41406f5 | ||
|
|
c8b8fb83dc | ||
|
|
cb9b8d4f2b | ||
|
|
69ce16192d | ||
|
|
46efd27079 | ||
|
|
141e866719 | ||
|
|
4e7f1a9937 | ||
|
|
3e7fee8fae | ||
|
|
b09e5b2680 | ||
|
|
fb00869e49 | ||
|
|
4521d5c10d | ||
|
|
5eb4cff991 | ||
|
|
20415b881c | ||
|
|
90b0a93547 | ||
|
|
f67a174c4a | ||
|
|
8224410e0c | ||
|
|
7f78106211 | ||
|
|
0025212b3e | ||
|
|
d79a35ea82 | ||
|
|
ce829f586b | ||
|
|
a3d41c560b | ||
|
|
5af4386a0f | ||
|
|
c08c9543af | ||
|
|
b25eeb334c | ||
|
|
5c2a44e6fe | ||
|
|
4c0d382fd8 | ||
|
|
8550213146 | ||
|
|
1a09b75974 | ||
|
|
ea73e4ec7d | ||
|
|
7e45e0676a | ||
|
|
6f09987b07 | ||
|
|
a09b7a4002 | ||
|
|
8a51e271a2 | ||
|
|
53c827e134 | ||
|
|
bcc17127a9 | ||
|
|
18ff4bee2d | ||
|
|
71068cf13e | ||
|
|
2666b30ba9 | ||
|
|
7a9ed24127 | ||
|
|
156c0b5d4d | ||
|
|
e5a31e7f99 | ||
|
|
ccc0b3bcf9 | ||
|
|
44f6cc978d | ||
|
|
c07646d13d | ||
|
|
e20ca04fab | ||
|
|
f14e710ac0 | ||
|
|
5546f6aa17 | ||
|
|
9cfbd7b37d | ||
|
|
62bfa75cf7 | ||
|
|
fe2a7e207c | ||
|
|
1c2d4e5b4c | ||
|
|
2b33d7890c | ||
|
|
e8c9222283 | ||
|
|
a62a177fb1 | ||
|
|
e954df4d8e | ||
|
|
d0a0c74fd9 | ||
|
|
552e99d460 | ||
|
|
ce30c7e1b7 | ||
|
|
876d78eec4 | ||
|
|
0313e21ca1 | ||
|
|
e6f94c9d16 | ||
|
|
cdc4be3a13 | ||
|
|
4131dd89d4 | ||
|
|
4936e580fd | ||
|
|
b9250e4be4 | ||
|
|
a59dfa8fc0 | ||
|
|
2fd2ea72fe | ||
|
|
a6d3810e19 | ||
|
|
5e29c68154 | ||
|
|
6b26b5d62b | ||
|
|
81e617a149 | ||
|
|
013620c862 | ||
|
|
086aa51c2e | ||
|
|
95719cbd24 | ||
|
|
a23c8c263c | ||
|
|
dfd1155790 | ||
|
|
3a92cabc3f | ||
|
|
47446315ad | ||
|
|
c586a9bb39 | ||
|
|
4df3416b46 | ||
|
|
a42a3436cd | ||
|
|
367823ea5e | ||
|
|
ccb5acbe70 | ||
|
|
b8e0dfa07d | ||
|
|
a167a1b4f4 | ||
|
|
c95ccaa5c9 | ||
|
|
5b5b8fe427 | ||
|
|
31af30175b | ||
|
|
a22aacf29b | ||
|
|
6a06522777 | ||
|
|
c47b04493a | ||
|
|
6d701cda2e | ||
|
|
8bb27e2762 | ||
|
|
437b62eb97 | ||
|
|
88fbdfbb6b | ||
|
|
92eb3afa6c | ||
|
|
ababeebbf0 | ||
|
|
fdb3f83d1f | ||
|
|
a0e6fc4611 | ||
|
|
2b62b3aa90 | ||
|
|
910c0705f7 | ||
|
|
35469364b4 | ||
|
|
2af5e9c209 | ||
|
|
21a3749086 | ||
|
|
bd2b8625fb | ||
|
|
7ced602be4 | ||
|
|
b59e4d8fc7 | ||
|
|
9809510782 | ||
|
|
ea7136816d | ||
|
|
73872202ec | ||
|
|
a72f1ae8de | ||
|
|
fdf40f4584 | ||
|
|
7a6cfcb498 | ||
|
|
4994f23134 | ||
|
|
8c3ce4bb73 | ||
|
|
48b2e3dd88 | ||
|
|
bcb5cfb235 | ||
|
|
27213245ef | ||
|
|
c07a1412f7 | ||
|
|
dae58d11c2 | ||
|
|
fb66b84b58 | ||
|
|
125c84b6b5 | ||
|
|
5f58ede7e3 | ||
|
|
0f69282eec | ||
|
|
24b0613bac | ||
|
|
4a87d5f0f3 | ||
|
|
09b127b648 | ||
|
|
4ab290928c | ||
|
|
b9178cfcb6 | ||
|
|
cbc6463019 | ||
|
|
a30fee95bf | ||
|
|
b1b2d87045 | ||
|
|
02896e669a | ||
|
|
df5b76aa87 | ||
|
|
ea60d5a205 | ||
|
|
02f056851d | ||
|
|
d4cbcf1a31 | ||
|
|
78190d0d7e | ||
|
|
05ae1ad56d | ||
|
|
5a5a01cf17 | ||
|
|
d9329a9e15 | ||
|
|
3ea285498f | ||
|
|
3921209c9f | ||
|
|
05e0aa0304 | ||
|
|
5c571ca863 | ||
|
|
721e889bee | ||
|
|
bcf3573131 | ||
|
|
153ce880dd | ||
|
|
eac102dbbf | ||
|
|
d9efdb0b88 | ||
|
|
3a583a92f9 | ||
|
|
187e147352 | ||
|
|
2c9439fd7b | ||
|
|
2cefc55865 | ||
|
|
74d1e032f2 | ||
|
|
eb2ec18add | ||
|
|
3c6915e217 | ||
|
|
629fa91fa0 | ||
|
|
516044f3c7 | ||
|
|
a47c0dd512 | ||
|
|
1b06997001 | ||
|
|
458ec4c9a5 | ||
|
|
f76b466fc4 | ||
|
|
d7479c742f | ||
|
|
b12312f998 | ||
|
|
f8e5565b49 | ||
|
|
4a7718820c | ||
|
|
c2d47cfcdb | ||
|
|
64fc2e5d8f | ||
|
|
7c5763439c | ||
|
|
d39e8bc03a | ||
|
|
6cd59f8ce1 | ||
|
|
37a3fbf69c | ||
|
|
684fd8c2c5 | ||
|
|
d481328b20 | ||
|
|
e694c587e5 | ||
|
|
3125cefd41 | ||
|
|
c27a622289 | ||
|
|
51875dc155 | ||
|
|
f9bdfa54e7 | ||
|
|
8fbee23797 | ||
|
|
153e4cc3fa | ||
|
|
5a5d619b89 | ||
|
|
97c56baebb | ||
|
|
9c99c5c3ae | ||
|
|
28d515436e | ||
|
|
f70ec7f53c | ||
|
|
74825c099f | ||
|
|
966670fe70 | ||
|
|
c406607fd2 | ||
|
|
e812a3a087 | ||
|
|
9e6744d1e3 | ||
|
|
835e3ef305 | ||
|
|
ac658af462 | ||
|
|
6c3fcdcc4c | ||
|
|
22cbd5097b | ||
|
|
d06ac09485 | ||
|
|
94b67db78a | ||
|
|
5536525564 | ||
|
|
e6c7463c03 | ||
|
|
11bf4c7c07 | ||
|
|
bc9c7e5aa4 | ||
|
|
9a0f54f4fe | ||
|
|
3b6f3c58e1 | ||
|
|
ff01c2a69f | ||
|
|
1322c1fbbd | ||
|
|
36e2c76456 | ||
|
|
c42a8df65c | ||
|
|
01b38bb2e0 | ||
|
|
9f9a69286c | ||
|
|
e3a0dea85f | ||
|
|
5b5094ced6 | ||
|
|
f5c2557338 | ||
|
|
802f1599a1 | ||
|
|
9781afa17e | ||
|
|
3319602f3f | ||
|
|
8bd2e4f0dd | ||
|
|
8eaa490664 | ||
|
|
db54eb87cd | ||
|
|
d1950efdd4 | ||
|
|
cf51ddb32e | ||
|
|
269551ead5 | ||
|
|
ee06e4f6de | ||
|
|
87e9949f33 | ||
|
|
0b02e88ee3 | ||
|
|
dd58698cef | ||
|
|
f9807bfad8 | ||
|
|
4f6bd86478 | ||
|
|
be6ff3782b | ||
|
|
7ae2360a55 | ||
|
|
1852e66abd | ||
|
|
367b2370a5 | ||
|
|
640df0f4a5 | ||
|
|
37984a6f9a | ||
|
|
c062621dd5 | ||
|
|
437a2ea0e2 | ||
|
|
fc2fb9a081 | ||
|
|
41d9ae92e6 | ||
|
|
4c659da533 | ||
|
|
21a9226c4a | ||
|
|
ee2c167be4 | ||
|
|
09b5359c45 | ||
|
|
e55e8ef000 | ||
|
|
bffd56f5fc | ||
|
|
ac0d7bda1c | ||
|
|
2912e541a0 | ||
|
|
442e6bf983 | ||
|
|
70ba1eca78 | ||
|
|
e925877ae4 | ||
|
|
0e7cc1ef87 | ||
|
|
909b0ef1b7 | ||
|
|
a573083291 | ||
|
|
6a42f738c9 | ||
|
|
9bee6ae666 | ||
|
|
533af4b39e | ||
|
|
99a200d0b2 | ||
|
|
889ec1339a | ||
|
|
0bdc3d04b4 | ||
|
|
bd9fbedbf9 | ||
|
|
6c0a958481 | ||
|
|
45a39ec213 | ||
|
|
800304270e | ||
|
|
9defed2680 | ||
|
|
7be9d7ff49 | ||
|
|
287abe7640 | ||
|
|
262df6c4a0 | ||
|
|
10e3d963ef | ||
|
|
d23920c886 | ||
|
|
be2647556d | ||
|
|
ea57864bcc | ||
|
|
0c121a5894 | ||
|
|
c19723ae27 | ||
|
|
00b43df267 | ||
|
|
edb6355960 | ||
|
|
8626b466b8 | ||
|
|
a80ab6e47a | ||
|
|
2b2978b8e7 | ||
|
|
3bdea42f6d | ||
|
|
b56ea161cd | ||
|
|
cde74a9c0b | ||
|
|
79b00f88b0 | ||
|
|
0da84f8f3e | ||
|
|
4f164a46a6 | ||
|
|
3d9b7eb7ab | ||
|
|
d3eb0ced83 | ||
|
|
08ac1f6b69 | ||
|
|
cc63be8ff0 | ||
|
|
b1d8b0c7d4 | ||
|
|
3f6fde033b | ||
|
|
0a9e10b21e | ||
|
|
25e17fedb9 | ||
|
|
8ec54ffa5b | ||
|
|
b516275c24 | ||
|
|
aea5d62556 | ||
|
|
de2fc69315 | ||
|
|
8629537c3f | ||
|
|
57401e48c1 | ||
|
|
76f4b8316b | ||
|
|
ef0e4e36f4 | ||
|
|
55296b676b | ||
|
|
5b9a2a8bac | ||
|
|
8031b184f3 | ||
|
|
1efd7bb33a | ||
|
|
53d663f77b | ||
|
|
9969523b70 | ||
|
|
2cd0a414d5 | ||
|
|
256a673a7f | ||
|
|
6d8a062e50 | ||
|
|
35a7159be4 | ||
|
|
764d9fa806 | ||
|
|
292ff3c961 | ||
|
|
071929d3a6 | ||
|
|
c7cc9273a6 | ||
|
|
55a11a24f4 | ||
|
|
d1f443bcd8 | ||
|
|
52fafe9976 | ||
|
|
9aa7b792be | ||
|
|
07e9fe3403 | ||
|
|
858967f420 | ||
|
|
e07df35b61 | ||
|
|
2a3aa9331f | ||
|
|
4863689248 | ||
|
|
8a53aa9fa4 | ||
|
|
57a990d91e | ||
|
|
76c0393c53 | ||
|
|
62d472fa54 | ||
|
|
6d9d69e01d | ||
|
|
38666cbda2 | ||
|
|
b3b81546f6 | ||
|
|
9c2151f802 | ||
|
|
1f2c4b5a9e | ||
|
|
c9ef28b2ad | ||
|
|
03a5f46131 | ||
|
|
f1e4297e00 | ||
|
|
9bb1425a81 | ||
|
|
b6277233a1 | ||
|
|
7bed8a7abb | ||
|
|
787df04419 | ||
|
|
8e142dfdcf | ||
|
|
2a216d41f0 | ||
|
|
241eb9df0f | ||
|
|
0bdd7c8973 | ||
|
|
a59932af00 | ||
|
|
c2a073dec0 | ||
|
|
7808fdd22f | ||
|
|
bf2f3ccec1 | ||
|
|
9c7c481f48 | ||
|
|
df26bc9013 | ||
|
|
b36a7621e0 | ||
|
|
f2c242f49e | ||
|
|
5175f13881 | ||
|
|
75a1854cc3 | ||
|
|
48c3263e28 | ||
|
|
f5e26fb2e6 | ||
|
|
1fadf204c2 | ||
|
|
f03b14a46b | ||
|
|
ab8e56e16a | ||
|
|
2f33a1ecff | ||
|
|
abadeb5e14 | ||
|
|
19c68895e9 | ||
|
|
bed6d6c591 | ||
|
|
b2652edf56 | ||
|
|
94bb3bee77 | ||
|
|
e909ece2da | ||
|
|
7bc250ce84 | ||
|
|
7706839fbe | ||
|
|
68ad58431d | ||
|
|
2059752fc6 | ||
|
|
302afae0bb | ||
|
|
3d1d94d628 | ||
|
|
d51fc2f12c | ||
|
|
2dee14b0d5 | ||
|
|
e75849da98 | ||
|
|
508c7b7a90 | ||
|
|
de67e051d7 | ||
|
|
9ce65c601a | ||
|
|
10d7eeb762 | ||
|
|
104a6dd0b3 | ||
|
|
2977120129 | ||
|
|
d8e8512d66 | ||
|
|
7bfbcf7e03 | ||
|
|
91cad7b49e | ||
|
|
26f1defc56 | ||
|
|
9a03654062 | ||
|
|
2576ef75bd | ||
|
|
752b45af42 | ||
|
|
23c70ac252 | ||
|
|
ad4de9e85d | ||
|
|
855d60f28e | ||
|
|
781a94c05f | ||
|
|
b3cc01b041 | ||
|
|
e23ced47ae | ||
|
|
f406b51a5f | ||
|
|
c81cb786ed | ||
|
|
846f1244c7 | ||
|
|
3ea723329a | ||
|
|
698c9ed1ea | ||
|
|
52e45211a2 | ||
|
|
63044ac13a | ||
|
|
68d9de8946 | ||
|
|
92a7717e50 | ||
|
|
d5c0a5d377 | ||
|
|
c0b7fb0b84 | ||
|
|
9b4e556837 | ||
|
|
1aec0b5f0d | ||
|
|
c5d4e06ea3 | ||
|
|
97bf907448 | ||
|
|
7ef2a2a9f4 | ||
|
|
547d82fe57 | ||
|
|
cca51628ef | ||
|
|
506cb696d3 | ||
|
|
a4e02bb8cd | ||
|
|
6d588130dd | ||
|
|
0ce2f9ea43 | ||
|
|
80c4908fcc | ||
|
|
30a29e23d3 | ||
|
|
661e80084c |
No files matched your search
@@ -6,7 +6,7 @@ ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.22.4 \
|
||||
NODE_VERSION=v12.22.10 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
@@ -22,6 +22,7 @@ ENV \
|
||||
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3 \
|
||||
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="" \
|
||||
@@ -131,7 +132,16 @@ ENV \
|
||||
SAML_IDENTIFIER_FORMAT="" \
|
||||
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
|
||||
SAML_ATTRIBUTES="" \
|
||||
DEFAULT_WAIT_SPINNER=""
|
||||
DEFAULT_WAIT_SPINNER="" \
|
||||
NODE_OPTIONS="--max_old_space_size=4096"
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap:
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# Install OS
|
||||
RUN set -o xtrace \
|
||||
@@ -195,6 +205,10 @@ COPY \
|
||||
settings.json \
|
||||
/home/wekan/app/
|
||||
|
||||
COPY \
|
||||
tests \
|
||||
/home/wekan/app/tests/
|
||||
|
||||
COPY \
|
||||
packages \
|
||||
/home/wekan/app/packages/
|
||||
@@ -224,10 +238,35 @@ RUN \
|
||||
set -o xtrace && \
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
chmod u+w package.json npm-shrinkwrap.json && \
|
||||
npm install
|
||||
npm install && \
|
||||
cd node_modules/fibers && \
|
||||
node build.js
|
||||
|
||||
USER root
|
||||
# Cleanup
|
||||
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/*
|
||||
|
||||
USER wekan
|
||||
|
||||
ENV PORT=3000
|
||||
EXPOSE $PORT
|
||||
WORKDIR /home/wekan/app
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap:
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------------
|
||||
#TODO:
|
||||
#CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 /build/main.js"]
|
||||
|
||||
CMD ["/home/wekan/.meteor/meteor", "run", "--verbose", "--settings", "settings.json"]
|
||||
@@ -9,4 +9,6 @@ cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/pa
|
||||
cd .build/bundle/programs/server
|
||||
rm -rf node_modules
|
||||
/home/wekan/.meteor/meteor npm install
|
||||
cd node_modules/fibers
|
||||
node build.js
|
||||
cd /home/wekan/app
|
||||
@@ -12,9 +12,9 @@ services:
|
||||
expose:
|
||||
- 27017
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./volumes/wekan-db:/data/db
|
||||
- ./volumes/wekan-db-dump:/dump
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
|
||||
wekan-dev:
|
||||
container_name: wekan-dev-app
|
||||
@@ -36,19 +36,13 @@ services:
|
||||
depends_on:
|
||||
- wekandb-dev
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ../client:/home/wekan/app/client
|
||||
- ../models:/home/wekan/app/models
|
||||
- ../config:/home/wekan/app/config
|
||||
- ../i18n:/home/wekan/app/i18n
|
||||
- ../server:/home/wekan/app/server
|
||||
- ../public:/home/wekan/app/public
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
|
||||
volumes:
|
||||
wekan-dev-db:
|
||||
driver: local
|
||||
wekan-dev-db-dump:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
wekan-dev-tier:
|
||||
|
||||
@@ -122,6 +122,7 @@
|
||||
"Activities": true,
|
||||
"Attachments": true,
|
||||
"Boards": true,
|
||||
"CardCommentReactions": true,
|
||||
"CardComments": true,
|
||||
"DatePicker": true,
|
||||
"Cards": true,
|
||||
@@ -156,6 +157,7 @@
|
||||
"Integrations": true,
|
||||
"HTTP": true,
|
||||
"AccountSettings": true,
|
||||
"TableVisibilityModeSettings": true,
|
||||
"Announcements": true,
|
||||
"Swimlanes": true,
|
||||
"ChecklistItems": true,
|
||||
|
||||
@@ -81,7 +81,7 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.22.4
|
||||
node-engine: 12.22.10
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
|
||||
@@ -83,7 +83,7 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.22.4
|
||||
node-engine: 12.22.10
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
|
||||
+24
-27
@@ -1,38 +1,35 @@
|
||||
## Issue
|
||||
<!--
|
||||
**[PLEASE UPGRADE](https://github.com/wekan/wekan/wiki/Backup)** to the newest WeKan ® before reporting an issue.
|
||||
Please search existing Open and Closed issues, most questions have already been answered.
|
||||
|
||||
Note: With Docker, please don't use latest tag. Only use release tags.
|
||||
See https://github.com/wekan/wekan/issues/3874
|
||||
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
|
||||
|
||||
If you can not login for any reason:
|
||||
- https://github.com/wekan/wekan/wiki/Forgot-Password
|
||||
|
||||
Email settings:
|
||||
- https://github.com/wekan/wekan/wiki/Troubleshooting-Mail
|
||||
|
||||
Add these issues to elsewhere:
|
||||
The following types of issues should be reported separately:
|
||||
- SECURITY ISSUES: https://github.com/wekan/wekan/blob/master/SECURITY.md
|
||||
- UCS: https://github.com/wekan/univention/issues
|
||||
-->
|
||||
|
||||
Other Wekan issues can be added here.
|
||||
|
||||
**Server Setup Information**:
|
||||
|
||||
* Note: Please anonymize info, and do not add to this public issue any of your Wekan board URLs, passwords, API tokens etc, do you understand?:
|
||||
### Server Setup Information
|
||||
<!-- 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?:
|
||||
* For new Wekan install, did you configure root-url correctly so Wekan cards open correctly https://github.com/wekan/wekan/wiki/Settings ?
|
||||
* Wekan version:
|
||||
* If this is about old version of Wekan, what upgrade problem you have?:
|
||||
* Did you configure root-url correctly so Wekan cards open correctly (see https://github.com/wekan/wekan/wiki/Settings)?
|
||||
* Operating System:
|
||||
* Deployment Method(snap/docker/sandstorm/mongodb bundle/source):
|
||||
* Deployment Method (Snap/Docker/Sandstorm/bundle/source):
|
||||
* Http frontend if any (Caddy, Nginx, Apache, see config examples from Wekan GitHub wiki first):
|
||||
* Node Version:
|
||||
* Node.js Version:
|
||||
* MongoDB Version:
|
||||
* Wekan only works on newest desktop Firefox/Chromium/Chrome/Edge/Chromium Edge and mobile Chrome. What webbrowser version are you using?
|
||||
* What webbrowser version are you using (Wekan should work on all modern browsers that support Javascript)?
|
||||
|
||||
**Problem description**:
|
||||
- *REQUIRED: Add recorded animated gif about how it works currently, and screenshot mockups how it should work. Use peek to record animgif in Linux https://github.com/phw/peek*
|
||||
- *Explain steps how to reproduce*
|
||||
- *In webbrowser, what does show Right Click / Inspect / Console ? Chrome shows more detailed info than Firefox.*
|
||||
- *If using Snap, what does show command `sudo snap logs wekan.wekan` ? Please anonymize logs.*
|
||||
- *If using Docker, what does show command `sudo docker logs wekan-app` ? Please anonymize logs.*
|
||||
- *If logs are very long, attach them in .zip file*
|
||||
### 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. -->
|
||||
#### Reproduction Steps
|
||||
|
||||
#### Logs
|
||||
<!-- Check Right Click>Inspect>Console in you browser - generally Chrome shows more detailed info than Firefox. -->
|
||||
|
||||
<!-- 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 -->
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Docker
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '28 23 * * *'
|
||||
push:
|
||||
branches: [ master ]
|
||||
# Publish semver tags as releases.
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY: ghcr.io
|
||||
# github.repository as <account>/<repo>
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Release Charts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "$GITHUB_ACTOR"
|
||||
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||
|
||||
- name: Run chart-releaser
|
||||
uses: helm/chart-releaser-action@v1.1.0
|
||||
env:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
.meteor/local/isopacks
|
||||
.meteor/local/bundler-cache/scanner
|
||||
key: v1-meteor_build_cache-${{ github.ref }}-${{ github.sha }}
|
||||
restore-key: |
|
||||
restore-keys: |
|
||||
v1-meteor_build_cache-
|
||||
|
||||
- name: Setup meteor
|
||||
|
||||
@@ -38,3 +38,4 @@ ehthumbs.db
|
||||
# Helm chart
|
||||
# Chart dependencies
|
||||
/helm/wekan/**/*.tgz
|
||||
/helm/wekan/charts
|
||||
+1
-2
@@ -60,11 +60,10 @@ reactive-var@1.0.11
|
||||
fortawesome:fontawesome
|
||||
mousetrap:mousetrap
|
||||
mquandalle:jquery-textcomplete
|
||||
mquandalle:jquery-ui-drag-drop-sort
|
||||
mquandalle:mousetrap-bindglobal
|
||||
peerlibrary:blaze-components@=0.15.1
|
||||
templates:tabs
|
||||
verron:autosize
|
||||
meteor-autosize
|
||||
simple:json-routes
|
||||
rajit:bootstrap3-datepicker
|
||||
shell-server@0.5.0
|
||||
|
||||
+1
-2
@@ -76,6 +76,7 @@ matb33:collection-hooks@0.9.1
|
||||
matteodem:easy-search@1.6.4
|
||||
mdg:validation-error@0.5.1
|
||||
meteor@1.9.3
|
||||
meteor-autosize@5.0.1
|
||||
meteor-base@1.4.0
|
||||
meteor-platform@1.2.6
|
||||
meteorhacks:aggregate@1.3.0
|
||||
@@ -106,7 +107,6 @@ mquandalle:collection-mutations@0.1.0
|
||||
mquandalle:jade@0.4.9
|
||||
mquandalle:jade-compiler@0.4.5
|
||||
mquandalle:jquery-textcomplete@0.8.0_1
|
||||
mquandalle:jquery-ui-drag-drop-sort@0.2.0
|
||||
mquandalle:moment@1.0.1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
msavin:usercache@1.8.0
|
||||
@@ -219,7 +219,6 @@ url@1.3.2
|
||||
useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.10.1
|
||||
webapp-hashing@1.1.0
|
||||
wekan-accounts-cas@0.1.0
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ sudo: required
|
||||
|
||||
env:
|
||||
TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
|
||||
TRAVIS_NODE_VERSION: 12.22.4
|
||||
TRAVIS_NODE_VERSION: 12.22.10
|
||||
TRAVIS_NPM_VERSION: latest
|
||||
|
||||
before_install:
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ host = https://www.transifex.com
|
||||
# tap:i18n requires us to use `-` separator in the language identifiers whereas
|
||||
# Transifex uses a `_` separator, without an option to customize it on one side
|
||||
# or the other, so we need to do a Manual mapping.
|
||||
lang_map = ar_EG:ar-EG, bg_BG:bg, de_CH:de-CH, en_IT:en-IT, en_GB:en-GB, es_AR:es-AR, es_CL:es-CL, es_419:es-LA, es_PE:es-PE, es_MX:es-MX, es_TX:es-TX, es_PY:es-PY, el_GR:el, fa_IR:fa-IR, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, sl_SI:sl, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
|
||||
lang_map = ar_EG:ar-EG, bg_BG:bg, de_AT:de-AT, de_CH:de-CH, en_DE:en-DE, en_IT:en-IT, en_GB:en-GB, et_EE:et-EE, es_AR:es-AR, es_CL:es-CL, es_419:es-LA, es_PE:es-PE, es_MX:es-MX, es_TX:es-TX, es_PY:es-PY, el_GR:el-GR, fa_IR:fa-IR, fi_FI:fi, fr_FR:fr-FR, fr_CH:fr-CH, gl_ES:gl-ES, gu_IN:gu-IN, hi_IN:hi-IN, hu_HU:hu, id_ID:id, mn_MN:mn, ms_MY:ms-MY, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, ru_UA:ru-UA, sl_SI:sl, uk_UA:uk-UA, zh_CN:zh-CN, zh_TW:zh-TW, zh_Hans:zh-Hans, zh_HK:zh-HK
|
||||
|
||||
[wekan.application]
|
||||
file_filter = i18n/<lang>.i18n.json
|
||||
|
||||
+1150
-5
File diff suppressed because it is too large.
Load diff
+30
-6
@@ -1,8 +1,8 @@
|
||||
FROM quay.io/wekan/ubuntu:groovy-20210115
|
||||
FROM ubuntu:rolling
|
||||
LABEL maintainer="wekan"
|
||||
|
||||
# 2020-12-03:
|
||||
# - Above Ubuntu base image copied from Docker Hub ubuntu:groovy-20201125.2
|
||||
# 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)
|
||||
@@ -12,7 +12,7 @@ ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.22.4 \
|
||||
NODE_VERSION=v12.22.10 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
@@ -28,6 +28,7 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3 \
|
||||
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="" \
|
||||
@@ -139,7 +140,16 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
|
||||
SAML_ATTRIBUTES="" \
|
||||
ORACLE_OIM_ENABLED=false \
|
||||
WAIT_SPINNER=""
|
||||
WAIT_SPINNER="" \
|
||||
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"
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# Copy the app to the image
|
||||
COPY ${SRC_PATH} /home/wekan/app
|
||||
@@ -297,6 +307,9 @@ RUN \
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
chmod u+w *.json && \
|
||||
gosu wekan:wekan npm install && \
|
||||
cd node_modules/fibers && \
|
||||
node build.js && \
|
||||
cd ../.. && \
|
||||
#gosu wekan:wekan npm install bcrypt && \
|
||||
# Remove legacy webbroser bundle, so that Wekan works also at Android Firefox, iOS Safari, etc.
|
||||
rm -rf /home/wekan/app_build/bundle/programs/web.browser.legacy && \
|
||||
@@ -309,6 +322,7 @@ RUN \
|
||||
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 && \
|
||||
@@ -321,4 +335,14 @@ ENV PORT=8080
|
||||
EXPOSE $PORT
|
||||
USER wekan
|
||||
|
||||
CMD ["node", "/build/main.js"]
|
||||
#---------------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap:
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------------
|
||||
#
|
||||
# CMD ["node", "/build/main.js"]
|
||||
|
||||
CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 /build/main.js"]
|
||||
+24
-4
@@ -4,9 +4,18 @@ FROM amd64/alpine:3.7 AS builder
|
||||
ENV QEMU_VERSION=v4.2.0-6 \
|
||||
QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.22.4 \
|
||||
NODE_VERSION=v12.22.10 \
|
||||
WEKAN_VERSION=latest \
|
||||
WEKAN_ARCHITECTURE=arm64
|
||||
WEKAN_ARCHITECTURE=arm64 \
|
||||
NODE_OPTIONS="--max_old_space_size=4096"
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# https://github.com/wekan/wekan/issues/3585#issuecomment-1021522132
|
||||
# Add more Node heap:
|
||||
# NODE_OPTIONS="--max_old_space_size=4096"
|
||||
# Add more stack:
|
||||
# bash -c "ulimit -s 65500; exec node --stack-size=65500 main.js"
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# Install dependencies
|
||||
RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
@@ -40,7 +49,7 @@ LABEL maintainer="wekan"
|
||||
# Set the environment variables (defaults where required)
|
||||
ENV QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.22.4 \
|
||||
NODE_VERSION=v12.22.10 \
|
||||
NODE_ENV=production \
|
||||
NPM_VERSION=latest \
|
||||
WITH_API=true \
|
||||
@@ -74,4 +83,15 @@ RUN \
|
||||
EXPOSE $PORT
|
||||
USER wekan
|
||||
|
||||
CMD ["node", "/home/wekan/bundle/main.js"]
|
||||
#---------------------------------------------------------------------
|
||||
# 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", "/home/wekan/bundle/main.js"]
|
||||
|
||||
CMD ["bash", "-c", "ulimit -s 65500; exec node --stack-size=65500 /home/wekan/bundle/main.js"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[](https://gitpod.io/#https://github.com/wekan/wekan)
|
||||
|
||||
# Wekan - Open Source kanban
|
||||
# WeKan ® - Open Source kanban
|
||||
|
||||
[](https://github.com/wekan/wekan/graphs/contributors)
|
||||
[](https://quay.io/repository/wekan/wekan)
|
||||
@@ -14,19 +14,19 @@
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwekan%2Fwekan?ref=badge_shield)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/4619)
|
||||
|
||||
## [Translate Wekan at Transifex](https://transifex.com/wekan/wekan)
|
||||
## [Translate WeKan ® at Transifex](https://transifex.com/wekan/wekan)
|
||||
|
||||
Translations to non-English languages are accepted only at [Transifex](https://transifex.com/wekan/wekan) using webbrowser.
|
||||
New English strings of new features can be added as PRs to edge branch file wekan/i18n/en.i18n.json .
|
||||
|
||||
## [Wekan feature requests and bugs](https://github.com/wekan/wekan/issues)
|
||||
|
||||
Please add most of your questions as GitHub issue: [Wekan Feature Requests and Bugs](https://github.com/wekan/wekan/issues).
|
||||
Please add most of your questions as GitHub issue: [WeKan ® Feature Requests and Bugs](https://github.com/wekan/wekan/issues).
|
||||
It's better than at chat where details get lost when chat scrolls up.
|
||||
|
||||
## Chat
|
||||
|
||||
[Discussions][discussions] - Wekan Community GitHub Discussions, that are not [Feature Requests and Bugs](https://github.com/wekan/wekan/issues).
|
||||
[Discussions][discussions] - WeKan Community GitHub Discussions, that are not [Feature Requests and Bugs](https://github.com/wekan/wekan/issues).
|
||||
|
||||
[Wekan IRC FAQ](https://github.com/wekan/wekan/wiki/IRC-FAQ)
|
||||
|
||||
@@ -41,9 +41,9 @@ See https://github.com/wekan/wekan/issues/3874
|
||||
- Please read the [FAQ](https://github.com/wekan/wekan/wiki/FAQ) first
|
||||
- Please don't feed the [trolls](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-troll) and [spammers](https://github.com/wekan/wekan/wiki/FAQ#why-am-i-called-a-spammer) that are mentioned in the FAQ :)
|
||||
|
||||
## About Wekan
|
||||
## About WeKan ®
|
||||
|
||||
Wekan is an completely [Open Source][open_source] and [Free software][free_software]
|
||||
WeKan ® is an completely [Open Source][open_source] and [Free software][free_software]
|
||||
collaborative kanban board application with MIT license.
|
||||
|
||||
Whether you’re maintaining a personal todo list, planning your holidays with some friends,
|
||||
@@ -51,58 +51,58 @@ or working in a team on your next revolutionary idea, Kanban boards are an unbea
|
||||
to keep your things organized. They give you a visual overview of the current state of your project,
|
||||
and make you productive by allowing you to focus on the few items that matter the most.
|
||||
|
||||
Since Wekan is a free software, you don’t have to trust us with your data and can
|
||||
Since WeKan ® is a free software, you don’t have to trust us with your data and can
|
||||
install Wekan on your own computer or server. In fact we encourage you to do
|
||||
that by providing one-click installation on various platforms.
|
||||
|
||||
- Wekan is used in [most countries of the world](https://snapcraft.io/wekan).
|
||||
- WeKan ® is used in [most countries of the world](https://snapcraft.io/wekan).
|
||||
- Wekan largest user has 13k users using Wekan in their company.
|
||||
- Wekan has been [translated](https://transifex.com/wekan/wekan) to about 63 languages.
|
||||
- [Features][features]: Wekan has real-time user interface.
|
||||
- [Platforms][platforms]: Wekan supports many platforms.
|
||||
Wekan is critical part of new platforms Wekan is currently being integrated to.
|
||||
- Wekan has been [translated](https://transifex.com/wekan/wekan) to about 70 languages.
|
||||
- [Features][features]: WeKan ® has real-time user interface.
|
||||
- [Platforms][platforms]: WeKan ® supports many platforms.
|
||||
WeKan ® is critical part of new platforms Wekan is currently being integrated to.
|
||||
|
||||
## Requirements
|
||||
|
||||
- 64bit: Linux [Snap](https://github.com/wekan/wekan-snap/wiki/Install) or [Sandstorm](https://sandstorm.io) /
|
||||
[Mac](https://github.com/wekan/wekan/wiki/Mac) / [Windows](https://github.com/wekan/wekan/wiki/Install-Wekan-from-source-on-Windows).
|
||||
[More Platforms](https://github.com/wekan/wekan/wiki/Platforms), bundle for RasPi3 ARM and other CPUs where Node.js and MongoDB exists.
|
||||
- 1 GB RAM minimum free for Wekan. Production server should have minimum total 4 GB RAM.
|
||||
- 1 GB RAM minimum free for WeKan ®. Production server should have minimum total 4 GB RAM.
|
||||
For thousands of users, for example with [Docker](https://github.com/wekan/wekan/blob/master/docker-compose.yml): 3 frontend servers,
|
||||
each having 2 CPU and 2 wekan-app containers. One backend wekan-db server with many CPUs.
|
||||
- Enough disk space and alerts about low disk space. If you run out disk space, MongoDB database gets corrupted.
|
||||
- SECURITY: Updating to newest Wekan version very often. Please check you do not have automatic updates of Sandstorm or Snap turned off.
|
||||
Old versions have security issues because of old versions Node.js etc. Only newest Wekan is supported.
|
||||
Wekan on Sandstorm is not usually affected by any Standalone Wekan (Snap/Docker/Source) security issues.
|
||||
- SECURITY: Updating to newest WeKan ® version very often. Please check you do not have automatic updates of Sandstorm or Snap turned off.
|
||||
Old versions have security issues because of old versions Node.js etc. Only newest WeKan ® is supported.
|
||||
WeKan ® on Sandstorm is not usually affected by any Standalone WeKan ® (Snap/Docker/Source) security issues.
|
||||
- [Reporting all new bugs immediately](https://github.com/wekan/wekan/issues).
|
||||
New features and fixes are added to Wekan [many times a day](https://github.com/wekan/wekan/blob/devel/CHANGELOG.md).
|
||||
- [Backups](https://github.com/wekan/wekan/wiki/Backup) of Wekan database once a day miminum.
|
||||
New features and fixes are added to WeKan ® [many times a day](https://github.com/wekan/wekan/blob/master/CHANGELOG.md).
|
||||
- [Backups](https://github.com/wekan/wekan/wiki/Backup) of WeKan ® database once a day miminum.
|
||||
Bugs, updates, users deleting list or card, harddrive full, harddrive crash etc can eat your data. There is no undo yet.
|
||||
Some bug can cause Wekan board to not load at all, requiring manual fixing of database content.
|
||||
Some bug can cause WeKan ® board to not load at all, requiring manual fixing of database content.
|
||||
|
||||
## Roadmap and Demo
|
||||
|
||||
[Roadmap][roadmap_wekan] - Public read-only board at Wekan demo.
|
||||
[Roadmap][roadmap_wekan] - Public read-only board at WeKan ® demo.
|
||||
|
||||
[Developer Documentation][dev_docs]
|
||||
|
||||
- There is many companies and individuals contributing code to Wekan, to add features and bugfixes
|
||||
[many times a day](https://github.com/wekan/wekan/blob/devel/CHANGELOG.md).
|
||||
- There is many companies and individuals contributing code to WeKan ®, to add features and bugfixes
|
||||
[many times a day](https://github.com/wekan/wekan/blob/master/CHANGELOG.md).
|
||||
- [Please add Add new Feature Requests and Bug Reports immediately](https://github.com/wekan/wekan/issues).
|
||||
- [Commercial Support](https://wekan.team/commercial-support/).
|
||||
|
||||
We also welcome sponsors for features and bugfixes.
|
||||
By working directly with Wekan you get the benefit of active maintenance and new features added by growing Wekan developer community.
|
||||
By working directly with WeKan ® you get the benefit of active maintenance and new features added by growing WeKan ® developer community.
|
||||
|
||||
## Screenshot
|
||||
|
||||
[More screenshots at Features page](https://github.com/wekan/wekan/wiki/Features)
|
||||
|
||||
[![Screenshot of Wekan][screenshot_wekan]][roadmap_wekan]
|
||||
[![Screenshot of WeKan ®][screenshot_wekan]][roadmap_wekan]
|
||||
|
||||
## License
|
||||
|
||||
Wekan is released under the very permissive [MIT license](LICENSE), and made
|
||||
WeKan ® is released under the very permissive [MIT license](LICENSE), and made
|
||||
with [Meteor](https://www.meteor.com).
|
||||
|
||||
[platforms]: https://github.com/wekan/wekan/wiki/Platforms
|
||||
|
||||
+4
-4
@@ -51,8 +51,8 @@ This also means all Standalone Wekan functionality works in offline local networ
|
||||
Wekan is used by companies that have [thousands of users](https://github.com/wekan/wekan/wiki/AWS) and at healthcare.
|
||||
|
||||
Wekan uses xss package for input fields like cards, as you can see from
|
||||
[package.json](https://github.com/wekan/wekan/blob/devel/package.json). Other used versions can be seen from
|
||||
[Meteor versions file](https://github.com/wekan/wekan/blob/devel/.meteor/versions).
|
||||
[package.json](https://github.com/wekan/wekan/blob/master/package.json). Other used versions can be seen from
|
||||
[Meteor versions file](https://github.com/wekan/wekan/blob/master/.meteor/versions).
|
||||
Forms can include markdown links, html, image tags etc like you see at https://wekan.github.io .
|
||||
It's possible to add attachments to cards, and markdown/html links to files.
|
||||
|
||||
@@ -69,7 +69,7 @@ access to outside of Wekan grain.
|
||||
Standalone Wekan only has password auth currently, there is work in progress to add
|
||||
[oauth2](https://github.com/wekan/wekan/pull/1578), [Openid](https://github.com/wekan/wekan/issues/538),
|
||||
[LDAP](https://github.com/wekan/wekan/issues/119) etc. If you need more login security for Standalone Wekan now,
|
||||
it's possible add additional [Google Auth proxybouncer](https://github.com/wekan/wekan/wiki/Let's-Encrypt-and-Google-Auth) in front of password auth, and then use Google Authenticator for Google Auth. Standalone Wekan does have [brute force protection with eluck:accounts-lockout and browser-policy clickjacking protection](https://github.com/wekan/wekan/blob/devel/CHANGELOG.md#v080-2018-04-04-wekan-release). You can also optionally use some [WAF](https://en.wikipedia.org/wiki/Web_application_firewall)
|
||||
it's possible add additional [Google Auth proxybouncer](https://github.com/wekan/wekan/wiki/Let's-Encrypt-and-Google-Auth) in front of password auth, and then use Google Authenticator for Google Auth. Standalone Wekan does have [brute force protection with eluck:accounts-lockout and browser-policy clickjacking protection](https://github.com/wekan/wekan/blob/master/CHANGELOG.md#v080-2018-04-04-wekan-release). You can also optionally use some [WAF](https://en.wikipedia.org/wiki/Web_application_firewall)
|
||||
like for example [AWS WAF](https://aws.amazon.com/waf/).
|
||||
|
||||
[All Wekan Platforms](https://github.com/wekan/wekan/wiki/Platforms)
|
||||
@@ -106,7 +106,7 @@ a security issue, we'd like to know about it, and also how to fix it:
|
||||
Typical already known or "no impact" bugs such as:
|
||||
|
||||
- Brute force password guessign. Currently there is
|
||||
[brute force protection with eluck:accounts-lockout](https://github.com/wekan/wekan/blob/devel/CHANGELOG.md#v080-2018-04-04-wekan-release).
|
||||
[brute force protection with eluck:accounts-lockout](https://github.com/wekan/wekan/blob/master/CHANGELOG.md#v080-2018-04-04-wekan-release).
|
||||
- Security issues related to that Wekan uses Meteor 1.6.0.1 related packages, and upgrading to newer
|
||||
Meteor 1.6.1 is complicated process that requires lots of changes to many dependency packages.
|
||||
Upgrading [has been tried many times, spending a lot of time](https://github.com/meteor/meteor/issues/9609)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v5.44.0"
|
||||
appVersion: "v6.09.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
# Wekan API Python CLI, originally from here, where is more details:
|
||||
# https://github.com/wekan/wekan/wiki/New-card-with-Python3-and-REST-API
|
||||
|
||||
# TODO:
|
||||
# addcustomfieldtoboard: There is error: Settings must be object. So adding does not work yet.
|
||||
|
||||
try:
|
||||
# python 3
|
||||
from urllib.parse import urlencode
|
||||
@@ -23,12 +26,16 @@ if arguments == 0:
|
||||
print("AUTHORID is USERID that writes card.")
|
||||
print("If *nix: chmod +x api.py => ./api.py users")
|
||||
print("Syntax:")
|
||||
print(" python3 api.py users # All users")
|
||||
print(" python3 api.py boards USERID # Boards of USERID")
|
||||
print(" python3 api.py board BOARDID # Info of BOARDID")
|
||||
print(" python3 api.py swimlanes BOARDID # Swimlanes of BOARDID")
|
||||
print(" python3 api.py lists BOARDID # Lists of BOARDID")
|
||||
print(" python3 api.py list BOARDID LISTID # Info of LISTID")
|
||||
print(" python3 api.py users # All users")
|
||||
print(" python3 api.py boards # All Public Boards")
|
||||
print(" python3 api.py boards USERID # Boards of USERID")
|
||||
print(" python3 api.py board BOARDID # Info of BOARDID")
|
||||
print(" python3 api.py customfields BOARDID # Custom Fields of BOARDID")
|
||||
print(" python3 api.py customfield BOARDID CUSTOMFIELDID # Info of CUSTOMFIELDID")
|
||||
print(" python3 api.py addcustomfieldtoboard AUTHORID BOARDID NAME TYPE SETTINGS SHOWONCARD AUTOMATICALLYONCARD SHOWLABELONMINICARD SHOWSUMATTOPOFLIST # Add Custom Field to Board")
|
||||
print(" python3 api.py swimlanes BOARDID # Swimlanes of BOARDID")
|
||||
print(" python3 api.py lists BOARDID # Lists of BOARDID")
|
||||
print(" python3 api.py list BOARDID LISTID # Info of LISTID")
|
||||
print(" python3 api.py createlist BOARDID LISTTITLE # Create list")
|
||||
print(" python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION")
|
||||
print(" python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION")
|
||||
@@ -65,12 +72,15 @@ chmod +x api.py
|
||||
=== Wekan API Python CLI: Shows IDs for addcard ===
|
||||
AUTHORID is USERID that writes card.
|
||||
Syntax:
|
||||
python3 api.py users # All users
|
||||
python3 api.py boards USERID # Boards of USERID
|
||||
python3 api.py board BOARDID # Info of BOARDID
|
||||
python3 api.py swimlanes BOARDID # Swimlanes of BOARDID
|
||||
python3 api.py lists BOARDID # Lists of BOARDID
|
||||
python3 api.py list BOARDID LISTID # Info of LISTID
|
||||
python3 api.py users # All users
|
||||
python3 api.py boards USERID # Boards of USERID
|
||||
python3 api.py board BOARDID # Info of BOARDID
|
||||
python3 api.py customfields BOARDID # Custom Fields of BOARDID
|
||||
python3 api.py customfield BOARDID CUSTOMFIELDID # Info of CUSTOMFIELDID
|
||||
python3 api.py addcustomfieldtoboard AUTHORID BOARDID NAME TYPE SETTINGS SHOWONCARD AUTOMATICALLYONCARD SHOWLABELONMINICARD SHOWSUMATTOPOFLIST # Add Custom Field to Board
|
||||
python3 api.py swimlanes BOARDID # Swimlanes of BOARDID
|
||||
python3 api.py lists BOARDID # Lists of BOARDID
|
||||
python3 api.py list BOARDID LISTID # Info of LISTID
|
||||
python3 api.py createlist BOARDID LISTTITLE # Create list
|
||||
python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION
|
||||
python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION
|
||||
@@ -78,6 +88,13 @@ Syntax:
|
||||
python3 api.py attachmentjson BOARDID ATTACHMENTID # One attachment as JSON base64
|
||||
python3 api.py attachmentbinary BOARDID ATTACHMENTID # One attachment as binary file
|
||||
|
||||
=== ADD CUSTOM FIELD TO BOARD ===
|
||||
|
||||
Type: text, number, date, dropdown, checkbox, currency, stringtemplate.
|
||||
|
||||
python3 api.py addcustomfieldtoboard cmx3gmHLKwAXLqjxz LcDW4QdooAx8hsZh8 "SomeField" "date" "" true true true true
|
||||
|
||||
|
||||
=== USERS ===
|
||||
|
||||
python3 api.py users
|
||||
@@ -133,6 +150,7 @@ l = 'lists'
|
||||
sw = 'swimlane'
|
||||
sws = 'swimlanes'
|
||||
cs = 'cards'
|
||||
cf = 'custom-fields'
|
||||
bs = 'boards'
|
||||
atl = 'attachmentslist'
|
||||
at = 'attachment'
|
||||
@@ -150,10 +168,34 @@ apikey = d['token']
|
||||
|
||||
# ------- LOGIN TOKEN END -----------
|
||||
|
||||
if arguments == 10:
|
||||
|
||||
if sys.argv[1] == 'addcustomfieldtoboard':
|
||||
# ------- ADD CUSTOM FIELD TO BOARD START -----------
|
||||
authorid = sys.argv[2]
|
||||
boardid = sys.argv[3]
|
||||
name = sys.argv[4]
|
||||
type1 = sys.argv[5]
|
||||
settings = str(json.loads(sys.argv[6]))
|
||||
# There is error: Settings must be object. So this does not work yet.
|
||||
#settings = {'currencyCode': 'EUR'}
|
||||
print(type(settings))
|
||||
showoncard = sys.argv[7]
|
||||
automaticallyoncard = sys.argv[8]
|
||||
showlabelonminicard = sys.argv[9]
|
||||
showsumattopoflist = sys.argv[10]
|
||||
customfieldtoboard = wekanurl + apiboards + boardid + s + cf
|
||||
# Add Custom Field to Board
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
post_data = {'authorId': '{}'.format(authorid), 'name': '{}'.format(name), 'type': '{}'.format(type1), 'settings': '{}'.format(settings), 'showoncard': '{}'.format(showoncard), 'automaticallyoncard': '{}'.format(automaticallyoncard), 'showlabelonminicard': '{}'.format(showlabelonminicard), 'showsumattopoflist': '{}'.format(showsumattopoflist)}
|
||||
body = requests.post(customfieldtoboard, data=post_data, headers=headers)
|
||||
print(body.text)
|
||||
# ------- ADD CUSTOM FIELD TO BOARD END -----------
|
||||
|
||||
if arguments == 7:
|
||||
|
||||
if sys.argv[1] == 'addcard':
|
||||
# ------- WRITE TO CARD START -----------
|
||||
# ------- ADD CARD START -----------
|
||||
authorid = sys.argv[2]
|
||||
boardid = sys.argv[3]
|
||||
swimlaneid = sys.argv[4]
|
||||
@@ -161,18 +203,18 @@ if arguments == 7:
|
||||
cardtitle = sys.argv[6]
|
||||
carddescription = sys.argv[7]
|
||||
cardtolist = wekanurl + apiboards + boardid + s + l + s + listid + s + cs
|
||||
# Write to card
|
||||
# Add card
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
post_data = {'authorId': '{}'.format(authorid), 'title': '{}'.format(cardtitle), 'description': '{}'.format(carddescription), 'swimlaneId': '{}'.format(swimlaneid)}
|
||||
body = requests.post(cardtolist, data=post_data, headers=headers)
|
||||
print(body.text)
|
||||
# ------- WRITE TO CARD END -----------
|
||||
# ------- ADD CARD END -----------
|
||||
|
||||
if arguments == 6:
|
||||
|
||||
if sys.argv[1] == 'editcard':
|
||||
|
||||
# ------- LIST OF BOARD START -----------
|
||||
# ------- EDIT CARD START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
cardid = sys.argv[4]
|
||||
@@ -187,7 +229,7 @@ if arguments == 6:
|
||||
body = requests.get(edcard, headers=headers)
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- LISTS OF BOARD END -----------
|
||||
# ------- EDIT CARD END -----------
|
||||
|
||||
if arguments == 3:
|
||||
|
||||
@@ -217,6 +259,19 @@ if arguments == 3:
|
||||
print(data2)
|
||||
# ------- LISTS OF BOARD END -----------
|
||||
|
||||
if sys.argv[1] == 'customfield':
|
||||
|
||||
# ------- INFO OF CUSTOM FIELD START -----------
|
||||
boardid = sys.argv[2]
|
||||
customfieldid = sys.argv[3]
|
||||
customfieldone = wekanurl + apiboards + boardid + s + cf + s + customfieldid
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
print("=== INFO OF ONE CUSTOM FIELD ===\n")
|
||||
body = requests.get(customfieldone, headers=headers)
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- INFO OF CUSTOM FIELD END -----------
|
||||
|
||||
if arguments == 2:
|
||||
|
||||
# ------- BOARDS LIST START -----------
|
||||
@@ -230,8 +285,8 @@ if arguments == 2:
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- BOARDS LIST END -----------
|
||||
if sys.argv[1] == 'board':
|
||||
|
||||
if sys.argv[1] == 'board':
|
||||
# ------- BOARD INFO START -----------
|
||||
boardid = sys.argv[2]
|
||||
board = wekanurl + apiboards + boardid
|
||||
@@ -242,6 +297,17 @@ if arguments == 2:
|
||||
print(data2)
|
||||
# ------- BOARD INFO END -----------
|
||||
|
||||
if sys.argv[1] == 'customfields':
|
||||
# ------- CUSTOM FIELDS OF BOARD START -----------
|
||||
boardid = sys.argv[2]
|
||||
boardcustomfields = wekanurl + apiboards + boardid + s + cf
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
body = requests.get(boardcustomfields, headers=headers)
|
||||
print("=== CUSTOM FIELDS OF BOARD ===\n")
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- CUSTOM FIELDS OF BOARD END -----------
|
||||
|
||||
if sys.argv[1] == 'swimlanes':
|
||||
boardid = sys.argv[2]
|
||||
swimlanes = wekanurl + apiboards + boardid + s + sws
|
||||
@@ -289,3 +355,14 @@ if arguments == 1:
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- LIST OF USERS END -----------
|
||||
|
||||
if sys.argv[1] == 'boards':
|
||||
|
||||
# ------- LIST OF PUBLIC BOARDS START -----------
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
print("=== PUBLIC BOARDS ===\n")
|
||||
listpublicboards = wekanurl + apiboards
|
||||
body = requests.get(listpublicboards, headers=headers)
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- LIST OF PUBLIC BOARDS END -----------
|
||||
@@ -12,7 +12,7 @@ template(name="boardActivities")
|
||||
+activity(activity=activityData card=card mode=mode)
|
||||
|
||||
template(name="cardActivities")
|
||||
each activityData in currentCard.activities
|
||||
each activityData in activities
|
||||
+activity(activity=activityData card=card mode=mode)
|
||||
|
||||
template(name="editOrDeleteComment")
|
||||
@@ -21,6 +21,26 @@ template(name="editOrDeleteComment")
|
||||
= ' - '
|
||||
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
|
||||
+userAvatar(userId=activity.user._id)
|
||||
@@ -120,10 +140,12 @@ template(name="activity")
|
||||
= 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
|
||||
@@ -150,20 +172,20 @@ template(name="activity")
|
||||
|
||||
if($eq activity.activityType 'a-startAt')
|
||||
| {{{_ 'activity-startDate' (sanitize startDate) cardLink}}}.
|
||||
|
||||
|
||||
if($eq activity.activityType 'a-dueAt')
|
||||
| {{{_ 'activity-dueDate' (sanitize dueDate) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'a-endAt')
|
||||
| {{{_ 'activity-endDate' (sanitize endDate) cardLink}}}.
|
||||
|
||||
|
||||
if($eq mode 'board')
|
||||
if($eq activity.activityType 'a-receivedAt')
|
||||
| {{{_ 'activity-receivedDate' (sanitize receivedDate) cardLink}}}.
|
||||
|
||||
if($eq activity.activityType 'a-startAt')
|
||||
| {{{_ 'activity-startDate' (sanitize startDate) cardLink}}}.
|
||||
|
||||
|
||||
if($eq activity.activityType 'a-dueAt')
|
||||
| {{{_ 'activity-dueDate' (sanitize dueDate) cardLink}}}.
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ BlazeComponent.extendComponent({
|
||||
this.autorun(() => {
|
||||
let mode = this.data().mode;
|
||||
const capitalizedMode = Utils.capitalize(mode);
|
||||
let thisId, searchId;
|
||||
let searchId;
|
||||
if (mode === 'linkedcard' || mode === 'linkedboard') {
|
||||
thisId = Session.get('currentCard');
|
||||
searchId = Cards.findOne({ _id: thisId }).linkedId;
|
||||
searchId = Utils.getCurrentCard().linkedId;
|
||||
mode = mode.replace('linked', '');
|
||||
} else if (mode === 'card') {
|
||||
searchId = Utils.getCurrentCardId();
|
||||
} else {
|
||||
thisId = Session.get(`current${capitalizedMode}`);
|
||||
searchId = thisId;
|
||||
searchId = Session.get(`current${capitalizedMode}`);
|
||||
}
|
||||
const limit = this.page.get() * activitiesPerPage;
|
||||
const user = Meteor.user();
|
||||
@@ -54,6 +54,13 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('activities');
|
||||
|
||||
Template.activities.helpers({
|
||||
activities() {
|
||||
const ret = this.card.activities();
|
||||
return ret;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
checkItem() {
|
||||
const checkItemId = this.currentData().activity.checklistItemId;
|
||||
@@ -113,8 +120,10 @@ BlazeComponent.extendComponent({
|
||||
).getLabelById(lastLabelId);
|
||||
if (lastLabel && (lastLabel.name === undefined || lastLabel.name === '')) {
|
||||
return lastLabel.color;
|
||||
} else {
|
||||
} else if (lastLabel.name !== undefined && lastLabel.name !== '') {
|
||||
return lastLabel.name;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -211,10 +220,11 @@ BlazeComponent.extendComponent({
|
||||
return [
|
||||
{
|
||||
// XXX We should use Popup.afterConfirmation here
|
||||
'click .js-delete-comment'() {
|
||||
const commentId = this.currentData().activity.commentId;
|
||||
'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()
|
||||
@@ -240,6 +250,60 @@ Template.activity.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
Template.commentReactions.events({
|
||||
'click .reaction'(event) {
|
||||
if (Meteor.user().isBoardMember()) {
|
||||
const codepoint = event.currentTarget.dataset['codepoint'];
|
||||
const commentId = Template.instance().data.commentId;
|
||||
const cardComment = CardComments.findOne({_id: commentId});
|
||||
cardComment.toggleReaction(codepoint);
|
||||
}
|
||||
},
|
||||
'click .open-comment-reaction-popup': Popup.open('addReaction'),
|
||||
})
|
||||
|
||||
Template.addReactionPopup.events({
|
||||
'click .add-comment-reaction'(event) {
|
||||
if (Meteor.user().isBoardMember()) {
|
||||
const codepoint = event.currentTarget.dataset['codepoint'];
|
||||
const commentId = Template.instance().data.commentId;
|
||||
const cardComment = CardComments.findOne({_id: commentId});
|
||||
cardComment.toggleReaction(codepoint);
|
||||
}
|
||||
Popup.back();
|
||||
},
|
||||
})
|
||||
|
||||
Template.addReactionPopup.helpers({
|
||||
codepoints() {
|
||||
// Starting set of unicode codepoints as comment reactions
|
||||
return [
|
||||
'👍',
|
||||
'👎',
|
||||
'👀',
|
||||
'✅',
|
||||
'❌',
|
||||
'🙏',
|
||||
'👏',
|
||||
'🎉',
|
||||
'🚀',
|
||||
'😊',
|
||||
'🤔',
|
||||
'😔'];
|
||||
}
|
||||
})
|
||||
|
||||
Template.commentReactions.helpers({
|
||||
isSelected(userIds) {
|
||||
return userIds.includes(Meteor.user()._id);
|
||||
},
|
||||
userNames(userIds) {
|
||||
return Users.find({_id: {$in: userIds}})
|
||||
.map(user => user.profile.fullname)
|
||||
.join(', ');
|
||||
}
|
||||
})
|
||||
|
||||
function createCardLink(card) {
|
||||
if (!card) return '';
|
||||
return (
|
||||
|
||||
@@ -5,6 +5,20 @@
|
||||
display: flex
|
||||
justify-content:space-between
|
||||
|
||||
.reactions-popup
|
||||
.add-comment-reaction
|
||||
display: inline-block
|
||||
cursor: pointer
|
||||
border-radius: 5px
|
||||
font-size: 22px
|
||||
text-align: center
|
||||
line-height: 30px
|
||||
width: 40px
|
||||
|
||||
&:hover {
|
||||
background-color: #b0c4de
|
||||
}
|
||||
|
||||
.activities
|
||||
clear: both
|
||||
|
||||
@@ -18,7 +32,7 @@
|
||||
height: @width
|
||||
|
||||
.activity-member
|
||||
font-weight: 700
|
||||
font-weight: 700
|
||||
|
||||
.activity-desc
|
||||
word-wrap: break-word
|
||||
@@ -39,6 +53,45 @@
|
||||
margin-top: 5px
|
||||
padding: 5px
|
||||
|
||||
.reactions
|
||||
display: flex
|
||||
margin-top: 5px
|
||||
gap: 5px
|
||||
|
||||
.open-comment-reaction-popup
|
||||
display: flex
|
||||
align-items: center
|
||||
text-decoration: none
|
||||
height: 24px;
|
||||
|
||||
i.fa.fa-smile-o
|
||||
font-size: 17px
|
||||
font-weight: 500
|
||||
margin-left: 2px
|
||||
|
||||
i.fa.fa-plus
|
||||
font-size: 8px;
|
||||
margin-top: -7px;
|
||||
margin-left: 1px;
|
||||
|
||||
.reaction
|
||||
cursor: pointer
|
||||
border: 1px solid grey
|
||||
border-radius: 15px
|
||||
display: flex
|
||||
padding: 2px 5px
|
||||
|
||||
&.selected {
|
||||
background-color: #b0c4de
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #b0c4de
|
||||
}
|
||||
|
||||
.reaction-count
|
||||
font-size: 12px
|
||||
|
||||
.activity-checklist
|
||||
display: block
|
||||
border-radius: 3px
|
||||
|
||||
@@ -26,6 +26,8 @@ BlazeComponent.extendComponent({
|
||||
if (card.isLinkedCard()) {
|
||||
boardId = Cards.findOne(card.linkedId).boardId;
|
||||
cardId = card.linkedId;
|
||||
} else if (card.isLinkedBoard()) {
|
||||
boardId = card.linkedId;
|
||||
}
|
||||
if (text) {
|
||||
CardComments.insert({
|
||||
@@ -64,7 +66,7 @@ function resetCommentInput(input) {
|
||||
// Tracker.autorun to register the component dependencies, and re-run when these
|
||||
// dependencies are invalidated. A better component API would remove this hack.
|
||||
Tracker.autorun(() => {
|
||||
Session.get('currentCard');
|
||||
Utils.getCurrentCardId();
|
||||
Tracker.afterFlush(() => {
|
||||
autosize.update($('.js-new-comment-input'));
|
||||
});
|
||||
@@ -75,7 +77,7 @@ EscapeActions.register(
|
||||
() => {
|
||||
const draftKey = {
|
||||
fieldName: 'cardComment',
|
||||
docId: Session.get('currentCard'),
|
||||
docId: Utils.getCurrentCardId(),
|
||||
};
|
||||
const commentInput = $('.js-new-comment-input');
|
||||
const draft = commentInput.val().trim();
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
background-color: #fff
|
||||
border: 0
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
|
||||
color: #8c8c8c
|
||||
height: 36px
|
||||
margin: 4px 4px 6px 0
|
||||
padding: 9px 11px
|
||||
|
||||
@@ -34,7 +34,7 @@ BlazeComponent.extendComponent({
|
||||
Utils.goBoardId(board._id);
|
||||
},
|
||||
'click .js-delete-board': Popup.afterConfirm('boardDelete', function() {
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
const isSandstorm =
|
||||
Meteor.settings &&
|
||||
Meteor.settings.public &&
|
||||
|
||||
@@ -13,26 +13,29 @@ template(name="board")
|
||||
+spinner
|
||||
|
||||
template(name="boardBody")
|
||||
.board-wrapper(class=currentBoard.colorClass)
|
||||
+sidebar
|
||||
.board-canvas.js-swimlanes(
|
||||
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}}")
|
||||
if showOverlay.get
|
||||
.board-overlay
|
||||
if currentBoard.isTemplatesBoard
|
||||
each currentBoard.swimlanes
|
||||
+swimlane(this)
|
||||
else if isViewSwimlanes
|
||||
each currentBoard.swimlanes
|
||||
+swimlane(this)
|
||||
else if isViewLists
|
||||
+listsGroup(currentBoard)
|
||||
else if isViewCalendar
|
||||
+calendarView
|
||||
else
|
||||
+listsGroup(currentBoard)
|
||||
if notDisplayThisBoard
|
||||
| {{_ 'tableVisibilityMode-allowPrivateOnly'}}
|
||||
else
|
||||
.board-wrapper(class=currentBoard.colorClass)
|
||||
+sidebar
|
||||
.board-canvas.js-swimlanes(
|
||||
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}}")
|
||||
if showOverlay.get
|
||||
.board-overlay
|
||||
if currentBoard.isTemplatesBoard
|
||||
each currentBoard.swimlanes
|
||||
+swimlane(this)
|
||||
else if isViewSwimlanes
|
||||
each currentBoard.swimlanes
|
||||
+swimlane(this)
|
||||
else if isViewLists
|
||||
+listsGroup(currentBoard)
|
||||
else if isViewCalendar
|
||||
+calendarView
|
||||
else
|
||||
+listsGroup(currentBoard)
|
||||
|
||||
template(name="calendarView")
|
||||
if isViewCalendar
|
||||
|
||||
@@ -23,7 +23,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
onlyShowCurrentCard() {
|
||||
return Utils.isMiniScreen() && Session.get('currentCard');
|
||||
return Utils.isMiniScreen() && Utils.getCurrentCardId(true);
|
||||
},
|
||||
|
||||
goHome() {
|
||||
@@ -33,6 +33,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
Meteor.subscribe('tableVisibilityModeSettings');
|
||||
this.showOverlay = new ReactiveVar(false);
|
||||
this.draggingActive = new ReactiveVar(false);
|
||||
this._isDragging = false;
|
||||
@@ -190,21 +191,11 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
|
||||
this.autorun(() => {
|
||||
let showDesktopDragHandles = false;
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
showDesktopDragHandles = (currentUser.profile || {})
|
||||
.showDesktopDragHandles;
|
||||
} else if (window.localStorage.getItem('showDesktopDragHandles')) {
|
||||
showDesktopDragHandles = true;
|
||||
} else {
|
||||
showDesktopDragHandles = false;
|
||||
}
|
||||
if (Utils.isMiniScreen() || showDesktopDragHandles) {
|
||||
if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
|
||||
$swimlanesDom.sortable({
|
||||
handle: '.js-swimlane-header-handle',
|
||||
});
|
||||
} else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
|
||||
} else {
|
||||
$swimlanesDom.sortable({
|
||||
handle: '.swimlane-header',
|
||||
});
|
||||
@@ -215,7 +206,7 @@ BlazeComponent.extendComponent({
|
||||
$swimlanesDom.sortable(
|
||||
'option',
|
||||
'disabled',
|
||||
!Meteor.user().isBoardAdmin(),
|
||||
!Meteor.user() || !Meteor.user().isBoardAdmin(),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -235,6 +226,16 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
},
|
||||
|
||||
notDisplayThisBoard(){
|
||||
let allowPrivateVisibilityOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly');
|
||||
let currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if(allowPrivateVisibilityOnly !== undefined && allowPrivateVisibilityOnly.booleanValue && currentBoard.permission == 'public'){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
isViewSwimlanes() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
@@ -325,6 +326,7 @@ BlazeComponent.extendComponent({
|
||||
defaultView: 'agendaDay',
|
||||
editable: true,
|
||||
timezone: 'local',
|
||||
weekNumbers: true,
|
||||
header: {
|
||||
left: 'title today prev,next',
|
||||
center:
|
||||
|
||||
@@ -15,9 +15,16 @@ setBoardColor(color)
|
||||
.is-selected .minicard
|
||||
border-left: 3px solid color
|
||||
|
||||
// All minicards rounded corners:
|
||||
.minicard
|
||||
border-radius: 7px
|
||||
padding: 10px 10px 4px 10px
|
||||
box-shadow: 2px 2px 4px 0px rgba(0,0,0,0.15)
|
||||
|
||||
button[type=submit].primary, input[type=submit].primary,
|
||||
.sidebar .sidebar-content .sidebar-btn
|
||||
background-color: darken(color, 20%)
|
||||
border-radius: 7px
|
||||
|
||||
&.pop-over .pop-over-list li a:not(.disabled):hover,
|
||||
.sidebar .sidebar-content .sidebar-btn:hover,
|
||||
@@ -186,9 +193,10 @@ setBoardColor(color)
|
||||
.list >.list-header, .swimlane-header
|
||||
color:rgba(255,255,255,.7)
|
||||
|
||||
/* minicards */
|
||||
/* minicards
|
||||
.minicard
|
||||
background-color:rgba(255,255,255,.4)
|
||||
*/
|
||||
|
||||
.minicard-wrapper.is-selected .minicard,
|
||||
.minicard:hover,
|
||||
@@ -196,9 +204,12 @@ setBoardColor(color)
|
||||
.open-minicard-composer:hover
|
||||
background-color:rgba(255,255,255,.8) !important
|
||||
color:#000
|
||||
border-radius: 7px
|
||||
|
||||
/*
|
||||
.minicard, .minicard .badge
|
||||
color:#fff
|
||||
*/
|
||||
|
||||
.minicard:hover .badge, .minicard-wrapper.is-selected .badge
|
||||
color:#000
|
||||
@@ -214,6 +225,10 @@ setBoardColor(color)
|
||||
.sidebar-content h3, .sidebar-content h2, .sidebar-content
|
||||
color:rgba(255,255,255,.7) !important
|
||||
|
||||
.card-details .activities .activity .activity-desc .activity-comment
|
||||
background-color: #cccccc
|
||||
color: #222222
|
||||
|
||||
.board-color-relax
|
||||
setBoardColor(#27AE61)
|
||||
|
||||
@@ -225,7 +240,7 @@ setBoardColor(color)
|
||||
|
||||
.list-header
|
||||
background-color:#a7e366
|
||||
border-bottom: 6px solid #a7e366
|
||||
// border-bottom: 6px solid #a7e366
|
||||
|
||||
.list-body
|
||||
background-color:#a7e366
|
||||
@@ -450,7 +465,7 @@ setBoardClear(color1,color2)
|
||||
background-color: rgba(255,255,255,0.7)
|
||||
color: rgba(0,0,0,0.8)
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
|
||||
border-radius: 7px
|
||||
.quiet, .quiet a
|
||||
color: rgba(0,0,0,0.4)
|
||||
|
||||
@@ -465,6 +480,7 @@ setBoardClear(color1,color2)
|
||||
|
||||
a:not(.disabled).is-active.fa, a:not(.disabled).is-active i.fa, a:not(.disabled):hover.fa, a:not(.disabled):hover i.fa
|
||||
color: rgba(0,0,0,0.6)
|
||||
border-radius: 7px
|
||||
|
||||
input[type="email"], input[type="password"], input[type="text"]
|
||||
border: 0
|
||||
@@ -541,8 +557,8 @@ setBoardClear(color1,color2)
|
||||
background-color:#dedede
|
||||
|
||||
.list-header
|
||||
background-color: #c9cfc3
|
||||
border-bottom: 6px solid #c9cfc3
|
||||
// background-color: #c9cfc3
|
||||
// border-bottom: 6px solid #c9cfc3
|
||||
|
||||
.swimlane .swimlane-header-wrap
|
||||
background-color: #c2c0ab
|
||||
@@ -645,7 +661,7 @@ setBoardClear(color1,color2)
|
||||
|
||||
.list-header
|
||||
background: none
|
||||
border-bottom-width: 0px
|
||||
// border-bottom-width: 0px
|
||||
|
||||
.list-header .list-header-name
|
||||
font-family: Poppins
|
||||
@@ -758,16 +774,17 @@ setBoardClear(color1,color2)
|
||||
|
||||
/* Forms */
|
||||
button[type=submit].primary, .board-color-modern input[type=submit].primary
|
||||
background-color: #819C5D
|
||||
background-color: #777777
|
||||
border-radius: 7px
|
||||
|
||||
.toggle-switch:checked~.toggle-label
|
||||
background-color: #D2E9B4
|
||||
background-color: #f7f7f7
|
||||
|
||||
.toggle-label:after, .board-color-modern .toggle-switch:checked~.toggle-label:after
|
||||
background-color: #819C5D !important
|
||||
background-color: #777777 !important
|
||||
|
||||
button, input:not([type=file]), select, textarea
|
||||
border-radius: 2px
|
||||
border-radius: 7px
|
||||
|
||||
/* Headers */
|
||||
&#header
|
||||
@@ -854,21 +871,22 @@ setBoardClear(color1,color2)
|
||||
.swimlane .list:first-child
|
||||
margin-left: 0
|
||||
|
||||
.swimlane .list:nth-child(even)
|
||||
background: #5f5f5f
|
||||
.swimlane .list:nth-child(even) .list-header,
|
||||
.swimlane .list:nth-child(even) .list-body
|
||||
background: #6a6a6a
|
||||
|
||||
.swimlane .list:nth-child(odd) .list-header
|
||||
background: #3b3b3b
|
||||
.swimlane .list:nth-child(odd) .list-header,
|
||||
.swimlane .list:nth-child(odd) .list-body
|
||||
background: #555555
|
||||
|
||||
.list-header
|
||||
background: #333333
|
||||
padding: 10px
|
||||
border-bottom: 0
|
||||
|
||||
.list-header .viewer
|
||||
padding-left: 10px
|
||||
|
||||
.list-header .list-header-name
|
||||
.list-header .list-header-name,
|
||||
.minicard
|
||||
line-height: 14px
|
||||
color: #eeeeee
|
||||
|
||||
@@ -876,7 +894,7 @@ setBoardClear(color1,color2)
|
||||
padding: 10px
|
||||
top: 0
|
||||
|
||||
.list-header .list-header-plus-icon
|
||||
.list-header .list-header-plus-top
|
||||
color: #a6a6a6
|
||||
|
||||
.list-body
|
||||
@@ -906,6 +924,7 @@ setBoardClear(color1,color2)
|
||||
|
||||
.list-body a.open-minicard-composer:hover, .list-body a.open-minicard-composer:hover i, .list .list-composer .open-list-composer:hover i
|
||||
color: #ffffff
|
||||
border-radius: 7px
|
||||
|
||||
/* Mini Card */
|
||||
.minicard-wrapper
|
||||
@@ -916,12 +935,13 @@ setBoardClear(color1,color2)
|
||||
color: #cccccc
|
||||
border-radius: 2px
|
||||
font-size: 0.95em
|
||||
padding: 10px
|
||||
box-shadow: 0 4px 3px -3px rgba(0,0,0,0.8)
|
||||
border-bottom: 1px solid #666666
|
||||
padding: 8px
|
||||
|
||||
.minicard:hover
|
||||
background-color: #494949 !important
|
||||
color: #f7f7f7
|
||||
background-color: #4d4d4d !important
|
||||
|
||||
.minicard .minicard-labels
|
||||
margin-bottom: 4px
|
||||
@@ -956,17 +976,24 @@ setBoardClear(color1,color2)
|
||||
|
||||
/* Card Details */
|
||||
.card-details
|
||||
position: absolute
|
||||
top: 30px
|
||||
left: calc(50% - 384px)
|
||||
width: 768px
|
||||
max-height: calc(100% - 60px)
|
||||
background-color: #454545
|
||||
color: #cccccc
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)
|
||||
border: 1px solid #111111
|
||||
z-index: 100 !important
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.card-details
|
||||
width: 98%
|
||||
|
||||
@media screen and (min-width: 801px)
|
||||
.card-details
|
||||
position: absolute
|
||||
top: 30px
|
||||
left: calc(50% - 384px)
|
||||
width: 768px
|
||||
max-height: calc(100% - 60px)
|
||||
|
||||
.card-details
|
||||
scrollbar-width: thin
|
||||
scrollbar-color: #343434 #999999
|
||||
@@ -1018,8 +1045,8 @@ setBoardClear(color1,color2)
|
||||
border-color: #ffffff
|
||||
|
||||
.card-details .check-box.materialCheckBox.is-checked
|
||||
border-bottom: 2px solid #819C5D
|
||||
border-right: 2px solid #819C5D
|
||||
border-bottom: 2px solid #ffffff
|
||||
border-right: 2px solid #ffffff
|
||||
border-top: 0
|
||||
border-left: 0
|
||||
|
||||
@@ -1059,5 +1086,5 @@ setBoardClear(color1,color2)
|
||||
font-size: 16px
|
||||
color: #cccccc
|
||||
|
||||
.pop-over.board-color-moderndark .pop-over-list li:hover > a
|
||||
background-color: #819C5D !important
|
||||
.pop-over.board-color-moderndark .pop-over-list li > a:hover
|
||||
background-color: rgba(255,255,255,0.2)
|
||||
@@ -80,6 +80,12 @@ template(name="boardHeaderBar")
|
||||
if $eq watchLevel "muted"
|
||||
i.fa.fa-bell-slash
|
||||
span {{_ watchLevel}}
|
||||
a.board-header-btn(title="{{_ 'sort-cards'}}" class="{{#if isSortActive }}emphasis{{else}} js-sort-cards {{/if}}")
|
||||
i.fa.fa-sort
|
||||
span {{#if isSortActive }}{{_ 'Sort is on'}}{{else}}{{_ 'sort-cards'}}{{/if}}
|
||||
if isSortActive
|
||||
a.board-header-btn-close.js-sort-reset(title="Remove Sort")
|
||||
i.fa.fa-times-thin
|
||||
|
||||
else
|
||||
a.board-header-btn.js-log-in(
|
||||
@@ -147,14 +153,15 @@ template(name="boardVisibilityList")
|
||||
if visibilityCheck
|
||||
i.fa.fa-check
|
||||
span.sub-name {{_ 'private-desc'}}
|
||||
li
|
||||
with "public"
|
||||
a.js-select-visibility
|
||||
i.fa.fa-globe.colorful
|
||||
| {{_ 'public'}}
|
||||
if visibilityCheck
|
||||
i.fa.fa-check
|
||||
span.sub-name {{_ 'public-desc'}}
|
||||
if notAllowPrivateVisibilityOnly
|
||||
li
|
||||
with "public"
|
||||
a.js-select-visibility
|
||||
i.fa.fa-globe.colorful
|
||||
| {{_ 'public'}}
|
||||
if visibilityCheck
|
||||
i.fa.fa-check
|
||||
span.sub-name {{_ 'public-desc'}}
|
||||
|
||||
template(name="boardChangeVisibilityPopup")
|
||||
+boardVisibilityList
|
||||
@@ -228,9 +235,9 @@ template(name="createBoard")
|
||||
= " "
|
||||
| {{{_ 'board-private-info'}}}
|
||||
a.js-change-visibility {{_ 'change'}}.
|
||||
//a.flex.js-toggle-add-template-container
|
||||
// .materialCheckBox#add-template-container
|
||||
// span {{_ 'add-template-container'}}
|
||||
a.flex.js-toggle-add-template-container
|
||||
.materialCheckBox#add-template-container
|
||||
span {{_ 'add-template-container'}}
|
||||
input.primary.wide(type="submit" value="{{_ 'create'}}")
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
|
||||
@@ -7,11 +7,11 @@ Template.boardMenuPopup.events({
|
||||
'click .js-rename-board': Popup.open('boardChangeTitle'),
|
||||
'click .js-custom-fields'() {
|
||||
Sidebar.setView('customFields');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-open-archives'() {
|
||||
Sidebar.setView('archives');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-change-board-color': Popup.open('boardChangeColor'),
|
||||
'click .js-change-language': Popup.open('changeLanguage'),
|
||||
@@ -24,7 +24,7 @@ Template.boardMenuPopup.events({
|
||||
}),
|
||||
'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
Boards.remove(currentBoard._id);
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
@@ -47,7 +47,7 @@ Template.boardChangeTitlePopup.events({
|
||||
if (newTitle) {
|
||||
this.rename(newTitle);
|
||||
this.setDescription(newDesc);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
event.preventDefault();
|
||||
},
|
||||
@@ -136,7 +136,7 @@ BlazeComponent.extendComponent({
|
||||
Sidebar.setView('search');
|
||||
},
|
||||
'click .js-multiselection-activate'() {
|
||||
const currentCard = Session.get('currentCard');
|
||||
const currentCard = Utils.getCurrentCardId();
|
||||
MultiSelection.activate();
|
||||
if (currentCard) {
|
||||
MultiSelection.add(currentCard);
|
||||
@@ -173,15 +173,15 @@ Template.boardHeaderBar.helpers({
|
||||
Template.boardChangeViewPopup.events({
|
||||
'click .js-open-lists-view'() {
|
||||
Utils.setBoardView('board-view-lists');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-open-swimlanes-view'() {
|
||||
Utils.setBoardView('board-view-swimlanes');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-open-cal-view'() {
|
||||
Utils.setBoardView('board-view-cal');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -194,6 +194,11 @@ const CreateBoard = BlazeComponent.extendComponent({
|
||||
this.visibilityMenuIsOpen = new ReactiveVar(false);
|
||||
this.visibility = new ReactiveVar('private');
|
||||
this.boardId = new ReactiveVar('');
|
||||
Meteor.subscribe('tableVisibilityModeSettings');
|
||||
},
|
||||
|
||||
notAllowPrivateVisibilityOnly(){
|
||||
return !TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly').booleanValue;
|
||||
},
|
||||
|
||||
visibilityCheck() {
|
||||
@@ -310,6 +315,9 @@ const CreateBoard = BlazeComponent.extendComponent({
|
||||
}.register('headerBarCreateBoardPopup'));
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
notAllowPrivateVisibilityOnly(){
|
||||
return !TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly').booleanValue;
|
||||
},
|
||||
visibilityCheck() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
return this.currentData() === currentBoard.permission;
|
||||
@@ -319,7 +327,7 @@ BlazeComponent.extendComponent({
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
const visibility = this.currentData();
|
||||
currentBoard.setVisibility(visibility);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
events() {
|
||||
@@ -352,7 +360,7 @@ BlazeComponent.extendComponent({
|
||||
Session.get('currentBoard'),
|
||||
level,
|
||||
(err, ret) => {
|
||||
if (!err && ret) Popup.close();
|
||||
if (!err && ret) Popup.back();
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -424,7 +432,7 @@ BlazeComponent.extendComponent({
|
||||
const direction = down ? -1 : 1;
|
||||
this.setSortBy([sortby, direction]);
|
||||
if (Utils.isMiniScreen) {
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -443,7 +451,7 @@ BlazeComponent.extendComponent({
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('due-date'));
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-sort-title'() {
|
||||
const sortBy = {
|
||||
@@ -451,7 +459,7 @@ BlazeComponent.extendComponent({
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('title'));
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-sort-created-asc'() {
|
||||
const sortBy = {
|
||||
@@ -459,7 +467,7 @@ BlazeComponent.extendComponent({
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('date-created-newest-first'));
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-sort-created-desc'() {
|
||||
const sortBy = {
|
||||
@@ -467,7 +475,7 @@ BlazeComponent.extendComponent({
|
||||
};
|
||||
Session.set('sortBy', sortBy);
|
||||
sortCardsBy.set(TAPi18n.__('date-created-oldest-first'));
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,11 +1,40 @@
|
||||
template(name="boardList")
|
||||
.wrapper
|
||||
ul.AllBoardTeamsOrgs
|
||||
li.AllBoardTeams
|
||||
if userHasTeams
|
||||
select.js-AllBoardTeams#jsAllBoardTeams("multiple")
|
||||
option(value="-1") {{_ 'teams'}} :
|
||||
each teamsDatas
|
||||
option(value="{{teamId}}") {{_ teamDisplayName}}
|
||||
|
||||
li.AllBoardOrgs
|
||||
if userHasOrgs
|
||||
select.js-AllBoardOrgs#jsAllBoardOrgs("multiple")
|
||||
option(value="-1") {{_ 'organizations'}} :
|
||||
each orgsDatas
|
||||
option(value="{{orgId}}") {{_ orgDisplayName}}
|
||||
|
||||
//li.AllBoardTemplates
|
||||
// if userHasTemplates
|
||||
// select.js-AllBoardTemplates#jsAllBoardTemplates("multiple")
|
||||
// option(value="-1") {{_ 'templates'}} :
|
||||
// each templatesDatas
|
||||
// option(value="{{templateId}}") {{_ templateDisplayName}}
|
||||
|
||||
li.AllBoardBtns
|
||||
div.AllBoardButtonsContainer
|
||||
if userHasOrgsOrTeams
|
||||
i.fa.fa-filter
|
||||
input#filterBtn(type="button" value="{{_ 'filter'}}")
|
||||
input#resetBtn(type="button" value="{{_ 'filter-clear'}}")
|
||||
|
||||
ul.board-list.clearfix.js-boards
|
||||
li.js-add-board
|
||||
a.board-list-item.label(title="{{_ 'add-board'}}")
|
||||
| {{_ 'add-board'}}
|
||||
each boards
|
||||
li(class="{{#if isStarred}}starred{{/if}}" class=colorClass).js-board
|
||||
li(class="{{_id}}" class="{{#if isStarred}}starred{{/if}}" class=colorClass).js-board
|
||||
if isInvited
|
||||
.board-list-item
|
||||
span.details
|
||||
@@ -33,11 +62,11 @@ template(name="boardList")
|
||||
i.fa.js-has-spenttime-cards(
|
||||
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
|
||||
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
|
||||
if isMiniScreen
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
i.fa.board-handle(
|
||||
class="fa-arrows"
|
||||
title="{{_ 'Drag board'}}")
|
||||
unless isMiniScreen
|
||||
else
|
||||
if isSandstorm
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
@@ -75,11 +104,11 @@ template(name="boardList")
|
||||
i.fa.js-has-spenttime-cards(
|
||||
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
|
||||
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
|
||||
if isMiniScreen
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
i.fa.board-handle(
|
||||
class="fa-arrows"
|
||||
title="{{_ 'Drag board'}}")
|
||||
unless isMiniScreen
|
||||
else
|
||||
if isSandstorm
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndex, enableClickOnTouch } = Utils;
|
||||
|
||||
Template.boardListHeaderBar.events({
|
||||
'click .js-open-archived-board'() {
|
||||
@@ -9,7 +8,11 @@ Template.boardListHeaderBar.events({
|
||||
|
||||
Template.boardListHeaderBar.helpers({
|
||||
title() {
|
||||
//if (FlowRouter.getRouteName() === 'template-container') {
|
||||
// return 'template-container';
|
||||
//} else {
|
||||
return FlowRouter.getRouteName() === 'home' ? 'my-boards' : 'public';
|
||||
//}
|
||||
},
|
||||
templatesBoardId() {
|
||||
return Meteor.user() && Meteor.user().getTemplatesBoardId();
|
||||
@@ -22,6 +25,7 @@ Template.boardListHeaderBar.helpers({
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('tableVisibilityModeSettings');
|
||||
let currUser = Meteor.user();
|
||||
let userLanguage;
|
||||
if(currUser && currUser.profile){
|
||||
@@ -55,7 +59,7 @@ BlazeComponent.extendComponent({
|
||||
// of the previous and the following card -- if any.
|
||||
const prevBoardDom = ui.item.prev('.js-board').get(0);
|
||||
const nextBoardBom = ui.item.next('.js-board').get(0);
|
||||
const sortIndex = calculateIndex(prevBoardDom, nextBoardBom, 1);
|
||||
const sortIndex = Utils.calculateIndex(prevBoardDom, nextBoardBom, 1);
|
||||
|
||||
const boardDomElement = ui.item.get(0);
|
||||
const board = Blaze.getData(boardDomElement);
|
||||
@@ -72,33 +76,81 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
});
|
||||
|
||||
// ugly touch event hotfix
|
||||
enableClickOnTouch(itemsSelector);
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member or is comment only
|
||||
this.autorun(() => {
|
||||
if (Utils.isMiniScreen()) {
|
||||
if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
|
||||
$boards.sortable({
|
||||
handle: '.board-handle',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
userHasTeams(){
|
||||
if(Meteor.user() != null && Meteor.user().teams && Meteor.user().teams.length > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
teamsDatas() {
|
||||
if(Meteor.user().teams)
|
||||
return Meteor.user().teams.sort((a, b) => a.teamDisplayName.localeCompare(b.teamDisplayName));
|
||||
else
|
||||
return [];
|
||||
},
|
||||
userHasOrgs(){
|
||||
if(Meteor.user() != null && Meteor.user().orgs && Meteor.user().orgs.length > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
/*
|
||||
userHasTemplates(){
|
||||
if(Meteor.user() != null && Meteor.user().orgs && Meteor.user().orgs.length > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
*/
|
||||
orgsDatas() {
|
||||
if(Meteor.user().orgs)
|
||||
return Meteor.user().orgs.sort((a, b) => a.orgDisplayName.localeCompare(b.orgDisplayName));
|
||||
else
|
||||
return [];
|
||||
},
|
||||
userHasOrgsOrTeams(){
|
||||
let boolUserHasOrgs;
|
||||
if(Meteor.user() != null && Meteor.user().orgs && Meteor.user().orgs.length > 0)
|
||||
boolUserHasOrgs = true;
|
||||
else
|
||||
boolUserHasOrgs = false;
|
||||
|
||||
let boolUserHasTeams;
|
||||
if(Meteor.user() != null && Meteor.user().teams && Meteor.user().teams.length > 0)
|
||||
boolUserHasTeams = true;
|
||||
else
|
||||
boolUserHasTeams = false;
|
||||
|
||||
return (boolUserHasOrgs || boolUserHasTeams);
|
||||
},
|
||||
boards() {
|
||||
const query = {
|
||||
//archived: false,
|
||||
////type: { $in: ['board','template-container'] },
|
||||
//type: 'board',
|
||||
let query = {
|
||||
// { type: 'board' },
|
||||
// { type: { $in: ['board','template-container'] } },
|
||||
$and: [
|
||||
{ archived: false },
|
||||
{ type: 'board' },
|
||||
{ type: { $in: ['board','template-container'] } },
|
||||
{ $or:[] }
|
||||
]
|
||||
};
|
||||
|
||||
let allowPrivateVisibilityOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly');
|
||||
|
||||
if (FlowRouter.getRouteName() === 'home'){
|
||||
query.$and[2].$or.push({'members.userId': Meteor.userId()});
|
||||
|
||||
if(allowPrivateVisibilityOnly !== undefined && allowPrivateVisibilityOnly.booleanValue){
|
||||
query.$and.push({'permission': 'private'});
|
||||
}
|
||||
const currUser = Users.findOne(Meteor.userId());
|
||||
|
||||
// const currUser = Users.findOne(Meteor.userId(), {
|
||||
@@ -108,7 +160,7 @@ BlazeComponent.extendComponent({
|
||||
// },
|
||||
// });
|
||||
|
||||
let orgIdsUserBelongs = currUser.teams !== 'undefined' ? currUser.orgIdsUserBelongs() : '';
|
||||
let orgIdsUserBelongs = currUser !== undefined && currUser.teams !== 'undefined' ? currUser.orgIdsUserBelongs() : '';
|
||||
if(orgIdsUserBelongs && orgIdsUserBelongs != ''){
|
||||
let orgsIds = orgIdsUserBelongs.split(',');
|
||||
// for(let i = 0; i < orgsIds.length; i++){
|
||||
@@ -119,7 +171,7 @@ BlazeComponent.extendComponent({
|
||||
query.$and[2].$or.push({'orgs.orgId': {$in : orgsIds}});
|
||||
}
|
||||
|
||||
let teamIdsUserBelongs = currUser.teams !== 'undefined' ? currUser.teamIdsUserBelongs() : '';
|
||||
let teamIdsUserBelongs = currUser !== undefined && currUser.teams !== 'undefined' ? currUser.teamIdsUserBelongs() : '';
|
||||
if(teamIdsUserBelongs && teamIdsUserBelongs != ''){
|
||||
let teamsIds = teamIdsUserBelongs.split(',');
|
||||
// for(let i = 0; i < teamsIds.length; i++){
|
||||
@@ -129,10 +181,17 @@ BlazeComponent.extendComponent({
|
||||
query.$and[2].$or.push({'teams.teamId': {$in : teamsIds}});
|
||||
}
|
||||
}
|
||||
else query.permission = 'public';
|
||||
else if(allowPrivateVisibilityOnly !== undefined && !allowPrivateVisibilityOnly.booleanValue){
|
||||
query = {
|
||||
archived: false,
|
||||
//type: { $in: ['board','template-container'] },
|
||||
type: 'board',
|
||||
permission: 'public',
|
||||
};
|
||||
}
|
||||
|
||||
return Boards.find(query, {
|
||||
//sort: { sort: 1 /* boards default sorting */ },
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
});
|
||||
},
|
||||
isStarred() {
|
||||
@@ -182,6 +241,7 @@ BlazeComponent.extendComponent({
|
||||
this.setError(err.error);
|
||||
} else {
|
||||
Session.set('fromBoard', null);
|
||||
subManager.subscribe('board', res, false);
|
||||
Utils.goBoardId(res);
|
||||
}
|
||||
},
|
||||
@@ -206,6 +266,74 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
});
|
||||
},
|
||||
'click #resetBtn'(event){
|
||||
let allBoards = document.getElementsByClassName("js-board");
|
||||
let currBoard;
|
||||
for(let i=0; i < allBoards.length; i++){
|
||||
currBoard = allBoards[i];
|
||||
currBoard.style.display = "block";
|
||||
}
|
||||
},
|
||||
'click #filterBtn'(event) {
|
||||
event.preventDefault();
|
||||
let selectedTeams = document.querySelectorAll('#jsAllBoardTeams option:checked');
|
||||
let selectedTeamsValues = Array.from(selectedTeams).map(function(elt){return elt.value});
|
||||
let index = selectedTeamsValues.indexOf("-1");
|
||||
if (index > -1) {
|
||||
selectedTeamsValues.splice(index, 1);
|
||||
}
|
||||
|
||||
let selectedOrgs = document.querySelectorAll('#jsAllBoardOrgs option:checked');
|
||||
let selectedOrgsValues = Array.from(selectedOrgs).map(function(elt){return elt.value});
|
||||
index = selectedOrgsValues.indexOf("-1");
|
||||
if (index > -1) {
|
||||
selectedOrgsValues.splice(index, 1);
|
||||
}
|
||||
|
||||
if(selectedTeamsValues.length > 0 || selectedOrgsValues.length > 0){
|
||||
const query = {
|
||||
$and: [
|
||||
{ archived: false },
|
||||
{ type: 'board' },
|
||||
{ $or:[] }
|
||||
]
|
||||
};
|
||||
if(selectedTeamsValues.length > 0)
|
||||
{
|
||||
query.$and[2].$or.push({'teams.teamId': {$in : selectedTeamsValues}});
|
||||
}
|
||||
if(selectedOrgsValues.length > 0)
|
||||
{
|
||||
query.$and[2].$or.push({'orgs.orgId': {$in : selectedOrgsValues}});
|
||||
}
|
||||
|
||||
let filteredBoards = Boards.find(query, {}).fetch();
|
||||
let allBoards = document.getElementsByClassName("js-board");
|
||||
let currBoard;
|
||||
if(filteredBoards.length > 0){
|
||||
let currBoardId;
|
||||
let found;
|
||||
for(let i=0; i < allBoards.length; i++){
|
||||
currBoard = allBoards[i];
|
||||
currBoardId = currBoard.classList[0];
|
||||
found = filteredBoards.find(function(board){
|
||||
return board._id == currBoardId;
|
||||
});
|
||||
|
||||
if(found !== undefined)
|
||||
currBoard.style.display = "block";
|
||||
else
|
||||
currBoard.style.display = "none";
|
||||
}
|
||||
}
|
||||
else{
|
||||
for(let i=0; i < allBoards.length; i++){
|
||||
currBoard = allBoards[i];
|
||||
currBoard.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
@@ -229,3 +229,25 @@ $spaceBetweenTiles = 16px
|
||||
transform: translateY(-50%)
|
||||
right: 10px
|
||||
font-size: 24px
|
||||
|
||||
.AllBoardTeamsOrgs
|
||||
list-style-type: none;
|
||||
overflow: hidden;
|
||||
|
||||
.AllBoardTeams,.AllBoardOrgs,.AllBoardBtns
|
||||
float: left;
|
||||
|
||||
.js-AllBoardOrgs
|
||||
margin-left: 16px;
|
||||
|
||||
.AllBoardTeams
|
||||
margin-left : 16px;
|
||||
|
||||
.AllBoardButtonsContainer
|
||||
margin: 16px;
|
||||
|
||||
#filterBtn,#resetBtn
|
||||
display: inline;
|
||||
|
||||
.js-board
|
||||
display: block;
|
||||
@@ -26,12 +26,25 @@ template(name="attachmentsGalery")
|
||||
if isUploaded
|
||||
if isImage
|
||||
img.attachment-thumbnail-img(src="{{url}}")
|
||||
else if($eq extension 'mp3')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="audio/mpeg")
|
||||
else if($eq extension 'ogg')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="video/ogg")
|
||||
else if($eq extension 'webm')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="video/webm")
|
||||
else if($eq extension 'mp4')
|
||||
video(width="100%" height="100%" controls="true")
|
||||
source(src="{{url}}" type="video/mp4")
|
||||
else
|
||||
span.attachment-thumbnail-ext= extension
|
||||
else
|
||||
+spinner
|
||||
p.attachment-details
|
||||
= name
|
||||
span.file-size ({{fileSize size}} KB)
|
||||
span.attachment-details-actions
|
||||
a.js-download(href="{{url download=true}}")
|
||||
i.fa.fa-download
|
||||
|
||||
@@ -4,7 +4,7 @@ Template.attachmentsGalery.events({
|
||||
'attachmentDelete',
|
||||
function() {
|
||||
Attachments.remove(this._id);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
),
|
||||
// If we let this event bubble, FlowRouter will handle it and empty the page
|
||||
@@ -49,11 +49,14 @@ Template.attachmentsGalery.helpers({
|
||||
isBoardAdmin() {
|
||||
return Meteor.user().isBoardAdmin();
|
||||
},
|
||||
fileSize(size) {
|
||||
return Math.round(size / 1024);
|
||||
},
|
||||
});
|
||||
|
||||
Template.previewAttachedImagePopup.events({
|
||||
'click .js-large-image-clicked'() {
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -65,7 +68,7 @@ Template.cardAttachmentsPopup.events({
|
||||
if (attachment && attachment._id && attachment.isImage()) {
|
||||
card.setCover(attachment._id);
|
||||
}
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -174,7 +177,7 @@ Template.previewClipboardImagePopup.events({
|
||||
|
||||
pastedResults = null;
|
||||
$(document.body).pasteImageReader(() => {});
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -9,7 +9,7 @@
|
||||
margin: 10px 1% 0
|
||||
text-align: center
|
||||
border-radius: 3px
|
||||
overflow: hidden
|
||||
overflow: auto
|
||||
background: darken(white, 7%)
|
||||
min-height: 120px
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ template(name="cardCustomField-checkbox")
|
||||
template(name="cardCustomField-currency")
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-customfield-currency")
|
||||
input(type="text" value=data.value)
|
||||
input(type="text" value=data.value autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
@@ -79,18 +79,22 @@ template(name="cardCustomField-currency")
|
||||
|
||||
template(name="cardCustomField-date")
|
||||
if canModifyCard
|
||||
a.js-edit-date(title="{{showTitle}}" class="{{classes}}")
|
||||
if value
|
||||
div.card-date
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
a.js-edit-date(title="{{showTitle}} {{_ 'predicate-week'}} {{showWeek}}" class="{{classes}}")
|
||||
if value
|
||||
div.card-date
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
div.card-date
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
b
|
||||
| {{showWeek}}
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
if value
|
||||
div.card-date
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
b
|
||||
| {{showWeek}}
|
||||
|
||||
template(name="cardCustomField-dropdown")
|
||||
if canModifyCard
|
||||
|
||||
@@ -3,7 +3,7 @@ import Cards from '/models/cards';
|
||||
|
||||
Template.cardCustomFieldsPopup.helpers({
|
||||
hasCustomField() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const card = Utils.getCurrentCard();
|
||||
const customFieldId = this._id;
|
||||
return card.customFieldIndex(customFieldId) > -1;
|
||||
},
|
||||
@@ -11,7 +11,7 @@ Template.cardCustomFieldsPopup.helpers({
|
||||
|
||||
Template.cardCustomFieldsPopup.events({
|
||||
'click .js-select-field'(event) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const card = Utils.getCurrentCard();
|
||||
const customFieldId = this._id;
|
||||
card.toggleCustomField(customFieldId);
|
||||
event.preventDefault();
|
||||
@@ -31,7 +31,7 @@ const CardCustomField = BlazeComponent.extendComponent({
|
||||
|
||||
onCreated() {
|
||||
const self = this;
|
||||
self.card = Cards.findOne(Session.get('currentCard'));
|
||||
self.card = Utils.getCurrentCard();
|
||||
self.customFieldId = this.data()._id;
|
||||
},
|
||||
|
||||
@@ -149,6 +149,10 @@ CardCustomField.register('cardCustomField');
|
||||
});
|
||||
}
|
||||
|
||||
showWeek() {
|
||||
return this.date.get().week().toString();
|
||||
}
|
||||
|
||||
showDate() {
|
||||
// this will start working once mquandalle:moment
|
||||
// is updated to at least moment.js 2.10.5
|
||||
@@ -190,7 +194,7 @@ CardCustomField.register('cardCustomField');
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
self.card = Cards.findOne(Session.get('currentCard'));
|
||||
self.card = Utils.getCurrentCard();
|
||||
self.customFieldId = this.data()._id;
|
||||
this.data().value && this.date.set(moment(this.data().value));
|
||||
}
|
||||
@@ -267,7 +271,7 @@ CardCustomField.register('cardCustomField');
|
||||
{
|
||||
'submit .js-card-customfield-stringtemplate'(event) {
|
||||
event.preventDefault();
|
||||
const items = this.getItems();
|
||||
const items = this.stringtemplateItems.get();
|
||||
this.card.setCustomField(this.customFieldId, items);
|
||||
},
|
||||
|
||||
@@ -275,9 +279,7 @@ CardCustomField.register('cardCustomField');
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
this.find('button[type=submit]').click();
|
||||
} else if (event.target.value.trim()) {
|
||||
if (event.target.value.trim() || event.metaKey || event.ctrlKey) {
|
||||
const inputLast = this.find('input.last');
|
||||
|
||||
let items = this.getItems();
|
||||
@@ -303,6 +305,9 @@ CardCustomField.register('cardCustomField');
|
||||
|
||||
this.stringtemplateItems.set(items);
|
||||
}
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
this.find('button[type=submit]').click();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
template(name="dateBadge")
|
||||
if canModifyCard
|
||||
a.js-edit-date.card-date(title="{{showTitle}}" class="{{classes}}")
|
||||
a.js-edit-date.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{showWeek}}" class="{{classes}}")
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
b
|
||||
| {{showWeek}}
|
||||
else
|
||||
a.card-date(title="{{showTitle}}" class="{{classes}}")
|
||||
a.card-date(title="{{showTitle}} {{_ 'predicate-week'}} {{showWeek}}" class="{{classes}}")
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
b
|
||||
| {{showWeek}}
|
||||
|
||||
template(name="dateCustomField")
|
||||
a(title="{{showTitle}}" class="{{classes}}")
|
||||
a(title="{{showTitle}} {{_ 'predicate-week'}} {{showWeek}}" class="{{classes}}")
|
||||
time(datetime="{{showISODate}}")
|
||||
| {{showDate}}
|
||||
b
|
||||
| {{showWeek}}
|
||||
@@ -24,7 +24,7 @@ Template.dateBadge.helpers({
|
||||
}
|
||||
|
||||
_deleteDate() {
|
||||
this.card.setReceived(null);
|
||||
this.card.unsetReceived();
|
||||
}
|
||||
}.register('editCardReceivedDatePopup'));
|
||||
|
||||
@@ -50,7 +50,7 @@ Template.dateBadge.helpers({
|
||||
}
|
||||
|
||||
_deleteDate() {
|
||||
this.card.setStart(null);
|
||||
this.card.unsetStart();
|
||||
}
|
||||
}.register('editCardStartDatePopup'));
|
||||
|
||||
@@ -73,7 +73,7 @@ Template.dateBadge.helpers({
|
||||
}
|
||||
|
||||
_deleteDate() {
|
||||
this.card.setDue(null);
|
||||
this.card.unsetDue();
|
||||
}
|
||||
}.register('editCardDueDatePopup'));
|
||||
|
||||
@@ -96,7 +96,7 @@ Template.dateBadge.helpers({
|
||||
}
|
||||
|
||||
_deleteDate() {
|
||||
this.card.setEnd(null);
|
||||
this.card.unsetEnd();
|
||||
}
|
||||
}.register('editCardEndDatePopup'));
|
||||
|
||||
@@ -115,6 +115,10 @@ const CardDate = BlazeComponent.extendComponent({
|
||||
}, 60000);
|
||||
},
|
||||
|
||||
showWeek() {
|
||||
return this.date.get().week().toString();
|
||||
},
|
||||
|
||||
showDate() {
|
||||
// this will start working once mquandalle:moment
|
||||
// is updated to at least moment.js 2.10.5
|
||||
@@ -284,12 +288,25 @@ class CardCustomFieldDate extends CardDate {
|
||||
});
|
||||
}
|
||||
|
||||
classes() {
|
||||
return 'customfield-date';
|
||||
showWeek() {
|
||||
return this.date.get().week().toString();
|
||||
}
|
||||
|
||||
showDate() {
|
||||
// this will start working once mquandalle:moment
|
||||
// is updated to at least moment.js 2.10.5
|
||||
// until then, the date is displayed in the "L" format
|
||||
return this.date.get().calendar(null, {
|
||||
sameElse: 'llll',
|
||||
});
|
||||
}
|
||||
|
||||
showTitle() {
|
||||
return '';
|
||||
return `${this.date.get().format('LLLL')}`;
|
||||
}
|
||||
|
||||
classes() {
|
||||
return 'customfield-date';
|
||||
}
|
||||
|
||||
events() {
|
||||
|
||||
@@ -25,7 +25,10 @@ BlazeComponent.extendComponent({
|
||||
// Pressing Ctrl+Enter should submit the form
|
||||
'keydown form textarea'(evt) {
|
||||
if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
|
||||
this.find('button[type=submit]').click();
|
||||
const submitButton = this.find('button[type=submit]');
|
||||
if (submitButton) {
|
||||
submitButton.click();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
template(name="cardDetailsPopup")
|
||||
+cardDetails(popupCard)
|
||||
|
||||
template(name="cardDetails")
|
||||
section.card-details.js-card-details(class='{{#if cardMaximized}}card-details-maximized{{/if}}'): .card-details-canvas
|
||||
section.card-details.js-card-details(class='{{#if cardMaximized}}card-details-maximized{{/if}}' class='{{#if isPopup}}card-details-popup{{/if}}'): .card-details-canvas
|
||||
.card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}')
|
||||
+inlinedForm(classNames="js-card-details-title")
|
||||
+editCardTitleForm
|
||||
else
|
||||
unless isMiniScreen
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
|
||||
unless cardMaximized
|
||||
a.fa.fa-window-maximize.maximize-card-details.js-maximize-card-details(title="{{_ 'maximize-card'}}")
|
||||
if cardMaximized
|
||||
a.fa.fa-window-minimize.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
|
||||
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
|
||||
a.fa.fa-navicon.card-details-menu.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
input.inline-input(type="text" id="cardURL_copy" value="{{ originRelativeUrl }}")
|
||||
a.fa.fa-link.card-copy-button.js-copy-link(
|
||||
id="cardURL_copy"
|
||||
class="fa-link"
|
||||
title="{{_ 'copy-card-link-to-clipboard'}}"
|
||||
href="{{ originRelativeUrl }}"
|
||||
)
|
||||
if isMiniScreen
|
||||
a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details(title="{{_ 'close-card'}}")
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
else
|
||||
unless isPopup
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
|
||||
if currentUser.isBoardMember
|
||||
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
|
||||
a.fa.fa-link.card-copy-mobile-button.js-copy-link(
|
||||
id="cardURL_copy"
|
||||
class="fa-link"
|
||||
title="{{_ 'copy-card-link-to-clipboard'}}"
|
||||
href="{{ originRelativeUrl }}"
|
||||
)
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
h2.card-details-title.js-card-title(
|
||||
class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
|
||||
+viewer
|
||||
@@ -66,8 +79,10 @@ template(name="cardDetails")
|
||||
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
if currentBoard.allowsReceivedDate
|
||||
if currentBoard.hasAnyAllowsDate
|
||||
hr
|
||||
|
||||
if currentBoard.allowsReceivedDate
|
||||
.card-details-item.card-details-item-received
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-sign-out
|
||||
@@ -119,7 +134,9 @@ template(name="cardDetails")
|
||||
a.card-label.add-label.js-end-date
|
||||
i.fa.fa-plus
|
||||
|
||||
hr
|
||||
if currentBoard.hasAnyAllowsUser
|
||||
hr
|
||||
|
||||
if currentBoard.allowsCreator
|
||||
.card-details-item.card-details-item-creator
|
||||
h3.card-details-item-title
|
||||
@@ -160,17 +177,6 @@ template(name="cardDetails")
|
||||
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
//.card-details-items
|
||||
if getSpentTime
|
||||
.card-details-item.card-details-item-spent
|
||||
if getIsOvertime
|
||||
h3.card-details-item-title
|
||||
| {{_ 'overtime-hours'}}
|
||||
else
|
||||
h3.card-details-item-title
|
||||
| {{_ 'spent-time-hours'}}
|
||||
+cardSpentTime
|
||||
|
||||
//.card-details-items
|
||||
if currentBoard.allowsRequestedBy
|
||||
.card-details-item.card-details-item-name
|
||||
@@ -212,6 +218,9 @@ template(name="cardDetails")
|
||||
+viewer
|
||||
= getAssignedBy
|
||||
|
||||
if $or currentBoard.allowsCardSortingByNumber getSpentTime
|
||||
hr
|
||||
|
||||
if currentBoard.allowsCardSortingByNumber
|
||||
.card-details-item.card-details-sort-order
|
||||
h3.card-details-item-title
|
||||
@@ -225,15 +234,45 @@ template(name="cardDetails")
|
||||
+viewer
|
||||
= sort
|
||||
|
||||
if currentBoard.allowsShowLists
|
||||
.card-details-item.card-details-show-lists
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-list
|
||||
| {{_ 'list'}}
|
||||
select.js-select-card-details-lists(disabled="{{#unless canModifyCard}}disabled{{/unless}}")
|
||||
each currentBoard.lists
|
||||
option(value="{{_id}}" selected="{{#if isCurrentListId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
//.card-details-items
|
||||
if getSpentTime
|
||||
.card-details-item.card-details-item-spent
|
||||
if getIsOvertime
|
||||
h3.card-details-item-title
|
||||
| {{_ 'overtime-hours'}}
|
||||
else
|
||||
h3.card-details-item-title
|
||||
| {{_ 'spent-time-hours'}}
|
||||
+cardSpentTime
|
||||
|
||||
//.card-details-items
|
||||
if customFieldsWD
|
||||
hr
|
||||
unless customFieldsGrid
|
||||
hr
|
||||
each customFieldsWD
|
||||
if customFieldsGrid
|
||||
hr
|
||||
.card-details-item.card-details-item-customfield
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-list-alt
|
||||
= definition.name
|
||||
+cardCustomField
|
||||
.material-toggle-switch(title="{{_ 'change'}} {{_ 'custom-fields'}} {{_ 'layout'}}")
|
||||
if customFieldsGrid
|
||||
input.toggle-switch(type="checkbox" id="toggleCustomFieldsGridButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleCustomFieldsGridButton")
|
||||
label.toggle-label(for="toggleCustomFieldsGridButton")
|
||||
a.fa.fa-plus.js-custom-fields.card-details-item.custom-fields(title="{{_ 'custom-fields'}}")
|
||||
|
||||
if getVoteQuestion
|
||||
hr
|
||||
@@ -519,6 +558,7 @@ template(name="cardDetails")
|
||||
.card-details-right
|
||||
|
||||
unless currentUser.isNoComments
|
||||
hr
|
||||
.activity-title
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-history
|
||||
@@ -545,6 +585,8 @@ template(name="cardDetails")
|
||||
+activities(card=this mode="card")
|
||||
|
||||
template(name="editCardTitleForm")
|
||||
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
textarea.js-edit-card-title(rows='1' autofocus dir="auto")
|
||||
= getTitle
|
||||
.edit-controls.clearfix
|
||||
@@ -692,24 +734,25 @@ template(name="boardsAndLists")
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
option(value="{{_id}}") {{title}}
|
||||
option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionBoardId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'swimlanes'}}:
|
||||
select.js-select-swimlanes
|
||||
each swimlanes
|
||||
option(value="{{_id}}") {{title}}
|
||||
option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'lists'}}:
|
||||
select.js-select-lists
|
||||
each aBoardLists
|
||||
option(value="{{_id}}") {{title}}
|
||||
option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionListId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
|
||||
template(name="cardMembersPopup")
|
||||
input.card-members-filter(type="text" placeholder="{{_ 'search'}}")
|
||||
ul.pop-over-list.js-card-member-list
|
||||
each board.activeMembers
|
||||
each members
|
||||
li.item(class="{{#if isCardMember}}active{{/if}}")
|
||||
a.name.js-select-member(href="#")
|
||||
+userAvatar(userId=user._id)
|
||||
@@ -720,9 +763,10 @@ template(name="cardMembersPopup")
|
||||
i.fa.fa-check
|
||||
|
||||
template(name="cardAssigneesPopup")
|
||||
input.card-assignees-filter(type="text" placeholder="{{_ 'search'}}")
|
||||
unless currentUser.isWorker
|
||||
ul.pop-over-list.js-card-assignee-list
|
||||
each board.activeMembers
|
||||
each members
|
||||
li.item(class="{{#if isCardAssignee}}active{{/if}}")
|
||||
a.name.js-select-assignee(href="#")
|
||||
+userAvatar(userId=user._id)
|
||||
@@ -767,6 +811,7 @@ template(name="cardMorePopup")
|
||||
i.fa.colorful(class="{{#if board.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
|
||||
input.inline-input(type="text" id="cardURL" readonly value="{{ originRelativeUrl }}" autofocus="autofocus")
|
||||
button.js-copy-card-link-to-clipboard(class="btn" id="clipboard") {{_ 'copy-card-link-to-clipboard'}}
|
||||
.copied-tooltip {{_ 'copied'}}
|
||||
span.clearfix
|
||||
br
|
||||
h2 {{_ 'change-card-parent'}}
|
||||
@@ -815,6 +860,12 @@ template(name="cardDeletePopup")
|
||||
p {{_ "card-delete-suggest-archive"}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
||||
template(name="cardArchivePopup")
|
||||
p {{_ "card-archive-pop"}}
|
||||
unless archived
|
||||
p {{_ "card-archive-suggest-cancel"}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
|
||||
|
||||
template(name="deleteVotePopup")
|
||||
p {{_ "vote-delete-pop"}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
||||
@@ -34,15 +34,25 @@ BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
this.isLoaded = new ReactiveVar(false);
|
||||
const boardBody = this.parentComponent().parentComponent();
|
||||
//in Miniview parent is Board, not BoardBody.
|
||||
if (boardBody !== null) {
|
||||
boardBody.showOverlay.set(true);
|
||||
boardBody.mouseHasEnterCardDetails = false;
|
||||
|
||||
if (this.parentComponent() && this.parentComponent().parentComponent()) {
|
||||
const boardBody = this.parentComponent().parentComponent();
|
||||
//in Miniview parent is Board, not BoardBody.
|
||||
if (boardBody !== null) {
|
||||
boardBody.showOverlay.set(true);
|
||||
boardBody.mouseHasEnterCardDetails = false;
|
||||
}
|
||||
}
|
||||
this.calculateNextPeak();
|
||||
|
||||
Meteor.subscribe('unsaved-edits');
|
||||
|
||||
// this.findUsersOptions = new ReactiveVar({});
|
||||
// this.page = new ReactiveVar(1);
|
||||
// this.autorun(() => {
|
||||
// const limitUsers = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
// this.subscribe('people', this.findUsersOptions.get(), limitUsers, () => {});
|
||||
// });
|
||||
},
|
||||
|
||||
isWatching() {
|
||||
@@ -54,8 +64,13 @@ BlazeComponent.extendComponent({
|
||||
return Meteor.user().hasHiddenSystemMessages();
|
||||
},
|
||||
|
||||
customFieldsGrid() {
|
||||
return Meteor.user().hasCustomFieldsGrid();
|
||||
},
|
||||
|
||||
|
||||
cardMaximized() {
|
||||
return Meteor.user().hasCardMaximized();
|
||||
return !Utils.getPopupCardId() && Meteor.user().hasCardMaximized();
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
@@ -153,6 +168,15 @@ BlazeComponent.extendComponent({
|
||||
);
|
||||
},
|
||||
|
||||
/** returns if the list id is the current list id
|
||||
* @param listId list id to check
|
||||
* @return is the list id the current list id ?
|
||||
*/
|
||||
isCurrentListId(listId) {
|
||||
const ret = this.data().listId == listId;
|
||||
return ret;
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
if (Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED) {
|
||||
// Send Webhook but not create Activities records ---
|
||||
@@ -180,7 +204,7 @@ BlazeComponent.extendComponent({
|
||||
integration,
|
||||
'CardSelected',
|
||||
params,
|
||||
() => {},
|
||||
() => { },
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -203,7 +227,7 @@ BlazeComponent.extendComponent({
|
||||
distance: 7,
|
||||
start(evt, ui) {
|
||||
ui.placeholder.height(ui.helper.height());
|
||||
EscapeActions.executeUpTo('popup-close');
|
||||
EscapeActions.clickExecute(evt.target, 'inlinedForm');
|
||||
},
|
||||
stop(evt, ui) {
|
||||
let prevChecklist = ui.item.prev('.js-checklist').get(0);
|
||||
@@ -285,6 +309,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
onDestroyed() {
|
||||
if (this.parentComponent() === null) return;
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not board body.
|
||||
if (parentComponent === null) return;
|
||||
@@ -307,30 +332,12 @@ BlazeComponent.extendComponent({
|
||||
'click .js-close-card-details'() {
|
||||
Utils.goBoardId(this.data().boardId);
|
||||
},
|
||||
'click .js-copy-link'() {
|
||||
const StringToCopyElement = document.getElementById('cardURL_copy');
|
||||
StringToCopyElement.value =
|
||||
window.location.origin + window.location.pathname;
|
||||
StringToCopyElement.select();
|
||||
if (document.execCommand('copy')) {
|
||||
StringToCopyElement.blur();
|
||||
} else {
|
||||
document.getElementById('cardURL_copy').selectionStart = 0;
|
||||
document.getElementById('cardURL_copy').selectionEnd = 999;
|
||||
document.execCommand('copy');
|
||||
if (window.getSelection) {
|
||||
if (window.getSelection().empty) {
|
||||
// Chrome
|
||||
window.getSelection().empty();
|
||||
} else if (window.getSelection().removeAllRanges) {
|
||||
// Firefox
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
} else if (document.selection) {
|
||||
// IE?
|
||||
document.selection.empty();
|
||||
}
|
||||
}
|
||||
'click .js-copy-link'(event) {
|
||||
event.preventDefault();
|
||||
const promise = Utils.copyTextToClipboard(event.target.href);
|
||||
|
||||
const $tooltip = this.$('.card-details-header .copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
|
||||
'submit .js-card-description'(event) {
|
||||
@@ -365,6 +372,12 @@ BlazeComponent.extendComponent({
|
||||
this.data().setRequestedBy('');
|
||||
}
|
||||
},
|
||||
'keydown input.js-edit-card-sort'(evt) {
|
||||
// enter = save
|
||||
if (evt.keyCode === 13) {
|
||||
this.find('button[type=submit]').click();
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-sort'(event) {
|
||||
event.preventDefault();
|
||||
const sort = parseFloat(this.currentComponent()
|
||||
@@ -375,6 +388,12 @@ BlazeComponent.extendComponent({
|
||||
card.move(card.boardId, card.swimlaneId, card.listId, sort);
|
||||
}
|
||||
},
|
||||
'change .js-select-card-details-lists'(event) {
|
||||
let card = this.data();
|
||||
const listSelect = this.$('.js-select-card-details-lists')[0];
|
||||
const listId = listSelect.options[listSelect.selectedIndex].value;
|
||||
card.move(card.boardId, card.swimlaneId, listId, card.sort);
|
||||
},
|
||||
'click .js-go-to-linked-card'() {
|
||||
Utils.goCardId(this.data().linkedId);
|
||||
},
|
||||
@@ -389,7 +408,9 @@ BlazeComponent.extendComponent({
|
||||
'click .js-end-date': Popup.open('editCardEndDate'),
|
||||
'click .js-show-positive-votes': Popup.open('positiveVoteMembers'),
|
||||
'click .js-show-negative-votes': Popup.open('negativeVoteMembers'),
|
||||
'click .js-custom-fields': Popup.open('cardCustomFields'),
|
||||
'mouseenter .js-card-details'() {
|
||||
if (this.parentComponent() === null) return;
|
||||
const parentComponent = this.parentComponent().parentComponent();
|
||||
//on mobile view parent is Board, not BoardBody.
|
||||
if (parentComponent === null) return;
|
||||
@@ -412,6 +433,9 @@ BlazeComponent.extendComponent({
|
||||
'click #toggleButton'() {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
},
|
||||
'click #toggleCustomFieldsGridButton'() {
|
||||
Meteor.call('toggleCustomFieldsGrid');
|
||||
},
|
||||
'click .js-maximize-card-details'() {
|
||||
Meteor.call('toggleCardMaximized');
|
||||
autosize($('.card-details'));
|
||||
@@ -511,6 +535,23 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('cardDetails');
|
||||
|
||||
Template.cardDetails.helpers({
|
||||
isPopup() {
|
||||
let ret = !!Utils.getPopupCardId();
|
||||
return ret;
|
||||
}
|
||||
});
|
||||
Template.cardDetailsPopup.onDestroyed(() => {
|
||||
Session.delete('popupCardId');
|
||||
Session.delete('popupCardBoardId');
|
||||
});
|
||||
Template.cardDetailsPopup.helpers({
|
||||
popupCard() {
|
||||
const ret = Utils.getPopupCard();
|
||||
return ret;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
template() {
|
||||
return 'exportCard';
|
||||
@@ -541,8 +582,8 @@ BlazeComponent.extendComponent({
|
||||
}).register('exportCardPopup');
|
||||
|
||||
// only allow number input
|
||||
Template.editCardSortOrderForm.onRendered(function() {
|
||||
this.$('input').on("keypress paste", function(event) {
|
||||
Template.editCardSortOrderForm.onRendered(function () {
|
||||
this.$('input').on("keypress paste", function (event) {
|
||||
let keyCode = event.keyCode;
|
||||
let charCode = String.fromCharCode(keyCode);
|
||||
let regex = new RegExp('[-0-9.]');
|
||||
@@ -561,16 +602,15 @@ Template.editCardSortOrderForm.onRendered(function() {
|
||||
// XXX Recovering the currentCard identifier form a session variable is
|
||||
// fragile because this variable may change for instance if the route
|
||||
// change. We should use some component props instead.
|
||||
docId: Session.get('currentCard'),
|
||||
docId: Utils.getCurrentCardId(),
|
||||
};
|
||||
}
|
||||
|
||||
close(isReset = false) {
|
||||
if (this.isOpen.get() && !isReset) {
|
||||
const draft = this.getValue().trim();
|
||||
if (
|
||||
draft !== Cards.findOne(Session.get('currentCard')).getDescription()
|
||||
) {
|
||||
let card = Utils.getCurrentCard();
|
||||
if (card && draft !== card.getDescription()) {
|
||||
UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue());
|
||||
}
|
||||
}
|
||||
@@ -615,7 +655,6 @@ Template.cardDetailsActionsPopup.events({
|
||||
'click .js-export-card': Popup.open('exportCard'),
|
||||
'click .js-members': Popup.open('cardMembers'),
|
||||
'click .js-assignees': Popup.open('cardAssignees'),
|
||||
'click .js-labels': Popup.open('cardLabels'),
|
||||
'click .js-attachments': Popup.open('cardAttachments'),
|
||||
'click .js-start-voting': Popup.open('cardStartVoting'),
|
||||
'click .js-start-planning-poker': Popup.open('cardStartPlanningPoker'),
|
||||
@@ -634,25 +673,27 @@ Template.cardDetailsActionsPopup.events({
|
||||
event.preventDefault();
|
||||
const minOrder = _.min(
|
||||
this.list()
|
||||
.cards(this.swimlaneId)
|
||||
.cardsUnfiltered(this.swimlaneId)
|
||||
.map((c) => c.sort),
|
||||
);
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-move-card-to-bottom'(event) {
|
||||
event.preventDefault();
|
||||
const maxOrder = _.max(
|
||||
this.list()
|
||||
.cards(this.swimlaneId)
|
||||
.cardsUnfiltered(this.swimlaneId)
|
||||
.map((c) => c.sort),
|
||||
);
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-archive'(event) {
|
||||
event.preventDefault();
|
||||
this.archive();
|
||||
'click .js-archive': Popup.afterConfirm('cardArchive', function () {
|
||||
Popup.close();
|
||||
},
|
||||
this.archive();
|
||||
Utils.goBoardId(this.boardId);
|
||||
}),
|
||||
'click .js-more': Popup.open('cardMore'),
|
||||
'click .js-toggle-watch-card'() {
|
||||
const currentCard = this;
|
||||
@@ -663,20 +704,90 @@ Template.cardDetailsActionsPopup.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardTitleForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-title'));
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
autosize(this.$('textarea.js-edit-card-title'));
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click a.fa.fa-copy'(event) {
|
||||
const $editor = this.$('textarea');
|
||||
const promise = Utils.copyTextToClipboard($editor[0].value);
|
||||
|
||||
const $tooltip = this.$('.copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
'keydown .js-edit-card-title'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
// Unless the shift key is also being pressed
|
||||
if (event.keyCode === 13 && !event.shiftKey) {
|
||||
$('.js-submit-edit-card-title-form').click();
|
||||
}
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
}).register('editCardTitleForm');
|
||||
|
||||
Template.cardMembersPopup.onCreated(function () {
|
||||
let currBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let members = currBoard.activeMembers();
|
||||
|
||||
// let query = {
|
||||
// "teams.teamId": { $in: currBoard.teams.map(t => t.teamId) },
|
||||
// };
|
||||
|
||||
// let boardTeamUsers = Users.find(query, {
|
||||
// sort: { sort: 1 },
|
||||
// });
|
||||
|
||||
// members = currBoard.activeMembers2(members, boardTeamUsers);
|
||||
|
||||
this.members = new ReactiveVar(members);
|
||||
});
|
||||
|
||||
Template.editCardTitleForm.events({
|
||||
'keydown .js-edit-card-title'(event) {
|
||||
// If enter key was pressed, submit the data
|
||||
// Unless the shift key is also being pressed
|
||||
if (event.keyCode === 13 && !event.shiftKey) {
|
||||
$('.js-submit-edit-card-title-form').click();
|
||||
}
|
||||
Template.cardMembersPopup.events({
|
||||
'keyup .card-members-filter'(event) {
|
||||
const members = filterMembers(event.target.value);
|
||||
Template.instance().members.set(members);
|
||||
}
|
||||
});
|
||||
|
||||
Template.cardMembersPopup.helpers({
|
||||
members() {
|
||||
return Template.instance().members.get();
|
||||
},
|
||||
});
|
||||
|
||||
const filterMembers = (filterTerm) => {
|
||||
let currBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let members = currBoard.activeMembers();
|
||||
|
||||
// let query = {
|
||||
// "teams.teamId": { $in: currBoard.teams.map(t => t.teamId) },
|
||||
// };
|
||||
|
||||
// let boardTeamUsers = Users.find(query, {
|
||||
// sort: { sort: 1 },
|
||||
// });
|
||||
|
||||
// members = currBoard.activeMembers2(members, boardTeamUsers);
|
||||
|
||||
if (filterTerm) {
|
||||
members = members
|
||||
.map(member => ({
|
||||
member,
|
||||
user: Users.findOne(member.userId)
|
||||
}))
|
||||
.filter(({ user }) =>
|
||||
(user.profile.fullname !== undefined && user.profile.fullname.toLowerCase().indexOf(filterTerm.toLowerCase()) !== -1)
|
||||
|| user.profile.fullname === undefined && user.profile.username !== undefined && user.profile.username.toLowerCase().indexOf(filterTerm.toLowerCase()) !== -1)
|
||||
.map(({ member }) => member);
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
Template.editCardRequesterForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-requester'));
|
||||
});
|
||||
@@ -705,27 +816,82 @@ Template.editCardAssignerForm.events({
|
||||
|
||||
Template.moveCardPopup.events({
|
||||
'click .js-done'() {
|
||||
// XXX We should *not* get the currentCard from the global state, but
|
||||
// instead from a “component” state.
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const card = Cards.findOne(this._id);
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
let boardId;
|
||||
// if we are a worker, we won't have a board select so we just use the
|
||||
// current boardId of the card.
|
||||
if (bSelect) boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
else boardId = card.boardId;
|
||||
if (bSelect) {
|
||||
boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
} else {
|
||||
boardId = card.boardId;
|
||||
}
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
const listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||
card.move(boardId, swimlaneId, listId, 0);
|
||||
Popup.close();
|
||||
|
||||
// set new id's to card object in case the card is moved to top by the comment "moveCard" after this command (.js-move-card)
|
||||
this.boardId = boardId;
|
||||
this.swimlaneId = swimlaneId;
|
||||
this.listId = listId;
|
||||
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
subManager.subscribe('board', Session.get('currentBoard'), false);
|
||||
this.selectedBoardId = new ReactiveVar(Session.get('currentBoard'));
|
||||
this.currentBoardId = Utils.getCurrentBoardId();
|
||||
this.selectedBoardId = new ReactiveVar(this.currentBoardId);
|
||||
this.setMoveAndCopyDialogOption(this.currentBoardId);
|
||||
},
|
||||
|
||||
/** set the last confirmed dialog field values
|
||||
* @param boardId the current board id
|
||||
*/
|
||||
setMoveAndCopyDialogOption(boardId) {
|
||||
this.moveAndCopyDialogOption = {
|
||||
'boardId' : "",
|
||||
'swimlaneId' : "",
|
||||
'listId' : "",
|
||||
}
|
||||
|
||||
let currentOptions = Meteor.user().getMoveAndCopyDialogOptions();
|
||||
if (currentOptions && boardId && currentOptions[boardId]) {
|
||||
this.moveAndCopyDialogOption = currentOptions[boardId];
|
||||
if (this.moveAndCopyDialogOption.boardId) {
|
||||
this.selectedBoardId.set(this.moveAndCopyDialogOption.boardId)
|
||||
}
|
||||
}
|
||||
subManager.subscribe('board', this.selectedBoardId.get(), false);
|
||||
},
|
||||
|
||||
/** returns if the board id was the last confirmed one
|
||||
* @param boardId check this board id
|
||||
* @return if the board id was the last confirmed one
|
||||
*/
|
||||
isMoveAndCopyDialogOptionBoardId(boardId) {
|
||||
let ret = this.moveAndCopyDialogOption.boardId == boardId;
|
||||
return ret;
|
||||
},
|
||||
|
||||
/** returns if the swimlane id was the last confirmed one
|
||||
* @param swimlaneId check this swimlane id
|
||||
* @return if the swimlane id was the last confirmed one
|
||||
*/
|
||||
isMoveAndCopyDialogOptionSwimlaneId(swimlaneId) {
|
||||
let ret = this.moveAndCopyDialogOption.swimlaneId == swimlaneId;
|
||||
return ret;
|
||||
},
|
||||
|
||||
/** returns if the list id was the last confirmed one
|
||||
* @param listId check this list id
|
||||
* @return if the list id was the last confirmed one
|
||||
*/
|
||||
isMoveAndCopyDialogOptionListId(listId) {
|
||||
let ret = this.moveAndCopyDialogOption.listId == listId;
|
||||
return ret;
|
||||
},
|
||||
|
||||
boards() {
|
||||
@@ -754,9 +920,27 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-done'() {
|
||||
const boardSelect = this.$('.js-select-boards')[0];
|
||||
const boardId = boardSelect.options[boardSelect.selectedIndex].value;
|
||||
|
||||
const listSelect = this.$('.js-select-lists')[0];
|
||||
const listId = listSelect.options[listSelect.selectedIndex].value;
|
||||
|
||||
const swimlaneSelect = this.$('.js-select-swimlanes')[0];
|
||||
const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
|
||||
|
||||
const options = {
|
||||
'boardId' : boardId,
|
||||
'swimlaneId' : swimlaneId,
|
||||
'listId' : listId,
|
||||
}
|
||||
Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
|
||||
},
|
||||
'change .js-select-boards'(event) {
|
||||
this.selectedBoardId.set($(event.currentTarget).val());
|
||||
subManager.subscribe('board', this.selectedBoardId.get(), false);
|
||||
const boardId = $(event.currentTarget).val();
|
||||
this.selectedBoardId.set(boardId);
|
||||
subManager.subscribe('board', boardId, false);
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -765,7 +949,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Template.copyCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const card = Utils.getCurrentCard();
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
const listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
@@ -787,14 +971,14 @@ Template.copyCardPopup.events({
|
||||
// See https://github.com/wekan/wekan/issues/80
|
||||
Filter.addException(_id);
|
||||
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.convertChecklistItemToCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const card = Utils.getCurrentCard();
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
const listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
@@ -814,7 +998,7 @@ Template.convertChecklistItemToCardPopup.events({
|
||||
});
|
||||
Filter.addException(_id);
|
||||
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
|
||||
}
|
||||
},
|
||||
@@ -822,7 +1006,7 @@ Template.convertChecklistItemToCardPopup.events({
|
||||
|
||||
Template.copyChecklistToManyCardsPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const card = Utils.getCurrentCard();
|
||||
const oldId = card._id;
|
||||
card._id = null;
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
@@ -870,7 +1054,7 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
cmt.copy(_id);
|
||||
});
|
||||
}
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -941,7 +1125,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
cards() {
|
||||
const currentId = Session.get('currentCard');
|
||||
const currentId = Utils.getCurrentCardId();
|
||||
if (this.parentBoard.get()) {
|
||||
return Cards.find({
|
||||
boardId: this.parentBoard.get(),
|
||||
@@ -980,30 +1164,11 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-copy-card-link-to-clipboard'() {
|
||||
// Clipboard code from:
|
||||
// https://stackoverflow.com/questions/6300213/copy-selected-text-to-the-clipboard-without-using-flash-must-be-cross-browser
|
||||
const StringToCopyElement = document.getElementById('cardURL');
|
||||
StringToCopyElement.select();
|
||||
if (document.execCommand('copy')) {
|
||||
StringToCopyElement.blur();
|
||||
} else {
|
||||
document.getElementById('cardURL').selectionStart = 0;
|
||||
document.getElementById('cardURL').selectionEnd = 999;
|
||||
document.execCommand('copy');
|
||||
if (window.getSelection) {
|
||||
if (window.getSelection().empty) {
|
||||
// Chrome
|
||||
window.getSelection().empty();
|
||||
} else if (window.getSelection().removeAllRanges) {
|
||||
// Firefox
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
} else if (document.selection) {
|
||||
// IE?
|
||||
document.selection.empty();
|
||||
}
|
||||
}
|
||||
'click .js-copy-card-link-to-clipboard'(event) {
|
||||
const promise = Utils.copyTextToClipboard(location.origin + document.getElementById('cardURL').value);
|
||||
|
||||
const $tooltip = this.$('.copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function () {
|
||||
Popup.close();
|
||||
@@ -1019,9 +1184,8 @@ BlazeComponent.extendComponent({
|
||||
// https://github.com/wekan/wekan/issues/2785
|
||||
const message = `${TAPi18n.__(
|
||||
'delete-linked-card-before-this-card',
|
||||
)} linkedId: ${
|
||||
this._id
|
||||
} at client/components/cards/cardDetails.js and https://github.com/wekan/wekan/issues/2785`;
|
||||
)} linkedId: ${this._id
|
||||
} at client/components/cards/cardDetails.js and https://github.com/wekan/wekan/issues/2785`;
|
||||
alert(message);
|
||||
}
|
||||
Utils.goBoardId(this.boardId);
|
||||
@@ -1074,12 +1238,12 @@ BlazeComponent.extendComponent({
|
||||
if (endString) {
|
||||
this.currentCard.setVoteEnd(endString);
|
||||
}
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-remove-vote': Popup.afterConfirm('deleteVote', () => {
|
||||
event.preventDefault();
|
||||
this.currentCard.unsetVote();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}),
|
||||
'click a.js-toggle-vote-public'(event) {
|
||||
event.preventDefault();
|
||||
@@ -1119,7 +1283,7 @@ BlazeComponent.extendComponent({
|
||||
// if active vote - store it
|
||||
if (this.currentData().getVoteQuestion()) {
|
||||
this._storeDate(newDate.toDate());
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
} else {
|
||||
this.currentData().vote = { end: newDate.toDate() }; // set vote end temp
|
||||
Popup.back();
|
||||
@@ -1153,86 +1317,77 @@ BlazeComponent.extendComponent({
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(usaDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: usaDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (euroAmDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(euroAmDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: euroAmDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (euro24hDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(euro24hDate.toDate());
|
||||
this.card.setPokerEnd(euro24hDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: euro24hDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (eurodotDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(eurodotDate.toDate());
|
||||
this.card.setPokerEnd(eurodotDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: eurodotDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (minusDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(minusDate.toDate());
|
||||
this.card.setPokerEnd(minusDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: minusDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (slashDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(slashDate.toDate());
|
||||
this.card.setPokerEnd(slashDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: slashDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (dotDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(dotDate.toDate());
|
||||
this.card.setPokerEnd(dotDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: dotDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (brezhonegDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(brezhonegDate.toDate());
|
||||
this.card.setPokerEnd(brezhonegDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: brezhonegDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (hrvatskiDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(hrvatskiDate.toDate());
|
||||
this.card.setPokerEnd(hrvatskiDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: hrvatskiDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
@@ -1242,41 +1397,37 @@ BlazeComponent.extendComponent({
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(latviaDate.toDate());
|
||||
this.card.setPokerEnd(latviaDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: latviaDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (nederlandsDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(nederlandsDate.toDate());
|
||||
this.card.setPokerEnd(nederlandsDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: nederlandsDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (greekDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(greekDate.toDate());
|
||||
this.card.setPokerEnd(greekDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: greekDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (macedonianDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(macedonianDate.toDate());
|
||||
this.card.setPokerEnd(macedonianDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: macedonianDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else {
|
||||
this.error.set('invalid-date');
|
||||
evt.target.date.focus();
|
||||
@@ -1285,7 +1436,7 @@ BlazeComponent.extendComponent({
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
this._deleteDate();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1323,11 +1474,11 @@ BlazeComponent.extendComponent({
|
||||
if (endString) {
|
||||
this.currentCard.setPokerEnd(endString);
|
||||
}
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-remove-poker': Popup.afterConfirm('deletePoker', (event) => {
|
||||
this.currentCard.unsetPoker();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}),
|
||||
'click a.js-toggle-poker-allow-non-members'(event) {
|
||||
event.preventDefault();
|
||||
@@ -1390,7 +1541,7 @@ BlazeComponent.extendComponent({
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(newDate.toDate());
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
} else {
|
||||
this.currentData().poker = { end: newDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
@@ -1422,130 +1573,117 @@ BlazeComponent.extendComponent({
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(usaDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: usaDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (euroAmDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(euroAmDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: euroAmDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (euro24hDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(euro24hDate.toDate());
|
||||
this.card.setPokerEnd(euro24hDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: euro24hDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (eurodotDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(eurodotDate.toDate());
|
||||
this.card.setPokerEnd(eurodotDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: eurodotDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (minusDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(minusDate.toDate());
|
||||
this.card.setPokerEnd(minusDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: minusDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (slashDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(slashDate.toDate());
|
||||
this.card.setPokerEnd(slashDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: slashDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (dotDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(dotDate.toDate());
|
||||
this.card.setPokerEnd(dotDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: dotDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (brezhonegDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(brezhonegDate.toDate());
|
||||
this.card.setPokerEnd(brezhonegDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: brezhonegDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (hrvatskiDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(hrvatskiDate.toDate());
|
||||
this.card.setPokerEnd(hrvatskiDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: hrvatskiDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (latviaDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(latviaDate.toDate());
|
||||
this.card.setPokerEnd(latviaDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: latviaDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (nederlandsDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(nederlandsDate.toDate());
|
||||
this.card.setPokerEnd(nederlandsDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: nederlandsDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (greekDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(greekDate.toDate());
|
||||
this.card.setPokerEnd(greekDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: greekDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else if (macedonianDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(macedonianDate.toDate());
|
||||
this.card.setPokerEnd(macedonianDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: macedonianDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
Popup.back();
|
||||
} else {
|
||||
// this.error.set('invalid-date);
|
||||
this.error.set('invalid-date' + ' ' + dateString);
|
||||
@@ -1555,7 +1693,7 @@ BlazeComponent.extendComponent({
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
this._deleteDate();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1589,13 +1727,34 @@ EscapeActions.register(
|
||||
},
|
||||
);
|
||||
|
||||
Template.cardAssigneesPopup.onCreated(function () {
|
||||
let currBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let members = currBoard.activeMembers();
|
||||
|
||||
// let query = {
|
||||
// "teams.teamId": { $in: currBoard.teams.map(t => t.teamId) },
|
||||
// };
|
||||
|
||||
// let boardTeamUsers = Users.find(query, {
|
||||
// sort: { sort: 1 },
|
||||
// });
|
||||
|
||||
// members = currBoard.activeMembers2(members, boardTeamUsers);
|
||||
|
||||
this.members = new ReactiveVar(members);
|
||||
});
|
||||
|
||||
Template.cardAssigneesPopup.events({
|
||||
'click .js-select-assignee'(event) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const card = Utils.getCurrentCard();
|
||||
const assigneeId = this.userId;
|
||||
card.toggleAssignee(assigneeId);
|
||||
event.preventDefault();
|
||||
},
|
||||
'keyup .card-assignees-filter'(event) {
|
||||
const members = filterMembers(event.target.value);
|
||||
Template.instance().members.set(members);
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardAssigneesPopup.helpers({
|
||||
@@ -1606,6 +1765,10 @@ Template.cardAssigneesPopup.helpers({
|
||||
return _.contains(cardAssignees, this.userId);
|
||||
},
|
||||
|
||||
members() {
|
||||
return Template.instance().members.get();
|
||||
},
|
||||
|
||||
user() {
|
||||
return Users.findOne(this.userId);
|
||||
},
|
||||
@@ -1657,7 +1820,7 @@ Template.cardAssigneePopup.helpers({
|
||||
Template.cardAssigneePopup.events({
|
||||
'click .js-remove-assignee'() {
|
||||
Cards.findOne(this.cardId).unassignAssignee(this.userId);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-edit-profile': Popup.open('editProfile'),
|
||||
});
|
||||
@@ -4,15 +4,6 @@
|
||||
|
||||
avatar-radius = 50%
|
||||
|
||||
#cardURL_copy
|
||||
// Have clipboard text not visible by moving it to far left
|
||||
position: absolute
|
||||
left: -2000px
|
||||
top: 0px
|
||||
|
||||
#clipboard
|
||||
white-space: normal
|
||||
|
||||
.assignee
|
||||
border-radius: 3px
|
||||
display: block
|
||||
@@ -85,6 +76,12 @@ avatar-radius = 50%
|
||||
box-shadow: 0 0 0 2px darken(white, 60%) inset
|
||||
|
||||
// Other card details
|
||||
.copied-tooltip
|
||||
display: none
|
||||
padding: 0px 10px;
|
||||
background-color: #000000df;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
|
||||
.card-details
|
||||
padding: 0
|
||||
@@ -95,7 +92,7 @@ avatar-radius = 50%
|
||||
overflow-x: hidden
|
||||
background: darken(white, 3%)
|
||||
border-radius: bottom 3px
|
||||
z-index: 20 !important
|
||||
z-index: 20
|
||||
animation: flexGrowIn 0.1s
|
||||
box-shadow: 0 0 7px 0 darken(white, 30%)
|
||||
transition: flex-basis 0.1s
|
||||
@@ -113,6 +110,9 @@ avatar-radius = 50%
|
||||
padding: 7px 20px
|
||||
background: darken(white, 7%)
|
||||
border-bottom: 1px solid darken(white, 14%)
|
||||
position: sticky
|
||||
top: 0px
|
||||
z-index: 500
|
||||
|
||||
.card-number {
|
||||
color: darken(white, 30%);
|
||||
@@ -127,7 +127,8 @@ avatar-radius = 50%
|
||||
.card-copy-button,
|
||||
.card-copy-mobile-button,
|
||||
.close-card-details-mobile-web,
|
||||
.card-details-menu-mobile-web
|
||||
.card-details-menu-mobile-web,
|
||||
.copied-tooltip
|
||||
float: right
|
||||
|
||||
.close-card-details,
|
||||
@@ -183,6 +184,9 @@ avatar-radius = 50%
|
||||
margin-top: 5px
|
||||
margin-bottom: 10px
|
||||
|
||||
.copied-tooltip
|
||||
padding: 0px 10px
|
||||
|
||||
.card-details-list
|
||||
font-size: 0.85em
|
||||
margin-bottom: 3px
|
||||
@@ -196,6 +200,10 @@ avatar-radius = 50%
|
||||
border-radius: 3px
|
||||
padding: 0px 5px
|
||||
|
||||
.copied-tooltip
|
||||
margin-right: 10px
|
||||
padding: 10px;
|
||||
|
||||
.card-description textarea
|
||||
min-height: 100px
|
||||
|
||||
@@ -206,13 +214,13 @@ avatar-radius = 50%
|
||||
|
||||
.card-details-item
|
||||
margin-right: 0.5em
|
||||
flex-grow: 1
|
||||
&:last-child
|
||||
margin-right: 0
|
||||
&.card-details-item-labels
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 95%
|
||||
flex-grow: 1
|
||||
&.card-details-item-members,
|
||||
&.card-details-item-assignees,
|
||||
&.card-details-item-customfield,
|
||||
@@ -220,7 +228,6 @@ avatar-radius = 50%
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 36%
|
||||
flex-grow: 1
|
||||
&.card-details-item-creator,
|
||||
&.card-details-item-received,
|
||||
&.card-details-item-start,
|
||||
@@ -229,56 +236,54 @@ avatar-radius = 50%
|
||||
display: block
|
||||
word-wrap: break-word
|
||||
max-width: 28%
|
||||
flex-grow: 1
|
||||
&.custom-fields
|
||||
padding-left: 10px
|
||||
|
||||
|
||||
.card-details-item-title
|
||||
font-size: 16px
|
||||
font-weight: bold
|
||||
color: #4d4d4d
|
||||
|
||||
.card-label
|
||||
padding-top: 5px
|
||||
padding-bottom: 5px
|
||||
|
||||
.activities
|
||||
padding-top: 10px
|
||||
|
||||
.card-details-maximized
|
||||
padding: 0
|
||||
flex-shrink: 0
|
||||
flex-basis: calc(100% - 20px)
|
||||
will-change: flex-basis
|
||||
overflow-y: scroll
|
||||
overflow-x: scroll
|
||||
background: darken(white, 3%)
|
||||
border-radius: bottom 3px
|
||||
z-index: 1000 !important
|
||||
animation: flexGrowIn 0.1s
|
||||
box-shadow: 0 0 7px 0 darken(white, 30%)
|
||||
transition: flex-basis 0.1s
|
||||
box-sizing: border-box
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
height: calc(100% - 20px)
|
||||
width: calc(100% - 20px)
|
||||
float: left
|
||||
|
||||
.card-details-left
|
||||
@media screen and (min-width: 801px)
|
||||
.card-details-maximized
|
||||
padding: 0
|
||||
flex-shrink: 0
|
||||
flex-basis: calc(100% - 20px)
|
||||
will-change: flex-basis
|
||||
overflow-y: scroll
|
||||
overflow-x: scroll
|
||||
background: darken(white, 3%)
|
||||
border-radius: bottom 3px
|
||||
z-index: 100
|
||||
animation: flexGrowIn 0.1s
|
||||
box-shadow: 0 0 7px 0 darken(white, 30%)
|
||||
transition: flex-basis 0.1s
|
||||
box-sizing: border-box
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
height: calc(100% - 20px)
|
||||
width: calc(100% - 20px)
|
||||
float: left
|
||||
top: 60px
|
||||
left: 20px
|
||||
width: 47%
|
||||
|
||||
.card-details-right
|
||||
position: absolute
|
||||
float: right
|
||||
top: 20px
|
||||
left: 50%
|
||||
.card-details-left
|
||||
float: left
|
||||
top: 60px
|
||||
left: 20px
|
||||
width: 47%
|
||||
|
||||
.card-details-header
|
||||
width: 47%
|
||||
.card-details-right
|
||||
position: absolute
|
||||
float: right
|
||||
top: 20px
|
||||
left: 50%
|
||||
|
||||
.card-details-header
|
||||
width: 47%
|
||||
|
||||
input[type="text"].attachment-add-link-input
|
||||
float: left
|
||||
@@ -297,6 +302,8 @@ input[type="submit"].attachment-add-link-submit
|
||||
padding: 0px 20px 0px 20px
|
||||
margin: 0px
|
||||
transition: none
|
||||
overflow-y: revert
|
||||
overflow-x: revert
|
||||
|
||||
.card-details-canvas
|
||||
width: 100%
|
||||
@@ -315,6 +322,21 @@ input[type="submit"].attachment-add-link-submit
|
||||
.minimize-card-details
|
||||
margin-right: 40px
|
||||
|
||||
.card-details-popup
|
||||
padding: 0px 10px
|
||||
|
||||
.pop-over > .content-wrapper > .popup-container-depth-0
|
||||
width: 100%
|
||||
|
||||
& > .content
|
||||
width: calc(100% - 10px)
|
||||
|
||||
& > .content > .card-details-popup hr
|
||||
margin: 15px 0px
|
||||
|
||||
.card-details-header
|
||||
margin: 0
|
||||
|
||||
card-details-color(background, color...)
|
||||
background: background !important
|
||||
if color
|
||||
|
||||
@@ -9,7 +9,6 @@ BlazeComponent.extendComponent({
|
||||
toggleOvertime() {
|
||||
this.card.setIsOvertime(!this.card.getIsOvertime());
|
||||
$('#overtime .materialCheckBox').toggleClass('is-checked');
|
||||
|
||||
$('#overtime').toggleClass('is-checked');
|
||||
},
|
||||
storeTime(spentTime, isOvertime) {
|
||||
@@ -18,6 +17,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
deleteTime() {
|
||||
this.card.setSpentTime(null);
|
||||
this.card.setIsOvertime(false);
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
@@ -27,11 +27,14 @@ BlazeComponent.extendComponent({
|
||||
evt.preventDefault();
|
||||
|
||||
const spentTime = parseFloat(evt.target.time.value);
|
||||
const isOvertime = this.card.getIsOvertime();
|
||||
|
||||
//const isOvertime = this.card.getIsOvertime();
|
||||
let isOvertime = false;
|
||||
if ($('#overtime').attr('class').indexOf('is-checked') >= 0) {
|
||||
isOvertime = true;
|
||||
}
|
||||
if (spentTime >= 0) {
|
||||
this.storeTime(spentTime, isOvertime);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
} else {
|
||||
this.error.set('invalid-time');
|
||||
evt.target.time.focus();
|
||||
@@ -40,7 +43,7 @@ BlazeComponent.extendComponent({
|
||||
'click .js-delete-time'(evt) {
|
||||
evt.preventDefault();
|
||||
this.deleteTime();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click a.js-toggle-overtime': this.toggleOvertime,
|
||||
},
|
||||
|
||||
@@ -12,20 +12,15 @@ template(name="checklists")
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton")
|
||||
label.toggle-label(for="toggleHideCheckedItemsButton")
|
||||
|
||||
if toggleDeleteDialog.get
|
||||
.board-overlay#card-details-overlay
|
||||
+checklistDeleteDialog(checklist = checklistToDelete)
|
||||
|
||||
|
||||
.card-checklist-items
|
||||
each checklist in currentCard.checklists
|
||||
each checklist in checklists
|
||||
+checklistDetail(checklist = checklist)
|
||||
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
|
||||
+addChecklistItemForm
|
||||
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=false)
|
||||
else
|
||||
a.js-open-inlined-form(title="{{_ 'add-checklist'}}")
|
||||
a.add-checklist.js-open-inlined-form(title="{{_ 'add-checklist'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
template(name="checklistDetail")
|
||||
@@ -36,7 +31,7 @@ template(name="checklistDetail")
|
||||
.checklist-title
|
||||
span
|
||||
if canModifyCard
|
||||
a.js-delete-checklist.toggle-delete-checklist-dialog {{_ "delete"}}...
|
||||
a.fa.fa-navicon.checklist-details-menu.js-open-checklist-details-menu(title="{{_ 'checklistActionsPopup-title'}}")
|
||||
|
||||
if canModifyCard
|
||||
h2.title.js-open-inlined-form.is-editable
|
||||
@@ -48,27 +43,33 @@ template(name="checklistDetail")
|
||||
h2.title
|
||||
+viewer
|
||||
= checklist.title
|
||||
|
||||
if $gt finishedPercent 0
|
||||
.checklist-progress-bar-container
|
||||
.checklist-progress-text {{finishedPercent}}%
|
||||
.checklist-progress-bar
|
||||
.checklist-progress(style="width:{{finishedPercent}}%")
|
||||
+checklistItems(checklist = checklist)
|
||||
|
||||
template(name="checklistDeleteDialog")
|
||||
.js-confirm-checklist-delete
|
||||
p
|
||||
i(class="fa fa-exclamation-triangle" aria-hidden="true")
|
||||
p
|
||||
| {{_ 'confirm-checklist-delete-dialog'}}
|
||||
span {{checklist.title}}
|
||||
| ?
|
||||
.js-checklist-delete-buttons
|
||||
button.confirm-checklist-delete(type="button") {{_ 'delete'}}
|
||||
button.toggle-delete-checklist-dialog(type="button") {{_ 'cancel'}}
|
||||
template(name="checklistDeletePopup")
|
||||
p {{_ 'confirm-checklist-delete-popup'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
||||
template(name="addChecklistItemForm")
|
||||
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
textarea.js-add-checklist-item(rows='1' autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-add-checklist-item-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
if showNewlineBecomesNewChecklistItem
|
||||
.material-toggle-switch(title="{{_ 'newlineBecomesNewChecklistItem'}}")
|
||||
input.toggle-switch(type="checkbox" id="toggleNewlineBecomesNewChecklistItem")
|
||||
label.toggle-label(for="toggleNewlineBecomesNewChecklistItem")
|
||||
|
||||
template(name="editChecklistItemForm")
|
||||
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
textarea.js-edit-checklist-item(rows='1' autofocus dir="auto")
|
||||
if $eq type 'item'
|
||||
= item.title
|
||||
@@ -93,7 +94,7 @@ template(name="checklistItems")
|
||||
+checklistItemDetail(item = item checklist = checklist)
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist)
|
||||
+addChecklistItemForm
|
||||
+addChecklistItemForm(checklist=checklist showNewlineBecomesNewChecklistItem=true)
|
||||
else
|
||||
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
|
||||
i.fa.fa-plus
|
||||
@@ -142,3 +143,50 @@ template(name="boardsSwimlanesAndLists")
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
|
||||
template(name="checklistActionsPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-delete-checklist.delete-checklist
|
||||
i.fa.fa-trash
|
||||
| {{_ "delete"}} ...
|
||||
a.js-move-checklist.move-checklist
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ "moveChecklist"}} ...
|
||||
a.js-copy-checklist.copy-checklist
|
||||
i.fa.fa-copy
|
||||
| {{_ "copyChecklist"}} ...
|
||||
|
||||
template(name="copyChecklistPopup")
|
||||
+copyAndMoveChecklist
|
||||
|
||||
template(name="moveChecklistPopup")
|
||||
+copyAndMoveChecklist
|
||||
|
||||
template(name="copyAndMoveChecklist")
|
||||
unless currentUser.isWorker
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards(autofocus)
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
option(value="{{_id}}" selected="{{#if isChecklistDialogOptionBoardId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'swimlanes'}}:
|
||||
select.js-select-swimlanes
|
||||
each swimlanes
|
||||
option(value="{{_id}}" selected="{{#if isChecklistDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'lists'}}:
|
||||
select.js-select-lists
|
||||
each lists
|
||||
option(value="{{_id}}" selected="{{#if isChecklistDialogOptionListId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
label {{_ 'cards'}}:
|
||||
select.js-select-cards
|
||||
each cards
|
||||
option(value="{{_id}}" selected="{{#if isChecklistDialogOptionCardId _id}}selected{{/if}}") {{title}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
@@ -13,10 +13,10 @@ function initSorting(items) {
|
||||
appendTo: 'parent',
|
||||
distance: 7,
|
||||
placeholder: 'checklist-item placeholder',
|
||||
scroll: false,
|
||||
scroll: true,
|
||||
start(evt, ui) {
|
||||
ui.placeholder.height(ui.helper.height());
|
||||
EscapeActions.executeUpTo('popup-close');
|
||||
EscapeActions.clickExecute(evt.target, 'inlinedForm');
|
||||
},
|
||||
stop(evt, ui) {
|
||||
const parent = ui.item.parents('.js-checklist-items');
|
||||
@@ -55,7 +55,7 @@ BlazeComponent.extendComponent({
|
||||
return Meteor.user() && Meteor.user().isBoardMember();
|
||||
}
|
||||
|
||||
// Disable sorting if the current user is not a board member or is a miniscreen
|
||||
// Disable sorting if the current user is not a board member
|
||||
self.autorun(() => {
|
||||
const $itemsDom = $(self.itemsDom);
|
||||
if ($itemsDom.data('uiSortable') || $itemsDom.data('sortable')) {
|
||||
@@ -77,6 +77,12 @@ BlazeComponent.extendComponent({
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
|
||||
/** returns the finished percent of the checklist */
|
||||
finishedPercent() {
|
||||
const ret = this.data().checklist.finishedPercent();
|
||||
return ret;
|
||||
},
|
||||
}).register('checklistDetail');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -86,7 +92,8 @@ BlazeComponent.extendComponent({
|
||||
const title = textarea.value.trim();
|
||||
let cardId = this.currentData().cardId;
|
||||
const card = Cards.findOne(cardId);
|
||||
if (card.isLinked()) cardId = card.linkedId;
|
||||
//if (card.isLinked()) cardId = card.linkedId;
|
||||
if (card.isLinkedCard()) cardId = card.linkedId;
|
||||
|
||||
if (title) {
|
||||
Checklists.insert({
|
||||
@@ -94,29 +101,34 @@ BlazeComponent.extendComponent({
|
||||
title,
|
||||
sort: card.checklists().count(),
|
||||
});
|
||||
this.closeAllInlinedForms();
|
||||
setTimeout(() => {
|
||||
this.$('.add-checklist-item')
|
||||
.last()
|
||||
.click();
|
||||
}, 100);
|
||||
}
|
||||
textarea.value = '';
|
||||
textarea.focus();
|
||||
},
|
||||
|
||||
addChecklistItem(event) {
|
||||
event.preventDefault();
|
||||
const textarea = this.find('textarea.js-add-checklist-item');
|
||||
const newlineBecomesNewChecklistItem = this.find('input#toggleNewlineBecomesNewChecklistItem');
|
||||
const title = textarea.value.trim();
|
||||
const checklist = this.currentData().checklist;
|
||||
|
||||
if (title) {
|
||||
ChecklistItems.insert({
|
||||
title,
|
||||
checklistId: checklist._id,
|
||||
cardId: checklist.cardId,
|
||||
sort: Utils.calculateIndexData(checklist.lastItem()).base,
|
||||
});
|
||||
let checklistItems = [title];
|
||||
if (newlineBecomesNewChecklistItem.checked) {
|
||||
checklistItems = title.split('\n').map(_value => _value.trim());
|
||||
}
|
||||
for (let checklistItem of checklistItems) {
|
||||
ChecklistItems.insert({
|
||||
title: checklistItem,
|
||||
checklistId: checklist._id,
|
||||
cardId: checklist.cardId,
|
||||
sort: Utils.calculateIndexData(checklist.lastItem()).base,
|
||||
});
|
||||
}
|
||||
}
|
||||
// We keep the form opened, empty it.
|
||||
textarea.value = '';
|
||||
@@ -132,14 +144,6 @@ BlazeComponent.extendComponent({
|
||||
);
|
||||
},
|
||||
|
||||
deleteChecklist() {
|
||||
const checklist = this.currentData().checklist;
|
||||
if (checklist && checklist._id) {
|
||||
Checklists.remove(checklist._id);
|
||||
this.toggleDeleteDialog.set(false);
|
||||
}
|
||||
},
|
||||
|
||||
deleteItem() {
|
||||
const checklist = this.currentData().checklist;
|
||||
const item = this.currentData().item;
|
||||
@@ -165,11 +169,6 @@ BlazeComponent.extendComponent({
|
||||
item.setTitle(title);
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
this.toggleDeleteDialog = new ReactiveVar(false);
|
||||
this.checklistToDelete = null; //Store data context to pass to checklistDeleteDialog template
|
||||
},
|
||||
|
||||
pressKey(event) {
|
||||
//If user press enter key inside a form, submit it
|
||||
//Unless the user is also holding down the 'shift' key
|
||||
@@ -190,14 +189,13 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
},
|
||||
|
||||
/** closes all inlined forms (checklist and checklist-item input fields) */
|
||||
closeAllInlinedForms() {
|
||||
this.$('.js-close-inlined-form').click();
|
||||
},
|
||||
|
||||
events() {
|
||||
const events = {
|
||||
'click .toggle-delete-checklist-dialog'(event) {
|
||||
if ($(event.target).hasClass('js-delete-checklist')) {
|
||||
this.checklistToDelete = this.currentData().checklist; //Store data context
|
||||
}
|
||||
this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
|
||||
},
|
||||
'click #toggleHideCheckedItemsButton'() {
|
||||
Meteor.call('toggleHideCheckedItems');
|
||||
},
|
||||
@@ -206,14 +204,16 @@ BlazeComponent.extendComponent({
|
||||
return [
|
||||
{
|
||||
...events,
|
||||
'click .js-open-checklist-details-menu': Popup.open('checklistActions'),
|
||||
'submit .js-add-checklist': this.addChecklist,
|
||||
'submit .js-edit-checklist-title': this.editChecklist,
|
||||
'submit .js-add-checklist-item': this.addChecklistItem,
|
||||
'submit .js-edit-checklist-item': this.editChecklistItem,
|
||||
'click .js-convert-checklist-item-to-card': Popup.open('convertChecklistItemToCard'),
|
||||
'click .js-delete-checklist-item': this.deleteItem,
|
||||
'click .confirm-checklist-delete': this.deleteChecklist,
|
||||
'focus .js-add-checklist-item': this.focusChecklistItem,
|
||||
// add and delete checklist / checklist-item
|
||||
'click .js-open-inlined-form': this.closeAllInlinedForms,
|
||||
keydown: this.pressKey,
|
||||
},
|
||||
];
|
||||
@@ -262,6 +262,11 @@ BlazeComponent.extendComponent({
|
||||
}).register('boardsSwimlanesAndLists');
|
||||
|
||||
Template.checklists.helpers({
|
||||
checklists() {
|
||||
const card = Cards.findOne(this.cardId);
|
||||
const ret = card.checklists();
|
||||
return ret;
|
||||
},
|
||||
hideCheckedItems() {
|
||||
const currentUser = Meteor.user();
|
||||
if (currentUser) return currentUser.hasHideCheckedItems();
|
||||
@@ -269,39 +274,77 @@ Template.checklists.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
Template.addChecklistItemForm.onRendered(() => {
|
||||
autosize($('textarea.js-add-checklist-item'));
|
||||
});
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
autosize(this.$('textarea.js-add-checklist-item'));
|
||||
},
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click a.fa.fa-copy'(event) {
|
||||
const $editor = this.$('textarea');
|
||||
const promise = Utils.copyTextToClipboard($editor[0].value);
|
||||
|
||||
Template.editChecklistItemForm.onRendered(() => {
|
||||
autosize($('textarea.js-edit-checklist-item'));
|
||||
});
|
||||
const $tooltip = this.$('.copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
}).register('addChecklistItemForm');
|
||||
|
||||
Template.checklistDeleteDialog.onCreated(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
this.scrollState = {
|
||||
position: $cardDetails.scrollTop(), //save current scroll position
|
||||
top: false, //required for smooth scroll animation
|
||||
};
|
||||
//Callback's purpose is to only prevent scrolling after animation is complete
|
||||
$cardDetails.animate({ scrollTop: 0 }, 500, () => {
|
||||
this.scrollState.top = true;
|
||||
});
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-delete-checklist' : Popup.afterConfirm('checklistDelete', function () {
|
||||
Popup.back(2);
|
||||
const checklist = this.checklist;
|
||||
if (checklist && checklist._id) {
|
||||
Checklists.remove(checklist._id);
|
||||
}
|
||||
}),
|
||||
'click .js-move-checklist' : Popup.open('moveChecklist'),
|
||||
'click .js-copy-checklist' : Popup.open('copyChecklist'),
|
||||
}
|
||||
]
|
||||
}
|
||||
}).register('checklistActionsPopup');
|
||||
|
||||
//Prevent scrolling while dialog is open
|
||||
$cardDetails.on('scroll', () => {
|
||||
if (this.scrollState.top) {
|
||||
//If it's already in position, keep it there. Otherwise let animation scroll
|
||||
$cardDetails.scrollTop(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
autosize(this.$('textarea.js-edit-checklist-item'));
|
||||
},
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
Meteor.user().isBoardMember() &&
|
||||
!Meteor.user().isCommentOnly() &&
|
||||
!Meteor.user().isWorker()
|
||||
);
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click a.fa.fa-copy'(event) {
|
||||
const $editor = this.$('textarea');
|
||||
const promise = Utils.copyTextToClipboard($editor[0].value);
|
||||
|
||||
Template.checklistDeleteDialog.onDestroyed(() => {
|
||||
const $cardDetails = this.$('.card-details');
|
||||
$cardDetails.off('scroll'); //Reactivate scrolling
|
||||
$cardDetails.animate({ scrollTop: this.scrollState.position });
|
||||
});
|
||||
const $tooltip = this.$('.copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
}).register('editChecklistItemForm');
|
||||
|
||||
Template.checklistItemDetail.helpers({
|
||||
canModifyCard() {
|
||||
@@ -335,3 +378,231 @@ BlazeComponent.extendComponent({
|
||||
];
|
||||
},
|
||||
}).register('checklistItemDetail');
|
||||
|
||||
class DialogWithBoardSwimlaneListAndCard extends BlazeComponent {
|
||||
/** returns the checklist dialog options
|
||||
* @return Object with properties { boardId, swimlaneId, listId, cardId }
|
||||
*/
|
||||
getChecklistDialogOptions() {
|
||||
}
|
||||
|
||||
/** checklist is done
|
||||
* @param cardId the selected card id
|
||||
* @param options the selected options (Object with properties { boardId, swimlaneId, listId, cardId })
|
||||
*/
|
||||
setDone(cardId, options) {
|
||||
}
|
||||
|
||||
onCreated() {
|
||||
this.currentBoardId = Utils.getCurrentBoardId();
|
||||
this.selectedBoardId = new ReactiveVar(this.currentBoardId);
|
||||
this.selectedSwimlaneId = new ReactiveVar('');
|
||||
this.selectedListId = new ReactiveVar('');
|
||||
this.setChecklistDialogOption(this.currentBoardId);
|
||||
}
|
||||
|
||||
/** set the last confirmed dialog field values
|
||||
* @param boardId the current board id
|
||||
*/
|
||||
setChecklistDialogOption(boardId) {
|
||||
this.checklistDialogOption = {
|
||||
'boardId' : "",
|
||||
'swimlaneId' : "",
|
||||
'listId' : "",
|
||||
'cardId': "",
|
||||
}
|
||||
|
||||
let currentOptions = this.getChecklistDialogOptions();
|
||||
if (currentOptions && boardId && currentOptions[boardId]) {
|
||||
this.checklistDialogOption = currentOptions[boardId];
|
||||
if (this.checklistDialogOption.boardId &&
|
||||
this.checklistDialogOption.swimlaneId &&
|
||||
this.checklistDialogOption.listId
|
||||
)
|
||||
{
|
||||
this.selectedBoardId.set(this.checklistDialogOption.boardId)
|
||||
this.selectedSwimlaneId.set(this.checklistDialogOption.swimlaneId);
|
||||
this.selectedListId.set(this.checklistDialogOption.listId);
|
||||
}
|
||||
}
|
||||
this.getBoardData(this.selectedBoardId.get());
|
||||
if (!this.selectedSwimlaneId.get() || !Swimlanes.findOne({_id: this.selectedSwimlaneId.get(), boardId: this.selectedBoardId.get()})) {
|
||||
this.setFirstSwimlaneId();
|
||||
}
|
||||
if (!this.selectedListId.get() || !Lists.findOne({_id: this.selectedListId.get(), boardId: this.selectedBoardId.get()})) {
|
||||
this.setFirstListId();
|
||||
}
|
||||
}
|
||||
/** sets the first swimlane id */
|
||||
setFirstSwimlaneId() {
|
||||
try {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const swimlaneId = board.swimlanes().fetch()[0]._id;
|
||||
this.selectedSwimlaneId.set(swimlaneId);
|
||||
} catch (e) {}
|
||||
}
|
||||
/** sets the first list id */
|
||||
setFirstListId() {
|
||||
try {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const listId = board.lists().fetch()[0]._id;
|
||||
this.selectedListId.set(listId);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
/** returns if the board id was the last confirmed one
|
||||
* @param boardId check this board id
|
||||
* @return if the board id was the last confirmed one
|
||||
*/
|
||||
isChecklistDialogOptionBoardId(boardId) {
|
||||
let ret = this.checklistDialogOption.boardId == boardId;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** returns if the swimlane id was the last confirmed one
|
||||
* @param swimlaneId check this swimlane id
|
||||
* @return if the swimlane id was the last confirmed one
|
||||
*/
|
||||
isChecklistDialogOptionSwimlaneId(swimlaneId) {
|
||||
let ret = this.checklistDialogOption.swimlaneId == swimlaneId;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** returns if the list id was the last confirmed one
|
||||
* @param listId check this list id
|
||||
* @return if the list id was the last confirmed one
|
||||
*/
|
||||
isChecklistDialogOptionListId(listId) {
|
||||
let ret = this.checklistDialogOption.listId == listId;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** returns if the card id was the last confirmed one
|
||||
* @param cardId check this card id
|
||||
* @return if the card id was the last confirmed one
|
||||
*/
|
||||
isChecklistDialogOptionCardId(cardId) {
|
||||
let ret = this.checklistDialogOption.cardId == cardId;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** returns all available board */
|
||||
boards() {
|
||||
const ret = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Meteor.user().getTemplatesBoardId() },
|
||||
},
|
||||
{
|
||||
sort: { sort: 1 },
|
||||
},
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** returns all available swimlanes of the current board */
|
||||
swimlanes() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const ret = board.swimlanes();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** returns all available lists of the current board */
|
||||
lists() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
const ret = board.lists();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** returns all available cards of the current list */
|
||||
cards() {
|
||||
const list = Lists.findOne(this.selectedListId.get());
|
||||
const ret = list.cards(this.selectedSwimlaneId.get());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** get the board data from the server
|
||||
* @param boardId get the board data of this board id
|
||||
*/
|
||||
getBoardData(boardId) {
|
||||
const self = this;
|
||||
Meteor.subscribe('board', boardId, false, {
|
||||
onReady() {
|
||||
const sameBoardId = self.selectedBoardId.get() == boardId;
|
||||
self.selectedBoardId.set(boardId);
|
||||
|
||||
if (!sameBoardId) {
|
||||
// reset swimlane id (for selection in cards())
|
||||
self.setFirstSwimlaneId();
|
||||
|
||||
// reset list id (for selection in cards())
|
||||
self.setFirstListId();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-done'() {
|
||||
const boardSelect = this.$('.js-select-boards')[0];
|
||||
const boardId = boardSelect.options[boardSelect.selectedIndex].value;
|
||||
|
||||
const listSelect = this.$('.js-select-lists')[0];
|
||||
const listId = listSelect.options[listSelect.selectedIndex].value;
|
||||
|
||||
const swimlaneSelect = this.$('.js-select-swimlanes')[0];
|
||||
const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
|
||||
|
||||
const cardSelect = this.$('.js-select-cards')[0];
|
||||
const cardId = cardSelect.options[cardSelect.selectedIndex].value;
|
||||
|
||||
const options = {
|
||||
'boardId' : boardId,
|
||||
'swimlaneId' : swimlaneId,
|
||||
'listId' : listId,
|
||||
'cardId': cardId,
|
||||
}
|
||||
this.setDone(cardId, options);
|
||||
Popup.back(2);
|
||||
},
|
||||
'change .js-select-boards'(event) {
|
||||
const boardId = $(event.currentTarget).val();
|
||||
this.getBoardData(boardId);
|
||||
},
|
||||
'change .js-select-swimlanes'(event) {
|
||||
this.selectedSwimlaneId.set($(event.currentTarget).val());
|
||||
},
|
||||
'change .js-select-lists'(event) {
|
||||
this.selectedListId.set($(event.currentTarget).val());
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/** Move Checklist Dialog */
|
||||
(class extends DialogWithBoardSwimlaneListAndCard {
|
||||
getChecklistDialogOptions() {
|
||||
const ret = Meteor.user().getMoveChecklistDialogOptions();
|
||||
return ret;
|
||||
}
|
||||
setDone(cardId, options) {
|
||||
Meteor.user().setMoveChecklistDialogOption(this.currentBoardId, options);
|
||||
this.data().checklist.move(cardId);
|
||||
}
|
||||
}).register('moveChecklistPopup');
|
||||
|
||||
/** Copy Checklist Dialog */
|
||||
(class extends DialogWithBoardSwimlaneListAndCard {
|
||||
getChecklistDialogOptions() {
|
||||
const ret = Meteor.user().getCopyChecklistDialogOptions();
|
||||
return ret;
|
||||
}
|
||||
setDone(cardId, options) {
|
||||
Meteor.user().setCopyChecklistDialogOption(this.currentBoardId, options);
|
||||
this.data().checklist.copy(cardId);
|
||||
}
|
||||
}).register('copyChecklistPopup');
|
||||
@@ -20,6 +20,25 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
|
||||
.checklist-progress-bar-container
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: center
|
||||
|
||||
.checklist-progress-text
|
||||
margin-right: 10px
|
||||
|
||||
.checklist-progress-bar
|
||||
width: 80%
|
||||
height: 10px
|
||||
|
||||
.checklist-progress
|
||||
color: #fff !important
|
||||
background-color: #2196F3 !important
|
||||
padding: 0.01em 16px
|
||||
border-radius: 16px
|
||||
height: 100%
|
||||
|
||||
.checklist-title
|
||||
.checkbox
|
||||
float: left
|
||||
@@ -39,49 +58,11 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
&.is-finished
|
||||
color: #3cb500
|
||||
|
||||
.js-delete-checklist
|
||||
@extends .delete-text
|
||||
|
||||
span.fa.checklist-handle
|
||||
padding-right: 20px
|
||||
padding-top: 3px
|
||||
float: left
|
||||
|
||||
|
||||
.js-confirm-checklist-delete
|
||||
background-color: darken(white, 3%)
|
||||
position: absolute
|
||||
float: left;
|
||||
width: 60%
|
||||
margin-top: 0
|
||||
margin-left: 13%
|
||||
padding-bottom: 2%
|
||||
padding-left: 3%
|
||||
padding-right: 3%
|
||||
z-index: 17
|
||||
border-radius: 3px
|
||||
|
||||
p
|
||||
position: relative
|
||||
margin-top: 3%
|
||||
width: 100%
|
||||
text-align: center
|
||||
span
|
||||
font-weight: bold
|
||||
|
||||
i
|
||||
font-size: 2em
|
||||
|
||||
.js-checklist-delete-buttons
|
||||
position: relative
|
||||
padding: left 2% right 2%
|
||||
.confirm-checklist-delete
|
||||
margin-left: 12%
|
||||
float: left
|
||||
.toggle-delete-checklist-dialog
|
||||
margin-right: 12%
|
||||
float: right
|
||||
|
||||
#card-details-overlay
|
||||
top: 0
|
||||
bottom: -600px
|
||||
@@ -167,4 +148,21 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
|
||||
.add-checklist-item
|
||||
margin: 0.2em 0 0.5em 1.33em
|
||||
display: inline-block
|
||||
|
||||
.add-checklist-item,.add-checklist
|
||||
&.js-open-inlined-form
|
||||
display: block
|
||||
width: 50%
|
||||
|
||||
&:hover
|
||||
background: #dbdbdb
|
||||
color: #222
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||
|
||||
.checklist-details-menu
|
||||
float: right
|
||||
padding: 6px 10px 6px 10px
|
||||
|
||||
.edit-controls
|
||||
label.toggle-label
|
||||
margin-left: 2px
|
||||
@@ -27,9 +27,11 @@ template(name="deleteLabelPopup")
|
||||
template(name="cardLabelsPopup")
|
||||
ul.edit-labels-pop-over
|
||||
each board.labels
|
||||
li
|
||||
li.js-card-label-item
|
||||
a.card-label-edit-button.fa.fa-pencil.js-edit-label
|
||||
span.card-label.card-label-selectable.js-select-label(class="card-label-{{color}}"
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
span.fa.label-handle(class="fa-arrows" title="{{_ 'dragLabel'}}")
|
||||
span.card-label.card-label-selectable.js-select-label.card-label-wrapper(class="card-label-{{color}}"
|
||||
class="{{# if isLabelSelected ../_id }}active{{/if}}")
|
||||
+viewer
|
||||
= name
|
||||
|
||||
@@ -39,15 +39,67 @@ Template.createLabelPopup.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardLabelsPopup.events({
|
||||
'click .js-select-label'(event) {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const labelId = this._id;
|
||||
card.toggleLabel(labelId);
|
||||
event.preventDefault();
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
const itemsSelector = 'li.js-card-label-item:not(.placeholder)';
|
||||
const $labels = this.$('.edit-labels-pop-over');
|
||||
|
||||
$labels.sortable({
|
||||
connectWith: '.edit-labels-pop-over',
|
||||
tolerance: 'pointer',
|
||||
appendTo: '.edit-labels-pop-over',
|
||||
helper(element, currentItem) {
|
||||
let ret = currentItem.clone();
|
||||
if (currentItem.closest('.popup-container-depth-0').size() == 0)
|
||||
{ // only set css transform at every sub-popup, not at the main popup
|
||||
const content = currentItem.closest('.content')[0]
|
||||
const offsetLeft = content.offsetLeft;
|
||||
const offsetTop = $('.pop-over > .header').height() * -1;
|
||||
ret.css("transform", `translate(${offsetLeft}px, ${offsetTop}px)`);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
distance: 7,
|
||||
items: itemsSelector,
|
||||
placeholder: 'card-label-wrapper placeholder',
|
||||
start(evt, ui) {
|
||||
ui.helper.css('z-index', 1000);
|
||||
ui.placeholder.height(ui.helper.height());
|
||||
EscapeActions.clickExecute(evt.target, 'inlinedForm');
|
||||
},
|
||||
stop(evt, ui) {
|
||||
const newLabelOrderOnlyIds = ui.item.parent().children().toArray().map(_element => Blaze.getData(_element)._id)
|
||||
const card = Blaze.getData(this);
|
||||
card.board().setNewLabelOrder(newLabelOrderOnlyIds);
|
||||
},
|
||||
});
|
||||
|
||||
// Disable drag-dropping if the current user is not a board member or is comment only
|
||||
this.autorun(() => {
|
||||
if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
|
||||
$labels.sortable({
|
||||
handle: '.label-handle',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
'click .js-edit-label': Popup.open('editLabel'),
|
||||
'click .js-add-label': Popup.open('createLabel'),
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-select-label'(event) {
|
||||
const card = this.data();
|
||||
const labelId = this.currentData()._id;
|
||||
card.toggleLabel(labelId);
|
||||
event.preventDefault();
|
||||
},
|
||||
'click .js-edit-label': Popup.open('editLabel'),
|
||||
'click .js-add-label': Popup.open('createLabel'),
|
||||
}
|
||||
];
|
||||
}
|
||||
}).register('cardLabelsPopup');
|
||||
|
||||
Template.cardLabelsPopup.events({
|
||||
});
|
||||
|
||||
Template.formLabel.events({
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
// XXX Use .board-widget-labels as a flexbox container
|
||||
.card-label
|
||||
border: 1px solid #000000
|
||||
border-radius: 4px
|
||||
color: white //Default white text, in select cases, changed to black to improve contrast between label colour and text
|
||||
display: inline-block
|
||||
@@ -12,10 +13,11 @@
|
||||
padding: 3px 8px
|
||||
max-width: 210px
|
||||
min-width: 8px
|
||||
overflow: ellipsis
|
||||
word-wrap: break-word
|
||||
height: 18px
|
||||
vertical-align: bottom
|
||||
min-height: 18px
|
||||
vertical-align: middle
|
||||
white-space: initial
|
||||
overflow: initial
|
||||
|
||||
&:hover
|
||||
color: white
|
||||
@@ -27,12 +29,13 @@
|
||||
|
||||
&.add-label
|
||||
box-shadow: 0 0 0 2px darken(white, 25%) inset
|
||||
border: initial
|
||||
|
||||
&:hover, &.is-active
|
||||
box-shadow: 0 0 0 2px darken(white, 60%) inset
|
||||
|
||||
i.fa-plus
|
||||
margin-top: 3px
|
||||
p
|
||||
margin: 0px
|
||||
|
||||
.palette-colors
|
||||
display: flex
|
||||
@@ -47,7 +50,6 @@
|
||||
.card-label-white
|
||||
background-color: #ffffff
|
||||
color: #000000 //Black text for better visibility
|
||||
border: 1px solid #c0c0c0
|
||||
|
||||
.card-label-white:hover
|
||||
color: #aaaaaa //grey text for better visibility
|
||||
@@ -144,6 +146,7 @@
|
||||
height: 25px
|
||||
margin: 0px 3% 7px 0px
|
||||
width: 10.5%
|
||||
max-width: 10.5%
|
||||
cursor: pointer
|
||||
|
||||
.edit-labels
|
||||
@@ -220,3 +223,9 @@
|
||||
&:hover
|
||||
background: #dbdbdb
|
||||
|
||||
ul.edit-labels-pop-over
|
||||
span.fa.label-handle
|
||||
padding-right: 10px;
|
||||
|
||||
span.fa.label-handle + .card-label
|
||||
max-width: 180px
|
||||
@@ -2,21 +2,17 @@ template(name="minicard")
|
||||
.minicard(
|
||||
class="{{#if isLinkedCard}}linked-card{{/if}}"
|
||||
class="{{#if isLinkedBoard}}linked-board{{/if}}"
|
||||
class="minicard-{{colorClass}}")
|
||||
if isMiniScreen
|
||||
class="{{#if colorClass}}minicard-{{colorClass}}{{/if}}")
|
||||
if isMiniScreenOrShowDesktopDragHandles
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
unless isMiniScreen
|
||||
if showDesktopDragHandles
|
||||
.handle
|
||||
.fa.fa-arrows
|
||||
if cover
|
||||
.minicard-cover(style="background-image: url('{{cover.url}}');")
|
||||
if labels
|
||||
.minicard-labels
|
||||
.minicard-labels(class="{{#if hiddenMinicardLabelText}}minicard-labels-no-text{{/if}}")
|
||||
each labels
|
||||
unless hiddenMinicardLabelText
|
||||
span.card-label(class="card-label-{{color}}" title=name)
|
||||
span.js-card-label.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if hiddenMinicardLabelText
|
||||
@@ -92,15 +88,17 @@ template(name="minicard")
|
||||
+viewer
|
||||
= trueValue
|
||||
|
||||
if getAssignees
|
||||
.minicard-assignees.js-minicard-assignees
|
||||
each getAssignees
|
||||
+userAvatar(userId=this)
|
||||
if showAssignee
|
||||
if getAssignees
|
||||
.minicard-assignees.js-minicard-assignees
|
||||
each getAssignees
|
||||
+userAvatar(userId=this)
|
||||
|
||||
if getMembers
|
||||
.minicard-members.js-minicard-members
|
||||
each getMembers
|
||||
+userAvatar(userId=this)
|
||||
if showMembers
|
||||
if getMembers
|
||||
.minicard-members.js-minicard-members
|
||||
each getMembers
|
||||
+userAvatar(userId=this)
|
||||
|
||||
if showCreator
|
||||
.minicard-creator
|
||||
@@ -145,4 +143,9 @@ template(name="minicard")
|
||||
if currentBoard.allowsCardSortingByNumber
|
||||
.badge
|
||||
span.badge-icon.fa.fa-sort
|
||||
span.badge-text {{ sort }}
|
||||
span.badge-text.check-list-sort {{ sort }}
|
||||
|
||||
template(name="editCardSortOrderPopup")
|
||||
input.js-edit-card-sort-popup(type='text' autofocus value=sort dir="auto")
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-sort-popup(type="submit") {{_ 'save'}}
|
||||
@@ -49,6 +49,38 @@ BlazeComponent.extendComponent({
|
||||
return false;
|
||||
},
|
||||
|
||||
showMembers() {
|
||||
if (this.data().board()) {
|
||||
return (
|
||||
this.data().board.allowsMembers === null ||
|
||||
this.data().board().allowsMembers === undefined ||
|
||||
this.data().board().allowsMembers
|
||||
);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
showAssignee() {
|
||||
if (this.data().board()) {
|
||||
return (
|
||||
this.data().board.allowsAssignee === null ||
|
||||
this.data().board().allowsAssignee === undefined ||
|
||||
this.data().board().allowsAssignee
|
||||
);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/** opens the card label popup only if clicked onto a label
|
||||
* <li> this is necessary to have the data context of the minicard.
|
||||
* if .js-card-label is used at click event, then only the data context of the label itself is available at this.currentData()
|
||||
*/
|
||||
cardLabelsPopup(event) {
|
||||
if (this.find('.js-card-label:hover')) {
|
||||
Popup.open("cardLabels")(event, {dataContextIfCurrentDataIsUndefined: this.currentData()});
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
@@ -57,8 +89,6 @@ BlazeComponent.extendComponent({
|
||||
else if (this.data().isLinkedBoard())
|
||||
Utils.goBoardId(this.data().linkedId);
|
||||
},
|
||||
},
|
||||
{
|
||||
'click .js-toggle-minicard-label-text'() {
|
||||
if (window.localStorage.getItem('hiddenMinicardLabelText')) {
|
||||
window.localStorage.removeItem('hiddenMinicardLabelText'); //true
|
||||
@@ -66,22 +96,14 @@ BlazeComponent.extendComponent({
|
||||
window.localStorage.setItem('hiddenMinicardLabelText', 'true'); //true
|
||||
}
|
||||
},
|
||||
},
|
||||
'click span.badge-icon.fa.fa-sort, click span.badge-text.check-list-sort' : Popup.open("editCardSortOrder"),
|
||||
'click .minicard-labels' : this.cardLabelsPopup,
|
||||
}
|
||||
];
|
||||
},
|
||||
}).register('minicard');
|
||||
|
||||
Template.minicard.helpers({
|
||||
showDesktopDragHandles() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).showDesktopDragHandles;
|
||||
} else if (window.localStorage.getItem('showDesktopDragHandles')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
hiddenMinicardLabelText() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
@@ -93,3 +115,30 @@ Template.minicard.helpers({
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keydown input.js-edit-card-sort-popup'(evt) {
|
||||
// enter = save
|
||||
if (evt.keyCode === 13) {
|
||||
this.find('button[type=submit]').click();
|
||||
}
|
||||
},
|
||||
'click button.js-submit-edit-card-sort-popup'(event) {
|
||||
// save button pressed
|
||||
event.preventDefault();
|
||||
const sort = this.$('.js-edit-card-sort-popup')[0]
|
||||
.value
|
||||
.trim();
|
||||
if (!Number.isNaN(sort)) {
|
||||
let card = this.data();
|
||||
card.move(card.boardId, card.swimlaneId, card.listId, sort);
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
}).register('editCardSortOrderPopup');
|
||||
@@ -80,8 +80,6 @@
|
||||
|
||||
.minicard-labels
|
||||
float: none
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
|
||||
.minicard-label
|
||||
width: 11px
|
||||
@@ -90,6 +88,10 @@
|
||||
margin-right: 3px
|
||||
margin-bottom: 3px
|
||||
|
||||
.minicard-labels-no-text
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
|
||||
.minicard-custom-fields
|
||||
display:block;
|
||||
.minicard-custom-field
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
template(name="resultCard")
|
||||
.result-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=originRelativeUrl)
|
||||
a.minicard-wrapper.js-minicard.card-title(href=originRelativeUrl)
|
||||
+minicard(this)
|
||||
//= card.title
|
||||
ul.result-card-context-list
|
||||
|
||||
@@ -5,7 +5,31 @@ Template.resultCard.helpers({
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
clickOnMiniCard(evt) {
|
||||
evt.preventDefault();
|
||||
const this_ = this;
|
||||
const cardId = this.currentData()._id;
|
||||
const boardId = this.currentData().boardId;
|
||||
Meteor.subscribe('popupCardData', cardId, {
|
||||
onReady() {
|
||||
Session.set('popupCardId', cardId);
|
||||
Session.set('popupCardBoardId', boardId);
|
||||
this_.cardDetailsPopup(evt);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
cardDetailsPopup(event) {
|
||||
if (!Popup.isOpen()) {
|
||||
Popup.open("cardDetails")(event);
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [{}];
|
||||
return [
|
||||
{
|
||||
'click .js-minicard': this.clickOnMiniCard,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('resultCard');
|
||||
@@ -37,6 +37,8 @@ BlazeComponent.extendComponent({
|
||||
? targetBoard.getDefaultSwimline()._id
|
||||
: targetSwimlane._id;
|
||||
|
||||
const nextCardNumber = targetBoard.getNextCardNumber();
|
||||
|
||||
if (title) {
|
||||
const _id = Cards.insert({
|
||||
title,
|
||||
@@ -49,6 +51,7 @@ BlazeComponent.extendComponent({
|
||||
sort: sortIndex,
|
||||
swimlaneId,
|
||||
type: 'cardType-card',
|
||||
cardNumber: nextCardNumber
|
||||
});
|
||||
|
||||
// In case the filter is active we need to add the newly inserted card in
|
||||
|
||||
@@ -117,7 +117,6 @@ button
|
||||
display: inline-block
|
||||
font-weight: 700
|
||||
line-height: 22px
|
||||
margin: 8px 4px 0 0
|
||||
padding: 7px 20px
|
||||
text-align: center
|
||||
|
||||
@@ -226,20 +225,19 @@ textarea
|
||||
.edit-controls,
|
||||
.add-controls
|
||||
display: flex
|
||||
align-items: baseline
|
||||
margin-top: 0
|
||||
align-items: center
|
||||
margin-top: 0px
|
||||
margin-bottom: 12px
|
||||
|
||||
button[type=submit]
|
||||
input[type=button]
|
||||
float: left
|
||||
height: 32px
|
||||
margin-top: -2px
|
||||
padding-top: 5px
|
||||
padding-bottom: 5px
|
||||
margin-bottom: 0px
|
||||
|
||||
.fa-times-thin
|
||||
font-size: 26px
|
||||
margin: 3px 4px
|
||||
margin: 3px 10px
|
||||
|
||||
// Material Design checkboxes
|
||||
[type="checkbox"]:not(:checked),
|
||||
@@ -247,6 +245,7 @@ textarea
|
||||
position: absolute
|
||||
left: -9999px
|
||||
visibility: hidden
|
||||
display: none
|
||||
|
||||
.materialCheckBox
|
||||
position: relative
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require('/client/lib/jquery-ui.js')
|
||||
|
||||
const { calculateIndex } = Utils;
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -93,7 +95,7 @@ BlazeComponent.extendComponent({
|
||||
$cards.sortable('cancel');
|
||||
|
||||
if (MultiSelection.isActive()) {
|
||||
Cards.find(MultiSelection.getMongoSelector()).forEach((card, i) => {
|
||||
Cards.find(MultiSelection.getMongoSelector(), {sort: ['sort']}).forEach((card, i) => {
|
||||
const newSwimlaneId = targetSwimlaneId
|
||||
? targetSwimlaneId
|
||||
: card.swimlaneId || defaultSwimlaneId;
|
||||
@@ -114,25 +116,51 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
boardComponent.setIsDragging(false);
|
||||
},
|
||||
sort(event, ui) {
|
||||
const $boardCanvas = $('.board-canvas');
|
||||
const boardCanvas = $boardCanvas[0];
|
||||
|
||||
if (event.pageX < 10)
|
||||
{ // scroll to the left
|
||||
boardCanvas.scrollLeft -= 15;
|
||||
ui.helper[0].offsetLeft -= 15;
|
||||
}
|
||||
if (
|
||||
event.pageX > boardCanvas.offsetWidth - 10 &&
|
||||
boardCanvas.scrollLeft < $boardCanvas.data('scrollLeftMax') // don't scroll more than possible
|
||||
)
|
||||
{ // scroll to the right
|
||||
boardCanvas.scrollLeft += 15;
|
||||
}
|
||||
if (
|
||||
event.pageY > boardCanvas.offsetHeight - 10 &&
|
||||
event.pageY + boardCanvas.scrollTop < $boardCanvas.data('scrollTopMax') // don't scroll more than possible
|
||||
)
|
||||
{ // scroll to the bottom
|
||||
boardCanvas.scrollTop += 15;
|
||||
}
|
||||
if (event.pageY < 10)
|
||||
{ // scroll to the top
|
||||
boardCanvas.scrollTop -= 15;
|
||||
}
|
||||
},
|
||||
activate(event, ui) {
|
||||
const $boardCanvas = $('.board-canvas');
|
||||
const boardCanvas = $boardCanvas[0];
|
||||
// scrollTopMax and scrollLeftMax only available at Firefox (https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTopMax)
|
||||
// https://www.it-swarm.com.de/de/javascript/so-erhalten-sie-den-maximalen-dokument-scrolltop-wert/1069126844/
|
||||
$boardCanvas.data('scrollTopMax', boardCanvas.scrollHeight - boardCanvas.clientTop);
|
||||
// https://stackoverflow.com/questions/5138373/how-do-i-get-the-max-value-of-scrollleft/5704386#5704386
|
||||
$boardCanvas.data('scrollLeftMax', boardCanvas.scrollWidth - boardCanvas.clientWidth);
|
||||
},
|
||||
});
|
||||
|
||||
this.autorun(() => {
|
||||
let showDesktopDragHandles = false;
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
showDesktopDragHandles = (currentUser.profile || {})
|
||||
.showDesktopDragHandles;
|
||||
} else if (window.localStorage.getItem('showDesktopDragHandles')) {
|
||||
showDesktopDragHandles = true;
|
||||
} else {
|
||||
showDesktopDragHandles = false;
|
||||
}
|
||||
|
||||
if (Utils.isMiniScreen() || showDesktopDragHandles) {
|
||||
if (Utils.isMiniScreenOrShowDesktopDragHandles()) {
|
||||
$cards.sortable({
|
||||
handle: '.handle',
|
||||
});
|
||||
} else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
|
||||
} else {
|
||||
$cards.sortable({
|
||||
handle: '.minicard',
|
||||
});
|
||||
@@ -178,19 +206,6 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('list');
|
||||
|
||||
Template.list.helpers({
|
||||
showDesktopDragHandles() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).showDesktopDragHandles;
|
||||
} else if (window.localStorage.getItem('showDesktopDragHandles')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.miniList.events({
|
||||
'click .js-select-list'() {
|
||||
const listId = this._id;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
box-sizing: border-box
|
||||
display: flex
|
||||
flex-direction: column
|
||||
flex: 0 0 270px
|
||||
position: relative
|
||||
// Even if this background color is the same as the body we can't leave it
|
||||
// transparent, because that won't work during a list drag.
|
||||
@@ -12,6 +11,8 @@
|
||||
border-left: 1px solid darken(white, 20%)
|
||||
padding: 0
|
||||
float: left
|
||||
min-width: 270px
|
||||
max-width: 270px
|
||||
|
||||
&:first-child
|
||||
margin-left: 5px
|
||||
@@ -85,13 +86,9 @@
|
||||
color: #a6a6a6
|
||||
|
||||
.list-header-menu
|
||||
position: absolute
|
||||
padding: 27px 19px
|
||||
margin-top: 1px
|
||||
top: -7px
|
||||
right: 3px
|
||||
float: right
|
||||
|
||||
.list-header-plus-icon
|
||||
.list-header-plus-top
|
||||
color: #a6a6a6
|
||||
margin-right: 15px
|
||||
|
||||
@@ -100,9 +97,10 @@
|
||||
|
||||
.cardCount
|
||||
color: #8c8c8c
|
||||
font-size: 0.8em
|
||||
font-size: 12px
|
||||
font-weight: bold
|
||||
|
||||
.list-header .list-header-plus-icon, .js-open-list-menu, .list-header-menu a
|
||||
.list-header .list-header-plus-top, .js-open-list-menu, .list-header-menu a
|
||||
color #4d4d4d
|
||||
padding-left 4px
|
||||
|
||||
@@ -160,18 +158,6 @@
|
||||
float: left
|
||||
|
||||
@media screen and (max-width: 800px)
|
||||
.list-header-menu
|
||||
position: absolute
|
||||
padding: 27px 19px
|
||||
margin-top: 1px
|
||||
top: -7px
|
||||
margin-right: 7px
|
||||
right: -3px
|
||||
|
||||
.list-header
|
||||
.list-header-name
|
||||
margin-left: 1.4rem
|
||||
|
||||
.mini-list
|
||||
flex: 0 0 60px
|
||||
height: auto
|
||||
@@ -215,7 +201,6 @@
|
||||
display: flex
|
||||
align-items: center
|
||||
.list-header-left-icon
|
||||
display: inline
|
||||
padding: 7px
|
||||
padding-right: 27px
|
||||
margin-top: 1px
|
||||
@@ -238,6 +223,30 @@
|
||||
right: 10px
|
||||
font-size: 24px
|
||||
|
||||
.list-header
|
||||
display: grid
|
||||
grid-template-columns: 30px 5fr 1fr
|
||||
.list-header-left-icon
|
||||
display: grid
|
||||
grid-row: 1/3
|
||||
grid-column: 1
|
||||
.list-header-name
|
||||
grid-row: 1
|
||||
grid-column: 2
|
||||
align-self: end
|
||||
.cardCount
|
||||
grid-row: 2
|
||||
grid-column: 2
|
||||
align-self: start
|
||||
.list-header-menu
|
||||
grid-row: 1/3
|
||||
grid-column: 3
|
||||
.inlined-form
|
||||
grid-row: 1/3
|
||||
grid-column: 1/4
|
||||
.edit-controls
|
||||
align-items: initial
|
||||
|
||||
.link-board-wrapper
|
||||
display: flex
|
||||
align-items: baseline
|
||||
|
||||
@@ -4,6 +4,17 @@ template(name="listBody")
|
||||
if cards.count
|
||||
+inlinedForm(autoclose=false position="top")
|
||||
+addCardForm(listId=_id position="top")
|
||||
ul.sidebar-list
|
||||
each customFieldsSum
|
||||
li
|
||||
+viewer
|
||||
= 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}}"
|
||||
@@ -42,6 +53,7 @@ template(name="addCardForm")
|
||||
|
||||
.add-controls.clearfix
|
||||
button.primary.confirm(type="submit") {{_ 'add'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
unless currentBoard.isTemplatesBoard
|
||||
unless currentBoard.isTemplateBoard
|
||||
span.quiet
|
||||
|
||||
@@ -13,6 +13,13 @@ BlazeComponent.extendComponent({
|
||||
return [];
|
||||
},
|
||||
|
||||
customFieldsSum() {
|
||||
return CustomFields.find({
|
||||
boardIds: { $in: [Session.get('currentBoard')] },
|
||||
showSumAtTopOfList: true,
|
||||
});
|
||||
},
|
||||
|
||||
openForm(options) {
|
||||
options = options || {};
|
||||
options.position = options.position || 'top';
|
||||
@@ -141,6 +148,10 @@ BlazeComponent.extendComponent({
|
||||
// If the card is already selected, we want to de-select it.
|
||||
// XXX We should probably modify the minicard href attribute instead of
|
||||
// overwriting the event in case the card is already selected.
|
||||
} else if (Utils.isMiniScreen()) {
|
||||
evt.preventDefault();
|
||||
Session.set('popupCardId', this.currentData()._id);
|
||||
this.cardDetailsPopup(evt);
|
||||
} else if (Session.equals('currentCard', this.currentData()._id)) {
|
||||
evt.stopImmediatePropagation();
|
||||
evt.preventDefault();
|
||||
@@ -209,6 +220,12 @@ BlazeComponent.extendComponent({
|
||||
);
|
||||
},
|
||||
|
||||
cardDetailsPopup(event) {
|
||||
if (!Popup.isOpen()) {
|
||||
Popup.open("cardDetails")(event);
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
@@ -260,9 +277,12 @@ BlazeComponent.extendComponent({
|
||||
|
||||
getLabels() {
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
return Boards.findOne(currentBoardId).labels.filter(label => {
|
||||
return this.labels.get().indexOf(label._id) > -1;
|
||||
});
|
||||
if (Boards.findOne(currentBoardId).labels) {
|
||||
return Boards.findOne(currentBoardId).labels.filter(label => {
|
||||
return this.labels.get().indexOf(label._id) > -1;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
pressKey(evt) {
|
||||
@@ -316,7 +336,7 @@ BlazeComponent.extendComponent({
|
||||
[
|
||||
// User mentions
|
||||
{
|
||||
match: /\B@([\w.]*)$/,
|
||||
match: /\B@([\w.-]*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback(
|
||||
@@ -327,6 +347,9 @@ BlazeComponent.extendComponent({
|
||||
);
|
||||
},
|
||||
template(user) {
|
||||
if (user.profile && user.profile.fullname) {
|
||||
return (user.username + " (" + user.profile.fullname + ")");
|
||||
}
|
||||
return user.username;
|
||||
},
|
||||
replace(user) {
|
||||
@@ -343,6 +366,9 @@ BlazeComponent.extendComponent({
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback(
|
||||
$.map(currentBoard.labels, label => {
|
||||
if (label.name == undefined) {
|
||||
label.name = "";
|
||||
}
|
||||
if (
|
||||
label.name.indexOf(term) > -1 ||
|
||||
label.color.indexOf(term) > -1
|
||||
@@ -479,7 +505,7 @@ BlazeComponent.extendComponent({
|
||||
evt.preventDefault();
|
||||
const linkedId = $('.js-select-cards option:selected').val();
|
||||
if (!linkedId) {
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
return;
|
||||
}
|
||||
const _id = Cards.insert({
|
||||
@@ -494,7 +520,7 @@ BlazeComponent.extendComponent({
|
||||
linkedId,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-link-board'(evt) {
|
||||
//LINK BOARD
|
||||
@@ -505,7 +531,7 @@ BlazeComponent.extendComponent({
|
||||
!impBoardId ||
|
||||
Cards.findOne({ linkedId: impBoardId, archived: false })
|
||||
) {
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
return;
|
||||
}
|
||||
const _id = Cards.insert({
|
||||
@@ -520,7 +546,7 @@ BlazeComponent.extendComponent({
|
||||
linkedId: impBoardId,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -552,7 +578,8 @@ BlazeComponent.extendComponent({
|
||||
this.isBoardTemplateSearch;
|
||||
let board = {};
|
||||
if (this.isTemplateSearch) {
|
||||
board = Boards.findOne((Meteor.user().profile || {}).templatesBoardId);
|
||||
//board = Boards.findOne((Meteor.user().profile || {}).templatesBoardId);
|
||||
board._id = (Meteor.user().profile || {}).templatesBoardId;
|
||||
} else {
|
||||
// Prefetch first non-current board id
|
||||
board = Boards.find({
|
||||
@@ -567,7 +594,7 @@ BlazeComponent.extendComponent({
|
||||
});
|
||||
}
|
||||
if (!board) {
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
return;
|
||||
}
|
||||
const boardId = board._id;
|
||||
@@ -691,10 +718,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
(err, data) => {
|
||||
_id = data;
|
||||
subManager.subscribe('board', _id, false);
|
||||
},
|
||||
);
|
||||
}
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -782,17 +810,12 @@ BlazeComponent.extendComponent({
|
||||
return false;
|
||||
}
|
||||
|
||||
const spinnerViewPosition = this.spinner.offsetTop - this.container.offsetTop + this.spinner.clientHeight;
|
||||
|
||||
const parentViewHeight = this.container.clientHeight;
|
||||
const bottomViewPosition = this.container.scrollTop + parentViewHeight;
|
||||
|
||||
let spinnerOffsetTop = this.spinner.offsetTop;
|
||||
|
||||
const addCard = $(this.container).find("a.open-minicard-composer").first()[0];
|
||||
if (addCard !== undefined) {
|
||||
spinnerOffsetTop -= addCard.clientHeight;
|
||||
}
|
||||
|
||||
return bottomViewPosition > spinnerOffsetTop;
|
||||
return bottomViewPosition > spinnerViewPosition;
|
||||
}
|
||||
|
||||
getSkSpinnerName() {
|
||||
|
||||
@@ -18,9 +18,9 @@ template(name="listHeader")
|
||||
span(class="{{#if exceededWipLimit}}highlight{{/if}}") {{cards.count}}
|
||||
|/#{wipLimit.value})
|
||||
|
||||
if showCardsCountForList cards.count
|
||||
|
|
||||
span(class="cardCount") {{cardsCount}} {{_ 'cards-count'}}
|
||||
if showCardsCountForList cards.count
|
||||
span.cardCount {{cardsCount}} {{cardsCountForListIsOne cards.count}}
|
||||
|
||||
if isMiniScreen
|
||||
if currentList
|
||||
if isWatching
|
||||
@@ -28,7 +28,7 @@ template(name="listHeader")
|
||||
div.list-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
if canSeeAddCard
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-icon(title="{{_ 'add-card-to-top-of-list'}}")
|
||||
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'}}")
|
||||
else
|
||||
a.list-header-menu-icon.fa.fa-angle-right.js-select-list
|
||||
@@ -39,12 +39,12 @@ template(name="listHeader")
|
||||
div.list-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
//if isBoardAdmin
|
||||
// a.fa.js-list-star.list-header-plus-icon(class="fa-star{{#unless starred}}-o{{/unless}}")
|
||||
// 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-icon(title="{{_ 'add-card-to-top-of-list'}}")
|
||||
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 showDesktopDragHandles
|
||||
if isShowDesktopDragHandles
|
||||
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
|
||||
template(name="editListTitleForm")
|
||||
@@ -55,6 +55,13 @@ template(name="editListTitleForm")
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="listActionPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-add-card.list-header-plus-bottom
|
||||
i.fa.fa-plus
|
||||
i.fa.fa-arrow-down
|
||||
| {{_ 'add-card-to-bottom-of-list'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-toggle-watch-list
|
||||
|
||||
@@ -85,6 +85,14 @@ BlazeComponent.extendComponent({
|
||||
return limit >= 0 && count >= limit;
|
||||
},
|
||||
|
||||
cardsCountForListIsOne(count) {
|
||||
if (count === 1) {
|
||||
return TAPi18n.__('cards-count-one');
|
||||
} else {
|
||||
return TAPi18n.__('cards-count');
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
@@ -93,7 +101,7 @@ BlazeComponent.extendComponent({
|
||||
this.starred(!this.starred());
|
||||
},
|
||||
'click .js-open-list-menu': Popup.open('listAction'),
|
||||
'click .js-add-card'(event) {
|
||||
'click .js-add-card.list-header-plus-top'(event) {
|
||||
const listDom = $(event.target).parents(
|
||||
`#js-list-${this.currentData()._id}`,
|
||||
)[0];
|
||||
@@ -114,18 +122,7 @@ BlazeComponent.extendComponent({
|
||||
Template.listHeader.helpers({
|
||||
isBoardAdmin() {
|
||||
return Meteor.user().isBoardAdmin();
|
||||
},
|
||||
|
||||
showDesktopDragHandles() {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
return (currentUser.profile || {}).showDesktopDragHandles;
|
||||
} else if (window.localStorage.getItem('showDesktopDragHandles')) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
Template.listActionPopup.helpers({
|
||||
@@ -144,23 +141,31 @@ Template.listActionPopup.helpers({
|
||||
|
||||
Template.listActionPopup.events({
|
||||
'click .js-list-subscribe'() {},
|
||||
'click .js-add-card.list-header-plus-bottom'(event) {
|
||||
const listDom = $(`#js-list-${this._id}`)[0];
|
||||
const listComponent = BlazeComponent.getComponentForElement(listDom);
|
||||
listComponent.openForm({
|
||||
position: 'bottom',
|
||||
});
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-set-color-list': Popup.open('setListColor'),
|
||||
'click .js-select-cards'() {
|
||||
const cardIds = this.allCards().map(card => card._id);
|
||||
MultiSelection.add(cardIds);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-toggle-watch-list'() {
|
||||
const currentList = this;
|
||||
const level = currentList.findWatcher(Meteor.userId()) ? null : 'watching';
|
||||
Meteor.call('watch', 'list', currentList._id, level, (err, ret) => {
|
||||
if (!err && ret) Popup.close();
|
||||
if (!err && ret) Popup.back();
|
||||
});
|
||||
},
|
||||
'click .js-close-list'(event) {
|
||||
event.preventDefault();
|
||||
this.archive();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-set-wip-limit': Popup.open('setWipLimit'),
|
||||
'click .js-more': Popup.open('listMore'),
|
||||
@@ -236,7 +241,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Template.listMorePopup.events({
|
||||
'click .js-delete': Popup.afterConfirm('listDelete', function() {
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
// TODO how can we avoid the fetch call?
|
||||
const allCards = this.allCards().fetch();
|
||||
const allCardIds = _.pluck(allCards, '_id');
|
||||
@@ -302,11 +307,11 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
'click .js-submit'() {
|
||||
this.currentList.setColor(this.currentColor.get());
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-remove-color'() {
|
||||
this.currentList.setColor(null);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -38,12 +38,12 @@ BlazeComponent.extendComponent({
|
||||
{
|
||||
'click .js-due-cards-view-me'() {
|
||||
Utils.setDueCardsView('me');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
'click .js-due-cards-view-all'() {
|
||||
Utils.setDueCardsView('all');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
template(name="editor")
|
||||
a.fa.fa-copy(title="{{_ 'copy-text-to-clipboard'}}")
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
textarea.editor(
|
||||
dir="auto"
|
||||
class="{{class}}"
|
||||
|
||||
+296
-265
@@ -1,277 +1,308 @@
|
||||
Template.editor.onRendered(() => {
|
||||
const textareaSelector = 'textarea';
|
||||
const mentions = [
|
||||
// User mentions
|
||||
{
|
||||
match: /\B@([\w.]*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback(
|
||||
currentBoard
|
||||
.activeMembers()
|
||||
.map(member => {
|
||||
const username = Users.findOne(member.userId).username;
|
||||
return username.includes(term) ? username : null;
|
||||
})
|
||||
.filter(Boolean),
|
||||
);
|
||||
},
|
||||
template(value) {
|
||||
return value;
|
||||
},
|
||||
replace(username) {
|
||||
return `@${username} `;
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
];
|
||||
const enableTextarea = function() {
|
||||
const $textarea = this.$(textareaSelector);
|
||||
autosize($textarea);
|
||||
$textarea.escapeableTextComplete(mentions);
|
||||
};
|
||||
if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) {
|
||||
const isSmall = Utils.isMiniScreen();
|
||||
const toolbar = isSmall
|
||||
? [
|
||||
['view', ['fullscreen']],
|
||||
['table', ['table']],
|
||||
['font', ['bold', 'underline']],
|
||||
//['fontsize', ['fontsize']],
|
||||
['color', ['color']],
|
||||
]
|
||||
: [
|
||||
['style', ['style']],
|
||||
['font', ['bold', 'underline', 'clear']],
|
||||
['fontsize', ['fontsize']],
|
||||
['fontname', ['fontname']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
//['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
['insert', ['link']], //, 'picture']], // modal popup has issue somehow :(
|
||||
['view', ['fullscreen', 'codeview', 'help']],
|
||||
];
|
||||
const cleanPastedHTML = function(input) {
|
||||
const badTags = [
|
||||
'style',
|
||||
'script',
|
||||
'applet',
|
||||
'embed',
|
||||
'noframes',
|
||||
'noscript',
|
||||
'meta',
|
||||
'link',
|
||||
'button',
|
||||
'form',
|
||||
].join('|');
|
||||
const badPatterns = new RegExp(
|
||||
`(?:${[
|
||||
`<(${badTags})s*[^>][\\s\\S]*?<\\/\\1>`,
|
||||
`<(${badTags})[^>]*?\\/>`,
|
||||
].join('|')})`,
|
||||
'gi',
|
||||
);
|
||||
let output = input;
|
||||
// remove bad Tags
|
||||
output = output.replace(badPatterns, '');
|
||||
// remove attributes ' style="..."'
|
||||
const badAttributes = new RegExp(
|
||||
`(?:${[
|
||||
'on\\S+=([\'"]?).*?\\1',
|
||||
'href=([\'"]?)javascript:.*?\\2',
|
||||
'style=([\'"]?).*?\\3',
|
||||
'target=\\S+',
|
||||
].join('|')})`,
|
||||
'gi',
|
||||
);
|
||||
output = output.replace(badAttributes, '');
|
||||
output = output.replace(/(<a )/gi, '$1target=_ '); // always to new target
|
||||
return output;
|
||||
};
|
||||
const editor = '.editor';
|
||||
const selectors = [
|
||||
`.js-new-description-form ${editor}`,
|
||||
`.js-new-comment-form ${editor}`,
|
||||
`.js-edit-comment ${editor}`,
|
||||
].join(','); // only new comment and edit comment
|
||||
const inputs = $(selectors);
|
||||
if (inputs.length === 0) {
|
||||
// only enable richereditor to new comment or edit comment no others
|
||||
enableTextarea();
|
||||
} else {
|
||||
const placeholder = inputs.attr('placeholder') || '';
|
||||
const mSummernotes = [];
|
||||
const getSummernote = function(input) {
|
||||
const idx = inputs.index(input);
|
||||
if (idx > -1) {
|
||||
return mSummernotes[idx];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
inputs.each(function(idx, input) {
|
||||
mSummernotes[idx] = $(input).summernote({
|
||||
placeholder,
|
||||
callbacks: {
|
||||
onInit(object) {
|
||||
const originalInput = this;
|
||||
$(originalInput).on('submitted', function() {
|
||||
// when comment is submitted, the original textarea will be set to '', so shall we
|
||||
if (!this.value) {
|
||||
const sn = getSummernote(this);
|
||||
sn && sn.summernote('code', '');
|
||||
}
|
||||
});
|
||||
const jEditor = object && object.editable;
|
||||
const toolbar = object && object.toolbar;
|
||||
if (jEditor !== undefined) {
|
||||
jEditor.escapeableTextComplete(mentions);
|
||||
}
|
||||
if (toolbar !== undefined) {
|
||||
const fBtn = toolbar.find('.btn-fullscreen');
|
||||
fBtn.on('click', function() {
|
||||
const $this = $(this),
|
||||
isActive = $this.hasClass('active');
|
||||
$('.minicards,#header-quick-access').toggle(!isActive); // mini card is still showing when editor is in fullscreen mode, we hide here manually
|
||||
});
|
||||
}
|
||||
},
|
||||
const specialHandles = [
|
||||
{userId: 'board_members', username: 'board_members'},
|
||||
{userId: 'card_members', username: 'card_members'}
|
||||
];
|
||||
const specialHandleNames = specialHandles.map(m => m.username);
|
||||
|
||||
onImageUpload(files) {
|
||||
const $summernote = getSummernote(this);
|
||||
if (files && files.length > 0) {
|
||||
const image = files[0];
|
||||
const currentCard = Cards.findOne(Session.get('currentCard'));
|
||||
const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
|
||||
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
|
||||
const insertImage = src => {
|
||||
// process all image upload types to the description/comment window
|
||||
const img = document.createElement('img');
|
||||
img.src = src;
|
||||
img.setAttribute('width', '100%');
|
||||
$summernote.summernote('insertNode', img);
|
||||
};
|
||||
const processData = function(fileObj) {
|
||||
Utils.processUploadedAttachment(
|
||||
currentCard,
|
||||
fileObj,
|
||||
attachment => {
|
||||
if (
|
||||
attachment &&
|
||||
attachment._id &&
|
||||
attachment.isImage()
|
||||
) {
|
||||
attachment.one('uploaded', function() {
|
||||
const maxTry = 3;
|
||||
const checkItvl = 500;
|
||||
let retry = 0;
|
||||
const checkUrl = function() {
|
||||
// even though uploaded event fired, attachment.url() is still null somehow //TODO
|
||||
const url = attachment.url();
|
||||
if (url) {
|
||||
insertImage(
|
||||
`${location.protocol}//${location.host}${url}`,
|
||||
);
|
||||
} else {
|
||||
retry++;
|
||||
if (retry < maxTry) {
|
||||
setTimeout(checkUrl, checkItvl);
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
const textareaSelector = 'textarea';
|
||||
const mentions = [
|
||||
// User mentions
|
||||
{
|
||||
match: /\B@([\w.-]*)$/,
|
||||
search(term, callback) {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
callback(
|
||||
_.union(
|
||||
currentBoard
|
||||
.activeMembers()
|
||||
.map(member => {
|
||||
const user = Users.findOne(member.userId);
|
||||
const username = user.username;
|
||||
const fullName = user.profile && user.profile !== undefined && user.profile.fullname ? user.profile.fullname : "";
|
||||
return username.includes(term) || fullName.includes(term) ? user : null;
|
||||
})
|
||||
.filter(Boolean), [...specialHandles])
|
||||
);
|
||||
},
|
||||
template(user) {
|
||||
if (user.profile && user.profile.fullname) {
|
||||
return (user.profile.fullname + " (" + user.username + ")");
|
||||
}
|
||||
return user.username;
|
||||
},
|
||||
replace(user) {
|
||||
if (user.profile && user.profile.fullname) {
|
||||
return `@${user.username} (${user.profile.fullname}) `;
|
||||
}
|
||||
return `@${user.username} `;
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
];
|
||||
const enableTextarea = function() {
|
||||
const $textarea = this.$(textareaSelector);
|
||||
autosize($textarea);
|
||||
$textarea.escapeableTextComplete(mentions);
|
||||
};
|
||||
if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) {
|
||||
const isSmall = Utils.isMiniScreen();
|
||||
const toolbar = isSmall
|
||||
? [
|
||||
['view', ['fullscreen']],
|
||||
['table', ['table']],
|
||||
['font', ['bold', 'underline']],
|
||||
//['fontsize', ['fontsize']],
|
||||
['color', ['color']],
|
||||
]
|
||||
: [
|
||||
['style', ['style']],
|
||||
['font', ['bold', 'underline', 'clear']],
|
||||
['fontsize', ['fontsize']],
|
||||
['fontname', ['fontname']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
//['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
['insert', ['link']], //, 'picture']], // modal popup has issue somehow :(
|
||||
['view', ['fullscreen', 'codeview', 'help']],
|
||||
];
|
||||
const cleanPastedHTML = function(input) {
|
||||
const badTags = [
|
||||
'style',
|
||||
'script',
|
||||
'applet',
|
||||
'embed',
|
||||
'noframes',
|
||||
'noscript',
|
||||
'meta',
|
||||
'link',
|
||||
'button',
|
||||
'form',
|
||||
].join('|');
|
||||
const badPatterns = new RegExp(
|
||||
`(?:${[
|
||||
`<(${badTags})s*[^>][\\s\\S]*?<\\/\\1>`,
|
||||
`<(${badTags})[^>]*?\\/>`,
|
||||
].join('|')})`,
|
||||
'gi',
|
||||
);
|
||||
let output = input;
|
||||
// remove bad Tags
|
||||
output = output.replace(badPatterns, '');
|
||||
// remove attributes ' style="..."'
|
||||
const badAttributes = new RegExp(
|
||||
`(?:${[
|
||||
'on\\S+=([\'"]?).*?\\1',
|
||||
'href=([\'"]?)javascript:.*?\\2',
|
||||
'style=([\'"]?).*?\\3',
|
||||
'target=\\S+',
|
||||
].join('|')})`,
|
||||
'gi',
|
||||
);
|
||||
output = output.replace(badAttributes, '');
|
||||
output = output.replace(/(<a )/gi, '$1target=_ '); // always to new target
|
||||
return output;
|
||||
};
|
||||
const editor = '.editor';
|
||||
const selectors = [
|
||||
`.js-new-description-form ${editor}`,
|
||||
`.js-new-comment-form ${editor}`,
|
||||
`.js-edit-comment ${editor}`,
|
||||
].join(','); // only new comment and edit comment
|
||||
const inputs = $(selectors);
|
||||
if (inputs.length === 0) {
|
||||
// only enable richereditor to new comment or edit comment no others
|
||||
enableTextarea();
|
||||
} else {
|
||||
const placeholder = inputs.attr('placeholder') || '';
|
||||
const mSummernotes = [];
|
||||
const getSummernote = function(input) {
|
||||
const idx = inputs.index(input);
|
||||
if (idx > -1) {
|
||||
return mSummernotes[idx];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
inputs.each(function(idx, input) {
|
||||
mSummernotes[idx] = $(input).summernote({
|
||||
placeholder,
|
||||
callbacks: {
|
||||
onInit(object) {
|
||||
const originalInput = this;
|
||||
$(originalInput).on('submitted', function() {
|
||||
// when comment is submitted, the original textarea will be set to '', so shall we
|
||||
if (!this.value) {
|
||||
const sn = getSummernote(this);
|
||||
sn && sn.summernote('code', '');
|
||||
}
|
||||
});
|
||||
const jEditor = object && object.editable;
|
||||
const toolbar = object && object.toolbar;
|
||||
if (jEditor !== undefined) {
|
||||
jEditor.escapeableTextComplete(mentions);
|
||||
}
|
||||
if (toolbar !== undefined) {
|
||||
const fBtn = toolbar.find('.btn-fullscreen');
|
||||
fBtn.on('click', function() {
|
||||
const $this = $(this),
|
||||
isActive = $this.hasClass('active');
|
||||
$('.minicards,#header-quick-access').toggle(!isActive); // mini card is still showing when editor is in fullscreen mode, we hide here manually
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onImageUpload(files) {
|
||||
const $summernote = getSummernote(this);
|
||||
if (files && files.length > 0) {
|
||||
const image = files[0];
|
||||
const currentCard = Utils.getCurrentCard();
|
||||
const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
|
||||
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
|
||||
const insertImage = src => {
|
||||
// process all image upload types to the description/comment window
|
||||
const img = document.createElement('img');
|
||||
img.src = src;
|
||||
img.setAttribute('width', '100%');
|
||||
$summernote.summernote('insertNode', img);
|
||||
};
|
||||
const processData = function(fileObj) {
|
||||
Utils.processUploadedAttachment(
|
||||
currentCard,
|
||||
fileObj,
|
||||
attachment => {
|
||||
if (
|
||||
attachment &&
|
||||
attachment._id &&
|
||||
attachment.isImage()
|
||||
) {
|
||||
attachment.one('uploaded', function() {
|
||||
const maxTry = 3;
|
||||
const checkItvl = 500;
|
||||
let retry = 0;
|
||||
const checkUrl = function() {
|
||||
// even though uploaded event fired, attachment.url() is still null somehow //TODO
|
||||
const url = attachment.url();
|
||||
if (url) {
|
||||
insertImage(
|
||||
`${location.protocol}//${location.host}${url}`,
|
||||
);
|
||||
} else {
|
||||
retry++;
|
||||
if (retry < maxTry) {
|
||||
setTimeout(checkUrl, checkItvl);
|
||||
}
|
||||
}
|
||||
};
|
||||
checkUrl();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
if (MAX_IMAGE_PIXEL) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const dataurl = e && e.target && e.target.result;
|
||||
if (dataurl !== undefined) {
|
||||
// need to shrink image
|
||||
Utils.shrinkImage({
|
||||
dataurl,
|
||||
maxSize: MAX_IMAGE_PIXEL,
|
||||
ratio: COMPRESS_RATIO,
|
||||
toBlob: true,
|
||||
callback(blob) {
|
||||
if (blob !== false) {
|
||||
blob.name = image.name;
|
||||
processData(blob);
|
||||
}
|
||||
};
|
||||
checkUrl();
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
if (MAX_IMAGE_PIXEL) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
const dataurl = e && e.target && e.target.result;
|
||||
if (dataurl !== undefined) {
|
||||
// need to shrink image
|
||||
Utils.shrinkImage({
|
||||
dataurl,
|
||||
maxSize: MAX_IMAGE_PIXEL,
|
||||
ratio: COMPRESS_RATIO,
|
||||
toBlob: true,
|
||||
callback(blob) {
|
||||
if (blob !== false) {
|
||||
blob.name = image.name;
|
||||
processData(blob);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(image);
|
||||
} else {
|
||||
processData(image);
|
||||
};
|
||||
reader.readAsDataURL(image);
|
||||
} else {
|
||||
processData(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onPaste(e) {
|
||||
var clipboardData = e.clipboardData;
|
||||
var pastedData = clipboardData.getData('Text');
|
||||
},
|
||||
onPaste(e) {
|
||||
var clipboardData = e.clipboardData;
|
||||
var pastedData = clipboardData.getData('Text');
|
||||
|
||||
//if pasted data is an image, exit
|
||||
if (!pastedData.length) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
//if pasted data is an image, exit
|
||||
if (!pastedData.length) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// clear up unwanted tag info when user pasted in text
|
||||
const thisNote = this;
|
||||
const updatePastedText = function(object) {
|
||||
const someNote = getSummernote(object);
|
||||
// Fix Pasting text into a card is adding a line before and after
|
||||
// (and multiplies by pasting more) by changing paste "p" to "br".
|
||||
// Fixes https://github.com/wekan/wekan/2890 .
|
||||
// == Fix Start ==
|
||||
someNote.execCommand('defaultParagraphSeparator', false, 'br');
|
||||
// == Fix End ==
|
||||
const original = someNote.summernote('code');
|
||||
const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
|
||||
someNote.summernote('code', ''); //clear original
|
||||
someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code.
|
||||
};
|
||||
setTimeout(function() {
|
||||
//this kinda sucks, but if you don't do a setTimeout,
|
||||
//the function is called before the text is really pasted.
|
||||
updatePastedText(thisNote);
|
||||
}, 10);
|
||||
// clear up unwanted tag info when user pasted in text
|
||||
const thisNote = this;
|
||||
const updatePastedText = function(object) {
|
||||
const someNote = getSummernote(object);
|
||||
// Fix Pasting text into a card is adding a line before and after
|
||||
// (and multiplies by pasting more) by changing paste "p" to "br".
|
||||
// Fixes https://github.com/wekan/wekan/2890 .
|
||||
// == Fix Start ==
|
||||
someNote.execCommand('defaultParagraphSeparator', false, 'br');
|
||||
// == Fix End ==
|
||||
const original = someNote.summernote('code');
|
||||
const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
|
||||
someNote.summernote('code', ''); //clear original
|
||||
someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code.
|
||||
};
|
||||
setTimeout(function() {
|
||||
//this kinda sucks, but if you don't do a setTimeout,
|
||||
//the function is called before the text is really pasted.
|
||||
updatePastedText(thisNote);
|
||||
}, 10);
|
||||
},
|
||||
},
|
||||
},
|
||||
dialogsInBody: true,
|
||||
spellCheck: true,
|
||||
disableGrammar: false,
|
||||
disableDragAndDrop: false,
|
||||
toolbar,
|
||||
popover: {
|
||||
image: [
|
||||
['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
|
||||
['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||
['remove', ['removeMedia']],
|
||||
],
|
||||
link: [['link', ['linkDialogShow', 'unlink']]],
|
||||
table: [
|
||||
['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
|
||||
['delete', ['deleteRow', 'deleteCol', 'deleteTable']],
|
||||
],
|
||||
air: [
|
||||
['color', ['color']],
|
||||
['font', ['bold', 'underline', 'clear']],
|
||||
],
|
||||
},
|
||||
height: 200,
|
||||
dialogsInBody: true,
|
||||
spellCheck: true,
|
||||
disableGrammar: false,
|
||||
disableDragAndDrop: false,
|
||||
toolbar,
|
||||
popover: {
|
||||
image: [
|
||||
['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
|
||||
['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||
['remove', ['removeMedia']],
|
||||
],
|
||||
link: [['link', ['linkDialogShow', 'unlink']]],
|
||||
table: [
|
||||
['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
|
||||
['delete', ['deleteRow', 'deleteCol', 'deleteTable']],
|
||||
],
|
||||
air: [
|
||||
['color', ['color']],
|
||||
['font', ['bold', 'underline', 'clear']],
|
||||
],
|
||||
},
|
||||
height: 200,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
enableTextarea();
|
||||
}
|
||||
} else {
|
||||
enableTextarea();
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click a.fa.fa-copy'(event) {
|
||||
const $editor = this.$('textarea.editor');
|
||||
const promise = Utils.copyTextToClipboard($editor[0].value);
|
||||
|
||||
const $tooltip = this.$('.copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}).register('editor');
|
||||
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
@@ -323,14 +354,14 @@ Blaze.Template.registerHelper(
|
||||
return HTML.Raw(
|
||||
DOMPurify.sanitize(content, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
);
|
||||
const knowedUsers = currentBoard.members.map(member => {
|
||||
const knowedUsers = _.union(currentBoard.members.map(member => {
|
||||
const u = Users.findOne(member.userId);
|
||||
if (u) {
|
||||
member.username = u.username;
|
||||
}
|
||||
return member;
|
||||
});
|
||||
const mentionRegex = /\B@([\w.]*)/gi;
|
||||
}), [...specialHandles]);
|
||||
const mentionRegex = /\B@([\w.-]*)/gi;
|
||||
|
||||
let currentMention;
|
||||
while ((currentMention = mentionRegex.exec(content)) !== null) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.new-comment,
|
||||
.inlined-form
|
||||
a.fa.fa-copy
|
||||
float: right
|
||||
position: relative
|
||||
top: 20px
|
||||
right: 6px
|
||||
@@ -42,6 +42,24 @@ template(name="globalSearch")
|
||||
autofocus dir="auto"
|
||||
)
|
||||
a.js-new-search.fa.fa-eraser
|
||||
if debug.get.show
|
||||
h1 Debug
|
||||
if debug.get.showSelector
|
||||
h2 Selector
|
||||
button.js-copy-debug-selector
|
||||
= 'Copy'
|
||||
pre(
|
||||
id="debug-selector"
|
||||
)
|
||||
= sessionData.selector
|
||||
if debug.get.showProjection
|
||||
h2 Projection
|
||||
button.js-copy-debug-projection
|
||||
= 'Copy'
|
||||
pre(
|
||||
id="debug-projection"
|
||||
)
|
||||
= sessionData.projection
|
||||
if searching.get
|
||||
+spinner
|
||||
else if hasResults.get
|
||||
|
||||
@@ -131,6 +131,9 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
operator_has: TAPi18n.__('operator-has'),
|
||||
operator_sort: TAPi18n.__('operator-sort'),
|
||||
operator_limit: TAPi18n.__('operator-limit'),
|
||||
operator_debug: TAPi18n.__('operator-debug'),
|
||||
operator_org: TAPi18n.__('operator-org'),
|
||||
operator_team: TAPi18n.__('operator-team'),
|
||||
predicate_overdue: TAPi18n.__('predicate-overdue'),
|
||||
predicate_archived: TAPi18n.__('predicate-archived'),
|
||||
predicate_all: TAPi18n.__('predicate-all'),
|
||||
@@ -151,6 +154,8 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
predicate_end: TAPi18n.__('predicate-end'),
|
||||
predicate_assignee: TAPi18n.__('predicate-assignee'),
|
||||
predicate_member: TAPi18n.__('predicate-member'),
|
||||
predicate_selector: TAPi18n.__('predicate-selector'),
|
||||
predicate_projection: TAPi18n.__('predicate-projection'),
|
||||
};
|
||||
|
||||
let text = '';
|
||||
@@ -169,6 +174,8 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
['\n- ', 'globalSearch-instructions-operator-member'],
|
||||
['\n- ', 'globalSearch-instructions-operator-assignee'],
|
||||
['\n- ', 'globalSearch-instructions-operator-creator'],
|
||||
['\n- ', 'globalSearch-instructions-operator-org'],
|
||||
['\n- ', 'globalSearch-instructions-operator-team'],
|
||||
['\n- ', 'globalSearch-instructions-operator-due'],
|
||||
['\n- ', 'globalSearch-instructions-operator-created'],
|
||||
['\n- ', 'globalSearch-instructions-operator-modified'],
|
||||
@@ -222,6 +229,30 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-copy-debug-selector'(evt) {
|
||||
/* Get the text field */
|
||||
const selector = document.getElementById("debug-selector");
|
||||
|
||||
try {
|
||||
navigator.clipboard.writeText(selector.textContent);
|
||||
alert("Selector copied to clipboard");
|
||||
} catch(err) {
|
||||
alert("Error copying text: " + err);
|
||||
}
|
||||
|
||||
},
|
||||
'click .js-copy-debug-projection'(evt) {
|
||||
/* Get the text field */
|
||||
const projection = document.getElementById("debug-projection");
|
||||
|
||||
try {
|
||||
navigator.clipboard.writeText(projection.textContent);
|
||||
alert("Projection copied to clipboard");
|
||||
} catch(err) {
|
||||
alert("Error copying text: " + err);
|
||||
}
|
||||
|
||||
},
|
||||
'click .js-board-title'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
|
||||
@@ -16,12 +16,14 @@ template(name="header")
|
||||
each currentBoard.lists
|
||||
li(class="{{#if $.Session.equals 'currentList' _id}}current{{/if}}")
|
||||
a.js-select-list
|
||||
= title
|
||||
+viewer
|
||||
= title
|
||||
else
|
||||
each currentUser.starredBoards
|
||||
li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
|
||||
a(href="{{pathFor 'board' id=_id slug=slug}}")
|
||||
= title
|
||||
+viewer
|
||||
= title
|
||||
#header-new-board-icon
|
||||
else
|
||||
//-
|
||||
@@ -36,7 +38,8 @@ template(name="header")
|
||||
unless currentSetting.customTopLeftCornerLogoLinkUrl
|
||||
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
|
||||
unless currentSetting.customTopLeftCornerLogoImageUrl
|
||||
img(src="{{pathFor '/logo-header.png'}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
|
||||
div#headerIsSettingDatabaseCallDone
|
||||
img(src="{{pathFor '/logo-header.png'}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
|
||||
span.allBoards
|
||||
a(href="{{pathFor 'home'}}")
|
||||
span.fa.fa-home
|
||||
@@ -49,7 +52,8 @@ template(name="header")
|
||||
each currentUser.starredBoards
|
||||
li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
|
||||
a(href="{{pathFor 'board' id=_id slug=slug}}")
|
||||
= title
|
||||
+viewer
|
||||
= title
|
||||
else
|
||||
li.current.empty {{_ 'quick-access-description'}}
|
||||
|
||||
@@ -90,3 +94,5 @@ template(name="offlineWarning")
|
||||
p
|
||||
i.fa.fa-warning
|
||||
| {{_ 'app-is-offline'}}
|
||||
|
||||
a.app-try-reconnect {{_ 'app-try-reconnect'}}
|
||||
@@ -1,7 +1,24 @@
|
||||
Meteor.subscribe('user-admin');
|
||||
Meteor.subscribe('boards');
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('announcements');
|
||||
Template.header.onCreated(function(){
|
||||
const templateInstance = this;
|
||||
templateInstance.currentSetting = new ReactiveVar();
|
||||
templateInstance.isLoading = new ReactiveVar(false);
|
||||
|
||||
Meteor.subscribe('setting', {
|
||||
onReady() {
|
||||
templateInstance.currentSetting.set(Settings.findOne());
|
||||
let currSetting = templateInstance.currentSetting.curValue;
|
||||
if(currSetting && currSetting !== undefined && currSetting.customLoginLogoImageUrl !== undefined && document.getElementById("headerIsSettingDatabaseCallDone") != null)
|
||||
document.getElementById("headerIsSettingDatabaseCallDone").style.display = 'none';
|
||||
else if(document.getElementById("headerIsSettingDatabaseCallDone") != null)
|
||||
document.getElementById("headerIsSettingDatabaseCallDone").style.display = 'block';
|
||||
return this.stop();
|
||||
},
|
||||
});
|
||||
});
|
||||
Template.header.helpers({
|
||||
wrappedHeader() {
|
||||
return !Session.get('currentBoard');
|
||||
@@ -41,3 +58,10 @@ Template.header.events({
|
||||
Session.set('currentCard', null);
|
||||
},
|
||||
});
|
||||
|
||||
Template.offlineWarning.events({
|
||||
'click a.app-try-reconnect'(event) {
|
||||
event.preventDefault();
|
||||
Meteor.reconnect();
|
||||
},
|
||||
});
|
||||
@@ -99,7 +99,7 @@
|
||||
height: 28px
|
||||
font-size: 12px
|
||||
display: flex
|
||||
z-index: 21
|
||||
z-index: 1000
|
||||
padding: 10px 0px
|
||||
|
||||
.allBoards
|
||||
@@ -135,6 +135,14 @@
|
||||
padding: 12px 10px
|
||||
margin: -10px 0px
|
||||
|
||||
.viewer
|
||||
display: inline
|
||||
white-space: nowrap
|
||||
|
||||
p
|
||||
display: inline
|
||||
white-space: nowrap
|
||||
|
||||
&.current
|
||||
color: darken(white, 5%)
|
||||
|
||||
@@ -242,3 +250,6 @@
|
||||
p
|
||||
margin: 7px
|
||||
padding: 0
|
||||
|
||||
#headerIsSettingDatabaseCallDone
|
||||
display: none;
|
||||
@@ -1,6 +1,6 @@
|
||||
head
|
||||
title
|
||||
meta(name="viewport" content="maximum-scale=1.0,width=device-width,initial-scale=1.0,user-scalable=0")
|
||||
meta(name="viewport" content="width=device-width, initial-scale=1")
|
||||
meta(http-equiv="X-UA-Compatible" content="IE=edge")
|
||||
//- XXX We should use pathFor in the following `href` to support the case
|
||||
where the application is deployed with a path prefix, but it seems to be
|
||||
@@ -34,8 +34,9 @@ template(name="userFormsLayout")
|
||||
img(src="{{currentSetting.customLoginLogoImageUrl}}" width="300" height="auto")
|
||||
br
|
||||
unless currentSetting.customLoginLogoImageUrl
|
||||
img(src="{{pathFor '/wekan-logo.svg'}}" alt="" width="300" height="auto")
|
||||
br
|
||||
div#isSettingDatabaseCallDone
|
||||
img(src="{{pathFor '/wekan-logo.svg'}}" alt="" width="300" height="auto")
|
||||
br
|
||||
if currentSetting.textBelowCustomLoginLogo
|
||||
+viewer
|
||||
| {{currentSetting.textBelowCustomLoginLogo}}
|
||||
@@ -47,6 +48,13 @@ template(name="userFormsLayout")
|
||||
+Template.dynamic(template=content)
|
||||
if currentSetting.displayAuthenticationMethod
|
||||
+connectionMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
|
||||
if isLegalNoticeLinkExist
|
||||
div#legalNoticeDiv
|
||||
span#legalNoticeSpan {{_ 'acceptance_of_our_legalNotice'}}
|
||||
a#legalNoticeAtLink.at-link(href="{{currentSetting.legalNotice}}", target="_blank", rel="noopener noreferrer")
|
||||
| {{_ 'legalNotice'}}
|
||||
if getLegalNoticeWithWritTraduction
|
||||
div
|
||||
div.at-form-lang
|
||||
select.select-lang.js-userform-set-language
|
||||
each languages
|
||||
|
||||
@@ -6,6 +6,9 @@ const i18nTagToT9n = i18nTag => {
|
||||
return i18nTag;
|
||||
};
|
||||
|
||||
let alreadyCheck = 1;
|
||||
let isCheckDone = false;
|
||||
|
||||
const validator = {
|
||||
set(obj, prop, value) {
|
||||
if (prop === 'state' && value !== 'signIn') {
|
||||
@@ -20,6 +23,8 @@ const validator = {
|
||||
},
|
||||
};
|
||||
|
||||
// let isSettingDatabaseFctCallDone = false;
|
||||
|
||||
Template.userFormsLayout.onCreated(function() {
|
||||
const templateInstance = this;
|
||||
templateInstance.currentSetting = new ReactiveVar();
|
||||
@@ -28,6 +33,18 @@ Template.userFormsLayout.onCreated(function() {
|
||||
Meteor.subscribe('setting', {
|
||||
onReady() {
|
||||
templateInstance.currentSetting.set(Settings.findOne());
|
||||
let currSetting = templateInstance.currentSetting.curValue;
|
||||
let oidcBtnElt = $("#at-oidc");
|
||||
if(currSetting && currSetting !== undefined && currSetting.oidcBtnText !== undefined && oidcBtnElt != null && oidcBtnElt != undefined){
|
||||
let htmlvalue = "<i class='fa fa-oidc'></i>" + currSetting.oidcBtnText;
|
||||
oidcBtnElt.html(htmlvalue);
|
||||
}
|
||||
|
||||
// isSettingDatabaseFctCallDone = true;
|
||||
if(currSetting && currSetting !== undefined && currSetting.customLoginLogoImageUrl !== undefined)
|
||||
document.getElementById("isSettingDatabaseCallDone").style.display = 'none';
|
||||
else
|
||||
document.getElementById("isSettingDatabaseCallDone").style.display = 'block';
|
||||
return this.stop();
|
||||
},
|
||||
});
|
||||
@@ -36,6 +53,19 @@ Template.userFormsLayout.onCreated(function() {
|
||||
$('.at-pwd-form').hide();
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.call('isDisableRegistration', (_, result) => {
|
||||
if (result) {
|
||||
$('.at-signup-link').hide();
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.call('isDisableForgotPassword', (_, result) => {
|
||||
if (result) {
|
||||
$('.at-pwd-link').hide();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Template.userFormsLayout.onRendered(() => {
|
||||
@@ -56,6 +86,31 @@ Template.userFormsLayout.helpers({
|
||||
return Template.instance().currentSetting.get();
|
||||
},
|
||||
|
||||
// isSettingDatabaseCallDone(){
|
||||
// return isSettingDatabaseFctCallDone;
|
||||
// },
|
||||
|
||||
isLegalNoticeLinkExist(){
|
||||
const currSet = Template.instance().currentSetting.get();
|
||||
if(currSet && currSet !== undefined && currSet != null){
|
||||
return currSet.legalNotice !== undefined && currSet.legalNotice.trim() != "";
|
||||
}
|
||||
else
|
||||
return false;
|
||||
},
|
||||
|
||||
getLegalNoticeWithWritTraduction(){
|
||||
let spanLegalNoticeElt = $("#legalNoticeSpan");
|
||||
if(spanLegalNoticeElt != null && spanLegalNoticeElt != undefined){
|
||||
spanLegalNoticeElt.html(TAPi18n.__('acceptance_of_our_legalNotice', {}, T9n.getLanguage() || 'en'));
|
||||
}
|
||||
let atLinkLegalNoticeElt = $("#legalNoticeAtLink");
|
||||
if(atLinkLegalNoticeElt != null && atLinkLegalNoticeElt != undefined){
|
||||
atLinkLegalNoticeElt.html(TAPi18n.__('legalNotice', {}, T9n.getLanguage() || 'en'));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
isLoading() {
|
||||
return Template.instance().isLoading.get();
|
||||
},
|
||||
@@ -79,6 +134,12 @@ Template.userFormsLayout.helpers({
|
||||
name = 'مَصرى';
|
||||
} else if (lang.name === 'de-CH') {
|
||||
name = 'Deutsch (Schweiz)';
|
||||
} else if (lang.name === 'de-AT') {
|
||||
name = 'Deutsch (Österreich)';
|
||||
} else if (lang.name === 'en-DE') {
|
||||
name = 'English (Germany)';
|
||||
} else if (lang.name === 'et-EE') {
|
||||
name = 'eesti keel (Eesti)';
|
||||
} else if (lang.name === 'fa-IR') {
|
||||
// fa-IR = Persian (Iran)
|
||||
name = 'فارسی/پارسی (ایران)';
|
||||
@@ -86,14 +147,28 @@ Template.userFormsLayout.helpers({
|
||||
name = 'Français (Belgique)';
|
||||
} else if (lang.name === 'fr-CA') {
|
||||
name = 'Français (Canada)';
|
||||
} else if (lang.name === 'fr-CH') {
|
||||
name = 'Français (Schweiz)';
|
||||
} else if (lang.name === 'gu-IN') {
|
||||
// gu-IN = Gurajati (India)
|
||||
name = 'ગુજરાતી';
|
||||
} else if (lang.name === 'hi-IN') {
|
||||
// hi-IN = Hindi (India)
|
||||
name = 'हिंदी (भारत)';
|
||||
} else if (lang.name === 'ig') {
|
||||
name = 'Igbo';
|
||||
} else if (lang.name === 'lv') {
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'latviešu valoda') {
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'ms-MY') {
|
||||
// ms-MY = Malay (Malaysia)
|
||||
name = 'بهاس ملايو';
|
||||
} else if (lang.name === 'en-IT') {
|
||||
name = 'English (Italy)';
|
||||
} else if (lang.name === 'el-GR') {
|
||||
// el-GR = Greek (Greece)
|
||||
name = 'Ελληνικά (Ελλάδα)';
|
||||
} else if (lang.name === 'Español') {
|
||||
name = 'español';
|
||||
} else if (lang.name === 'es_419') {
|
||||
@@ -120,11 +195,18 @@ Template.userFormsLayout.helpers({
|
||||
name = 'español de Perú';
|
||||
} else if (lang.name === 'Español de Puerto Rico') {
|
||||
name = 'español de Puerto Rico';
|
||||
} else if (lang.name === 'gl-ES') {
|
||||
name = 'Galego (España)';
|
||||
} else if (lang.name === 'oc') {
|
||||
name = 'Occitan';
|
||||
} else if (lang.name === 'ru-UA') {
|
||||
name = 'Русский (Украина)';
|
||||
} else if (lang.name === 'st') {
|
||||
name = 'Sãotomense';
|
||||
} else if (lang.name === 'uk-UA') {
|
||||
name = 'українська (Україна)';
|
||||
} else if (lang.name === '繁体中文(台湾)') {
|
||||
// Traditional Chinese (Taiwan)
|
||||
name = '繁體中文(台灣)';
|
||||
}
|
||||
return { tag, name };
|
||||
@@ -157,6 +239,53 @@ Template.userFormsLayout.events({
|
||||
templateInstance.isLoading.set(false);
|
||||
});
|
||||
}
|
||||
isCheckDone = false;
|
||||
},
|
||||
'click #at-signUp'(event, templateInstance){
|
||||
isCheckDone = false;
|
||||
},
|
||||
'DOMSubtreeModified #at-oidc'(event){
|
||||
if(alreadyCheck <= 2){
|
||||
let currSetting = Settings.findOne();
|
||||
let oidcBtnElt = $("#at-oidc");
|
||||
if(currSetting && currSetting !== undefined && currSetting.oidcBtnText !== undefined && oidcBtnElt != null && oidcBtnElt != undefined){
|
||||
let htmlvalue = "<i class='fa fa-oidc'></i>" + currSetting.oidcBtnText;
|
||||
if(alreadyCheck == 1){
|
||||
alreadyCheck++;
|
||||
oidcBtnElt.html("");
|
||||
}
|
||||
else{
|
||||
alreadyCheck++;
|
||||
oidcBtnElt.html(htmlvalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
alreadyCheck = 1;
|
||||
}
|
||||
},
|
||||
'DOMSubtreeModified .at-form'(event){
|
||||
if(alreadyCheck <= 2 && !isCheckDone){
|
||||
if(document.getElementById("at-oidc") != null){
|
||||
let currSetting = Settings.findOne();
|
||||
let oidcBtnElt = $("#at-oidc");
|
||||
if(currSetting && currSetting !== undefined && currSetting.oidcBtnText !== undefined && oidcBtnElt != null && oidcBtnElt != undefined){
|
||||
let htmlvalue = "<i class='fa fa-oidc'></i>" + currSetting.oidcBtnText;
|
||||
if(alreadyCheck == 1){
|
||||
alreadyCheck++;
|
||||
oidcBtnElt.html("");
|
||||
}
|
||||
else{
|
||||
alreadyCheck++;
|
||||
isCheckDone = true;
|
||||
oidcBtnElt.html(htmlvalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
alreadyCheck = 1;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -317,6 +317,9 @@ kbd
|
||||
.grabbing
|
||||
cursor: grabbing
|
||||
|
||||
.textcomplete-dropdown
|
||||
z-index: 2000 !important;
|
||||
|
||||
// Implement a thiner close icon as suggested in
|
||||
// https://github.com/FortAwesome/Font-Awesome/issues/1540#issuecomment-68689950
|
||||
.fa.fa-times-thin:before
|
||||
@@ -430,7 +433,7 @@ a
|
||||
margin-top: 0px
|
||||
|
||||
.wrapper
|
||||
height: 100%
|
||||
height: calc(100% - 31px)
|
||||
margin: 0px
|
||||
|
||||
.panel-default
|
||||
@@ -539,3 +542,11 @@ a
|
||||
|
||||
100%
|
||||
transform: rotate(360deg)
|
||||
|
||||
#isSettingDatabaseCallDone
|
||||
display: none;
|
||||
|
||||
.at-link
|
||||
color: #17683a;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #17683a;
|
||||
@@ -14,8 +14,7 @@ $popupWidth = 300px
|
||||
margin-top: 5px
|
||||
|
||||
hr
|
||||
margin: 4px -10px
|
||||
width: $popupWidth
|
||||
margin: 4px 0px
|
||||
|
||||
p,
|
||||
textarea,
|
||||
@@ -23,7 +22,6 @@ $popupWidth = 300px
|
||||
input[type="email"],
|
||||
input[type="password"],
|
||||
input[type="file"]
|
||||
margin: 4px 0 12px
|
||||
width: 100%
|
||||
|
||||
select
|
||||
@@ -313,22 +311,13 @@ $popupWidth = 300px
|
||||
input[type="email"],
|
||||
input[type="password"],
|
||||
input[type="file"]
|
||||
margin: 4px 0 12px
|
||||
width: 100%
|
||||
box-sizing: border-box
|
||||
|
||||
.pop-over-list
|
||||
li > a
|
||||
width: calc(100% - 20px)
|
||||
padding: 10px 10px
|
||||
margin: 0px 0px
|
||||
border-bottom: 1px solid #eee
|
||||
|
||||
hr
|
||||
width: 100%
|
||||
height: 20px
|
||||
margin: 0px 0px
|
||||
color: #eee
|
||||
|
||||
for depth in (1..6)
|
||||
.popup-container-depth-{depth}
|
||||
|
||||
@@ -232,7 +232,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
'click .js-submit'() {
|
||||
this.colorButtonValue.set(this.currentColor.get());
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
.trigger-item
|
||||
overflow:auto
|
||||
padding:10px
|
||||
height:40px
|
||||
height:100px
|
||||
margin-bottom:5px
|
||||
border-radius: 3px
|
||||
position: relative
|
||||
|
||||
@@ -116,6 +116,6 @@ Template.boardCardTitlePopup.events({
|
||||
.trim();
|
||||
Popup.getOpenerComponent().setNameFilter(title);
|
||||
event.preventDefault();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
template(name="adminReports")
|
||||
.setting-content
|
||||
.setting-content.admin-reports-content
|
||||
unless currentUser.isAdmin
|
||||
| {{_ 'error-notAuthorized'}}
|
||||
else
|
||||
@@ -26,6 +26,16 @@ template(name="adminReports")
|
||||
i.fa.fa-magic
|
||||
| {{_ 'rulesReportTitle'}}
|
||||
|
||||
li
|
||||
a.js-report-boards(data-id="report-boards")
|
||||
i.fa.fa-magic
|
||||
| {{_ 'boardsReportTitle'}}
|
||||
|
||||
li
|
||||
a.js-report-cards(data-id="report-cards")
|
||||
i.fa.fa-magic
|
||||
| {{_ 'cardsReportTitle'}}
|
||||
|
||||
.main-body
|
||||
if loading.get
|
||||
+spinner
|
||||
@@ -37,6 +47,10 @@ template(name="adminReports")
|
||||
+orphanedFilesReport
|
||||
else if showRulesReport.get
|
||||
+rulesReport
|
||||
else if showBoardsReport.get
|
||||
+boardsReport
|
||||
else if showCardsReport.get
|
||||
+cardsReport
|
||||
|
||||
|
||||
template(name="brokenCardsReport")
|
||||
@@ -57,7 +71,7 @@ template(name="rulesReport")
|
||||
th actionType
|
||||
th activityType
|
||||
|
||||
each rule in rows
|
||||
each rule in results
|
||||
tr
|
||||
td {{ rule.title }}
|
||||
td {{ rule.boardTitle }}
|
||||
@@ -78,7 +92,7 @@ template(name="filesReport")
|
||||
th MD5 Sum
|
||||
th ID
|
||||
|
||||
each att in attachmentFiles
|
||||
each att in results
|
||||
tr
|
||||
td {{ att.filename }}
|
||||
td.right {{fileSize att.length }}
|
||||
@@ -100,7 +114,7 @@ template(name="orphanedFilesReport")
|
||||
th MD5 Sum
|
||||
th ID
|
||||
|
||||
each att in attachmentFiles
|
||||
each att in results
|
||||
tr
|
||||
td {{ att.filename }}
|
||||
td.right {{fileSize att.length }}
|
||||
@@ -109,3 +123,50 @@ template(name="orphanedFilesReport")
|
||||
td {{ att._id.toHexString }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
template(name="cardsReport")
|
||||
h1 {{_ 'cardsReportTitle'}}
|
||||
if resultsCount
|
||||
table.table
|
||||
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 }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
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
|
||||
tr
|
||||
td {{abbreviate board.title }}
|
||||
td {{abbreviate board._id }}
|
||||
td {{ board.permission }}
|
||||
td
|
||||
= yesOrNo(board.archived)
|
||||
td {{userNames board.members }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
@@ -1,6 +1,8 @@
|
||||
import { AttachmentStorage } from '/models/attachments';
|
||||
import { CardSearchPagedComponent } from '/client/lib/cardSearch';
|
||||
import SessionData from '/models/usersessiondata';
|
||||
import { QueryParams } from '/config/query-classes';
|
||||
import { OPERATOR_LIMIT } from '/config/search-const';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
subscription: null,
|
||||
@@ -8,10 +10,14 @@ BlazeComponent.extendComponent({
|
||||
showBrokenCardsReport: new ReactiveVar(false),
|
||||
showOrphanedFilesReport: new ReactiveVar(false),
|
||||
showRulesReport: new ReactiveVar(false),
|
||||
showCardsReport: new ReactiveVar(false),
|
||||
showBoardsReport: new ReactiveVar(false),
|
||||
sessionId: null,
|
||||
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.sessionId = SessionData.getSessionId();
|
||||
},
|
||||
|
||||
events() {
|
||||
@@ -21,6 +27,8 @@ BlazeComponent.extendComponent({
|
||||
'click a.js-report-files': this.switchMenu,
|
||||
'click a.js-report-orphaned-files': this.switchMenu,
|
||||
'click a.js-report-rules': this.switchMenu,
|
||||
'click a.js-report-cards': this.switchMenu,
|
||||
'click a.js-report-boards': this.switchMenu,
|
||||
},
|
||||
];
|
||||
},
|
||||
@@ -32,6 +40,9 @@ BlazeComponent.extendComponent({
|
||||
this.showFilesReport.set(false);
|
||||
this.showBrokenCardsReport.set(false);
|
||||
this.showOrphanedFilesReport.set(false);
|
||||
this.showRulesReport.set(false)
|
||||
this.showBoardsReport.set(false);
|
||||
this.showCardsReport.set(false);
|
||||
if (this.subscription) {
|
||||
this.subscription.stop();
|
||||
}
|
||||
@@ -64,68 +75,79 @@ BlazeComponent.extendComponent({
|
||||
this.showRulesReport.set(true);
|
||||
this.loading.set(false);
|
||||
});
|
||||
} else if ('report-boards' === targetID) {
|
||||
this.subscription = Meteor.subscribe('boardsReport', () => {
|
||||
this.showBoardsReport.set(true);
|
||||
this.loading.set(false);
|
||||
});
|
||||
} else if ('report-cards' === targetID) {
|
||||
const qp = new QueryParams();
|
||||
qp.addPredicate(OPERATOR_LIMIT, 300);
|
||||
this.subscription = Meteor.subscribe(
|
||||
'globalSearch',
|
||||
this.sessionId,
|
||||
qp.getParams(),
|
||||
qp.text,
|
||||
() => {
|
||||
this.showCardsReport.set(true);
|
||||
this.loading.set(false);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
}).register('adminReports');
|
||||
|
||||
Template.filesReport.helpers({
|
||||
attachmentFiles() {
|
||||
class AdminReport extends BlazeComponent {
|
||||
collection;
|
||||
|
||||
results() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('attachments:', AttachmentStorage.find());
|
||||
// console.log('attachments.count:', AttachmentStorage.find().count());
|
||||
return AttachmentStorage.find();
|
||||
},
|
||||
return this.collection.find();
|
||||
}
|
||||
|
||||
rulesReport() {
|
||||
const rules = [];
|
||||
|
||||
Rules.find().forEach(rule => {
|
||||
rules.push({
|
||||
_id: rule._id,
|
||||
title: rule.title,
|
||||
boardId: rule.boardId,
|
||||
boardTitle: rule.board().title,
|
||||
action: rule.action().fetch(),
|
||||
trigger: rule.trigger().fetch(),
|
||||
});
|
||||
});
|
||||
|
||||
return rules;
|
||||
},
|
||||
yesOrNo(value) {
|
||||
if (value) {
|
||||
return TAPi18n.__('yes');
|
||||
} else {
|
||||
return TAPi18n.__('no');
|
||||
}
|
||||
}
|
||||
|
||||
resultsCount() {
|
||||
return AttachmentStorage.find().count();
|
||||
},
|
||||
return this.collection.find().count();
|
||||
}
|
||||
|
||||
fileSize(size) {
|
||||
return Math.round(size / 1024);
|
||||
},
|
||||
}
|
||||
|
||||
usageCount(key) {
|
||||
return Attachments.find({ 'copies.attachments.key': key }).count();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Template.orphanedFilesReport.helpers({
|
||||
attachmentFiles() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('attachments:', AttachmentStorage.find());
|
||||
// console.log('attachments.count:', AttachmentStorage.find().count());
|
||||
return AttachmentStorage.find();
|
||||
},
|
||||
abbreviate(text) {
|
||||
if (text.length > 30) {
|
||||
return `${text.substr(0, 29)}...`;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
resultsCount() {
|
||||
return AttachmentStorage.find().count();
|
||||
},
|
||||
(class extends AdminReport {
|
||||
collection = AttachmentStorage;
|
||||
}.register('filesReport'));
|
||||
|
||||
fileSize(size) {
|
||||
return Math.round(size / 1024);
|
||||
},
|
||||
});
|
||||
(class extends AdminReport {
|
||||
collection = AttachmentStorage;
|
||||
}.register('orphanedFilesReport'));
|
||||
|
||||
Template.rulesReport.helpers({
|
||||
rows() {
|
||||
(class extends AdminReport {
|
||||
collection = Rules;
|
||||
|
||||
results() {
|
||||
const rules = [];
|
||||
|
||||
Rules.find().forEach(rule => {
|
||||
@@ -139,14 +161,43 @@ Template.rulesReport.helpers({
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rows:', rules);
|
||||
return rules;
|
||||
},
|
||||
}
|
||||
}.register('rulesReport'));
|
||||
|
||||
resultsCount() {
|
||||
return Rules.find().count();
|
||||
},
|
||||
});
|
||||
(class extends AdminReport {
|
||||
collection = Boards;
|
||||
|
||||
userNames(members) {
|
||||
let text = '';
|
||||
members.forEach(member => {
|
||||
const user = Users.findOne(member.userId);
|
||||
text += text ? ', ' : '';
|
||||
if (user) {
|
||||
text += user.username;
|
||||
} else {
|
||||
text += member.userId
|
||||
}
|
||||
});
|
||||
return text;
|
||||
}
|
||||
}.register('boardsReport'));
|
||||
|
||||
(class extends AdminReport {
|
||||
collection = Cards;
|
||||
|
||||
userNames(userIds) {
|
||||
let text = '';
|
||||
userIds.forEach(userId => {
|
||||
const user = Users.findOne(userId);
|
||||
text += text ? ', ' : '';
|
||||
text += user.username;
|
||||
});
|
||||
return text;
|
||||
}
|
||||
}.register('cardsReport'));
|
||||
|
||||
class BrokenCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.admin-reports-content
|
||||
height: auto !important
|
||||
|
||||
@@ -21,7 +21,7 @@ template(name='statistics')
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
th Wekan {{_ 'info'}}
|
||||
th WeKan ® {{_ 'info'}}
|
||||
td {{statistics.version}}
|
||||
tr
|
||||
th {{_ 'Meteor_version'}}
|
||||
@@ -65,3 +65,49 @@ template(name='statistics')
|
||||
tr
|
||||
th {{_ 'OS_Cpus'}}
|
||||
td {{statistics.os.cpus.length}}
|
||||
unless isSandstorm
|
||||
tr
|
||||
th {{_ 'Node_heap_total_heap_size'}}
|
||||
td {{bytesToSize statistics.nodeHeapStats.totalHeapSize}}
|
||||
tr
|
||||
th {{_ 'Node_heap_total_heap_size_executable'}}
|
||||
td {{bytesToSize statistics.nodeHeapStats.totalHeapSizeExecutable}}
|
||||
tr
|
||||
th {{_ 'Node_heap_total_physical_size'}}
|
||||
td {{bytesToSize statistics.nodeHeapStats.totalPhysicalSize}}
|
||||
tr
|
||||
th {{_ 'Node_heap_total_available_size'}}
|
||||
td {{bytesToSize statistics.nodeHeapStats.totalAvailableSize}}
|
||||
tr
|
||||
th {{_ 'Node_heap_used_heap_size'}}
|
||||
td {{bytesToSize statistics.nodeHeapStats.usedHeapSize}}
|
||||
tr
|
||||
th {{_ 'Node_heap_heap_size_limit'}}
|
||||
td {{bytesToSize statistics.nodeHeapStats.heapSizeLimit}}
|
||||
tr
|
||||
th {{_ 'Node_heap_malloced_memory'}}
|
||||
td {{bytesToSize statistics.nodeHeapStats.mallocedMemory}}
|
||||
tr
|
||||
th {{_ 'Node_heap_peak_malloced_memory'}}
|
||||
td {{bytesToSize statistics.nodeHeapStats.peakMallocedMemory}}
|
||||
tr
|
||||
th {{_ 'Node_heap_does_zap_garbage'}}
|
||||
td {{statistics.nodeHeapStats.doesZapGarbage}}
|
||||
tr
|
||||
th {{_ 'Node_heap_number_of_native_contexts'}}
|
||||
td {{statistics.nodeHeapStats.numberOfNativeContexts}}
|
||||
tr
|
||||
th {{_ 'Node_heap_number_of_detached_contexts'}}
|
||||
td {{statistics.nodeHeapStats.numberOfDetachedContexts}}
|
||||
tr
|
||||
th {{_ 'Node_memory_usage_rss'}}
|
||||
td {{bytesToSize statistics.nodeMemoryUsage.rss}}
|
||||
tr
|
||||
th {{_ 'Node_memory_usage_heap_total'}}
|
||||
td {{bytesToSize statistics.nodeMemoryUsage.heapTotal}}
|
||||
tr
|
||||
th {{_ 'Node_memory_usage_heap_used'}}
|
||||
td {{bytesToSize statistics.nodeMemoryUsage.heapUsed}}
|
||||
tr
|
||||
th {{_ 'Node_memory_usage_external'}}
|
||||
td {{bytesToSize statistics.nodeMemoryUsage.external}}
|
||||
@@ -40,6 +40,11 @@ template(name="people")
|
||||
| {{_ 'search'}}
|
||||
.ext-box-right
|
||||
span {{#unless isMiniScreen}}{{_ 'people-number'}}{{/unless}} #{peopleNumber}
|
||||
.divAddOrRemoveTeam#divAddOrRemoveTeam
|
||||
button#addOrRemoveTeam
|
||||
i.fa.fa-edit
|
||||
| {{_ 'add'}} / {{_ 'delete'}} {{_ 'teams'}}
|
||||
|
||||
.content-body
|
||||
.side-menu
|
||||
ul
|
||||
@@ -97,9 +102,13 @@ template(name="teamGeneral")
|
||||
+teamRow(teamId=team._id)
|
||||
|
||||
template(name="peopleGeneral")
|
||||
#divAddOrRemoveTeamContainer
|
||||
+modifyTeamsUsers
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
th
|
||||
+selectAllUser
|
||||
th {{_ 'username'}}
|
||||
th {{_ 'fullname'}}
|
||||
th {{_ 'initials'}}
|
||||
@@ -117,6 +126,10 @@ template(name="peopleGeneral")
|
||||
each user in peopleList
|
||||
+peopleRow(userId=user._id)
|
||||
|
||||
template(name="selectAllUser")
|
||||
| {{_ 'dueCardsViewChange-choice-all'}}
|
||||
input.allUserChkBox(type="checkbox", id="chkSelectAll")
|
||||
|
||||
template(name="newOrgRow")
|
||||
a.new-org
|
||||
i.fa.fa-plus-square
|
||||
@@ -202,6 +215,12 @@ template(name="teamRow")
|
||||
|
||||
template(name="peopleRow")
|
||||
tr
|
||||
if userData.loginDisabled
|
||||
td
|
||||
input.selectUserChkBox(type="checkbox", disabled="disabled", id="{{userData._id}}")
|
||||
else
|
||||
td
|
||||
input.selectUserChkBox(type="checkbox", id="{{userData._id}}")
|
||||
if userData.loginDisabled
|
||||
td.username <s>{{ userData.username }}</s>
|
||||
else
|
||||
@@ -342,7 +361,7 @@ template(name="editUserPopup")
|
||||
input.js-profile-fullname(type="text" value=user.profile.fullname required)
|
||||
label
|
||||
| {{_ 'initials'}}
|
||||
input.js-profile-initials(type="text" value=user.profile.initials required)
|
||||
input.js-profile-initials(type="text" value=user.profile.initials)
|
||||
label
|
||||
| {{_ 'admin'}}
|
||||
select.select-role.js-profile-isadmin
|
||||
@@ -453,6 +472,24 @@ template(name="newTeamPopup")
|
||||
div.buttonsContainer
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
|
||||
template(name="modifyTeamsUsers")
|
||||
label
|
||||
| {{_ 'teams'}}
|
||||
select.js-teamsUser#jsteamsUser
|
||||
each value in teamsDatas
|
||||
option(value="{{value._id}}") {{_ value.teamDisplayName}}
|
||||
hr
|
||||
label
|
||||
| {{_ 'r-action'}}
|
||||
.form-group.flex
|
||||
input.wekan-form-control#addAction(type="radio" name="action" value="true" checked="checked")
|
||||
span {{_ 'add'}}
|
||||
input.wekan-form-control#deleteAction(type="radio" name="action" value="false")
|
||||
span {{_ 'delete'}}
|
||||
div.buttonsContainer
|
||||
input.primary.wide#addTeamBtn(type="submit" value="{{_ 'save'}}")
|
||||
input.primary.wide#cancelBtn(type="submit" value="{{_ 'cancel'}}")
|
||||
|
||||
template(name="newUserPopup")
|
||||
form
|
||||
//label.hide.userId(type="text" value=user._id)
|
||||
@@ -469,7 +506,7 @@ template(name="newUserPopup")
|
||||
input.js-profile-username(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'initials'}}
|
||||
input.js-profile-initials(type="text" value="" required)
|
||||
input.js-profile-initials(type="text" value="")
|
||||
label
|
||||
| {{_ 'email'}}
|
||||
span.error.hide.email-taken
|
||||
@@ -571,10 +608,14 @@ template(name="settingsUserPopup")
|
||||
a.impersonate-user
|
||||
i.fa.fa-user
|
||||
| {{_ 'impersonate-user'}}
|
||||
br
|
||||
hr
|
||||
li
|
||||
form
|
||||
label.hide.userId(type="text" value=user._id)
|
||||
label
|
||||
| {{_ 'delete-user-confirm-popup' }}
|
||||
br
|
||||
div.buttonsContainer
|
||||
input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
|
||||
// Delete is enabled, but there is still bug of leaving empty user avatars
|
||||
|
||||
@@ -2,6 +2,7 @@ const orgsPerPage = 25;
|
||||
const teamsPerPage = 25;
|
||||
const usersPerPage = 25;
|
||||
let userOrgsTeamsAction = ""; //poosible actions 'addOrg', 'addTeam', 'removeOrg' or 'removeTeam' when adding or modifying a user
|
||||
let selectedUserChkBoxUserIds = [];
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
mixins() {
|
||||
@@ -81,6 +82,9 @@ BlazeComponent.extendComponent({
|
||||
'click #searchButton'() {
|
||||
this.filterPeople();
|
||||
},
|
||||
'click #addOrRemoveTeam'(){
|
||||
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'block';
|
||||
},
|
||||
'keydown #searchInput'(event) {
|
||||
if (event.keyCode === 13 && !event.shiftKey) {
|
||||
this.filterPeople();
|
||||
@@ -140,6 +144,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
orgList() {
|
||||
const orgs = Org.find(this.findOrgsOptions.get(), {
|
||||
sort: { orgDisplayName: 1 },
|
||||
fields: { _id: true },
|
||||
});
|
||||
this.numberOrgs.set(orgs.count(false));
|
||||
@@ -147,6 +152,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
teamList() {
|
||||
const teams = Team.find(this.findTeamsOptions.get(), {
|
||||
sort: { teamDisplayName: 1 },
|
||||
fields: { _id: true },
|
||||
});
|
||||
this.numberTeams.set(teams.count(false));
|
||||
@@ -154,6 +160,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
peopleList() {
|
||||
const users = Users.find(this.findUsersOptions.get(), {
|
||||
sort: { username: 1 },
|
||||
fields: { _id: true },
|
||||
});
|
||||
this.numberPeople.set(users.count(false));
|
||||
@@ -247,10 +254,10 @@ Template.editUserPopup.helpers({
|
||||
return Template.instance().authenticationMethods.get();
|
||||
},
|
||||
orgsDatas() {
|
||||
return Org.find({}, {sort: { createdAt: -1 }});
|
||||
return Org.find({}, {sort: { orgDisplayName: 1 }});
|
||||
},
|
||||
teamsDatas() {
|
||||
return Team.find({}, {sort: { createdAt: -1 }});
|
||||
return Team.find({}, {sort: { teamDisplayName: 1 }});
|
||||
},
|
||||
isSelected(match) {
|
||||
const userId = Template.instance().data.userId;
|
||||
@@ -320,10 +327,10 @@ Template.newUserPopup.helpers({
|
||||
return Template.instance().authenticationMethods.get();
|
||||
},
|
||||
orgsDatas() {
|
||||
return Org.find({}, {sort: { createdAt: -1 }});
|
||||
return Org.find({}, {sort: { orgDisplayName: 1 }});
|
||||
},
|
||||
teamsDatas() {
|
||||
return Team.find({}, {sort: { createdAt: -1 }});
|
||||
return Team.find({}, {sort: { teamDisplayName: 1 }});
|
||||
},
|
||||
isSelected(match) {
|
||||
const userId = Template.instance().data.userId;
|
||||
@@ -385,11 +392,111 @@ BlazeComponent.extendComponent({
|
||||
{
|
||||
'click a.edit-user': Popup.open('editUser'),
|
||||
'click a.more-settings-user': Popup.open('settingsUser'),
|
||||
'click .selectUserChkBox': function(ev){
|
||||
if(ev.currentTarget){
|
||||
if(ev.currentTarget.checked){
|
||||
if(!selectedUserChkBoxUserIds.includes(ev.currentTarget.id)){
|
||||
selectedUserChkBoxUserIds.push(ev.currentTarget.id);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(selectedUserChkBoxUserIds.includes(ev.currentTarget.id)){
|
||||
let index = selectedUserChkBoxUserIds.indexOf(ev.currentTarget.id);
|
||||
if(index > -1)
|
||||
selectedUserChkBoxUserIds.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(selectedUserChkBoxUserIds.length > 0)
|
||||
document.getElementById("divAddOrRemoveTeam").style.display = 'block';
|
||||
else
|
||||
document.getElementById("divAddOrRemoveTeam").style.display = 'none';
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('peopleRow');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {},
|
||||
teamsDatas() {
|
||||
return Team.find({}, {sort: { teamDisplayName: 1 }});
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click #cancelBtn': function(){
|
||||
let selectedElt = document.getElementById("jsteamsUser");
|
||||
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'none';
|
||||
},
|
||||
'click #addTeamBtn': function(){
|
||||
let selectedElt;
|
||||
let selectedEltValue;
|
||||
let selectedEltValueId;
|
||||
let userTms = [];
|
||||
let currentUser;
|
||||
let currUserTeamIndex;
|
||||
|
||||
selectedElt = document.getElementById("jsteamsUser");
|
||||
selectedEltValue = selectedElt.options[selectedElt.selectedIndex].text;
|
||||
selectedEltValueId = selectedElt.options[selectedElt.selectedIndex].value;
|
||||
|
||||
if(document.getElementById('addAction').checked){
|
||||
for(let i = 0; i < selectedUserChkBoxUserIds.length; i++){
|
||||
currentUser = Users.findOne(selectedUserChkBoxUserIds[i]);
|
||||
userTms = currentUser.teams;
|
||||
if(userTms == undefined || userTms.length == 0){
|
||||
userTms = [];
|
||||
userTms.push({
|
||||
"teamId": selectedEltValueId,
|
||||
"teamDisplayName": selectedEltValue,
|
||||
})
|
||||
}
|
||||
else if(userTms.length > 0)
|
||||
{
|
||||
currUserTeamIndex = userTms.findIndex(function(t){ return t.teamId == selectedEltValueId});
|
||||
if(currUserTeamIndex == -1){
|
||||
userTms.push({
|
||||
"teamId": selectedEltValueId,
|
||||
"teamDisplayName": selectedEltValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Users.update(selectedUserChkBoxUserIds[i], {
|
||||
$set:{
|
||||
teams: userTms
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else{
|
||||
for(let i = 0; i < selectedUserChkBoxUserIds.length; i++){
|
||||
currentUser = Users.findOne(selectedUserChkBoxUserIds[i]);
|
||||
userTms = currentUser.teams;
|
||||
if(userTms !== undefined || userTms.length > 0)
|
||||
{
|
||||
currUserTeamIndex = userTms.findIndex(function(t){ return t.teamId == selectedEltValueId});
|
||||
if(currUserTeamIndex != -1){
|
||||
userTms.splice(currUserTeamIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Users.update(selectedUserChkBoxUserIds[i], {
|
||||
$set:{
|
||||
teams: userTms
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'none';
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('modifyTeamsUsers');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
@@ -420,6 +527,41 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('newUserRow');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .allUserChkBox': function(ev){
|
||||
selectedUserChkBoxUserIds = [];
|
||||
const checkboxes = document.getElementsByClassName("selectUserChkBox");
|
||||
if(ev.currentTarget){
|
||||
if(ev.currentTarget.checked){
|
||||
for (let i=0; i<checkboxes.length; i++) {
|
||||
if (!checkboxes[i].disabled) {
|
||||
selectedUserChkBoxUserIds.push(checkboxes[i].id);
|
||||
checkboxes[i].checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
for (let i=0; i<checkboxes.length; i++) {
|
||||
if (!checkboxes[i].disabled) {
|
||||
checkboxes[i].checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(selectedUserChkBoxUserIds.length > 0)
|
||||
document.getElementById("divAddOrRemoveTeam").style.display = 'block';
|
||||
else
|
||||
document.getElementById("divAddOrRemoveTeam").style.display = 'none';
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('selectAllUser');
|
||||
|
||||
Template.editOrgPopup.events({
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
@@ -431,8 +573,7 @@ Template.editOrgPopup.events({
|
||||
const orgDesc = templateInstance.find('.js-orgDesc').value.trim();
|
||||
const orgShortName = templateInstance.find('.js-orgShortName').value.trim();
|
||||
const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim();
|
||||
const orgIsActive =
|
||||
templateInstance.find('.js-org-isactive').value.trim() == 'true';
|
||||
const orgIsActive = templateInstance.find('.js-org-isactive').value.trim() == 'true';
|
||||
|
||||
const isChangeOrgDisplayName = orgDisplayName !== org.orgDisplayName;
|
||||
const isChangeOrgDesc = orgDesc !== org.orgDesc;
|
||||
@@ -458,7 +599,7 @@ Template.editOrgPopup.events({
|
||||
);
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -502,7 +643,7 @@ Template.editTeamPopup.events({
|
||||
);
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -617,7 +758,7 @@ Template.editUserPopup.events({
|
||||
} else {
|
||||
usernameMessageElement.hide();
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -631,7 +772,7 @@ Template.editUserPopup.events({
|
||||
}
|
||||
} else {
|
||||
usernameMessageElement.hide();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
});
|
||||
} else if (isChangeEmail) {
|
||||
@@ -648,11 +789,11 @@ Template.editUserPopup.events({
|
||||
}
|
||||
} else {
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
);
|
||||
} else Popup.close();
|
||||
} else Popup.back();
|
||||
},
|
||||
'click #addUserOrg'(event) {
|
||||
event.preventDefault();
|
||||
@@ -787,7 +928,7 @@ Template.newOrgPopup.events({
|
||||
orgWebsite,
|
||||
orgIsActive,
|
||||
);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -813,7 +954,7 @@ Template.newTeamPopup.events({
|
||||
teamWebsite,
|
||||
teamIsActive,
|
||||
);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -839,20 +980,24 @@ Template.newUserPopup.events({
|
||||
let userTeamsIdsList = userTeamsIds.split(",");
|
||||
let userTms = [];
|
||||
for(let i = 0; i < userTeamsList.length; i++){
|
||||
userTms.push({
|
||||
"teamId": userTeamsIdsList[i],
|
||||
"teamDisplayName": userTeamsList[i],
|
||||
})
|
||||
if(!!userTeamsIdsList[i] && !!userTeamsList[i]) {
|
||||
userTms.push({
|
||||
"teamId": userTeamsIdsList[i],
|
||||
"teamDisplayName": userTeamsList[i],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let userOrgsList = userOrgs.split(",");
|
||||
let userOrgsIdsList = userOrgsIds.split(",");
|
||||
let userOrganizations = [];
|
||||
for(let i = 0; i < userOrgsList.length; i++){
|
||||
userOrganizations.push({
|
||||
"orgId": userOrgsIdsList[i],
|
||||
"orgDisplayName": userOrgsList[i],
|
||||
})
|
||||
if(!!userOrgsIdsList[i] && !!userOrgsList[i]) {
|
||||
userOrganizations.push({
|
||||
"orgId": userOrgsIdsList[i],
|
||||
"orgDisplayName": userOrgsList[i],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Meteor.call(
|
||||
@@ -882,11 +1027,11 @@ Template.newUserPopup.events({
|
||||
} else {
|
||||
usernameMessageElement.hide();
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
},
|
||||
);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click #addUserOrgNewUser'(event) {
|
||||
event.preventDefault();
|
||||
@@ -940,7 +1085,7 @@ Template.settingsOrgPopup.events({
|
||||
return;
|
||||
}
|
||||
Org.remove(this.orgId);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -958,7 +1103,7 @@ Template.settingsTeamPopup.events({
|
||||
return;
|
||||
}
|
||||
Team.remove(this.teamId);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -975,10 +1120,13 @@ Template.settingsUserPopup.events({
|
||||
},
|
||||
'click #deleteButton'(event) {
|
||||
event.preventDefault();
|
||||
Users.remove(this.userId);
|
||||
/*
|
||||
// Delete is not enabled yet, because it does leave empty user avatars
|
||||
// to boards: boards members, card members and assignees have
|
||||
// empty users. See:
|
||||
// Delete user is enabled, but you should remove user from all boards
|
||||
// before deleting user, because there is possibility of leaving empty user avatars
|
||||
// to boards. You can remove non-existing user ids manually from database,
|
||||
// if that happens.
|
||||
//. See:
|
||||
// - wekan/client/components/settings/peopleBody.jade deleteButton
|
||||
// - wekan/client/components/settings/peopleBody.js deleteButton
|
||||
// - wekan/client/components/sidebar/sidebar.js Popup.afterConfirm('removeMember'
|
||||
@@ -986,9 +1134,9 @@ Template.settingsUserPopup.events({
|
||||
// but that should be used to remove user from all boards similarly
|
||||
// - wekan/models/users.js Delete is not enabled
|
||||
//
|
||||
//Users.remove(this.userId);
|
||||
//
|
||||
*/
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -55,3 +55,32 @@ table
|
||||
|
||||
.js-teams,.js-teamsNewUser
|
||||
display: none;
|
||||
|
||||
.selectUserChkBox,.allUserChkBox
|
||||
position: static !important;
|
||||
visibility: visible !important;
|
||||
left: 0 !important;
|
||||
display: block !important;
|
||||
|
||||
#divAddOrRemoveTeam
|
||||
background: green;
|
||||
display: none;
|
||||
|
||||
#addOrRemoveTeam
|
||||
background: green;
|
||||
color: white;
|
||||
|
||||
#divAddOrRemoveTeamContainer
|
||||
display: none;
|
||||
margin: auto;
|
||||
width: 50%;
|
||||
border: 3px solid green;
|
||||
padding: 10px;
|
||||
|
||||
#cancelBtn
|
||||
margin-left: 5% !important;
|
||||
background: orange;
|
||||
color: white;
|
||||
|
||||
#deleteAction
|
||||
margin-left: 5% !important;
|
||||
@@ -22,6 +22,10 @@ template(name="setting")
|
||||
a.js-setting-menu(data-id="account-setting")
|
||||
i.fa.fa-users
|
||||
| {{_ 'accounts'}}
|
||||
li
|
||||
a.js-setting-menu(data-id="tableVisibilityMode-setting")
|
||||
i.fa.fa-eye
|
||||
| {{_ 'tableVisibilityMode'}}
|
||||
li
|
||||
a.js-setting-menu(data-id="announcement-setting")
|
||||
i.fa.fa-bullhorn
|
||||
@@ -44,6 +48,8 @@ template(name="setting")
|
||||
+email
|
||||
else if accountSetting.get
|
||||
+accountSettings
|
||||
else if tableVisibilityModeSetting.get
|
||||
+tableVisibilityModeSettings
|
||||
else if announcementSetting.get
|
||||
+announcementSettings
|
||||
else if layoutSetting.get
|
||||
@@ -57,6 +63,11 @@ template(name="webhookSettings")
|
||||
|
||||
template(name="general")
|
||||
ul#registration-setting.setting-detail
|
||||
li
|
||||
a.flex.js-toggle-forgot-password
|
||||
.materialCheckBox(class="{{#if currentSetting.disableForgotPassword}}is-checked{{/if}}")
|
||||
|
||||
span {{_ 'disable-forgot-password'}}
|
||||
li
|
||||
a.flex.js-toggle-registration
|
||||
.materialCheckBox(class="{{#if currentSetting.disableRegistration}}is-checked{{/if}}")
|
||||
@@ -96,7 +107,7 @@ template(name='email')
|
||||
// li.smtp-form
|
||||
// .title {{_ 'smtp-username'}}
|
||||
// .form-group
|
||||
// input.wekan-form-control#mail-server-username(type="text", placeholder="{{_ 'username'}}" value="{{currentSetting.mailServer.username}}")
|
||||
// input.wekan-form-control#mail-server-u"accounts-allowUserNameChange": "Allow Username Change",sername(type="text", placeholder="{{_ 'username'}}" value="{{currentSetting.mailServer.username}}")
|
||||
// li.smtp-form
|
||||
// .title {{_ 'smtp-password'}}
|
||||
// .form-group
|
||||
@@ -120,6 +131,17 @@ template(name='email')
|
||||
li
|
||||
button.js-send-smtp-test-email.primary {{_ 'send-smtp-test'}}
|
||||
|
||||
template(name='tableVisibilityModeSettings')
|
||||
ul#tableVisibilityMode-setting.setting-detail
|
||||
li.tableVisibilityMode-form
|
||||
.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'}}
|
||||
input.wekan-form-control#accounts-allowPrivateOnly(type="radio" name="allowPrivateOnly" value="false" checked="{{#unless allowPrivateOnly}}checked{{/unless}}")
|
||||
span {{_ 'no'}}
|
||||
button.js-tableVisibilityMode-save.primary {{_ 'save'}}
|
||||
|
||||
template(name='accountSettings')
|
||||
ul#account-setting.setting-detail
|
||||
li
|
||||
@@ -163,6 +185,18 @@ template(name='announcementSettings')
|
||||
|
||||
template(name='layoutSettings')
|
||||
ul#layout-setting.setting-detail
|
||||
li.layout-form
|
||||
.title {{_ 'oidc-button-text'}}
|
||||
.form-group
|
||||
input.wekan-form-control#oidcBtnTextvalue(type="text", placeholder="" value="{{currentSetting.oidcBtnText}}")
|
||||
li.layout-form
|
||||
.title {{_ 'can-invite-if-same-mailDomainName'}}
|
||||
.form-group
|
||||
input.wekan-form-control#mailDomainNamevalue(type="text", placeholder="" value="{{currentSetting.mailDomainName}}")
|
||||
li.layout-form
|
||||
.title {{_ 'custom-legal-notice-link-url'}}
|
||||
.form-group
|
||||
input.wekan-form-control#legalNoticevalue(type="text", placeholder="" value="{{currentSetting.legalNotice}}")
|
||||
li.layout-form
|
||||
.title {{_ 'display-authentication-method'}}
|
||||
.form-group.flex
|
||||
|
||||
@@ -4,9 +4,11 @@ BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.forgotPasswordSetting = new ReactiveVar(true);
|
||||
this.generalSetting = new ReactiveVar(true);
|
||||
this.emailSetting = new ReactiveVar(false);
|
||||
this.accountSetting = new ReactiveVar(false);
|
||||
this.tableVisibilityModeSetting = new ReactiveVar(false);
|
||||
this.announcementSetting = new ReactiveVar(false);
|
||||
this.layoutSetting = new ReactiveVar(false);
|
||||
this.webhookSetting = new ReactiveVar(false);
|
||||
@@ -14,6 +16,7 @@ BlazeComponent.extendComponent({
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('mailServer');
|
||||
Meteor.subscribe('accountSettings');
|
||||
Meteor.subscribe('tableVisibilityModeSettings');
|
||||
Meteor.subscribe('announcements');
|
||||
Meteor.subscribe('globalwebhooks');
|
||||
},
|
||||
@@ -54,6 +57,14 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
);
|
||||
},
|
||||
toggleForgotPassword() {
|
||||
this.setLoading(true);
|
||||
const forgotPasswordClosed = this.currentSetting().disableForgotPassword;
|
||||
Settings.update(Settings.findOne()._id, {
|
||||
$set: { disableForgotPassword: !forgotPasswordClosed },
|
||||
});
|
||||
this.setLoading(false);
|
||||
},
|
||||
toggleRegistration() {
|
||||
this.setLoading(true);
|
||||
const registrationClosed = this.currentSetting().disableRegistration;
|
||||
@@ -82,12 +93,14 @@ BlazeComponent.extendComponent({
|
||||
$('.side-menu li.active').removeClass('active');
|
||||
target.parent().addClass('active');
|
||||
const targetID = target.data('id');
|
||||
this.forgotPasswordSetting.set('forgot-password-setting' === targetID);
|
||||
this.generalSetting.set('registration-setting' === targetID);
|
||||
this.emailSetting.set('email-setting' === targetID);
|
||||
this.accountSetting.set('account-setting' === targetID);
|
||||
this.announcementSetting.set('announcement-setting' === targetID);
|
||||
this.layoutSetting.set('layout-setting' === targetID);
|
||||
this.webhookSetting.set('webhook-setting' === targetID);
|
||||
this.tableVisibilityModeSetting.set('tableVisibilityMode-setting' === targetID);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -196,6 +209,22 @@ BlazeComponent.extendComponent({
|
||||
)
|
||||
.val()
|
||||
.trim();
|
||||
|
||||
const oidcBtnText = $(
|
||||
'#oidcBtnTextvalue',
|
||||
)
|
||||
.val()
|
||||
.trim();
|
||||
const mailDomainName = $(
|
||||
'#mailDomainNamevalue',
|
||||
)
|
||||
.val()
|
||||
.trim();
|
||||
const legalNotice = $(
|
||||
'#legalNoticevalue',
|
||||
)
|
||||
.val()
|
||||
.trim();
|
||||
const hideLogoChange = $('input[name=hideLogo]:checked').val() === 'true';
|
||||
const displayAuthenticationMethod =
|
||||
$('input[name=displayAuthenticationMethod]:checked').val() === 'true';
|
||||
@@ -218,6 +247,9 @@ BlazeComponent.extendComponent({
|
||||
defaultAuthenticationMethod,
|
||||
automaticLinkedUrlSchemes,
|
||||
spinnerName,
|
||||
oidcBtnText,
|
||||
mailDomainName,
|
||||
legalNotice,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -245,6 +277,7 @@ BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click a.js-toggle-forgot-password': this.toggleForgotPassword,
|
||||
'click a.js-toggle-registration': this.toggleRegistration,
|
||||
'click a.js-toggle-tls': this.toggleTLS,
|
||||
'click a.js-setting-menu': this.switchMenu,
|
||||
@@ -317,6 +350,46 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('accountSettings');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
saveTableVisibilityChange() {
|
||||
const allowPrivateOnly =
|
||||
$('input[name=allowPrivateOnly]:checked').val() === 'true';
|
||||
TableVisibilityModeSettings.update('tableVisibilityMode-allowPrivateOnly', {
|
||||
$set: { booleanValue: allowPrivateOnly },
|
||||
});
|
||||
},
|
||||
allowPrivateOnly() {
|
||||
return TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly').booleanValue;
|
||||
},
|
||||
allHideSystemMessages() {
|
||||
Meteor.call('setAllUsersHideSystemMessages', (err, ret) => {
|
||||
if (!err && ret) {
|
||||
if (ret === true) {
|
||||
const message = `${TAPi18n.__(
|
||||
'now-system-messages-of-all-users-are-hidden',
|
||||
)}`;
|
||||
alert(message);
|
||||
}
|
||||
} else {
|
||||
const reason = err.reason || '';
|
||||
const message = `${TAPi18n.__(err.error)}\n${reason}`;
|
||||
alert(message);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click button.js-tableVisibilityMode-save': this.saveTableVisibilityChange,
|
||||
},
|
||||
{
|
||||
'click button.js-all-hide-system-messages': this.allHideSystemMessages,
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('tableVisibilityModeSettings');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.loading = new ReactiveVar(false);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
color: #727479
|
||||
background: #dedede
|
||||
width 100%
|
||||
height 100%
|
||||
height calc(100% - 80px)
|
||||
position: absolute;
|
||||
|
||||
.content-title
|
||||
@@ -88,6 +88,8 @@
|
||||
&.is-checked
|
||||
background #fff
|
||||
|
||||
input[type=radio]
|
||||
margin: 4px
|
||||
|
||||
.option
|
||||
@extends .flex
|
||||
|
||||
@@ -31,26 +31,28 @@ template(name='homeSidebar')
|
||||
+activities(mode="board")
|
||||
|
||||
template(name="membersWidget")
|
||||
.board-widget.board-widget-members
|
||||
h3
|
||||
i.fa.fa-users
|
||||
| {{_ 'organizations'}}
|
||||
if AtLeastOneOrgWasCreated
|
||||
.board-widget.board-widget-members
|
||||
h3
|
||||
i.fa.fa-users
|
||||
| {{_ 'organizations'}}
|
||||
|
||||
.board-widget-content
|
||||
+boardOrgGeneral
|
||||
.clearfix
|
||||
br
|
||||
hr
|
||||
.board-widget.board-widget-members
|
||||
h3
|
||||
i.fa.fa-users
|
||||
| {{_ 'teams'}}
|
||||
.board-widget-content
|
||||
+boardOrgGeneral
|
||||
.clearfix
|
||||
br
|
||||
hr
|
||||
if AtLeastOneTeamWasCreated
|
||||
.board-widget.board-widget-members
|
||||
h3
|
||||
i.fa.fa-users
|
||||
| {{_ 'teams'}}
|
||||
|
||||
.board-widget-content
|
||||
+boardTeamGeneral
|
||||
.clearfix
|
||||
br
|
||||
hr
|
||||
.board-widget-content
|
||||
+boardTeamGeneral
|
||||
.clearfix
|
||||
br
|
||||
hr
|
||||
.board-widget.board-widget-members
|
||||
h3
|
||||
i.fa.fa-users
|
||||
@@ -89,11 +91,20 @@ template(name="boardOrgGeneral")
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
th {{_ 'displayName'}}
|
||||
th
|
||||
| {{_ 'add-organizations'}}
|
||||
br
|
||||
i.addOrganizationsLabel
|
||||
| {{_ 'to-create-organizations-contact-admin'}}
|
||||
br
|
||||
i.addOrganizationsLabel
|
||||
| {{_ 'add-organizations-label'}}
|
||||
th
|
||||
if currentUser.isBoardAdmin
|
||||
a.member.orgOrTeamMember.add-member.js-manage-board-addOrg(title="{{_ 'add-members'}}")
|
||||
i.fa.fa-plus
|
||||
i.addTeamFaPlus.fa.fa-plus
|
||||
.divaddfaplusminus
|
||||
| {{_ 'add'}}
|
||||
each org in currentBoard.activeOrgs
|
||||
+boardOrgRow(orgId=org.orgId)
|
||||
|
||||
@@ -101,11 +112,20 @@ template(name="boardTeamGeneral")
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
th {{_ 'displayName'}}
|
||||
th
|
||||
| {{_ 'add-teams'}}
|
||||
br
|
||||
i.addTeamsLabel
|
||||
| {{_ 'to-create-teams-contact-admin'}}
|
||||
br
|
||||
i.addTeamsLabel
|
||||
| {{_ 'add-teams-label'}}
|
||||
th
|
||||
if currentUser.isBoardAdmin
|
||||
a.member.orgOrTeamMember.add-member.js-manage-board-addTeam(title="{{_ 'add-members'}}")
|
||||
i.fa.fa-plus
|
||||
i.addTeamFaPlus.fa.fa-plus
|
||||
.divaddfaplusminus
|
||||
| {{_ 'add'}}
|
||||
each currentBoard.activeTeams
|
||||
+boardTeamRow(teamId=this.teamId)
|
||||
|
||||
@@ -183,6 +203,12 @@ template(name="boardCardSettingsPopup")
|
||||
span
|
||||
i.fa.fa-sort
|
||||
| {{_ 'card-sorting-by-number'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-card-show-lists(class="{{#if allowsShowLists}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsShowLists}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-list
|
||||
| {{_ 'card-show-lists'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-labels(class="{{#if allowsLabels}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsLabels}}is-checked{{/if}}")
|
||||
@@ -398,7 +424,11 @@ template(name="exportBoard")
|
||||
li
|
||||
a(href="{{exportCsvUrl}}", download="{{exportCsvFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
| {{_ 'export-board-csv'}}
|
||||
| {{_ 'export-board-csv'}} ,
|
||||
li
|
||||
a(href="{{exportScsvUrl}}", download="{{exportCsvFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
| {{_ 'export-board-csv'}} ;
|
||||
li
|
||||
a(href="{{exportTsvUrl}}", download="{{exportTsvFilename}}")
|
||||
i.fa.fa-share-alt
|
||||
@@ -470,12 +500,12 @@ template(name="removeBoardOrgPopup")
|
||||
form
|
||||
input.hide#hideOrgId(type="text" value=org._id)
|
||||
label
|
||||
| {{_ 'leave-board'}} ?
|
||||
| {{_ 'remove-organization-from-board'}}
|
||||
br
|
||||
hr
|
||||
div.buttonsContainer
|
||||
input.primary.wide.leaveBoardBtn#leaveBoardBtn(type="submit" value="{{_ 'leave-board'}}")
|
||||
input.primary.wide.cancelLeaveBoardBtn#cancelLeaveBoardBtn(type="submit" value="{{_ 'Cancel'}}")
|
||||
input.primary.wide.leaveBoardBtn#leaveBoardBtn(type="submit" value="{{_ 'confirm-btn'}}")
|
||||
input.primary.wide.cancelLeaveBoardBtn#cancelLeaveBoardBtn(type="submit" value="{{_ 'cancel'}}")
|
||||
|
||||
template(name="addBoardTeamPopup")
|
||||
select.js-boardTeams#jsBoardTeams
|
||||
@@ -487,12 +517,12 @@ template(name="removeBoardTeamPopup")
|
||||
form
|
||||
input.hide#hideTeamId(type="text" value=team._id)
|
||||
label
|
||||
| {{_ 'leave-board'}} ?
|
||||
| {{_ 'remove-team-from-table'}}
|
||||
br
|
||||
hr
|
||||
div.buttonsContainer
|
||||
input.primary.wide.leaveBoardBtn#leaveBoardTeamBtn(type="submit" value="{{_ 'leave-board'}}")
|
||||
input.primary.wide.cancelLeaveBoardBtn#cancelLeaveBoardTeamBtn(type="submit" value="{{_ 'Cancel'}}")
|
||||
input.primary.wide.leaveBoardBtn#leaveBoardTeamBtn(type="submit" value="{{_ 'confirm-btn'}}")
|
||||
input.primary.wide.cancelLeaveBoardBtn#cancelLeaveBoardTeamBtn(type="submit" value="{{_ 'cancel'}}")
|
||||
|
||||
template(name="addMemberPopup")
|
||||
.js-search-member
|
||||
|
||||
@@ -183,19 +183,20 @@ Template.memberPopup.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Template.boardMenuPopup.events({
|
||||
'click .js-rename-board': Popup.open('boardChangeTitle'),
|
||||
'click .js-open-rules-view'() {
|
||||
Modal.openWide('rulesMain');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-custom-fields'() {
|
||||
Sidebar.setView('customFields');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-open-archives'() {
|
||||
Sidebar.setView('archives');
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-change-board-color': Popup.open('boardChangeColor'),
|
||||
'click .js-change-language': Popup.open('changeLanguage'),
|
||||
@@ -208,7 +209,7 @@ Template.boardMenuPopup.events({
|
||||
}),
|
||||
'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() {
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
Boards.remove(currentBoard._id);
|
||||
FlowRouter.go('home');
|
||||
}),
|
||||
@@ -251,7 +252,7 @@ Template.boardMenuPopup.helpers({
|
||||
Template.memberPopup.events({
|
||||
'click .js-filter-member'() {
|
||||
Filter.members.toggle(this.userId);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click .js-change-role': Popup.open('changePermissions'),
|
||||
'click .js-remove-member': Popup.afterConfirm('removeMember', function() {
|
||||
@@ -265,12 +266,12 @@ Template.memberPopup.events({
|
||||
card.unassignAssignee(memberId);
|
||||
});
|
||||
Boards.findOne(boardId).removeMember(memberId);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}),
|
||||
'click .js-leave-member': Popup.afterConfirm('leaveBoard', () => {
|
||||
const boardId = Session.get('currentBoard');
|
||||
Meteor.call('quitBoard', boardId, () => {
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
FlowRouter.go('home');
|
||||
});
|
||||
}),
|
||||
@@ -290,6 +291,42 @@ Template.leaveBoardPopup.helpers({
|
||||
return Boards.findOne(Session.get('currentBoard'));
|
||||
},
|
||||
});
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.findOrgsOptions = new ReactiveVar({});
|
||||
this.findTeamsOptions = new ReactiveVar({});
|
||||
|
||||
this.page = new ReactiveVar(1);
|
||||
this.teamPage = new ReactiveVar(1);
|
||||
this.autorun(() => {
|
||||
const limitOrgs = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => {});
|
||||
});
|
||||
|
||||
this.autorun(() => {
|
||||
const limitTeams = this.teamPage.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('team', this.findTeamsOptions.get(), limitTeams, () => {});
|
||||
});
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
this.setLoading(false);
|
||||
},
|
||||
|
||||
setError(error) {
|
||||
this.error.set(error);
|
||||
},
|
||||
|
||||
setLoading(w) {
|
||||
this.loading.set(w);
|
||||
},
|
||||
|
||||
isLoading() {
|
||||
return this.loading.get();
|
||||
},
|
||||
}).register('membersWidget');
|
||||
|
||||
Template.membersWidget.helpers({
|
||||
isInvited() {
|
||||
@@ -307,6 +344,21 @@ Template.membersWidget.helpers({
|
||||
isBoardAdmin() {
|
||||
return Meteor.user().isBoardAdmin();
|
||||
},
|
||||
AtLeastOneOrgWasCreated(){
|
||||
let orgs = Org.find({}, {sort: { createdAt: -1 }});
|
||||
if(orgs === undefined)
|
||||
return false;
|
||||
|
||||
return orgs.count() > 0;
|
||||
},
|
||||
|
||||
AtLeastOneTeamWasCreated(){
|
||||
let teams = Team.find({}, {sort: { createdAt: -1 }});
|
||||
if(teams === undefined)
|
||||
return false;
|
||||
|
||||
return teams.count() > 0;
|
||||
},
|
||||
});
|
||||
|
||||
Template.membersWidget.events({
|
||||
@@ -408,7 +460,7 @@ BlazeComponent.extendComponent({
|
||||
activities: ['all'],
|
||||
});
|
||||
}
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -460,6 +512,21 @@ BlazeComponent.extendComponent({
|
||||
};
|
||||
const queryParams = {
|
||||
authToken: Accounts._storedLoginToken(),
|
||||
delimiter: ',',
|
||||
};
|
||||
return FlowRouter.path(
|
||||
'/api/boards/:boardId/export/csv',
|
||||
params,
|
||||
queryParams,
|
||||
);
|
||||
},
|
||||
exportScsvUrl() {
|
||||
const params = {
|
||||
boardId: Session.get('currentBoard'),
|
||||
};
|
||||
const queryParams = {
|
||||
authToken: Accounts._storedLoginToken(),
|
||||
delimiter: ';',
|
||||
};
|
||||
return FlowRouter.path(
|
||||
'/api/boards/:boardId/export/csv',
|
||||
@@ -760,6 +827,10 @@ BlazeComponent.extendComponent({
|
||||
return this.currentBoard.allowsCardSortingByNumber;
|
||||
},
|
||||
|
||||
allowsShowLists() {
|
||||
return this.currentBoard.allowsShowLists;
|
||||
},
|
||||
|
||||
allowsLabels() {
|
||||
return this.currentBoard.allowsLabels;
|
||||
},
|
||||
@@ -994,6 +1065,22 @@ BlazeComponent.extendComponent({
|
||||
this.currentBoard.allowsCardSortingByNumber,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-card-show-lists'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsShowLists = !this.currentBoard
|
||||
.allowsShowLists;
|
||||
this.currentBoard.setAllowsShowLists(
|
||||
this.currentBoard.allowsShowLists,
|
||||
);
|
||||
$(`.js-field-has-card-show-lists ${MCB}`).toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsShowLists,
|
||||
);
|
||||
$('.js-field-has-card-show-lists').toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsShowLists,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-labels'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsLabels = !this.currentBoard.allowsLabels;
|
||||
@@ -1162,7 +1249,7 @@ BlazeComponent.extendComponent({
|
||||
self.setLoading(false);
|
||||
if (err) self.setError(err.error);
|
||||
else if (ret.email) self.setError('email-sent');
|
||||
else Popup.close();
|
||||
else Popup.back();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1249,7 +1336,7 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1258,8 +1345,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Template.addBoardOrgPopup.helpers({
|
||||
orgsDatas() {
|
||||
// return Org.find({}, {sort: { createdAt: -1 }});
|
||||
let orgs = Org.find({}, {sort: { createdAt: -1 }});
|
||||
let orgs = Org.find({}, {sort: { orgDisplayName: 1 }});
|
||||
return orgs;
|
||||
},
|
||||
});
|
||||
@@ -1313,10 +1399,10 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Meteor.call('setBoardOrgs', boardOrganizations, currentBoard._id);
|
||||
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
'click #cancelLeaveBoardBtn'(){
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1340,6 +1426,13 @@ BlazeComponent.extendComponent({
|
||||
const limitTeams = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('team', this.findOrgsOptions.get(), limitTeams, () => {});
|
||||
});
|
||||
|
||||
this.findUsersOptions = new ReactiveVar({});
|
||||
this.userPage = new ReactiveVar(1);
|
||||
this.autorun(() => {
|
||||
const limitUsers = this.userPage.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('people', this.findUsersOptions.get(), limitUsers, () => {});
|
||||
});
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
@@ -1384,11 +1477,39 @@ BlazeComponent.extendComponent({
|
||||
})
|
||||
|
||||
if (selectedTeamId != "-1") {
|
||||
Meteor.call('setBoardTeams', boardTeams, currentBoard._id);
|
||||
let members = currentBoard.members;
|
||||
|
||||
let query = {
|
||||
"teams.teamId": { $in: boardTeams.map(t => t.teamId) },
|
||||
};
|
||||
|
||||
const boardTeamUsers = Users.find(query, {
|
||||
sort: { sort: 1 },
|
||||
});
|
||||
|
||||
if(boardTeams !== undefined && boardTeams.length > 0){
|
||||
let index;
|
||||
if(boardTeamUsers && boardTeamUsers.count() > 0){
|
||||
boardTeamUsers.forEach((u) => {
|
||||
index = members.findIndex(function(m){ return m.userId == u._id});
|
||||
if(index == -1){
|
||||
members.push({
|
||||
"isActive": true,
|
||||
"isAdmin": u.isAdmin !== undefined ? u.isAdmin : false,
|
||||
"isCommentOnly" : false,
|
||||
"isNoComments" : false,
|
||||
"userId": u._id,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Meteor.call('setBoardTeams', boardTeams, members, currentBoard._id);
|
||||
}
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1397,7 +1518,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Template.addBoardTeamPopup.helpers({
|
||||
teamsDatas() {
|
||||
let teams = Team.find({}, {sort: { createdAt: -1 }});
|
||||
let teams = Team.find({}, {sort: { teamDisplayName: 1 }});
|
||||
return teams;
|
||||
},
|
||||
});
|
||||
@@ -1413,6 +1534,13 @@ BlazeComponent.extendComponent({
|
||||
const limitTeams = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('team', this.findOrgsOptions.get(), limitTeams, () => {});
|
||||
});
|
||||
|
||||
this.findUsersOptions = new ReactiveVar({});
|
||||
this.userPage = new ReactiveVar(1);
|
||||
this.autorun(() => {
|
||||
const limitUsers = this.userPage.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('people', this.findUsersOptions.get(), limitUsers, () => {});
|
||||
});
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
@@ -1449,12 +1577,33 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
}
|
||||
|
||||
Meteor.call('setBoardTeams', boardTeams, currentBoard._id);
|
||||
let members = currentBoard.members;
|
||||
let query = {
|
||||
"teams.teamId": stringTeamId
|
||||
};
|
||||
|
||||
Popup.close();
|
||||
const boardTeamUsers = Users.find(query, {
|
||||
sort: { sort: 1 },
|
||||
});
|
||||
|
||||
if(currentBoard.teams !== undefined && currentBoard.teams.length > 0){
|
||||
let index;
|
||||
if(boardTeamUsers && boardTeamUsers.count() > 0){
|
||||
boardTeamUsers.forEach((u) => {
|
||||
index = members.findIndex(function(m){ return m.userId == u._id});
|
||||
if(index !== -1 && (u.isAdmin === undefined || u.isAdmin == false)){
|
||||
members.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Meteor.call('setBoardTeams', boardTeams, members, currentBoard._id);
|
||||
|
||||
Popup.back();
|
||||
},
|
||||
'click #cancelLeaveBoardTeamBtn'(){
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -224,3 +224,24 @@
|
||||
.cancelLeaveBoardBtn
|
||||
margin-left: 5% !important
|
||||
background-color: red !important
|
||||
|
||||
.addTeamsLabel, .addOrganizationsLabel
|
||||
font-weight: normal
|
||||
|
||||
.js-manage-board-removeTeam:hover, .js-manage-board-removeTeam.is-active,
|
||||
.js-manage-board-removeOrg:hover, .js-manage-board-removeOrg.is-active
|
||||
box-shadow: 0 0 0 2px #e23210 inset !important
|
||||
|
||||
.js-manage-board-addTeam:hover, .js-manage-board-addTeam.is-active,
|
||||
.js-manage-board-addOrg:hover , .js-manage-board-addOrg.is-active
|
||||
box-shadow: 0 0 0 2px #73ea10 inset !important
|
||||
|
||||
.addTeamFaPlus
|
||||
color: green !important
|
||||
|
||||
.removeTeamFaMinus
|
||||
color: red !important
|
||||
|
||||
.divaddfaplusminus
|
||||
padding-top: 5px;
|
||||
margin-left: 40px;
|
||||
@@ -1,4 +1,4 @@
|
||||
archivedRequested = false;
|
||||
//archivedRequested = false;
|
||||
const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
@@ -13,7 +13,7 @@ BlazeComponent.extendComponent({
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (!currentBoardId) return;
|
||||
const handle = subManager.subscribe('board', currentBoardId, true);
|
||||
archivedRequested = true;
|
||||
//archivedRequested = true;
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isArchiveReady.set(handle.ready());
|
||||
@@ -94,13 +94,13 @@ BlazeComponent.extendComponent({
|
||||
'click .js-delete-card': Popup.afterConfirm('cardDelete', function() {
|
||||
const cardId = this._id;
|
||||
Cards.remove(cardId);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}),
|
||||
'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', () => {
|
||||
this.archivedCards().forEach(card => {
|
||||
Cards.remove(card._id);
|
||||
});
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}),
|
||||
|
||||
'click .js-restore-list'() {
|
||||
@@ -115,13 +115,13 @@ BlazeComponent.extendComponent({
|
||||
|
||||
'click .js-delete-list': Popup.afterConfirm('listDelete', function() {
|
||||
this.remove();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}),
|
||||
'click .js-delete-all-lists': Popup.afterConfirm('listDelete', () => {
|
||||
this.archivedLists().forEach(list => {
|
||||
list.remove();
|
||||
});
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}),
|
||||
|
||||
'click .js-restore-swimlane'() {
|
||||
@@ -138,7 +138,7 @@ BlazeComponent.extendComponent({
|
||||
'swimlaneDelete',
|
||||
function() {
|
||||
this.remove();
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
),
|
||||
'click .js-delete-all-swimlanes': Popup.afterConfirm(
|
||||
@@ -147,7 +147,7 @@ BlazeComponent.extendComponent({
|
||||
this.archivedSwimlanes().forEach(swimlane => {
|
||||
swimlane.remove();
|
||||
});
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
),
|
||||
},
|
||||
|
||||
@@ -43,6 +43,14 @@ template(name="createCustomFieldPopup")
|
||||
option(value=value selected="selected") {{name}}
|
||||
else
|
||||
option(value=value) {{name}}
|
||||
a.flex.js-field-show-sum-at-top-of-list(class="{{#if showSumAtTopOfList}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if showSumAtTopOfList}}is-checked{{/if}}")
|
||||
span {{_ 'showSum-field-on-list'}}
|
||||
|
||||
div.js-field-settings.js-field-settings-currency(class="{{#if isTypeNotSelected 'number'}}hide{{/if}}")
|
||||
a.flex.js-field-show-sum-at-top-of-list(class="{{#if showSumAtTopOfList}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if showSumAtTopOfList}}is-checked{{/if}}")
|
||||
span {{_ 'showSum-field-on-list'}}
|
||||
|
||||
div.js-field-settings.js-field-settings-dropdown(class="{{#if isTypeNotSelected 'dropdown'}}hide{{/if}}")
|
||||
label
|
||||
|
||||
@@ -234,6 +234,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .js-field-show-sum-at-top-of-list'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if (!$target.hasClass('js-field-show-sum-at-top-of-list')) {
|
||||
$target = $target.parent();
|
||||
}
|
||||
$target.find('.materialCheckBox').toggleClass('is-checked');
|
||||
$target.toggleClass('is-checked');
|
||||
},
|
||||
'click .primary'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
@@ -248,6 +256,8 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
this.find('.js-field-automatically-on-card.is-checked') !== null,
|
||||
alwaysOnCard:
|
||||
this.find('.js-field-always-on-card.is-checked') !== null,
|
||||
showSumAtTopOfList:
|
||||
this.find('.js-field-show-sum-at-top-of-list.is-checked') !== null,
|
||||
};
|
||||
|
||||
// insert or update
|
||||
@@ -273,7 +283,7 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
} else {
|
||||
CustomFields.remove(customField._id);
|
||||
}
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
},
|
||||
),
|
||||
},
|
||||
@@ -292,6 +302,6 @@ CreateCustomFieldPopup.register('createCustomFieldPopup');
|
||||
'submit'(evt) {
|
||||
const customFieldId = this._id;
|
||||
CustomFields.remove(customFieldId);
|
||||
Popup.close();
|
||||
Popup.back();
|
||||
}
|
||||
});*/
|
||||
@@ -4,11 +4,18 @@
|
||||
and #each x in y constructors to fix this.
|
||||
|
||||
template(name="filterSidebar")
|
||||
h3 {{_ 'list-filter-label'}}
|
||||
h3
|
||||
i.fa.fa-trello
|
||||
| {{_ 'list-filter-label'}}
|
||||
ul.sidebar-list
|
||||
form.js-list-filter
|
||||
input(type="text")
|
||||
hr
|
||||
h3
|
||||
i.fa.fa-list-alt
|
||||
| {{_ 'filter-card-title-label'}}
|
||||
input.js-field-card-filter(type="text")
|
||||
hr
|
||||
h3
|
||||
i.fa.fa-tags
|
||||
| {{_ 'filter-labels-label'}}
|
||||
|
||||
Loaded 100 of 374 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user