Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d08b208bc6 | ||
|
|
c5d8117052 | ||
|
|
b769d18f73 | ||
|
|
b17760d228 | ||
|
|
2983c51db1 | ||
|
|
d6e11940cc | ||
|
|
17f15f546f | ||
|
|
c5df78fd8b | ||
|
|
62bd96e06e | ||
|
|
399fd098b8 | ||
|
|
f9c21a9b52 | ||
|
|
41893cf06a | ||
|
|
83a8d4c14f | ||
|
|
6ce5ab40a7 | ||
|
|
328fa194a0 | ||
|
|
482b9c9333 | ||
|
|
122757ca9c | ||
|
|
49dd2204f4 | ||
|
|
07a3a0b388 | ||
|
|
56efbf1de7 | ||
|
|
57f31d443f | ||
|
|
65f308434a | ||
|
|
007e0f1c16 | ||
|
|
c249a2d187 | ||
|
|
78324263c1 | ||
|
|
c20229b56a | ||
|
|
1fd10771b0 | ||
|
|
1637920170 | ||
|
|
87ba966db8 | ||
|
|
258391cd8c | ||
|
|
481404e8d7 | ||
|
|
48737dcd01 | ||
|
|
b95585b0f6 | ||
|
|
14b2c1309f | ||
|
|
7e036b9df3 | ||
|
|
72fa32151d | ||
|
|
f5dbe8c6f6 | ||
|
|
93950a3b2e | ||
|
|
eec818f9fd | ||
|
|
a6f15af270 | ||
|
|
646497c3f0 | ||
|
|
2342c27ced | ||
|
|
80f6004404 | ||
|
|
a94946c77c | ||
|
|
659a65b8b9 | ||
|
|
c4b55e862f | ||
|
|
f8a75930b3 | ||
|
|
3a04b1b787 | ||
|
|
ad61a15611 | ||
|
|
d21bc0c277 | ||
|
|
fa3d117372 | ||
|
|
26249f5928 | ||
|
|
b7e6600e88 | ||
|
|
6e46cad56c | ||
|
|
e7340c5021 | ||
|
|
05d6c5f15a | ||
|
|
428442b310 | ||
|
|
753d247cf6 | ||
|
|
802f544cd5 | ||
|
|
bf62a947fb | ||
|
|
5b08ae12a6 | ||
|
|
46e881bea3 | ||
|
|
5d1714dbd5 | ||
|
|
bd1de94312 | ||
|
|
06937d0aa0 | ||
|
|
9e39731133 | ||
|
|
3908cd5413 | ||
|
|
6be1a33093 | ||
|
|
c656cbf6ae | ||
|
|
83045a6512 | ||
|
|
199485640f | ||
|
|
2e41ff1121 | ||
|
|
487c6fae8d | ||
|
|
ea64b17b82 | ||
|
|
6d0578fd5a | ||
|
|
76a4deda78 | ||
|
|
595abd303c | ||
|
|
5dd6466c0a | ||
|
|
38655ee478 | ||
|
|
23ca25a1ca | ||
|
|
120cb4820e | ||
|
|
3ee0add6f6 | ||
|
|
8911fe5c8d | ||
|
|
1d9346513e | ||
|
|
62150ce6c4 | ||
|
|
b2d4135dca | ||
|
|
49e3e01ece | ||
|
|
23c2ff0e29 | ||
|
|
a550c255e6 | ||
|
|
6b897faaa8 | ||
|
|
48255f6f1e | ||
|
|
45bc3bd3db | ||
|
|
5ab20a9257 | ||
|
|
88e4918208 | ||
|
|
521ef8b6da | ||
|
|
0d86dfa571 | ||
|
|
472350e56f | ||
|
|
68569b9640 | ||
|
|
823f11d5dd | ||
|
|
ae19018831 | ||
|
|
d5b09c7b3e | ||
|
|
d3b4aa4dc7 | ||
|
|
c2da477735 | ||
|
|
843ff8eaaa | ||
|
|
c5143e2c54 | ||
|
|
0596f5e864 | ||
|
|
8ca1b25daf | ||
|
|
1f76159bfe | ||
|
|
39ece35f52 | ||
|
|
63c1bb9e17 | ||
|
|
354622c5ad | ||
|
|
df7fd8c900 | ||
|
|
7957690fca | ||
|
|
63d19447a0 | ||
|
|
018a84ec59 | ||
|
|
e5c0e623a0 | ||
|
|
9a2d3e15e8 | ||
|
|
c07e262e68 | ||
|
|
cacb2b91ee | ||
|
|
6f269d598f | ||
|
|
57ea254bff | ||
|
|
7e183c838f | ||
|
|
3ffdfd9736 | ||
|
|
621c701bef | ||
|
|
3c24c9966c | ||
|
|
995de525d9 | ||
|
|
ae30b1220a | ||
|
|
e380ad26c2 | ||
|
|
9bb31d2380 | ||
|
|
8ec1e4f3fa | ||
|
|
9f0f6841b0 | ||
|
|
5d18f98b44 | ||
|
|
e738177e08 | ||
|
|
bb201935b9 | ||
|
|
bcfe6bd616 | ||
|
|
9c6352a7bc | ||
|
|
9110e41f20 | ||
|
|
7ae871d3a6 | ||
|
|
0ddf027d43 | ||
|
|
45124a39f3 | ||
|
|
4940af0573 | ||
|
|
924b5d1733 | ||
|
|
2119a3a51e | ||
|
|
dcda99ddf3 | ||
|
|
0e35dcd5ad | ||
|
|
991d42ca0b | ||
|
|
ac242c96b7 | ||
|
|
79d45c8e33 | ||
|
|
8e9f7102f9 | ||
|
|
1952dc309b | ||
|
|
5e7767247f | ||
|
|
676bf686c7 | ||
|
|
5cd6985368 | ||
|
|
807e72dcce | ||
|
|
71702b5821 | ||
|
|
40c56248f1 | ||
|
|
74ac7d8d3f | ||
|
|
e7186b47b0 | ||
|
|
12aa7ea2cb | ||
|
|
ef584e2826 | ||
|
|
f902be10b6 | ||
|
|
29b835de59 | ||
|
|
6ff9c5b58d | ||
|
|
1b86aeb4f5 | ||
|
|
dce99c00be | ||
|
|
e47c700c36 | ||
|
|
09506c78f3 | ||
|
|
9ec1fc0902 | ||
|
|
0b3ec2c19e | ||
|
|
a94bec921c | ||
|
|
3884da3c06 | ||
|
|
d3fa177c7f | ||
|
|
d2176e2c08 | ||
|
|
8d9c59486d | ||
|
|
0e7c2b4b94 | ||
|
|
5c9bc1c745 | ||
|
|
5e7efa0098 | ||
|
|
424862e313 | ||
|
|
9b80ad5a3c | ||
|
|
96c6fa0276 | ||
|
|
69305e1833 | ||
|
|
8aa74ab65e | ||
|
|
aee12c2494 | ||
|
|
83976b3229 | ||
|
|
39f8a138d7 | ||
|
|
120e4af7b2 | ||
|
|
15bddbc86a | ||
|
|
4e8b193801 | ||
|
|
37b65996a0 | ||
|
|
9cd0d3488f | ||
|
|
eaf07cef07 | ||
|
|
02a8775537 | ||
|
|
f2d5698acb | ||
|
|
b0c0e456af | ||
|
|
0857a2ea91 | ||
|
|
1f2e6d33ce | ||
|
|
250e0690d1 | ||
|
|
e709094533 | ||
|
|
4a5b16ee2e | ||
|
|
378d64f99a | ||
|
|
4395760a40 | ||
|
|
8417fae89c | ||
|
|
6c8fae944c | ||
|
|
decab9256b | ||
|
|
183ed35ce8 | ||
|
|
2fcf85fd61 | ||
|
|
3613c1c75e | ||
|
|
e7813046f6 | ||
|
|
9871bf1963 | ||
|
|
b32e7b659b | ||
|
|
2201372744 | ||
|
|
7431a082e2 | ||
|
|
6fe3edebb1 | ||
|
|
4b6406c264 | ||
|
|
86f6bdf9af | ||
|
|
1eeed6645d | ||
|
|
bb8c4325c6 | ||
|
|
07a3301414 | ||
|
|
1590701fc9 | ||
|
|
1d4a65d0b4 | ||
|
|
c845d25ae5 | ||
|
|
ceec27bf90 | ||
|
|
4b90424773 | ||
|
|
31eb9b26b4 | ||
|
|
a8a3c9ad19 | ||
|
|
5cfa03ec07 | ||
|
|
17ab5978ad | ||
|
|
d4c69c3932 | ||
|
|
3778a9ebcf | ||
|
|
880d94e220 | ||
|
|
37607f72b2 | ||
|
|
b1c26e6f64 | ||
|
|
8164af281d | ||
|
|
af676c8f26 | ||
|
|
b8dc7ff18e | ||
|
|
4aee129cdc | ||
|
|
03d29dd674 | ||
|
|
5a6f84ce34 | ||
|
|
0e593c7d81 | ||
|
|
4de830d663 | ||
|
|
769bb7a55d | ||
|
|
ed6c6e5a25 | ||
|
|
302ba75729 | ||
|
|
69dc8f304c | ||
|
|
e43002d5ad | ||
|
|
edd07befe2 | ||
|
|
5cc6a2f7d0 | ||
|
|
926f39c96b | ||
|
|
abd38082de | ||
|
|
5ca90f4d22 | ||
|
|
c9586e579a |
No files matched your search
@@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.22.0 \
|
||||
NODE_VERSION=v12.22.1 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
|
||||
@@ -81,7 +81,7 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.22.0
|
||||
node-engine: 12.22.1
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
|
||||
@@ -83,7 +83,7 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.22.0
|
||||
node-engine: 12.22.1
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
## Issue
|
||||
|
||||
If you can not login for any reason:
|
||||
- https://github.com/wekan/wekan/wiki/Forgot-Password
|
||||
|
||||
Email settings:
|
||||
- https://github.com/wekan/wekan/wiki/Troubleshooting-Mail
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
zap_scan:
|
||||
runs-on: ubuntu-latest
|
||||
name: Scan the webapplication
|
||||
steps:
|
||||
- name: OWASP ZAP Scan
|
||||
runs-on: ubuntu-latest
|
||||
uses: zaproxy/action-baseline@v0.4.0
|
||||
with:
|
||||
target: 'https://boards.wekan.team/sign-in'
|
||||
+5
-6
@@ -6,7 +6,7 @@
|
||||
meteor-base@1.4.0
|
||||
|
||||
# Build system
|
||||
ecmascript@0.15.0
|
||||
ecmascript@0.15.1
|
||||
standard-minifier-css@1.7.2
|
||||
standard-minifier-js@2.6.0
|
||||
mquandalle:jade
|
||||
@@ -17,13 +17,13 @@ es5-shim@4.8.0
|
||||
|
||||
# Collections
|
||||
aldeed:collection2
|
||||
cfs:standard-packages
|
||||
wekan-cfs-standard-packages
|
||||
cottz:publish-relations
|
||||
dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
matb33:collection-hooks
|
||||
matteodem:easy-search
|
||||
mongo@1.10.1
|
||||
mongo@1.11.0
|
||||
mquandalle:collection-mutations
|
||||
|
||||
# Account system
|
||||
@@ -75,7 +75,7 @@ horka:swipebox
|
||||
dynamic-import@0.6.0
|
||||
|
||||
accounts-password@1.7.0
|
||||
cfs:gridfs
|
||||
wekan-cfs-gridfs
|
||||
rzymek:fullcalendar
|
||||
momentjs:moment@2.22.2
|
||||
browser-policy-framing@1.1.0
|
||||
@@ -91,8 +91,7 @@ meteorhacks:aggregate@1.3.0
|
||||
wekan-markdown
|
||||
konecty:mongo-counter
|
||||
percolate:synced-cron
|
||||
cfs:filesystem
|
||||
tmeasday:check-npm-versions
|
||||
wekan-cfs-filesystem
|
||||
steffo:meteor-accounts-saml
|
||||
rajit:bootstrap3-datepicker-fi
|
||||
rajit:bootstrap3-datepicker-ar
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
METEOR@2.1
|
||||
METEOR@2.2
|
||||
+46
-46
@@ -1,7 +1,7 @@
|
||||
3stack:presence@1.1.2
|
||||
accounts-base@1.8.0
|
||||
accounts-base@1.9.0
|
||||
accounts-oauth@1.2.0
|
||||
accounts-password@1.7.0
|
||||
accounts-password@1.7.1
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
@@ -11,36 +11,19 @@ allow-deny@1.1.0
|
||||
arillo:flow-router-helpers@0.5.2
|
||||
audit-argument-checks@1.0.7
|
||||
autoupdate@1.7.0
|
||||
babel-compiler@7.6.0
|
||||
babel-compiler@7.6.1
|
||||
babel-runtime@1.5.0
|
||||
base64@1.0.12
|
||||
binary-heap@1.0.11
|
||||
blaze@2.3.4
|
||||
blaze-tools@1.0.10
|
||||
blaze@2.5.0
|
||||
blaze-tools@1.1.2
|
||||
boilerplate-generator@1.7.1
|
||||
browser-policy-common@1.0.11
|
||||
browser-policy-framing@1.1.0
|
||||
caching-compiler@1.2.2
|
||||
caching-html-compiler@1.1.3
|
||||
caching-html-compiler@1.2.0
|
||||
callback-hook@1.3.0
|
||||
cfs:access-point@0.1.49
|
||||
cfs:base-package@0.0.30
|
||||
cfs:collection@0.5.5
|
||||
cfs:collection-filters@0.2.4
|
||||
cfs:data-man@0.0.6
|
||||
cfs:file@0.1.17
|
||||
cfs:filesystem@0.1.2
|
||||
cfs:gridfs@0.0.34
|
||||
cfs:http-methods@0.0.32
|
||||
cfs:http-publish@0.0.13
|
||||
cfs:power-queue@0.9.11
|
||||
cfs:reactive-list@0.0.9
|
||||
cfs:reactive-property@0.0.4
|
||||
cfs:standard-packages@0.5.10
|
||||
cfs:storage-adapter@0.2.4
|
||||
cfs:tempstore@0.1.6
|
||||
cfs:upload-http@0.0.20
|
||||
cfs:worker@0.1.5
|
||||
check@1.3.1
|
||||
chuangbo:cookie@1.1.0
|
||||
coagmano:stylus@1.1.0
|
||||
@@ -49,15 +32,15 @@ coffeescript-compiler@2.4.1
|
||||
cottz:publish-relations@2.0.8
|
||||
dburles:collection-helpers@1.1.0
|
||||
ddp@1.4.0
|
||||
ddp-client@2.4.0
|
||||
ddp-client@2.4.1
|
||||
ddp-common@1.4.0
|
||||
ddp-rate-limiter@1.0.9
|
||||
ddp-server@2.3.2
|
||||
ddp-server@2.3.3
|
||||
deps@1.0.12
|
||||
diff-sequence@1.1.1
|
||||
dynamic-import@0.6.0
|
||||
easylogic:summernote@0.8.8
|
||||
ecmascript@0.15.0
|
||||
ecmascript@0.15.1
|
||||
ecmascript-runtime@0.7.0
|
||||
ecmascript-runtime-client@0.11.0
|
||||
ecmascript-runtime-server@0.10.0
|
||||
@@ -70,10 +53,10 @@ fortawesome:fontawesome@4.7.0
|
||||
geojson-utils@1.0.10
|
||||
horka:swipebox@1.0.2
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.0.11
|
||||
htmljs@1.0.11
|
||||
http@1.4.3
|
||||
id-map@1.1.0
|
||||
html-tools@1.1.2
|
||||
htmljs@1.1.1
|
||||
http@1.4.4
|
||||
id-map@1.1.1
|
||||
idmontie:migrations@1.0.3
|
||||
inter-process-messaging@0.1.1
|
||||
jquery@1.11.11
|
||||
@@ -84,7 +67,7 @@ kenton:accounts-sandstorm@0.7.0
|
||||
konecty:mongo-counter@0.0.5_3
|
||||
lamhieu:meteorx@2.1.1
|
||||
lamhieu:unblock@1.0.0
|
||||
launch-screen@1.2.0
|
||||
launch-screen@1.2.1
|
||||
livedata@1.0.18
|
||||
localstorage@1.2.0
|
||||
logging@1.2.0
|
||||
@@ -100,19 +83,19 @@ meteorhacks:collection-utils@1.2.0
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
meteorspark:util@0.2.0
|
||||
minifier-css@1.5.3
|
||||
minifier-css@1.5.4
|
||||
minifier-js@2.6.0
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.6.1
|
||||
minimongo@1.6.2
|
||||
mobile-status-bar@1.1.0
|
||||
modern-browsers@0.1.5
|
||||
modules@0.16.0
|
||||
modules-runtime@0.12.0
|
||||
momentjs:moment@2.29.1
|
||||
mongo@1.10.1
|
||||
mongo@1.11.1
|
||||
mongo-decimal@0.1.2
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.7
|
||||
mongo-id@1.0.8
|
||||
mongo-livedata@1.0.12
|
||||
mousetrap:mousetrap@1.4.6_1
|
||||
mquandalle:autofocus@1.0.0
|
||||
@@ -125,7 +108,7 @@ mquandalle:moment@1.0.1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
msavin:usercache@1.8.0
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@3.8.1
|
||||
npm-mongo@3.9.0
|
||||
oauth@1.3.2
|
||||
oauth2@1.3.0
|
||||
observe-sequence@1.0.16
|
||||
@@ -191,7 +174,7 @@ rajit:bootstrap3-datepicker-zh-cn@1.7.1
|
||||
rajit:bootstrap3-datepicker-zh-tw@1.7.1
|
||||
random@1.2.0
|
||||
rate-limit@1.0.9
|
||||
react-fast-refresh@0.1.0
|
||||
react-fast-refresh@0.1.1
|
||||
reactive-dict@1.3.0
|
||||
reactive-var@1.0.11
|
||||
reload@1.3.1
|
||||
@@ -208,10 +191,10 @@ simple:json-routes@2.1.0
|
||||
simple:rest-accounts-password@1.1.2
|
||||
simple:rest-bearer-token-parser@1.0.1
|
||||
simple:rest-json-error-handler@1.0.1
|
||||
socket-stream-client@0.3.1
|
||||
socket-stream-client@0.3.3
|
||||
softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.0.15
|
||||
spacebars-compiler@1.1.3
|
||||
spacebars@1.2.0
|
||||
spacebars-compiler@1.2.1
|
||||
srp@1.1.0
|
||||
standard-minifier-css@1.7.2
|
||||
standard-minifier-js@2.6.0
|
||||
@@ -220,11 +203,10 @@ staringatlights:inject-data@2.3.0
|
||||
steffo:meteor-accounts-saml@0.0.18
|
||||
tap:i18n@1.8.2
|
||||
templates:tabs@2.3.0
|
||||
templating@1.3.2
|
||||
templating-compiler@1.3.3
|
||||
templating-runtime@1.3.2
|
||||
templating-tools@1.1.2
|
||||
tmeasday:check-npm-versions@0.3.2
|
||||
templating@1.4.0
|
||||
templating-compiler@1.4.1
|
||||
templating-runtime@1.4.0
|
||||
templating-tools@1.2.0
|
||||
tracker@1.2.0
|
||||
twbs:bootstrap@3.3.6
|
||||
ui@1.0.13
|
||||
@@ -234,10 +216,28 @@ useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
verron:autosize@3.0.8
|
||||
webapp@1.10.0
|
||||
webapp@1.10.1
|
||||
webapp-hashing@1.1.0
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-oidc@1.0.10
|
||||
wekan-cfs-access-point@0.1.50
|
||||
wekan-cfs-base-package@0.0.30
|
||||
wekan-cfs-collection@0.5.5
|
||||
wekan-cfs-collection-filters@0.2.4
|
||||
wekan-cfs-data-man@0.0.6
|
||||
wekan-cfs-file@0.1.17
|
||||
wekan-cfs-filesystem@0.1.2
|
||||
wekan-cfs-gridfs@0.0.34
|
||||
wekan-cfs-http-methods@0.0.32
|
||||
wekan-cfs-http-publish@0.0.13
|
||||
wekan-cfs-power-queue@0.9.11
|
||||
wekan-cfs-reactive-list@0.0.9
|
||||
wekan-cfs-reactive-property@0.0.4
|
||||
wekan-cfs-standard-packages@0.5.10
|
||||
wekan-cfs-storage-adapter@0.2.4
|
||||
wekan-cfs-tempstore@0.1.6
|
||||
wekan-cfs-upload-http@0.0.21
|
||||
wekan-cfs-worker@0.1.5
|
||||
wekan-ldap@0.0.2
|
||||
wekan-markdown@1.0.9
|
||||
wekan-oidc@1.0.12
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ sudo: required
|
||||
|
||||
env:
|
||||
TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
|
||||
TRAVIS_NODE_VERSION: 12.22.0
|
||||
TRAVIS_NODE_VERSION: 12.22.1
|
||||
TRAVIS_NPM_VERSION: latest
|
||||
|
||||
before_install:
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ host = https://www.transifex.com
|
||||
# tap:i18n requires us to use `-` separator in the language identifiers whereas
|
||||
# Transifex uses a `_` separator, without an option to customize it on one side
|
||||
# or the other, so we need to do a Manual mapping.
|
||||
lang_map = ar_EG:ar-EG, bg_BG:bg, 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, no:nb, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, sl_SI:sl, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
|
||||
lang_map = ar_EG:ar-EG, bg_BG:bg, de_CH:de-CH, en_GB:en-GB, es_AR:es-AR, es_CL:es-CL, es_419:es-LA, es_PE:es-PE, es_MX:es-MX, es_TX:es-TX, es_PY:es-PY, el_GR:el, fa_IR:fa-IR, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, sl_SI:sl, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
|
||||
|
||||
[wekan.application]
|
||||
file_filter = i18n/<lang>.i18n.json
|
||||
|
||||
+326
@@ -1,3 +1,329 @@
|
||||
[Mac ChangeLog](https://github.com/wekan/wekan/wiki/Mac)
|
||||
|
||||
# v5.33 2021-06-10 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Assigning a user to a team or an organization](https://github.com/wekan/wekan/pull/3850).
|
||||
Thanks to Emile840.
|
||||
|
||||
and adds the following new improvements:
|
||||
|
||||
- [Custom Fields stringtemplate, autofocus the last input box](https://github.com/wekan/wekan/pull/3849).
|
||||
Thanks to mfilser.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.32 2021-06-09 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Moved many button texts etc to tooltips. Added more tooltips](https://github.com/wekan/wekan/commit/6ce5ab40a7dc013247717b5107a306eb0402cd63).
|
||||
Thanks to JFa-Orkis and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.31 2021-06-09 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Admin Panel: Edit Organizations and Teams](https://github.com/wekan/wekan/issues/802).
|
||||
Thanks to Emile840.
|
||||
- [Admin Panel: Delete Organizations and Teams](https://github.com/wekan/wekan/commit/14b2c1309f0f910c1e46b5681d3612d7ff0cbf81).
|
||||
Thanks to xet7.
|
||||
- [Admin Panel Organizations/Teams: Show confirm text above delete button](https://github.com/wekan/wekan/commit/16379201704ea1a43ce14859633ffb1b9fae6710).
|
||||
Thanks to xet7.
|
||||
- [Gantt: Retain links created between tasks. Part 1: Database changes, not active in
|
||||
MIT Wekan](https://github.com/wekan/wekan/commit/07a3a0b3882147effac890514b19ff84f1d76bdb).
|
||||
Thanks to benjaminhrivera.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Removed extra package](https://github.com/wekan/wekan/commit/646497c3f041e2f562d032fe28ef29169f671ac1).
|
||||
Thanks to xet7.
|
||||
- [Updated dependencies](https://github.com/wekan/wekan/commit/122757ca9c091e98b31d34c3abc25caa295dbdc0).
|
||||
Thanks to developers of dependencies.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Now new boards do not have any labels added by default](https://github.com/wekan/wekan/commit/481404e8d7bad7799c2ad34d6a94eaf5e87602c2).
|
||||
Thanks to tedkoch and xet7.
|
||||
- [Try to fix OAUTH2_LOGIN_STYLE=redirect Has No Effect](https://github.com/wekan/wekan/commit/78324263c1c78e7e9e99f153e3158e39f564b67a).
|
||||
Thanks to 1ubuntuuser and xet7.
|
||||
- [Try to fix: Wekan UI fails to finish import of closed Trello boards](https://github.com/wekan/wekan/commit/007e0f1c16c935ce580093a6aec31305c75d1e45).
|
||||
Thanks to berezovskyi and xet7.
|
||||
- [Partial Fix: Vote and Planning Poker: Setting date and time now works for some languages that have
|
||||
ascii characters in date format](https://github.com/wekan/wekan/commit/57f31d443faaa32d6c7b53d81af3be133af5f040).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.30 2021-06-03 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Planning Poker / Scrum Poker](https://github.com/wekan/wekan/pull/3836),
|
||||
see https://github.com/wekan/wekan/wiki/Planning-Poker .
|
||||
Thanks to helioguardabaxo.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fixed Python API example: Edit card, etc](https://github.com/wekan/wekan/commit/bf62a947fbfa7d387074550288376e682fd6ad47).
|
||||
Thanks to Lucky-Shi and xet7.
|
||||
- [Default language is still used although this one has been modified previously](https://github.com/wekan/wekan/pull/3833).
|
||||
Thanks to Emile840.
|
||||
- [Moved Keyboard Shortcuts from bottom to top of Sidebar](https://github.com/wekan/wekan/commit/659a65b8b919a49ba0beef5cc53d8e61e0f794aa).
|
||||
Thanks to ClaudiaK21 and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.29 2021-05-29 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Excel parent card name export](https://github.com/wekan/wekan/pull/3799).
|
||||
Thanks to marcungeschikts and Enishowk.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- Updated dependencies
|
||||
[Part 1](https://github.com/wekan/wekan/commit/62150ce6c406359fba068552b4526c60faf392bb),
|
||||
[Part 2](https://github.com/wekan/wekan/commit/1d9346513e4f378379b9f5192e8dad5535287f8a),
|
||||
[Part 3](https://github.com/wekan/wekan/commit/6be1a330936c89fcf478efe98dd15244a98d266d).
|
||||
Thanks to developers of dependencies.
|
||||
- Added updated `Forgot Password` page to GitHub issue template
|
||||
[Part 1](https://github.com/wekan/wekan/commit/6d0578fd5ad5f13f5ff9a285577e35fd62bba95f),
|
||||
[Part 2](https://github.com/wekan/wekan/commit/ea64b17b82cd52320c0495e16385f11031dfbe3a).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Try to fix Snap: Removed linting packages](https://github.com/wekan/wekan/commit/8911fe5c8de941808585a7d3462305d5b3d2763d).
|
||||
Thanks to xet7.
|
||||
- [Removed not working GitHub workflow](https://github.com/wekan/wekan/commit/5dd6466c0aa7479015c72519f36c2485b16e3341).
|
||||
Thanks to xet7.
|
||||
- [Fix typos](https://github.com/wekan/wekan/pull/3813).
|
||||
Thanks to spasche.
|
||||
- [Fix: Impersonate user can now export Excel/CSV/TSV/JSON.
|
||||
Impersonate user and export Excel/CSV/TSV/JSON is now logged into database table
|
||||
impersonatedUsers](https://github.com/wekan/wekan/commit/3908cd5413b775d1ee549f0a95304cf9998d3855).
|
||||
Thanks to xet7.
|
||||
- [Fixed Importing JSON exports fails](https://github.com/wekan/wekan/commit/bd1de94312e428e56d6cf5f343098475573cba0b).
|
||||
Thanks to KeptnArgo and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.28 2021-05-07 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Mermaid Diagram](https://github.com/wekan/wekan/wiki/Mermaid-Diagram).
|
||||
Thanks to xuguotong and xet7.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- Updated dependencies
|
||||
[Part 1](https://github.com/wekan/wekan/commit/521ef8b6dad4f00662f22702331193c16b91b482),
|
||||
[Part 2](https://github.com/wekan/wekan/commit/48255f6f1e4a0caf0be006196f28295d0825eb95),
|
||||
[Part 3](https://github.com/wekan/wekan/commit/a550c255e6c3bd2d609a1a45a213cdae7ab4f74d).
|
||||
Thanks to developers of dependencies.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix: BG color of StartDate](https://github.com/wekan/wekan/pull/3793).
|
||||
Thanks to listenerri.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.27 2021-04-29 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fixed Non-ASCII attachment filename will crash when downloading](https://github.com/wekan/wekan/commit/c2da47773552a61d45b010a095f73d2e441f687c).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.26 2021-04-28 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Feature/mini card subtask count](https://github.com/wekan/wekan/pull/3765).
|
||||
Thanks to ryanMushy.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Bring back Almost-Due for Start Date](https://github.com/wekan/wekan/commit/8ca1b25daf3be60c2dc64f03830dea8437bbd8ad).
|
||||
Thanks to darren-teo.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.25 2021-04-27 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Swimlane in Export to Excel](https://github.com/wekan/wekan/pull/3764).
|
||||
Thanks to Keelan and ryanMushy.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Updated release scripts](https://github.com/wekan/wekan/commit/9f0f6841b01b88f5559724b047d5e245617a02c8).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Added missing PostgreSQL password to ToroDB](https://github.com/wekan/wekan/commit/995de525d96946702536f0cdcb98ef281b9df94e).
|
||||
Thanks to xet7.
|
||||
- [Fixed language name of Deutsch (Schweiz)](https://github.com/wekan/wekan/commit/621c701bef1d09d4ddfc93be411cfad98869f0ae).
|
||||
Thanks to urmel1960.
|
||||
- [Bugfix/Summernote on paste](https://github.com/wekan/wekan/pull/3761).
|
||||
Thanks to ryanMushy.
|
||||
- [OpenAPI: Better handle nested schemas](https://github.com/wekan/wekan/pull/3762).
|
||||
Thanks to bentiss.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.24 2021-04-24 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Copy Swimlane](https://github.com/wekan/wekan/pull/3753).
|
||||
Thanks to jrsupplee.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Updated dependencies](https://github.com/wekan/wekan/commit/e738177e081e4a7e83fed3389f47847403551fc2).
|
||||
Thanks to developers of dependencies.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix Snap: Delete extra symlink that prevented building Snap](https://github.com/wekan/wekan/commit/45124a39f34a918b251a4a36fb016639b558f119).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.23 2021-04-22 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Filtering by due date](https://github.com/wekan/wekan/pull/3731).
|
||||
Thanks to mcrute.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Updated dependencies](https://github.com/wekan/wekan/commit/676bf686c7a121b0da744afce5911807a6be48fe).
|
||||
Thanks to developers of dependencies.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix: Trello data without labels definition](https://github.com/wekan/wekan/pull/3733).
|
||||
Thanks to jrsupplee.
|
||||
- [Bug fix for Due Cards](https://github.com/wekan/wekan/pull/3741).
|
||||
Thanks to jrsupplee.
|
||||
- [Fix: The bg color of start at button is almost-due](https://github.com/wekan/wekan/pull/3749).
|
||||
Thanks to listenerri.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.22 2021-04-16 Wekan release
|
||||
|
||||
This release adds the following new translations:
|
||||
|
||||
- Added German (Switzerland) (de_CH) (Schwiizerdütsch). Updated translations.
|
||||
[Part 1](https://github.com/wekan/wekan/commit/09506c78f3c3439db622574eb851fa0c20d3a066),
|
||||
[Part 2](https://github.com/wekan/wekan/commit/dce99c00be80cceba686fd73b4b78b6c778d78a6),
|
||||
[Part 3](https://github.com/wekan/wekan/commit/6ff9c5b58d25ba52b11e5429c9cfe6ed6a97000e).
|
||||
Thanks to translators.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Remove allowedValues from Cards.type schema](https://github.com/wekan/wekan/pull/3724).
|
||||
Thanks to jrsupplee.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.21 2021-04-16 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Summernote Rich Text Editor](https://github.com/wekan/wekan/pull/3720):
|
||||
1) Add new button to insert a URL link.
|
||||
2) Add new popover allowing you to edit existing URL links.
|
||||
3) Enable spell check.
|
||||
4) Allow client side grammerly extension.
|
||||
Thanks to ryanMushy.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Upgraded to Meteor 2.2](https://github.com/wekan/wekan/commit/0e7c2b4b94b1c48e8839cfba635b53cdc1a797b1).
|
||||
Thanks to Meteor developers.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Bugfix, date format not changed to local format](https://github.com/wekan/wekan/pull/3723).
|
||||
Thanks to mfilser.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.20 2021-04-14 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [OpenAPI: rework the allowedValues to allow for imported variables](https://github.com/wekan/wekan/pull/3715).
|
||||
Thanks to bentiss.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.19 2021-04-14 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Custom Field "String Template"](https://github.com/wekan/wekan/pull/3701).
|
||||
Thanks to tod31.
|
||||
- [1) Admin reports. An option added to the admin panel that has reports an admin can run.
|
||||
Right now it has two reports for attachments and broken cards.
|
||||
2) Add the creator avatar to `cardDetails` and `minicard`. Avatar is only shown if it is selected in card settings.
|
||||
3) Added a new search operator `creator`.
|
||||
4) Bug fix for multiple label predicates](https://github.com/wekan/wekan/pull/3705).
|
||||
Thanks to jrsupplee.
|
||||
- [Update Admin Panel Rules report icon and add missing translations](https://github.com/wekan/wekan/commit/8417fae89cc89adb4559874050ff7c56cc08eb00).
|
||||
Thanks to xet7.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Upgraded to Meteor 2.1.1 an updated dependencies](https://github.com/wekan/wekan/commit/bb8c4325c60582cdcda5d406071586f18681e737).
|
||||
Thanks to developers of dependencies.
|
||||
- [Updated to Node.js v12.22.1](https://github.com/wekan/wekan/commit/2201372744639ade3ba74b6ff9115988f011b9ac).
|
||||
Thanks to Node.js developers.
|
||||
- [Updated release scripts](https://github.com/wekan/wekan/commit/9871bf196352edcb5475e1b0ee4983e8f312e449).
|
||||
Thanks to xet7.
|
||||
- [Updated caniuse-lite etc dependencies](https://github.com/wekan/wekan/commit/0857a2ea91f672201ba96f2ba635165784b30fd8).
|
||||
Thanks to developers of dependencies.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Revert stable tag, because it did break Wekan version numbers](https://github.com/wekan/wekan/commit/5ca90f4d2245910580cb0af885fac17dcec44ef0).
|
||||
Thanks to xet7.
|
||||
- [Updating ARM Dockerfile](https://github.com/wekan/wekan/pull/3692).
|
||||
Thanks to loganballard.
|
||||
- [Added latest arm64 bundle symlink](https://github.com/wekan/wekan/commit/6fe3edebb18414ebe7e69b2de3269438662b6163).
|
||||
Thanks to xet7.
|
||||
- [Bug fix: Rules for moving from list/swimlane](https://github.com/wekan/wekan/pull/3706).
|
||||
Thanks to jrsupplee.
|
||||
- [Fixed Elements are duplicated on the view "My cards".
|
||||
Rewrite routine for building the My Cards hierarchical list.
|
||||
Use a separate publication for retrieving My Cards.
|
||||
Fixed bug with limit and skip projection](https://github.com/wekan/wekan/pull/3708).
|
||||
Thanks to jrsupplee.
|
||||
- [Popover needs to be destroyed anytime the details panel is closed](https://github.com/wekan/wekan/pull/3712).
|
||||
Thanks to ryanMushy.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.18 2021-04-14 Wekan release
|
||||
|
||||
Not released version, because some version numbers not changed in all release files.
|
||||
|
||||
# v5.17 2021-04-01 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
+1
-1
@@ -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.0 \
|
||||
NODE_VERSION=v12.22.1 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
|
||||
+9
-9
@@ -4,16 +4,16 @@ FROM amd64/alpine:3.7 AS builder
|
||||
ENV QEMU_VERSION=v4.2.0-6 \
|
||||
QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.22.0 \
|
||||
WEKAN_VERSION=3.96 \
|
||||
NODE_VERSION=v12.22.1 \
|
||||
WEKAN_VERSION=latest \
|
||||
WEKAN_ARCHITECTURE=arm64
|
||||
|
||||
# Install dependencies
|
||||
RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
\
|
||||
# Download qemu static for our architecture
|
||||
wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
|
||||
\
|
||||
# Install dependencies
|
||||
RUN apk update && apk add ca-certificates outils-sha1 && \
|
||||
\
|
||||
# Download qemu static for our architecture
|
||||
wget https://github.com/multiarch/qemu-user-static/releases/download/${QEMU_VERSION}/qemu-${QEMU_ARCHITECTURE}-static.tar.gz -O - | tar -xz && \
|
||||
\
|
||||
# Download wekan and shasum
|
||||
wget https://releases.wekan.team/raspi3/wekan-${WEKAN_VERSION}-${WEKAN_ARCHITECTURE}.zip && \
|
||||
wget https://releases.wekan.team/raspi3/SHA256SUMS.txt && \
|
||||
@@ -40,7 +40,7 @@ LABEL maintainer="wekan"
|
||||
# Set the environment variables (defaults where required)
|
||||
ENV QEMU_ARCHITECTURE=aarch64 \
|
||||
NODE_ARCHITECTURE=linux-arm64 \
|
||||
NODE_VERSION=v12.22.0 \
|
||||
NODE_VERSION=v12.22.1 \
|
||||
NODE_ENV=production \
|
||||
NPM_VERSION=latest \
|
||||
WITH_API=true \
|
||||
|
||||
@@ -52,7 +52,7 @@ that by providing one-click installation on various platforms.
|
||||
|
||||
- Wekan is used in [most countries of the world](https://snapcraft.io/wekan).
|
||||
- Wekan largest user has 13k users using Wekan in their company.
|
||||
- Wekan has been [translated](https://transifex.com/wekan/wekan) to about 62 languages.
|
||||
- Wekan has been [translated](https://transifex.com/wekan/wekan) to about 63 languages.
|
||||
- [Features][features]: Wekan has real-time user interface.
|
||||
- [Platforms][platforms]: Wekan supports many platforms.
|
||||
Wekan is critical part of new platforms Wekan is currently being integrated to.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v5.17.0"
|
||||
appVersion: "v5.33.0"
|
||||
files:
|
||||
userUploads:
|
||||
- README.md
|
||||
|
||||
@@ -28,12 +28,16 @@ if arguments == 0:
|
||||
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 createlist BOARDID LISTTITLE # Create list")
|
||||
print(" python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION")
|
||||
print(" python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION")
|
||||
print(" python3 api.py listattachments BOARDID # List attachments")
|
||||
# TODO:
|
||||
# print(" python3 api.py attachmentdownload BOARDID ATTACHMENTID # One attachment as file")
|
||||
# print(" python3 api.py attachmentsdownload BOARDID # All attachments as files")
|
||||
# print(" python3 api.py attachmentjson BOARDID ATTACHMENTID # One attachment as JSON base64")
|
||||
# print(" python3 api.py attachmentbinary BOARDID ATTACHMENTID # One attachment as binary file")
|
||||
# print(" python3 api.py attachmentdownload BOARDID ATTACHMENTID # One attachment as file")
|
||||
# print(" python3 api.py attachmentsdownload BOARDID # All attachments as files")
|
||||
exit
|
||||
|
||||
# ------- SETTINGS START -------------
|
||||
@@ -61,13 +65,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 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 createlist BOARDID LISTTITLE # Create list
|
||||
python3 api.py addcard AUTHORID BOARDID SWIMLANEID LISTID CARDTITLE CARDDESCRIPTION
|
||||
python3 api.py editcard BOARDID LISTID CARDID NEWCARDTITLE NEWCARDDESCRIPTION
|
||||
python3 api.py listattachments BOARDID # List attachments
|
||||
python3 api.py attachmentjson BOARDID ATTACHMENTID # One attachment as JSON base64
|
||||
python3 api.py attachmentbinary BOARDID ATTACHMENTID # One attachment as binary file
|
||||
@@ -162,6 +168,27 @@ if arguments == 7:
|
||||
print(body.text)
|
||||
# ------- WRITE TO CARD END -----------
|
||||
|
||||
if arguments == 6:
|
||||
|
||||
if sys.argv[1] == 'editcard':
|
||||
|
||||
# ------- LIST OF BOARD START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
cardid = sys.argv[4]
|
||||
newcardtitle = sys.argv[5]
|
||||
newcarddescription = sys.argv[6]
|
||||
edcard = wekanurl + apiboards + boardid + s + l + s + listid + s + cs + s + cardid
|
||||
print(edcard)
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
put_data = {'title': '{}'.format(newcardtitle), 'description': '{}'.format(newcarddescription)}
|
||||
body = requests.put(edcard, data=put_data, headers=headers)
|
||||
print("=== EDIT CARD ===\n")
|
||||
body = requests.get(edcard, headers=headers)
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- LISTS OF BOARD END -----------
|
||||
|
||||
if arguments == 3:
|
||||
|
||||
if sys.argv[1] == 'createlist':
|
||||
@@ -177,6 +204,19 @@ if arguments == 3:
|
||||
print(body.text)
|
||||
# ------- CREATE LIST END -----------
|
||||
|
||||
if sys.argv[1] == 'list':
|
||||
|
||||
# ------- LIST OF BOARD START -----------
|
||||
boardid = sys.argv[2]
|
||||
listid = sys.argv[3]
|
||||
listone = wekanurl + apiboards + boardid + s + l + s + listid
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
print("=== INFO OF ONE LIST ===\n")
|
||||
body = requests.get(listone, headers=headers)
|
||||
data2 = body.text.replace('}',"}\n")
|
||||
print(data2)
|
||||
# ------- LISTS OF BOARD END -----------
|
||||
|
||||
if arguments == 2:
|
||||
|
||||
# ------- BOARDS LIST START -----------
|
||||
@@ -217,7 +257,7 @@ if arguments == 2:
|
||||
|
||||
# ------- LISTS OF BOARD START -----------
|
||||
boardid = sys.argv[2]
|
||||
attachments = wekanurl + apiboards + boardid
|
||||
lists = wekanurl + apiboards + boardid + s + l
|
||||
headers = {'Accept': 'application/json', 'Authorization': 'Bearer {}'.format(apikey)}
|
||||
print("=== LISTS ===\n")
|
||||
body = requests.get(lists, headers=headers)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import sanitizeXss from 'xss';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
const activitiesPerPage = 500;
|
||||
|
||||
@@ -162,11 +162,15 @@ BlazeComponent.extendComponent({
|
||||
{
|
||||
href: source.url,
|
||||
},
|
||||
sanitizeXss(source.system),
|
||||
DOMPurify.sanitize(source.system, {
|
||||
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||
}),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return sanitizeXss(source.system);
|
||||
return DOMPurify.sanitize(source.system, {
|
||||
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -190,10 +194,10 @@ BlazeComponent.extendComponent({
|
||||
href: attachment.url({ download: true }),
|
||||
target: '_blank',
|
||||
},
|
||||
sanitizeXss(attachment.name()),
|
||||
DOMPurify.sanitize(attachment.name()),
|
||||
),
|
||||
)) ||
|
||||
sanitizeXss(this.currentData().activity.attachmentName)
|
||||
DOMPurify.sanitize(this.currentData().activity.attachmentName)
|
||||
);
|
||||
},
|
||||
|
||||
@@ -232,7 +236,7 @@ BlazeComponent.extendComponent({
|
||||
|
||||
Template.activity.helpers({
|
||||
sanitize(value) {
|
||||
return sanitizeXss(value);
|
||||
return DOMPurify.sanitize(value, { ALLOW_UNKNOWN_PROTOCOLS: true });
|
||||
},
|
||||
});
|
||||
|
||||
@@ -246,7 +250,7 @@ function createCardLink(card) {
|
||||
href: card.originRelativeUrl(),
|
||||
class: 'action-card',
|
||||
},
|
||||
sanitizeXss(card.title),
|
||||
DOMPurify.sanitize(card.title, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
),
|
||||
)
|
||||
);
|
||||
@@ -263,7 +267,7 @@ function createBoardLink(board, list) {
|
||||
href: board.originRelativeUrl(),
|
||||
class: 'action-board',
|
||||
},
|
||||
sanitizeXss(text),
|
||||
DOMPurify.sanitize(text, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
template(name="commentForm")
|
||||
.new-comment.js-new-comment(
|
||||
class="{{#if commentFormIsOpen}}is-open{{/if}}")
|
||||
+userAvatar(userId=currentUser._id)
|
||||
+userAvatar(userId=currentUser._id noRemove=true)
|
||||
form.js-new-comment-form
|
||||
+editor(class="js-new-comment-input")
|
||||
| {{getUnsavedValue 'cardComment' currentCard._id}}
|
||||
|
||||
@@ -3,6 +3,7 @@ const commentFormIsOpen = new ReactiveVar(false);
|
||||
BlazeComponent.extendComponent({
|
||||
onDestroyed() {
|
||||
commentFormIsOpen.set(false);
|
||||
$('.note-popover').hide();
|
||||
},
|
||||
|
||||
commentFormIsOpen() {
|
||||
|
||||
@@ -131,7 +131,7 @@ template(name="boardHeaderBar")
|
||||
i.fa.fa-times-thin
|
||||
|
||||
.separator
|
||||
a.board-header-btn.js-toggle-sidebar
|
||||
a.board-header-btn.js-toggle-sidebar(title="{{_ 'sidebar-open'}} {{_ 'or'}} {{_ 'sidebar-close'}}")
|
||||
i.fa.fa-navicon
|
||||
|
||||
template(name="boardVisibilityList")
|
||||
|
||||
@@ -2,7 +2,8 @@ template(name="boardList")
|
||||
.wrapper
|
||||
ul.board-list.clearfix.js-boards
|
||||
li.js-add-board
|
||||
a.board-list-item.label {{_ 'add-board'}}
|
||||
a.board-list-item.label(title="{{_ 'add-board'}}")
|
||||
| {{_ 'add-board'}}
|
||||
each boards
|
||||
li(class="{{#if isStarred}}starred{{/if}}" class=colorClass).js-board
|
||||
if isInvited
|
||||
@@ -18,7 +19,7 @@ template(name="boardList")
|
||||
else
|
||||
a.js-open-board.board-list-item(href="{{pathFor 'board' id=_id slug=slug}}")
|
||||
span.details
|
||||
span.board-list-item-name
|
||||
span.board-list-item-name(title="{{_ 'board-drag-drop-reorder-or-click-open'}}")
|
||||
+viewer
|
||||
= title
|
||||
i.fa.js-star-board(
|
||||
|
||||
@@ -22,6 +22,15 @@ Template.boardListHeaderBar.helpers({
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
Meteor.subscribe('setting');
|
||||
let currUser = Meteor.user();
|
||||
let userLanguage;
|
||||
if(currUser && currUser.profile){
|
||||
userLanguage = currUser.profile.language
|
||||
}
|
||||
if (userLanguage) {
|
||||
TAPi18n.setLanguage(userLanguage);
|
||||
T9n.setLanguage(userLanguage);
|
||||
}
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
|
||||
@@ -55,6 +55,5 @@ template(name="attachmentsGalery")
|
||||
unless currentUser.isCommentOnly
|
||||
unless currentUser.isWorker
|
||||
//li.attachment-item.add-attachment
|
||||
a.js-add-attachment
|
||||
a.js-add-attachment(title="{{_ 'add-attachment' }}")
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-attachment' }}
|
||||
@@ -119,3 +119,24 @@ template(name="cardCustomField-dropdown")
|
||||
if value
|
||||
+viewer
|
||||
= selectedItem
|
||||
|
||||
template(name="cardCustomField-stringtemplate")
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-customfield-stringtemplate")
|
||||
each item in stringtemplateItems.get
|
||||
input.js-card-customfield-stringtemplate-item(type="text" value=item placeholder="")
|
||||
input.js-card-customfield-stringtemplate-item.last(type="text" value="" placeholder="{{_ 'custom-field-stringtemplate-item-placeholder'}}" autofocus)
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if value
|
||||
+viewer
|
||||
= formattedValue
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
else
|
||||
if value
|
||||
+viewer
|
||||
= formattedValue
|
||||
@@ -1,3 +1,6 @@
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
import Cards from '/models/cards';
|
||||
|
||||
Template.cardCustomFieldsPopup.helpers({
|
||||
hasCustomField() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
@@ -234,3 +237,90 @@ CardCustomField.register('cardCustomField');
|
||||
];
|
||||
}
|
||||
}.register('cardCustomField-dropdown'));
|
||||
|
||||
// cardCustomField-stringtemplate
|
||||
(class extends CardCustomField {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
|
||||
this.stringtemplateFormat = this.data().definition.settings.stringtemplateFormat;
|
||||
this.stringtemplateSeparator = this.data().definition.settings.stringtemplateSeparator;
|
||||
|
||||
this.stringtemplateItems = new ReactiveVar(this.data().value ?? []);
|
||||
}
|
||||
|
||||
formattedValue() {
|
||||
return (this.data().value ?? [])
|
||||
.filter(value => !!value.trim())
|
||||
.map(value => this.stringtemplateFormat.replace(/%\{value\}/gi, value))
|
||||
.join(this.stringtemplateSeparator ?? '');
|
||||
}
|
||||
|
||||
getItems() {
|
||||
return Array.from(this.findAll('input'))
|
||||
.map(input => input.value)
|
||||
.filter(value => !!value.trim());
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'submit .js-card-customfield-stringtemplate'(event) {
|
||||
event.preventDefault();
|
||||
const items = this.getItems();
|
||||
this.card.setCustomField(this.customFieldId, items);
|
||||
},
|
||||
|
||||
'keydown .js-card-customfield-stringtemplate-item'(event) {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
this.find('button[type=submit]').click();
|
||||
} else if (event.target.value.trim()) {
|
||||
const inputLast = this.find('input.last');
|
||||
|
||||
let items = this.getItems();
|
||||
|
||||
if (event.target === inputLast) {
|
||||
inputLast.value = '';
|
||||
} else if (event.target.nextSibling === inputLast) {
|
||||
inputLast.focus();
|
||||
} else {
|
||||
event.target.blur();
|
||||
|
||||
const idx = Array.from(this.findAll('input')).indexOf(
|
||||
event.target,
|
||||
);
|
||||
items.splice(idx + 1, 0, '');
|
||||
|
||||
Tracker.afterFlush(() => {
|
||||
const element = this.findAll('input')[idx + 1];
|
||||
element.focus();
|
||||
element.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
this.stringtemplateItems.set(items);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'blur .js-card-customfield-stringtemplate-item'(event) {
|
||||
if (
|
||||
!event.target.value.trim() ||
|
||||
event.target === this.find('input.last')
|
||||
) {
|
||||
const items = this.getItems();
|
||||
this.stringtemplateItems.set(items);
|
||||
this.find('input.last').value = '';
|
||||
}
|
||||
},
|
||||
|
||||
'click .js-close-inlined-form'(event) {
|
||||
this.stringtemplateItems.set(this.data().value ?? []);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}.register('cardCustomField-stringtemplate'));
|
||||
@@ -1,122 +1,4 @@
|
||||
// Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours
|
||||
function adjustedTimeFormat() {
|
||||
return moment
|
||||
.localeData()
|
||||
.longDateFormat('LT')
|
||||
.replace(/HH/i, 'H');
|
||||
}
|
||||
|
||||
// Edit received, start, due & end dates
|
||||
BlazeComponent.extendComponent({
|
||||
template() {
|
||||
return 'editCardDate';
|
||||
},
|
||||
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.card = this.data();
|
||||
this.date = new ReactiveVar(moment.invalid());
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
const $picker = this.$('.js-datepicker')
|
||||
.datepicker({
|
||||
todayHighlight: true,
|
||||
todayBtn: 'linked',
|
||||
language: TAPi18n.getLanguage(),
|
||||
})
|
||||
.on(
|
||||
'changeDate',
|
||||
function(evt) {
|
||||
this.find('#date').value = moment(evt.date).format('L');
|
||||
this.error.set('');
|
||||
this.find('#time').focus();
|
||||
}.bind(this),
|
||||
);
|
||||
|
||||
if (this.date.get().isValid()) {
|
||||
$picker.datepicker('update', this.date.get().toDate());
|
||||
}
|
||||
},
|
||||
|
||||
showDate() {
|
||||
if (this.date.get().isValid()) return this.date.get().format('L');
|
||||
return '';
|
||||
},
|
||||
showTime() {
|
||||
if (this.date.get().isValid()) return this.date.get().format('LT');
|
||||
return '';
|
||||
},
|
||||
dateFormat() {
|
||||
return moment.localeData().longDateFormat('L');
|
||||
},
|
||||
timeFormat() {
|
||||
return moment.localeData().longDateFormat('LT');
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keyup .js-date-field'() {
|
||||
// parse for localized date format in strict mode
|
||||
const dateMoment = moment(this.find('#date').value, 'L', true);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
|
||||
}
|
||||
},
|
||||
'keyup .js-time-field'() {
|
||||
// parse for localized time format in strict mode
|
||||
const dateMoment = moment(
|
||||
this.find('#time').value,
|
||||
adjustedTimeFormat(),
|
||||
true,
|
||||
);
|
||||
if (dateMoment.isValid()) {
|
||||
this.error.set('');
|
||||
}
|
||||
},
|
||||
'submit .edit-date'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
// if no time was given, init with 12:00
|
||||
const time =
|
||||
evt.target.time.value ||
|
||||
moment(new Date().setHours(12, 0, 0)).format('LT');
|
||||
const newTime = moment(time, adjustedTimeFormat(), true);
|
||||
const newDate = moment(evt.target.date.value, 'L', true);
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
const newCompleteDate = moment(
|
||||
dateString,
|
||||
'L ' + adjustedTimeFormat(),
|
||||
true,
|
||||
);
|
||||
if (!newTime.isValid()) {
|
||||
this.error.set('invalid-time');
|
||||
evt.target.time.focus();
|
||||
}
|
||||
if (!newDate.isValid()) {
|
||||
this.error.set('invalid-date');
|
||||
evt.target.date.focus();
|
||||
}
|
||||
if (newCompleteDate.isValid()) {
|
||||
this._storeDate(newCompleteDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
if (!this.error) {
|
||||
this.error.set('invalid');
|
||||
}
|
||||
}
|
||||
},
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
this._deleteDate();
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
|
||||
Template.dateBadge.helpers({
|
||||
canModifyCard() {
|
||||
@@ -305,7 +187,7 @@ class CardStartDate extends CardDate {
|
||||
// if dueAt or endAt exist & are > startAt, startAt doesn't need to be flagged
|
||||
if ((endAt && theDate.isAfter(endAt)) || (dueAt && theDate.isAfter(dueAt)))
|
||||
classes += 'long-overdue';
|
||||
else if (theDate.isBefore(now, 'minute')) classes += 'almost-due';
|
||||
else if (theDate.isAfter(now)) classes += '';
|
||||
else classes += 'current';
|
||||
return classes;
|
||||
}
|
||||
@@ -472,3 +354,30 @@ class VoteEndDate extends CardDate {
|
||||
}
|
||||
}
|
||||
VoteEndDate.register('voteEndDate');
|
||||
|
||||
class PokerEndDate extends CardDate {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
const self = this;
|
||||
self.autorun(() => {
|
||||
self.date.set(moment(self.data().getPokerEnd()));
|
||||
});
|
||||
}
|
||||
classes() {
|
||||
const classes = 'end-date' + ' ';
|
||||
return classes;
|
||||
}
|
||||
showDate() {
|
||||
return this.date.get().format('l LT');
|
||||
}
|
||||
showTitle() {
|
||||
return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`;
|
||||
}
|
||||
|
||||
events() {
|
||||
return super.events().concat({
|
||||
'click .js-edit-date': Popup.open('editPokerEndDate'),
|
||||
});
|
||||
}
|
||||
}
|
||||
PokerEndDate.register('pokerEndDate');
|
||||
@@ -3,6 +3,7 @@ const descriptionFormIsOpen = new ReactiveVar(false);
|
||||
BlazeComponent.extendComponent({
|
||||
onDestroyed() {
|
||||
descriptionFormIsOpen.set(false);
|
||||
$('.note-popover').hide();
|
||||
},
|
||||
|
||||
descriptionFormIsOpen() {
|
||||
|
||||
@@ -5,18 +5,18 @@ template(name="cardDetails")
|
||||
+editCardTitleForm
|
||||
else
|
||||
unless isMiniScreen
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details
|
||||
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.js-open-card-details-menu
|
||||
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(
|
||||
class="fa-link"
|
||||
title="{{_ 'copy-card-link-to-clipboard'}}"
|
||||
)
|
||||
if isMiniScreen
|
||||
a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details
|
||||
a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details(title="{{_ 'close-card'}}")
|
||||
if currentUser.isBoardMember
|
||||
a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu
|
||||
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
|
||||
h2.card-details-title.js-card-title(
|
||||
class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}")
|
||||
@@ -110,15 +110,24 @@ template(name="cardDetails")
|
||||
a.card-label.add-label.js-end-date
|
||||
i.fa.fa-plus
|
||||
|
||||
hr
|
||||
if currentBoard.allowsCreator
|
||||
.card-details-item.card-details-item-creator
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-user
|
||||
| {{_ 'creator'}}
|
||||
|
||||
+userAvatar(userId=userId noRemove=true)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
|
||||
//.card-details-items
|
||||
if currentBoard.allowsMembers
|
||||
hr
|
||||
.card-details-item.card-details-item-members
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-users
|
||||
| {{_ 'members'}}
|
||||
each getMembers
|
||||
+userAvatar(userId=this cardId=../_id)
|
||||
each userId in getMembers
|
||||
+userAvatar(userId=userId cardId=_id)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
@@ -131,8 +140,8 @@ template(name="cardDetails")
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-user
|
||||
| {{_ 'assignee'}}
|
||||
each getAssignees
|
||||
+userAvatarAssignee(userId=this cardId=../_id)
|
||||
each userId in getAssignees
|
||||
+userAvatar(userId=userId cardId=_id assignee=true)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
if canModifyCard
|
||||
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
|
||||
@@ -234,6 +243,205 @@ template(name="cardDetails")
|
||||
i.fa.fa-thumbs-down
|
||||
| {{_ 'vote-against'}}
|
||||
|
||||
if getPokerQuestion
|
||||
hr
|
||||
.poker-title
|
||||
div.flex
|
||||
h3
|
||||
i.fa.fa-thumbs-up
|
||||
| {{_ 'poker-question'}}
|
||||
if getPokerEnd
|
||||
+pokerEndDate
|
||||
div.flex
|
||||
.poker-result
|
||||
if expiredPoker
|
||||
unless ($and currentBoard.isPublic pokerAllowNonBoardMembers )
|
||||
.card-label.card-label-gray {{ pokerCount }} {{_ 'r-of' }} {{ currentBoard.activeMembers.length }}
|
||||
if showPlanningPokerButtons
|
||||
.poker-result
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-one(class="{{#if $eq pokerState 'one'}}poker-voted{{/if}}") {{_ 'poker-one'}}
|
||||
if $eq pokerState "one"
|
||||
i.fa.fa-check
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-two(class="{{#if $eq pokerState 'two'}}poker-voted{{/if}}") {{_ 'poker-two'}}
|
||||
if $eq pokerState "two"
|
||||
i.fa.fa-check
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-three(class="{{#if $eq pokerState 'three'}}poker-voted{{/if}}") {{_ 'poker-three'}}
|
||||
if $eq pokerState "three"
|
||||
i.fa.fa-check
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-five(class="{{#if $eq pokerState 'five'}}poker-voted{{/if}}") {{_ 'poker-five'}}
|
||||
if $eq pokerState "five"
|
||||
i.fa.fa-check
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-eight(class="{{#if $eq pokerState 'eight'}}poker-voted{{/if}}") {{_ 'poker-eight'}}
|
||||
if $eq pokerState "eight"
|
||||
i.fa.fa-check
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-thirteen(class="{{#if $eq pokerState 'thirteen'}}poker-voted{{/if}}") {{_ 'poker-thirteen'}}
|
||||
if $eq pokerState "thirteen"
|
||||
i.fa.fa-check
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-twenty(class="{{#if $eq pokerState 'twenty'}}poker-voted{{/if}}") {{_ 'poker-twenty'}}
|
||||
if $eq pokerState "twenty"
|
||||
i.fa.fa-check
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-forty(class="{{#if $eq pokerState 'forty'}}poker-voted{{/if}}") {{_ 'poker-forty'}}
|
||||
if $eq pokerState "forty"
|
||||
i.fa.fa-check
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-one-hundred(class="{{#if $eq pokerState 'oneHundred'}}poker-voted{{/if}}") {{_ 'poker-oneHundred'}}
|
||||
if $eq pokerState "oneHundred"
|
||||
i.fa.fa-check
|
||||
.poker-deck
|
||||
.poker-card
|
||||
span.inner.js-poker.js-poker-vote-unsure(class="{{#if $eq pokerState 'unsure'}}poker-voted{{/if}}") {{_ 'poker-unsure'}}
|
||||
if $eq pokerState "unsure"
|
||||
i.fa.fa-check
|
||||
|
||||
if currentUser.isBoardAdmin
|
||||
button.card-details-blue.js-poker-finish(class="{{#if $eq voteState false}}poker-voted{{/if}}") {{_ 'poker-finish'}}
|
||||
|
||||
if expiredPoker
|
||||
.poker-table
|
||||
.poker-table-side-left
|
||||
.poker-table-heading-left
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
.poker-table-cell
|
||||
| {{_ 'poker-result-votes' }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
| {{_ 'poker-result-who' }}
|
||||
.poker-table-body
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 1}}winner{{else}}loser{{/if}}") {{_ 'poker-one'}}
|
||||
.poker-table-cell {{ pokerCountOne }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberOne
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 2}}winner{{else}}loser{{/if}}") {{_ 'poker-two'}}
|
||||
.poker-table-cell {{ pokerCountTwo }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberTwo
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 3}}winner{{else}}loser{{/if}}") {{_ 'poker-three'}}
|
||||
.poker-table-cell {{ pokerCountThree }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberThree
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 5}}winner{{else}}loser{{/if}}") {{_ 'poker-five'}}
|
||||
.poker-table-cell {{ pokerCountFive }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberFive
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 8}}winner{{else}}loser{{/if}}") {{_ 'poker-eight'}}
|
||||
.poker-table-cell {{ pokerCountEight }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberEight
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
.poker-table-side-right
|
||||
.poker-table-heading-right
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
.poker-table-cell
|
||||
| {{_ 'poker-result-votes' }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
| {{_ 'poker-result-who' }}
|
||||
.poker-table-body
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 13}}winner{{else}}loser{{/if}}") {{_ 'poker-thirteen'}}
|
||||
.poker-table-cell {{ pokerCountThirteen }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberThirteen
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 20}}winner{{else}}loser{{/if}}") {{_ 'poker-twenty'}}
|
||||
.poker-table-cell {{ pokerCountTwenty }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberTwenty
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 40}}winner{{else}}loser{{/if}}") {{_ 'poker-forty'}}
|
||||
.poker-table-cell {{ pokerCountForty }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberForty
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 100}}winner{{else}}loser{{/if}}") {{_ 'poker-oneHundred'}}
|
||||
.poker-table-cell {{ pokerCountOneHundred }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberOneHundred
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
.poker-table-row
|
||||
.poker-table-cell
|
||||
button.card-details-gray.js-poker.poker-card-result(class="{{#if $eq pokerWinner 'unsure'}}winner{{else}}loser{{/if}}") {{_ 'poker-unsure'}}
|
||||
.poker-table-cell {{ pokerCountUnsure }}
|
||||
.poker-table-cell.poker-table-cell-who
|
||||
.poker-result
|
||||
each m in pokerMemberUnsure
|
||||
a.name
|
||||
+userAvatar(userId=m._id noRemove=true)
|
||||
|
||||
if currentUser.isBoardAdmin
|
||||
div.estimation-add
|
||||
button.card-details-red.js-poker-replay(class="{{#if $eq voteState false}}voted{{/if}}") {{_ 'poker-replay'}}
|
||||
div.estimation-add
|
||||
button.js-poker-estimation
|
||||
i.fa.fa-plus
|
||||
| {{_ 'set-estimation'}}
|
||||
input(type=text,autofocus value=getPokerEstimation,id="pokerEstimation")
|
||||
|
||||
//- XXX We should use "editable" to avoid repetiting ourselves
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
@@ -293,8 +501,8 @@ template(name="cardDetails")
|
||||
i.fa.fa-history
|
||||
| {{ _ 'activity'}}
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch
|
||||
span.toggle-switch-title {{_ 'hide-system-messages'}}
|
||||
.material-toggle-switch(title="{{_ 'hide-system-messages'}}")
|
||||
//span.toggle-switch-title
|
||||
if hiddenSystemMessages
|
||||
input.toggle-switch(type="checkbox" id="toggleButton" checked="checked")
|
||||
else
|
||||
@@ -353,6 +561,10 @@ template(name="cardDetailsActionsPopup")
|
||||
a.js-start-voting
|
||||
i.fa.fa-thumbs-up
|
||||
| {{_ 'card-edit-voting'}}
|
||||
li
|
||||
a.js-start-planning-poker
|
||||
i.fa.fa-thumbs-up
|
||||
| {{_ 'card-edit-planning-poker'}}
|
||||
if currentUser.isBoardAdmin
|
||||
li
|
||||
a.js-custom-fields
|
||||
@@ -488,23 +700,6 @@ template(name="cardAssigneesPopup")
|
||||
if currentUser.isCardAssignee
|
||||
i.fa.fa-check
|
||||
|
||||
template(name="userAvatarAssignee")
|
||||
a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})")
|
||||
if userData.profile.avatarUrl
|
||||
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
|
||||
else
|
||||
+userAvatarAssigneeInitials(userId=userData._id)
|
||||
|
||||
if showStatus
|
||||
span.assignee-presence-status(class=presenceStatusClassName)
|
||||
span.member-type(class=memberType)
|
||||
|
||||
unless isSandstorm
|
||||
if showEdit
|
||||
if $eq currentUser._id userData._id
|
||||
a.edit-avatar.js-change-avatar
|
||||
i.fa.fa-pencil
|
||||
|
||||
template(name="cardAssigneePopup")
|
||||
.board-assignee-menu
|
||||
.mini-profile-info
|
||||
@@ -522,10 +717,6 @@ template(name="cardAssigneePopup")
|
||||
with currentUser
|
||||
li: a.js-edit-profile {{_ 'edit-profile'}}
|
||||
|
||||
template(name="userAvatarAssigneeInitials")
|
||||
svg.avatar.avatar-assignee-initials(viewBox="0 0 {{viewPortWidth}} 15")
|
||||
text(x="50%" y="13" text-anchor="middle")= initials
|
||||
|
||||
template(name="cardMorePopup")
|
||||
p.quiet
|
||||
span.clearfix
|
||||
@@ -633,3 +824,29 @@ template(name="negativeVoteMembersPopup")
|
||||
span.full-name
|
||||
= m.profile.fullname
|
||||
| (<span class="username">{{ m.username }}</span>)
|
||||
|
||||
template(name="deletePokerPopup")
|
||||
p {{_ "poker-delete-pop"}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
||||
template(name="cardStartPlanningPokerPopup")
|
||||
form.edit-poker-question
|
||||
.fields
|
||||
.check-div
|
||||
a.flex(class="{{#if getPokerQuestion}}is-disabled{{else}}js-toggle-poker-allow-non-members{{/if}}")
|
||||
.materialCheckBox#poker-allow-non-members(name="poker-allow-non-members" class="{{#if pokerAllowNonBoardMembers}}is-checked{{/if}}")
|
||||
span {{_ 'allowNonBoardMembers'}}
|
||||
.check-div.flex
|
||||
i.fa.fa-hourglass-end
|
||||
a.js-end-date
|
||||
span
|
||||
| {{_ 'card-end'}}
|
||||
unless getPokerEnd
|
||||
i.fa.fa-plus
|
||||
if getPokerEnd
|
||||
+pokerEndDate
|
||||
|
||||
button.primary.js-submit {{_ 'save'}}
|
||||
if getPokerQuestion
|
||||
if currentUser.isBoardAdmin
|
||||
button.js-remove-poker.negate.wide.right {{_ 'delete'}}
|
||||
@@ -1,11 +1,18 @@
|
||||
import { DatePicker } from '/client/lib/datepicker';
|
||||
import Cards from '/models/cards';
|
||||
import Boards from '/models/boards';
|
||||
import Checklists from '/models/checklists';
|
||||
import Integrations from '/models/integrations';
|
||||
import Users from '/models/users';
|
||||
import Lists from '/models/lists';
|
||||
import CardComments from '/models/cardComments';
|
||||
import { ALLOWED_COLORS } from '/config/const';
|
||||
import moment from 'moment';
|
||||
import { UserAvatar } from '../users/userAvatar';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndexData } = Utils;
|
||||
|
||||
let cardColors;
|
||||
Meteor.startup(() => {
|
||||
cardColors = Cards.simpleSchema()._schema.color.allowedValues;
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
mixins() {
|
||||
return [Mixins.InfiniteScrolling];
|
||||
@@ -133,6 +140,15 @@ BlazeComponent.extendComponent({
|
||||
);
|
||||
},
|
||||
|
||||
showPlanningPokerButtons() {
|
||||
const card = this.currentData();
|
||||
return (
|
||||
(currentUser.isBoardMember() ||
|
||||
(currentUser && card.pokerAllowNonBoardMembers())) &&
|
||||
!card.expiredPoker()
|
||||
);
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
if (Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED) {
|
||||
// Send Webhook but not create Activities records ---
|
||||
@@ -154,15 +170,13 @@ BlazeComponent.extendComponent({
|
||||
}).fetch();
|
||||
|
||||
if (integrations.length > 0) {
|
||||
integrations.forEach(integration => {
|
||||
integrations.forEach((integration) => {
|
||||
Meteor.call(
|
||||
'outgoingWebhooks',
|
||||
integration,
|
||||
'CardSelected',
|
||||
params,
|
||||
() => {
|
||||
return;
|
||||
},
|
||||
() => {},
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -290,7 +304,7 @@ BlazeComponent.extendComponent({
|
||||
Utils.goBoardId(this.data().boardId);
|
||||
},
|
||||
'click .js-copy-link'() {
|
||||
StringToCopyElement = document.getElementById('cardURL_copy');
|
||||
const StringToCopyElement = document.getElementById('cardURL_copy');
|
||||
StringToCopyElement.value =
|
||||
window.location.origin + window.location.pathname;
|
||||
StringToCopyElement.select();
|
||||
@@ -322,9 +336,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
'submit .js-card-details-title'(event) {
|
||||
event.preventDefault();
|
||||
const title = this.currentComponent()
|
||||
.getValue()
|
||||
.trim();
|
||||
const title = this.currentComponent().getValue().trim();
|
||||
if (title) {
|
||||
this.data().setTitle(title);
|
||||
} else {
|
||||
@@ -333,9 +345,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
'submit .js-card-details-assigner'(event) {
|
||||
event.preventDefault();
|
||||
const assigner = this.currentComponent()
|
||||
.getValue()
|
||||
.trim();
|
||||
const assigner = this.currentComponent().getValue().trim();
|
||||
if (assigner) {
|
||||
this.data().setAssignedBy(assigner);
|
||||
} else {
|
||||
@@ -344,9 +354,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
'submit .js-card-details-requester'(event) {
|
||||
event.preventDefault();
|
||||
const requester = this.currentComponent()
|
||||
.getValue()
|
||||
.trim();
|
||||
const requester = this.currentComponent().getValue().trim();
|
||||
if (requester) {
|
||||
this.data().setRequestedBy(requester);
|
||||
} else {
|
||||
@@ -402,127 +410,85 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
this.data().setVote(Meteor.userId(), newState);
|
||||
},
|
||||
'click .js-poker'(e) {
|
||||
let newState = null;
|
||||
if ($(e.target).hasClass('js-poker-vote-one')) {
|
||||
newState = 'one';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
if ($(e.target).hasClass('js-poker-vote-two')) {
|
||||
newState = 'two';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
if ($(e.target).hasClass('js-poker-vote-three')) {
|
||||
newState = 'three';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
if ($(e.target).hasClass('js-poker-vote-five')) {
|
||||
newState = 'five';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
if ($(e.target).hasClass('js-poker-vote-eight')) {
|
||||
newState = 'eight';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
if ($(e.target).hasClass('js-poker-vote-thirteen')) {
|
||||
newState = 'thirteen';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
if ($(e.target).hasClass('js-poker-vote-twenty')) {
|
||||
newState = 'twenty';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
if ($(e.target).hasClass('js-poker-vote-forty')) {
|
||||
newState = 'forty';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
if ($(e.target).hasClass('js-poker-vote-one-hundred')) {
|
||||
newState = 'oneHundred';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
if ($(e.target).hasClass('js-poker-vote-unsure')) {
|
||||
newState = 'unsure';
|
||||
this.data().setPoker(Meteor.userId(), newState);
|
||||
}
|
||||
},
|
||||
'click .js-poker-finish'(e) {
|
||||
if ($(e.target).hasClass('js-poker-finish')) {
|
||||
e.preventDefault();
|
||||
const now = moment().format('YYYY-MM-DD HH:mm');
|
||||
this.data().setPokerEnd(now);
|
||||
}
|
||||
},
|
||||
|
||||
'click .js-poker-replay'(e) {
|
||||
if ($(e.target).hasClass('js-poker-replay')) {
|
||||
e.preventDefault();
|
||||
this.currentCard = this.currentData();
|
||||
this.currentCard.replayPoker();
|
||||
this.data().unsetPokerEnd();
|
||||
this.data().unsetPokerEstimation();
|
||||
}
|
||||
},
|
||||
'click .js-poker-estimation'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const ruleTitle = this.find('#pokerEstimation').value;
|
||||
if (ruleTitle !== undefined && ruleTitle !== '') {
|
||||
this.find('#pokerEstimation').value = '';
|
||||
|
||||
if (ruleTitle) {
|
||||
this.data().setPokerEstimation(parseInt(ruleTitle, 10));
|
||||
} else {
|
||||
this.data().setPokerEstimation('');
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('cardDetails');
|
||||
|
||||
Template.cardDetails.helpers({
|
||||
userData() {
|
||||
// We need to handle a special case for the search results provided by the
|
||||
// `matteodem:easy-search` package. Since these results gets published in a
|
||||
// separate collection, and not in the standard Meteor.Users collection as
|
||||
// expected, we use a component parameter ("property") to distinguish the
|
||||
// two cases.
|
||||
const userCollection = this.esSearch ? ESSearchResults : Users;
|
||||
return userCollection.findOne(this.userId, {
|
||||
fields: {
|
||||
profile: 1,
|
||||
username: 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
receivedSelected() {
|
||||
if (this.getReceived().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
startSelected() {
|
||||
if (this.getStart().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
endSelected() {
|
||||
if (this.getEnd().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
dueSelected() {
|
||||
if (this.getDue().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
memberSelected() {
|
||||
if (this.getMembers().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
labelSelected() {
|
||||
if (this.getLabels().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
assigneeSelected() {
|
||||
if (this.getAssignees().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
requestBySelected() {
|
||||
if (this.getRequestBy().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
assigneeBySelected() {
|
||||
if (this.getAssigneeBy().length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
memberType() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return user && user.isBoardAdmin() ? 'admin' : 'normal';
|
||||
},
|
||||
|
||||
presenceStatusClassName() {
|
||||
const user = Users.findOne(this.userId);
|
||||
const userPresence = presences.findOne({ userId: this.userId });
|
||||
if (user && user.isInvitedTo(Session.get('currentBoard'))) return 'pending';
|
||||
else if (!userPresence) return 'disconnected';
|
||||
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
|
||||
return 'active';
|
||||
else return 'idle';
|
||||
},
|
||||
});
|
||||
|
||||
Template.userAvatarAssigneeInitials.helpers({
|
||||
initials() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return user && user.getInitials();
|
||||
},
|
||||
|
||||
viewPortWidth() {
|
||||
const user = Users.findOne(this.userId);
|
||||
return ((user && user.getInitials().length) || 1) * 12;
|
||||
},
|
||||
});
|
||||
|
||||
// We extends the normal InlinedForm component to support UnsavedEdits draft
|
||||
// feature.
|
||||
(class extends InlinedForm {
|
||||
@@ -588,6 +554,7 @@ Template.cardDetailsActionsPopup.events({
|
||||
'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'),
|
||||
'click .js-custom-fields': Popup.open('cardCustomFields'),
|
||||
'click .js-received-date': Popup.open('editCardReceivedDate'),
|
||||
'click .js-start-date': Popup.open('editCardStartDate'),
|
||||
@@ -603,7 +570,7 @@ Template.cardDetailsActionsPopup.events({
|
||||
const minOrder = _.min(
|
||||
this.list()
|
||||
.cards(this.swimlaneId)
|
||||
.map(c => c.sort),
|
||||
.map((c) => c.sort),
|
||||
);
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, minOrder - 1);
|
||||
},
|
||||
@@ -612,7 +579,7 @@ Template.cardDetailsActionsPopup.events({
|
||||
const maxOrder = _.max(
|
||||
this.list()
|
||||
.cards(this.swimlaneId)
|
||||
.map(c => c.sort),
|
||||
.map((c) => c.sort),
|
||||
);
|
||||
this.move(this.boardId, this.swimlaneId, this.listId, maxOrder + 1);
|
||||
},
|
||||
@@ -631,7 +598,7 @@ Template.cardDetailsActionsPopup.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardTitleForm.onRendered(function() {
|
||||
Template.editCardTitleForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-title'));
|
||||
});
|
||||
|
||||
@@ -645,7 +612,7 @@ Template.editCardTitleForm.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardRequesterForm.onRendered(function() {
|
||||
Template.editCardRequesterForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-requester'));
|
||||
});
|
||||
|
||||
@@ -658,7 +625,7 @@ Template.editCardRequesterForm.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.onRendered(function() {
|
||||
Template.editCardAssignerForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-assigner'));
|
||||
});
|
||||
|
||||
@@ -697,7 +664,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find(
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
@@ -707,7 +674,6 @@ BlazeComponent.extendComponent({
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
swimlanes() {
|
||||
@@ -736,7 +702,7 @@ Template.copyCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
@@ -744,9 +710,7 @@ Template.copyCardPopup.events({
|
||||
const textarea = $('#copy-card-title');
|
||||
const title = textarea.val().trim();
|
||||
// insert new card to the bottom of new list
|
||||
card.sort = Lists.findOne(card.listId)
|
||||
.cards()
|
||||
.count();
|
||||
card.sort = Lists.findOne(card.listId).cards().count();
|
||||
|
||||
if (title) {
|
||||
card.title = title;
|
||||
@@ -777,9 +741,7 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
const textarea = $('#copy-card-title');
|
||||
const titleEntry = textarea.val().trim();
|
||||
// insert new card to the bottom of new list
|
||||
card.sort = Lists.findOne(card.listId)
|
||||
.cards()
|
||||
.count();
|
||||
card.sort = Lists.findOne(card.listId).cards().count();
|
||||
|
||||
if (titleEntry) {
|
||||
const titleList = JSON.parse(titleEntry);
|
||||
@@ -796,13 +758,13 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
Filter.addException(_id);
|
||||
|
||||
// copy checklists
|
||||
Checklists.find({ cardId: oldId }).forEach(ch => {
|
||||
Checklists.find({ cardId: oldId }).forEach((ch) => {
|
||||
ch.copy(_id);
|
||||
});
|
||||
|
||||
// copy subtasks
|
||||
cursor = Cards.find({ parentId: oldId });
|
||||
cursor.forEach(function() {
|
||||
const cursor = Cards.find({ parentId: oldId });
|
||||
cursor.forEach(function () {
|
||||
'use strict';
|
||||
const subtask = arguments[0];
|
||||
subtask.parentId = _id;
|
||||
@@ -811,7 +773,7 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
});
|
||||
|
||||
// copy card comments
|
||||
CardComments.find({ cardId: oldId }).forEach(cmt => {
|
||||
CardComments.find({ cardId: oldId }).forEach((cmt) => {
|
||||
cmt.copy(_id);
|
||||
});
|
||||
}
|
||||
@@ -827,7 +789,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
colors() {
|
||||
return cardColors.map(color => ({ color, name: '' }));
|
||||
return ALLOWED_COLORS.map((color) => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
@@ -871,7 +833,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
boards() {
|
||||
const boards = Boards.find(
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
@@ -883,7 +845,6 @@ BlazeComponent.extendComponent({
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
return boards;
|
||||
},
|
||||
|
||||
cards() {
|
||||
@@ -951,7 +912,7 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
}
|
||||
},
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function() {
|
||||
'click .js-delete': Popup.afterConfirm('cardDelete', function () {
|
||||
Popup.close();
|
||||
// verify that there are no linked cards
|
||||
if (Cards.find({ linkedId: this._id }).count() === 0) {
|
||||
@@ -1058,6 +1019,8 @@ BlazeComponent.extendComponent({
|
||||
moment(new Date().setHours(12, 0, 0)).format('LT');
|
||||
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
|
||||
/*
|
||||
const newDate = moment(dateString, 'L LT', true);
|
||||
if (newDate.isValid()) {
|
||||
// if active vote - store it
|
||||
@@ -1068,6 +1031,159 @@ BlazeComponent.extendComponent({
|
||||
this.currentData().vote = { end: newDate.toDate() }; // set vote end temp
|
||||
Popup.back();
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// Try to parse different date formats of all languages.
|
||||
// This code is same for vote and planning poker.
|
||||
const usaDate = moment(dateString, 'L LT', true);
|
||||
const euroAmDate = moment(dateString, 'DD.MM.YYYY LT', true);
|
||||
const euro24hDate = moment(dateString, 'DD.MM.YYYY HH.mm', true);
|
||||
const eurodotDate = moment(dateString, 'DD.MM.YYYY HH:mm', true);
|
||||
const minusDate = moment(dateString, 'YYYY-MM-DD HH:mm', true);
|
||||
const slashDate = moment(dateString, 'DD/MM/YYYY HH.mm', true);
|
||||
const dotDate = moment(dateString, 'DD/MM/YYYY HH:mm', true);
|
||||
const brezhonegDate = moment(dateString, 'DD/MM/YYYY h[e]mm A', true);
|
||||
const hrvatskiDate = moment(dateString, 'DD. MM. YYYY H:mm', true);
|
||||
const latviaDate = moment(dateString, 'YYYY.MM.DD. H:mm', true);
|
||||
const nederlandsDate = moment(dateString, 'DD-MM-YYYY HH:mm', true);
|
||||
// greekDate does not work: el Greek Ελληνικά ,
|
||||
// it has date format DD/MM/YYYY h:mm MM like 20/06/2021 11:15 MM
|
||||
// where MM is maybe some text like AM/PM ?
|
||||
// Also some other languages that have non-ascii characters in dates
|
||||
// do not work.
|
||||
const greekDate = moment(dateString, 'DD/MM/YYYY h:mm A', true);
|
||||
const macedonianDate = moment(dateString, 'D.MM.YYYY H:mm', true);
|
||||
|
||||
if (usaDate.isValid()) {
|
||||
// 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} else {
|
||||
this.error.set('invalid-date');
|
||||
evt.target.date.focus();
|
||||
@@ -1089,6 +1205,277 @@ BlazeComponent.extendComponent({
|
||||
}
|
||||
}.register('editVoteEndDatePopup'));
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.currentCard = this.currentData();
|
||||
this.pokerQuestion = new ReactiveVar(this.currentCard.pokerQuestion);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-end-date': Popup.open('editPokerEndDate'),
|
||||
'submit .edit-poker-question'(evt) {
|
||||
evt.preventDefault();
|
||||
const pokerQuestion = true;
|
||||
const allowNonBoardMembers = $('#poker-allow-non-members').hasClass(
|
||||
'is-checked',
|
||||
);
|
||||
const endString = this.currentCard.getPokerEnd();
|
||||
|
||||
this.currentCard.setPokerQuestion(
|
||||
pokerQuestion,
|
||||
allowNonBoardMembers,
|
||||
);
|
||||
if (endString) {
|
||||
this.currentCard.setPokerEnd(endString);
|
||||
}
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-remove-poker': Popup.afterConfirm('deletePoker', (event) => {
|
||||
event.preventDefault();
|
||||
this.currentCard.unsetPoker();
|
||||
Popup.close();
|
||||
}),
|
||||
'click a.js-toggle-poker-allow-non-members'(event) {
|
||||
event.preventDefault();
|
||||
$('#poker-allow-non-members').toggleClass('is-checked');
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('cardStartPlanningPokerPopup');
|
||||
|
||||
// editPokerEndDatePopup
|
||||
(class extends DatePicker {
|
||||
onCreated() {
|
||||
super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
|
||||
this.data().getPokerEnd() &&
|
||||
this.date.set(moment(this.data().getPokerEnd()));
|
||||
}
|
||||
|
||||
/*
|
||||
Tried to use dateFormat and timeFormat from client/components/lib/datepicker.js
|
||||
to make detecting all date formats not necessary,
|
||||
but got error "language mk does not exist".
|
||||
Maybe client/components/lib/datepicker.jade could have hidden input field for
|
||||
datepicker format that could be used to detect date format?
|
||||
|
||||
dateFormat() {
|
||||
return moment.localeData().longDateFormat('L');
|
||||
}
|
||||
|
||||
timeFormat() {
|
||||
return moment.localeData().longDateFormat('LT');
|
||||
}
|
||||
|
||||
const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true);
|
||||
*/
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'submit .edit-date'(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
// if no time was given, init with 12:00
|
||||
const time =
|
||||
evt.target.time.value ||
|
||||
moment(new Date().setHours(12, 0, 0)).format('LT');
|
||||
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
|
||||
/*
|
||||
Tried to use dateFormat and timeFormat from client/components/lib/datepicker.js
|
||||
to make detecting all date formats not necessary,
|
||||
but got error "language mk does not exist".
|
||||
Maybe client/components/lib/datepicker.jade could have hidden input field for
|
||||
datepicker format that could be used to detect date format?
|
||||
|
||||
const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true);
|
||||
|
||||
if (newDate.isValid()) {
|
||||
// if active poker - store it
|
||||
if (this.currentData().getPokerQuestion()) {
|
||||
this._storeDate(newDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
this.currentData().poker = { end: newDate.toDate() }; // set poker end temp
|
||||
Popup.back();
|
||||
}
|
||||
*/
|
||||
|
||||
// Try to parse different date formats of all languages.
|
||||
// This code is same for vote and planning poker.
|
||||
const usaDate = moment(dateString, 'L LT', true);
|
||||
const euroAmDate = moment(dateString, 'DD.MM.YYYY LT', true);
|
||||
const euro24hDate = moment(dateString, 'DD.MM.YYYY HH.mm', true);
|
||||
const eurodotDate = moment(dateString, 'DD.MM.YYYY HH:mm', true);
|
||||
const minusDate = moment(dateString, 'YYYY-MM-DD HH:mm', true);
|
||||
const slashDate = moment(dateString, 'DD/MM/YYYY HH.mm', true);
|
||||
const dotDate = moment(dateString, 'DD/MM/YYYY HH:mm', true);
|
||||
const brezhonegDate = moment(dateString, 'DD/MM/YYYY h[e]mm A', true);
|
||||
const hrvatskiDate = moment(dateString, 'DD. MM. YYYY H:mm', true);
|
||||
const latviaDate = moment(dateString, 'YYYY.MM.DD. H:mm', true);
|
||||
const nederlandsDate = moment(dateString, 'DD-MM-YYYY HH:mm', true);
|
||||
// greekDate does not work: el Greek Ελληνικά ,
|
||||
// it has date format DD/MM/YYYY h:mm MM like 20/06/2021 11:15 MM
|
||||
// where MM is maybe some text like AM/PM ?
|
||||
// Also some other languages that have non-ascii characters in dates
|
||||
// do not work.
|
||||
const greekDate = moment(dateString, 'DD/MM/YYYY h:mm A', true);
|
||||
const macedonianDate = moment(dateString, 'D.MM.YYYY H:mm', true);
|
||||
|
||||
if (usaDate.isValid()) {
|
||||
// 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
} else {
|
||||
// this.error.set('invalid-date);
|
||||
this.error.set('invalid-date' + ' ' + dateString);
|
||||
evt.target.date.focus();
|
||||
}
|
||||
},
|
||||
'click .js-delete-date'(evt) {
|
||||
evt.preventDefault();
|
||||
this._deleteDate();
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
_storeDate(newDate) {
|
||||
this.card.setPokerEnd(newDate);
|
||||
}
|
||||
_deleteDate() {
|
||||
this.card.unsetPokerEnd();
|
||||
}
|
||||
}.register('editPokerEndDatePopup'));
|
||||
|
||||
// Close the card details pane by pressing escape
|
||||
EscapeActions.register(
|
||||
'detailsPane',
|
||||
|
||||
@@ -211,6 +211,7 @@ avatar-radius = 50%
|
||||
word-wrap: break-word
|
||||
max-width: 36%
|
||||
flex-grow: 1
|
||||
&.card-details-item-creator,
|
||||
&.card-details-item-received,
|
||||
&.card-details-item-start,
|
||||
&.card-details-item-due,
|
||||
@@ -356,3 +357,131 @@ card-details-color(background, color...)
|
||||
display: flex
|
||||
.js-show-positive-votes
|
||||
cursor: pointer
|
||||
|
||||
.poker-voted
|
||||
opacity: .7
|
||||
|
||||
.poker-title
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
|
||||
.js-edit-date
|
||||
align-self: baseline
|
||||
margin-left: 5px
|
||||
|
||||
.poker-result
|
||||
display: flex
|
||||
flex-flow: row wrap
|
||||
.js-show-positive-poker-votes
|
||||
cursor: pointer
|
||||
|
||||
.poker-deck
|
||||
display: grid
|
||||
flex-direction: column
|
||||
text-align: center
|
||||
|
||||
.poker-card-result
|
||||
width: 32px
|
||||
font-size: 1em
|
||||
font-weight: bold
|
||||
padding: 4px 2px 4px 2px
|
||||
cursor: default
|
||||
|
||||
.winner
|
||||
font-weight: bold
|
||||
outline: #2d2d2d solid 2px
|
||||
|
||||
.loser
|
||||
opacity: .5
|
||||
|
||||
.responsive-table
|
||||
overflow-x: auto
|
||||
|
||||
.poker-table
|
||||
display: table
|
||||
width: 100%
|
||||
padding-top: 10px
|
||||
|
||||
.poker-table-row
|
||||
display: table-row
|
||||
|
||||
.poker-table-heading
|
||||
background-color: #EEE
|
||||
display: table-header-group
|
||||
|
||||
.poker-table-cell
|
||||
display: table-cell
|
||||
padding: 0 0 5px 2px
|
||||
border-bottom: 1px solid #d2d0d0
|
||||
text-align: center
|
||||
min-width: 45px
|
||||
|
||||
.poker-table-cell-who
|
||||
width: 150px
|
||||
vertical-align: middle
|
||||
|
||||
.poker-table-heading-left,
|
||||
.poker-table-heading-right
|
||||
display: table-header-group
|
||||
font-weight: bold
|
||||
border-top: 1px solid #808080
|
||||
|
||||
@media (max-width: 400px)
|
||||
.poker-table-heading-right
|
||||
display: none
|
||||
|
||||
.poker-table-body
|
||||
display: table-row-group
|
||||
|
||||
.poker-table-side-left,
|
||||
.poker-table-side-right
|
||||
display: inline-block
|
||||
|
||||
.poker-table-side-right
|
||||
padding-left: 10px
|
||||
|
||||
@media (max-width: 400px)
|
||||
.poker-table-side-right
|
||||
padding-left: 0px
|
||||
|
||||
.estimation-add
|
||||
display: block
|
||||
overflow: auto
|
||||
margin-top: 15px
|
||||
margin-bottom: 5px
|
||||
input
|
||||
display: inline-block
|
||||
float: right
|
||||
margin: auto
|
||||
margin-right: 10px
|
||||
width: 100px
|
||||
button
|
||||
display: inline-block
|
||||
float: right
|
||||
margin: auto
|
||||
|
||||
.poker-card
|
||||
width:48px
|
||||
height:72px
|
||||
float:left
|
||||
background:#fff
|
||||
border-radius:5px
|
||||
display:table
|
||||
box-sizing:border-box
|
||||
padding:5px
|
||||
margin:3px
|
||||
font-size:20px
|
||||
font-weight: bold
|
||||
text-shadow: #2d2d2d 1px 1px 0
|
||||
box-shadow:0 0 5px #aaaaaa
|
||||
text-align:center
|
||||
position:relative
|
||||
cursor: pointer
|
||||
|
||||
.inner
|
||||
display:table-cell
|
||||
vertical-align:middle
|
||||
border-radius:5px
|
||||
overflow:hidden
|
||||
background-color: #cecece
|
||||
|
||||
@@ -4,8 +4,8 @@ template(name="checklists")
|
||||
i.fa.fa-check
|
||||
| {{_ 'checklists'}}
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch
|
||||
span.toggle-switch-title {{_ 'hide-checked-items'}}
|
||||
.material-toggle-switch(title="{{_ 'hide-checked-items'}}")
|
||||
//span.toggle-switch-title
|
||||
if hideCheckedItems
|
||||
input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton" checked="checked")
|
||||
else
|
||||
@@ -25,9 +25,8 @@ template(name="checklists")
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
|
||||
+addChecklistItemForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
a.js-open-inlined-form(title="{{_ 'add-checklist'}}")
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-checklist'}}...
|
||||
|
||||
template(name="checklistDetail")
|
||||
.js-checklist.checklist
|
||||
@@ -93,9 +92,8 @@ template(name="checklistItems")
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist)
|
||||
+addChecklistItemForm
|
||||
else
|
||||
a.add-checklist-item.js-open-inlined-form
|
||||
a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-checklist-item'}}...
|
||||
|
||||
template(name='checklistItemDetail')
|
||||
.js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if hideCheckedItems}} invisible{{/if}}{{/if}}")
|
||||
|
||||
@@ -82,6 +82,9 @@ template(name="minicard")
|
||||
+minicardCustomFieldDate
|
||||
else if $eq definition.type "checkbox"
|
||||
.materialCheckBox(class="{{#if value }}is-checked{{/if}}")
|
||||
else if $eq definition.type "stringtemplate"
|
||||
+viewer
|
||||
= formattedStringtemplateCustomFieldValue(definition)
|
||||
else
|
||||
+viewer
|
||||
= trueValue
|
||||
@@ -96,6 +99,10 @@ template(name="minicard")
|
||||
each getMembers
|
||||
+userAvatar(userId=this)
|
||||
|
||||
if showCreator
|
||||
.minicard-creator
|
||||
+userAvatar(userId=this.userId noRemove=true)
|
||||
|
||||
.badges
|
||||
unless currentUser.isNoComments
|
||||
if comments.count
|
||||
@@ -114,6 +121,11 @@ template(name="minicard")
|
||||
span.badge-text {{ voteCountPositive }}
|
||||
span.badge-icon.fa.fa-thumbs-down(class="{{#if $eq voteState false}}text-red{{/if}}")
|
||||
span.badge-text {{ voteCountNegative }}
|
||||
if getPokerQuestion
|
||||
.badge.badge-state-image-only(title=getPokerQuestion)
|
||||
span.badge-icon.fa.fa-check(class="{{#if pokerState}}text-green{{/if}}")
|
||||
if expiredPoker
|
||||
span.badge-text {{ getPokerEstimation }}
|
||||
if attachments.count
|
||||
.badge
|
||||
span.badge-icon.fa.fa-paperclip
|
||||
@@ -122,3 +134,8 @@ template(name="minicard")
|
||||
.badge(class="{{#if checklistFinished}}is-finished{{/if}}")
|
||||
span.badge-icon.fa.fa-check-square-o
|
||||
span.badge-text.check-list-text {{checklistFinishedCount}}/{{checklistItemCount}}
|
||||
if allSubtasks.count
|
||||
.badge
|
||||
span.badge-icon.fa.fa-sitemap
|
||||
span.badge-text.check-list-text {{subtasksFinishedCount}}/{{allSubtasksCount}}
|
||||
//{{subtasksFinishedCount}}/{{subtasksCount}} does not work because when a subtaks is archived, the count goes down
|
||||
@@ -21,6 +21,34 @@ BlazeComponent.extendComponent({
|
||||
}).format(customFieldTrueValue);
|
||||
},
|
||||
|
||||
formattedStringtemplateCustomFieldValue(definition) {
|
||||
const customField = this.data()
|
||||
.customFieldsWD()
|
||||
.find(f => f._id === definition._id);
|
||||
|
||||
const customFieldTrueValue =
|
||||
customField && customField.trueValue ? customField.trueValue : [];
|
||||
|
||||
return customFieldTrueValue
|
||||
.filter(value => !!value.trim())
|
||||
.map(value =>
|
||||
definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, value),
|
||||
)
|
||||
.join(definition.settings.stringtemplateSeparator ?? '');
|
||||
},
|
||||
|
||||
showCreator() {
|
||||
if (this.data().board()) {
|
||||
return (
|
||||
this.data().board.allowsCreator === null ||
|
||||
this.data().board().allowsCreator === undefined ||
|
||||
this.data().board().allowsCreator
|
||||
);
|
||||
// return this.data().board().allowsCreator;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
border-radius: 2px
|
||||
margin-right: 3px
|
||||
margin-bottom: 3px
|
||||
|
||||
|
||||
.minicard-custom-fields
|
||||
display:block;
|
||||
.minicard-custom-field
|
||||
@@ -163,7 +163,8 @@
|
||||
line-height: 12px
|
||||
|
||||
.minicard-members,
|
||||
.minicard-assignees
|
||||
.minicard-assignees,
|
||||
.minicard-creator
|
||||
float: right
|
||||
margin-left: 5px
|
||||
margin-bottom: 4px
|
||||
@@ -187,6 +188,9 @@
|
||||
.minicard-assignees
|
||||
border-bottom: 1px solid red
|
||||
|
||||
.minicard-creator
|
||||
border-bottom: 1px solid green
|
||||
|
||||
.minicard-members:empty,
|
||||
.minicard-assignees:empty
|
||||
display: none
|
||||
|
||||
@@ -15,9 +15,8 @@ template(name="subtasks")
|
||||
+inlinedForm(autoclose=false classNames="js-add-subtask" cardId = cardId)
|
||||
+addSubtaskItemForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
a.js-open-inlined-form(title="{{_ 'add-subtask'}}")
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-subtask'}}...
|
||||
|
||||
template(name="subtaskDetail")
|
||||
.js-subtasks.subtask
|
||||
|
||||
@@ -19,9 +19,8 @@ template(name="listBody")
|
||||
+inlinedForm(autoclose=false position="bottom")
|
||||
+addCardForm(listId=_id position="bottom")
|
||||
else
|
||||
a.open-minicard-composer.js-card-composer.js-open-inlined-form
|
||||
a.open-minicard-composer.js-card-composer.js-open-inlined-form(title="{{_ 'add-card-to-bottom-of-list'}}")
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-card'}}
|
||||
|
||||
template(name="spinnerList")
|
||||
.sk-spinner.sk-spinner-wave.sk-spinner-list(
|
||||
|
||||
@@ -28,8 +28,8 @@ template(name="listHeader")
|
||||
div.list-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
if canSeeAddCard
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-icon
|
||||
a.fa.fa-navicon.js-open-list-menu
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-icon(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
|
||||
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
@@ -41,8 +41,8 @@ template(name="listHeader")
|
||||
//if isBoardAdmin
|
||||
// a.fa.js-list-star.list-header-plus-icon(class="fa-star{{#unless starred}}-o{{/unless}}")
|
||||
if canSeeAddCard
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-icon
|
||||
a.fa.fa-navicon.js-open-list-menu
|
||||
a.js-add-card.fa.fa-plus.list-header-plus-icon(title="{{_ 'add-card-to-top-of-list'}}")
|
||||
a.fa.fa-navicon.js-open-list-menu(title="{{_ 'listActionPopup-title'}}")
|
||||
if currentUser.isBoardAdmin
|
||||
if showDesktopDragHandles
|
||||
a.list-header-handle.handle.fa.fa-arrows.js-list-handle
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
OPERATOR_HAS,
|
||||
OPERATOR_SORT,
|
||||
OPERATOR_USER,
|
||||
ORDER_DESCENDING,
|
||||
ORDER_ASCENDING,
|
||||
PREDICATE_DUE_AT,
|
||||
} from '../../../config/search-const';
|
||||
import { QueryParams } from '../../../config/query-classes';
|
||||
@@ -62,14 +62,14 @@ class DueCardsComponent extends CardSearchPagedComponent {
|
||||
// queryParams[OPERATOR_LIMIT] = 5;
|
||||
queryParams.addPredicate(OPERATOR_SORT, {
|
||||
name: PREDICATE_DUE_AT,
|
||||
order: ORDER_DESCENDING,
|
||||
order: ORDER_ASCENDING,
|
||||
});
|
||||
|
||||
if (Utils.dueCardsView() !== 'all') {
|
||||
queryParams.addPredicate(OPERATOR_USER, Meteor.user().username);
|
||||
}
|
||||
|
||||
this.runGlobalSearch(queryParams.getQueryParams());
|
||||
this.runGlobalSearch(queryParams);
|
||||
}
|
||||
|
||||
dueCardsView() {
|
||||
|
||||
@@ -49,7 +49,7 @@ Template.editor.onRendered(() => {
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
//['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
//['insert', ['link', 'picture']], // modal popup has issue somehow :(
|
||||
['insert', ['link']], //, 'picture']], // modal popup has issue somehow :(
|
||||
['view', ['fullscreen', 'help']],
|
||||
];
|
||||
const cleanPastedHTML = function(input) {
|
||||
@@ -145,6 +145,7 @@ Template.editor.onRendered(() => {
|
||||
const MAX_IMAGE_PIXEL = Utils.MAX_IMAGE_PIXEL;
|
||||
const COMPRESS_RATIO = Utils.IMAGE_COMPRESS_RATIO;
|
||||
const insertImage = src => {
|
||||
// process all image upload types to the description/comment window
|
||||
const img = document.createElement('img');
|
||||
img.src = src;
|
||||
img.setAttribute('width', '100%');
|
||||
@@ -210,7 +211,16 @@ Template.editor.onRendered(() => {
|
||||
}
|
||||
}
|
||||
},
|
||||
onPaste() {
|
||||
onPaste(e) {
|
||||
var clipboardData = e.clipboardData;
|
||||
var pastedData = clipboardData.getData('Text');
|
||||
|
||||
//if pasted data is an image, exit
|
||||
if (!pastedData.length) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// clear up unwanted tag info when user pasted in text
|
||||
const thisNote = this;
|
||||
const updatePastedText = function(object) {
|
||||
@@ -219,7 +229,7 @@ Template.editor.onRendered(() => {
|
||||
// (and multiplies by pasting more) by changing paste "p" to "br".
|
||||
// Fixes https://github.com/wekan/wekan/2890 .
|
||||
// == Fix Start ==
|
||||
someNote.execCommand('defaultParagraphSeparator', false, 'br');
|
||||
//someNote.execCommand('defaultParagraphSeparator', false, 'br');
|
||||
// == Fix End ==
|
||||
const original = someNote.summernote('code');
|
||||
const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.
|
||||
@@ -234,17 +244,17 @@ Template.editor.onRendered(() => {
|
||||
},
|
||||
},
|
||||
dialogsInBody: true,
|
||||
disableDragAndDrop: true,
|
||||
spellCheck: true,
|
||||
disableGrammar: false,
|
||||
disableDragAndDrop: false,
|
||||
toolbar,
|
||||
popover: {
|
||||
image: [
|
||||
[
|
||||
'image',
|
||||
['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone'],
|
||||
],
|
||||
['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
|
||||
['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||
['remove', ['removeMedia']],
|
||||
],
|
||||
link: [['link', ['linkDialogShow', 'unlink']]],
|
||||
table: [
|
||||
['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
|
||||
['delete', ['deleteRow', 'deleteCol', 'deleteTable']],
|
||||
@@ -263,10 +273,12 @@ Template.editor.onRendered(() => {
|
||||
}
|
||||
});
|
||||
|
||||
import sanitizeXss from 'xss';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
// Additional safeAttrValue function to allow for other specific protocols
|
||||
// See https://github.com/leizongmin/js-xss/issues/52#issuecomment-241354114
|
||||
|
||||
/*
|
||||
function mySafeAttrValue(tag, name, value, cssFilter) {
|
||||
// only when the tag is 'a' and attribute is 'href'
|
||||
// then use your custom function
|
||||
@@ -292,6 +304,7 @@ function mySafeAttrValue(tag, name, value, cssFilter) {
|
||||
return sanitizeXss.safeAttrValue(tag, name, value, cssFilter);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// XXX I believe we should compute a HTML rendered field on the server that
|
||||
// would handle markdown and user mentions. We can simply have two
|
||||
@@ -307,7 +320,9 @@ Blaze.Template.registerHelper(
|
||||
let content = Blaze.toHTML(view.templateContentBlock);
|
||||
const currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
if (!currentBoard)
|
||||
return HTML.Raw(sanitizeXss(content, { safeAttrValue: mySafeAttrValue }));
|
||||
return HTML.Raw(
|
||||
DOMPurify.sanitize(content, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
);
|
||||
const knowedUsers = currentBoard.members.map(member => {
|
||||
const u = Users.findOne(member.userId);
|
||||
if (u) {
|
||||
@@ -351,7 +366,9 @@ Blaze.Template.registerHelper(
|
||||
content = content.replace(fullMention, Blaze.toHTML(link));
|
||||
}
|
||||
|
||||
return HTML.Raw(sanitizeXss(content, { safeAttrValue: mySafeAttrValue }));
|
||||
return HTML.Raw(
|
||||
DOMPurify.sanitize(content, { ALLOW_UNKNOWN_PROTOCOLS: true }),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -11,9 +11,10 @@ template(name="globalSearchModalTitle")
|
||||
| {{_ 'globalSearch-title'}}
|
||||
|
||||
template(name="resultsPaged")
|
||||
h1
|
||||
= resultsHeading.get
|
||||
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
|
||||
if resultsHeading.get
|
||||
h1
|
||||
= resultsHeading.get
|
||||
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
|
||||
each card in results.get
|
||||
+resultCard(card)
|
||||
table.global-search-footer
|
||||
@@ -50,8 +51,7 @@ template(name="globalSearch")
|
||||
each msg in errorMessages
|
||||
li.global-search-error-messages
|
||||
= msg
|
||||
else
|
||||
+resultsPaged(this)
|
||||
+resultsPaged(this)
|
||||
else if serverError.get
|
||||
.global-search-page
|
||||
.global-search-help
|
||||
|
||||
@@ -123,6 +123,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
operator_member_abbrev: TAPi18n.__('operator-member-abbrev'),
|
||||
operator_assignee: TAPi18n.__('operator-assignee'),
|
||||
operator_assignee_abbrev: TAPi18n.__('operator-assignee-abbrev'),
|
||||
operator_creator: TAPi18n.__('operator-creator'),
|
||||
operator_due: TAPi18n.__('operator-due'),
|
||||
operator_created: TAPi18n.__('operator-created'),
|
||||
operator_modified: TAPi18n.__('operator-modified'),
|
||||
@@ -167,6 +168,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
['\n* ', 'globalSearch-instructions-operator-at'],
|
||||
['\n* ', 'globalSearch-instructions-operator-member'],
|
||||
['\n* ', 'globalSearch-instructions-operator-assignee'],
|
||||
['\n* ', 'globalSearch-instructions-operator-creator'],
|
||||
['\n* ', 'globalSearch-instructions-operator-due'],
|
||||
['\n* ', 'globalSearch-instructions-operator-created'],
|
||||
['\n* ', 'globalSearch-instructions-operator-modified'],
|
||||
@@ -202,9 +204,8 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
return super.events().concat([
|
||||
{
|
||||
...super.events()[0],
|
||||
'submit .js-search-query-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.searchAllBoards(evt.target.searchQuery.value);
|
||||
@@ -257,7 +258,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
this.hasResults.set(false);
|
||||
},
|
||||
},
|
||||
];
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,12 @@ template(name="header")
|
||||
unless currentSetting.hideLogo
|
||||
if currentSetting.customTopLeftCornerLogoImageUrl
|
||||
if currentSetting.customTopLeftCornerLogoLinkUrl
|
||||
a(href="{{currentSetting.customTopLeftCornerLogoLinkUrl}}")
|
||||
a(href="{{currentSetting.customTopLeftCornerLogoLinkUrl}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
|
||||
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0")
|
||||
unless currentSetting.customTopLeftCornerLogoLinkUrl
|
||||
img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0")
|
||||
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="")
|
||||
img(src="{{pathFor '/logo-header.png'}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
|
||||
span.allBoards
|
||||
a(href="{{pathFor 'home'}}")
|
||||
span.fa.fa-home
|
||||
|
||||
@@ -77,6 +77,8 @@ Template.userFormsLayout.helpers({
|
||||
} else if (lang.name === 'ar-EG') {
|
||||
// ar-EG = Arabic (Egypt), simply Masri (مَصرى, [ˈmɑsˤɾi], Egyptian, Masr refers to Cairo)
|
||||
name = 'مَصرى';
|
||||
} else if (lang.name === 'de-CH') {
|
||||
name = 'Deutsch (Schweiz)';
|
||||
} else if (lang.name === 'fa-IR') {
|
||||
// fa-IR = Persian (Iran)
|
||||
name = 'فارسی/پارسی (ایران)';
|
||||
|
||||
@@ -6,16 +6,6 @@ template(name="myCardsHeaderBar")
|
||||
i.fa.fa-list
|
||||
| {{_ 'my-cards'}}
|
||||
|
||||
.board-header-btns.left
|
||||
a.board-header-btn.js-toggle-my-cards-choose-sort(title="{{_ 'myCardsSortChange-title'}}")
|
||||
i.fa.fa-caret-down
|
||||
if $eq myCardsSort 'board'
|
||||
i.fa.fa-th-large
|
||||
| {{_ 'myCardsSortChange-choice-board'}}
|
||||
if $eq myCardsSort 'dueAt'
|
||||
i.fa.fa-calendar
|
||||
| {{_ 'myCardsSortChange-choice-dueat'}}
|
||||
|
||||
template(name="myCardsModalTitle")
|
||||
if currentUser
|
||||
h2
|
||||
@@ -28,46 +18,22 @@ template(name="myCards")
|
||||
+spinner
|
||||
else
|
||||
.wrapper
|
||||
if $eq myCardsSort 'board'
|
||||
each board in myCardsList
|
||||
.my-cards-board-wrapper
|
||||
.my-cards-board-title(class=board.colorClass, id="header")
|
||||
a(href=board.originRelativeUrl)
|
||||
+viewer
|
||||
= board.title
|
||||
each swimlane in board.mySwimlanes
|
||||
.my-cards-swimlane-title(class="{{#if swimlane.colorClass}}{{ swimlane.colorClass }}{{else}}swimlane-default-color{{/if}}")
|
||||
+viewer
|
||||
= swimlane.title
|
||||
each list in swimlane.myLists
|
||||
.my-cards-list-wrapper
|
||||
.my-cards-list-title(class=list.colorClass)
|
||||
+viewer
|
||||
= list.title
|
||||
each card in list.myCards
|
||||
.my-cards-card-wrapper
|
||||
a.minicard-wrapper(href=card.originRelativeUrl)
|
||||
+minicard(card)
|
||||
else
|
||||
.my-cards-dueat-list-wrapper
|
||||
each card in myDueCardsList
|
||||
+resultCard(card)
|
||||
|
||||
template(name="myCardsSortChangePopup")
|
||||
if currentUser
|
||||
ul.pop-over-list
|
||||
li
|
||||
with "my-cards-sort-board"
|
||||
a.js-my-cards-sort-board
|
||||
i.fa.fa-th-large.colorful
|
||||
| {{_ 'myCardsSortChange-choice-board'}}
|
||||
if $eq Utils.myCardsSort "board"
|
||||
i.fa.fa-check
|
||||
hr
|
||||
li
|
||||
with "my-cards-sort-dueat"
|
||||
a.js-my-cards-sort-dueat
|
||||
i.fa.fa-calendar.colorful
|
||||
| {{_ 'myCardsSortChange-choice-dueat'}}
|
||||
if $eq Utils.myCardsSort "dueAt"
|
||||
i.fa.fa-check
|
||||
each board in myCardsList
|
||||
.my-cards-board-wrapper
|
||||
.my-cards-board-title(class=board.colorClass, id="header")
|
||||
a(href=board.originRelativeUrl)
|
||||
+viewer
|
||||
= board.title
|
||||
each swimlane in board.mySwimlanes
|
||||
.my-cards-swimlane-title(class="{{#if swimlane.colorClass}}{{ swimlane.colorClass }}{{else}}swimlane-default-color{{/if}}")
|
||||
+viewer
|
||||
= swimlane.title
|
||||
each list in swimlane.myLists
|
||||
.my-cards-list-wrapper
|
||||
.my-cards-list-title(class=list.colorClass)
|
||||
+viewer
|
||||
= list.title
|
||||
each card in list.myCards
|
||||
.my-cards-card-wrapper
|
||||
a.minicard-wrapper(href=card.originRelativeUrl)
|
||||
+minicard(card)
|
||||
@@ -1,14 +1,4 @@
|
||||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
import { QueryParams } from '../../../config/query-classes';
|
||||
import {
|
||||
OPERATOR_LIMIT,
|
||||
OPERATOR_SORT,
|
||||
OPERATOR_USER,
|
||||
ORDER_DESCENDING,
|
||||
PREDICATE_DUE_AT,
|
||||
} from '../../../config/search-const';
|
||||
|
||||
// const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
myCardsSort() {
|
||||
@@ -34,48 +24,21 @@ Template.myCards.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-my-cards-sort-board'() {
|
||||
Utils.setMyCardsSort('board');
|
||||
Popup.close();
|
||||
},
|
||||
|
||||
'click .js-my-cards-sort-dueat'() {
|
||||
Utils.setMyCardsSort('dueAt');
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('myCardsSortChangePopup');
|
||||
|
||||
class MyCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
|
||||
const queryParams = new QueryParams();
|
||||
queryParams.addPredicate(OPERATOR_USER, Meteor.user().username);
|
||||
queryParams.addPredicate(OPERATOR_SORT, {
|
||||
name: PREDICATE_DUE_AT,
|
||||
order: ORDER_DESCENDING,
|
||||
});
|
||||
queryParams.addPredicate(OPERATOR_LIMIT, 100);
|
||||
|
||||
this.runGlobalSearch(queryParams);
|
||||
this.runGlobalSearch(null);
|
||||
Meteor.subscribe('setting');
|
||||
}
|
||||
|
||||
myCardsSort() {
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('sort:', Utils.myCardsSort());
|
||||
return Utils.myCardsSort();
|
||||
}
|
||||
|
||||
sortByBoard() {
|
||||
return this.myCardsSort() === 'board';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
getSubscription(queryParams) {
|
||||
return Meteor.subscribe(
|
||||
'myCards',
|
||||
this.sessionId,
|
||||
this.subscriptionCallbacks,
|
||||
);
|
||||
}
|
||||
|
||||
myCardsList() {
|
||||
@@ -87,35 +50,9 @@ class MyCardsComponent extends CardSearchPagedComponent {
|
||||
const cursor = this.getResults();
|
||||
|
||||
if (cursor) {
|
||||
let newBoard = false;
|
||||
let newSwimlane = false;
|
||||
let newList = false;
|
||||
|
||||
cursor.forEach(card => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('card:', card.title);
|
||||
if (list === null || card.listId !== list._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new list');
|
||||
list = card.getList();
|
||||
if (list.archived) {
|
||||
list = null;
|
||||
return;
|
||||
}
|
||||
list.myCards = [card];
|
||||
newList = true;
|
||||
}
|
||||
if (swimlane === null || card.swimlaneId !== swimlane._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new swimlane');
|
||||
swimlane = card.getSwimlane();
|
||||
if (swimlane.archived) {
|
||||
swimlane = null;
|
||||
return;
|
||||
}
|
||||
swimlane.myLists = [list];
|
||||
newSwimlane = true;
|
||||
}
|
||||
if (board === null || card.boardId !== board._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new board');
|
||||
@@ -126,23 +63,38 @@ class MyCardsComponent extends CardSearchPagedComponent {
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('board:', b, b._id, b.title);
|
||||
board.mySwimlanes = [swimlane];
|
||||
newBoard = true;
|
||||
}
|
||||
|
||||
if (newBoard) {
|
||||
boards.push(board);
|
||||
} else if (newSwimlane) {
|
||||
board.mySwimlanes.push(swimlane);
|
||||
} else if (newList) {
|
||||
swimlane.myLists.push(list);
|
||||
} else {
|
||||
list.myCards.push(card);
|
||||
board.mySwimlanes = [];
|
||||
swimlane = null;
|
||||
list = null;
|
||||
}
|
||||
|
||||
newBoard = false;
|
||||
newSwimlane = false;
|
||||
newList = false;
|
||||
if (swimlane === null || card.swimlaneId !== swimlane._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new swimlane');
|
||||
swimlane = card.getSwimlane();
|
||||
if (swimlane.archived) {
|
||||
swimlane = null;
|
||||
return;
|
||||
}
|
||||
board.mySwimlanes.push(swimlane);
|
||||
swimlane.myLists = [];
|
||||
list = null;
|
||||
}
|
||||
|
||||
if (list === null || card.listId !== list._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new list');
|
||||
list = card.getList();
|
||||
if (list.archived) {
|
||||
list = null;
|
||||
return;
|
||||
}
|
||||
swimlane.myLists.push(list);
|
||||
list.myCards = [];
|
||||
}
|
||||
|
||||
list.myCards.push(card);
|
||||
});
|
||||
|
||||
// sort the data structure
|
||||
@@ -182,27 +134,5 @@ class MyCardsComponent extends CardSearchPagedComponent {
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
myDueCardsList() {
|
||||
const cursor = this.getResults();
|
||||
const cards = [];
|
||||
cursor.forEach(card => {
|
||||
cards.push(card);
|
||||
});
|
||||
|
||||
cards.sort((a, b) => {
|
||||
const x = a.dueAt === null ? new Date('2100-12-31') : a.dueAt;
|
||||
const y = b.dueAt === null ? new Date('2100-12-31') : b.dueAt;
|
||||
|
||||
if (x > y) return 1;
|
||||
else if (x < y) return -1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cursor:', cards);
|
||||
return cards;
|
||||
}
|
||||
}
|
||||
MyCardsComponent.register('myCards');
|
||||
@@ -1,5 +1,5 @@
|
||||
template(name='notifications')
|
||||
#notifications.board-header-btns.right
|
||||
a.notifications-drawer-toggle.fa.fa-bell(class="{{#if $gt unreadNotifications 0}}alert{{/if}}")
|
||||
a.notifications-drawer-toggle.fa.fa-bell(class="{{#if $gt unreadNotifications 0}}alert{{/if}}" title="{{_ 'notifications'}}")
|
||||
if $.Session.get 'showNotificationsDrawer'
|
||||
+notificationsDrawer(unreadNotifications=unreadNotifications)
|
||||
@@ -0,0 +1,111 @@
|
||||
template(name="adminReports")
|
||||
.setting-content
|
||||
unless currentUser.isAdmin
|
||||
| {{_ 'error-notAuthorized'}}
|
||||
else
|
||||
.content-body
|
||||
.side-menu
|
||||
ul
|
||||
li
|
||||
a.js-report-broken(data-id="report-broken")
|
||||
i.fa.fa-chain-broken
|
||||
| {{_ 'broken-cards'}}
|
||||
|
||||
li
|
||||
a.js-report-files(data-id="report-orphaned-files")
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'orphanedFilesReportTitle'}}
|
||||
|
||||
li
|
||||
a.js-report-files(data-id="report-files")
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'filesReportTitle'}}
|
||||
|
||||
li
|
||||
a.js-report-rules(data-id="report-rules")
|
||||
i.fa.fa-magic
|
||||
| {{_ 'rulesReportTitle'}}
|
||||
|
||||
.main-body
|
||||
if loading.get
|
||||
+spinner
|
||||
else if showBrokenCardsReport.get
|
||||
+brokenCardsReport
|
||||
else if showFilesReport.get
|
||||
+filesReport
|
||||
else if showOrphanedFilesReport.get
|
||||
+orphanedFilesReport
|
||||
else if showRulesReport.get
|
||||
+rulesReport
|
||||
|
||||
|
||||
template(name="brokenCardsReport")
|
||||
.global-search-results-list-wrapper
|
||||
h1 {{_ 'broken-cards'}}
|
||||
if resultsCount
|
||||
+resultsPaged(this)
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
template(name="rulesReport")
|
||||
h1 {{_ 'rulesReportTitle'}}
|
||||
if resultsCount
|
||||
table.table
|
||||
tr
|
||||
th Rule Title
|
||||
th Board Title
|
||||
th actionType
|
||||
th activityType
|
||||
|
||||
each rule in rows
|
||||
tr
|
||||
td {{ rule.title }}
|
||||
td {{ rule.boardTitle }}
|
||||
td {{ rule.action.actionType }}
|
||||
td {{ rule.trigger.activityType }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
template(name="filesReport")
|
||||
h1 {{_ 'filesReportTitle'}}
|
||||
if resultsCount
|
||||
table.table
|
||||
tr
|
||||
th Filename
|
||||
th.right Size (kB)
|
||||
th MIME Type
|
||||
th.center Usage
|
||||
th MD5 Sum
|
||||
th ID
|
||||
|
||||
each att in attachmentFiles
|
||||
tr
|
||||
td {{ att.filename }}
|
||||
td.right {{fileSize att.length }}
|
||||
td {{ att.contentType }}
|
||||
td.center {{usageCount att._id.toHexString }}
|
||||
td {{ att.md5 }}
|
||||
td {{ att._id.toHexString }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
|
||||
template(name="orphanedFilesReport")
|
||||
h1 {{_ 'orphanedFilesReportTitle'}}
|
||||
if resultsCount
|
||||
table.table
|
||||
tr
|
||||
th Filename
|
||||
th.right Size (kB)
|
||||
th MIME Type
|
||||
th MD5 Sum
|
||||
th ID
|
||||
|
||||
each att in attachmentFiles
|
||||
tr
|
||||
td {{ att.filename }}
|
||||
td.right {{fileSize att.length }}
|
||||
td {{ att.contentType }}
|
||||
td {{ att.md5 }}
|
||||
td {{ att._id.toHexString }}
|
||||
else
|
||||
div {{_ 'no-results' }}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { AttachmentStorage } from '/models/attachments';
|
||||
import { CardSearchPagedComponent } from '/client/lib/cardSearch';
|
||||
import SessionData from '/models/usersessiondata';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
subscription: null,
|
||||
showFilesReport: new ReactiveVar(false),
|
||||
showBrokenCardsReport: new ReactiveVar(false),
|
||||
showOrphanedFilesReport: new ReactiveVar(false),
|
||||
showRulesReport: new ReactiveVar(false),
|
||||
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click a.js-report-broken': this.switchMenu,
|
||||
'click a.js-report-files': this.switchMenu,
|
||||
'click a.js-report-orphaned-files': this.switchMenu,
|
||||
'click a.js-report-rules': this.switchMenu,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
switchMenu(event) {
|
||||
const target = $(event.target);
|
||||
if (!target.hasClass('active')) {
|
||||
this.loading.set(true);
|
||||
this.showFilesReport.set(false);
|
||||
this.showBrokenCardsReport.set(false);
|
||||
this.showOrphanedFilesReport.set(false);
|
||||
if (this.subscription) {
|
||||
this.subscription.stop();
|
||||
}
|
||||
|
||||
$('.side-menu li.active').removeClass('active');
|
||||
target.parent().addClass('active');
|
||||
const targetID = target.data('id');
|
||||
|
||||
if ('report-broken' === targetID) {
|
||||
this.showBrokenCardsReport.set(true);
|
||||
this.subscription = Meteor.subscribe(
|
||||
'brokenCards',
|
||||
SessionData.getSessionId(),
|
||||
() => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
);
|
||||
} else if ('report-files' === targetID) {
|
||||
this.showFilesReport.set(true);
|
||||
this.subscription = Meteor.subscribe('attachmentsList', () => {
|
||||
this.loading.set(false);
|
||||
});
|
||||
} else if ('report-orphaned-files' === targetID) {
|
||||
this.showOrphanedFilesReport.set(true);
|
||||
this.subscription = Meteor.subscribe('orphanedAttachments', () => {
|
||||
this.loading.set(false);
|
||||
});
|
||||
} else if ('report-rules' === targetID) {
|
||||
this.subscription = Meteor.subscribe('rulesReport', () => {
|
||||
this.showRulesReport.set(true);
|
||||
this.loading.set(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
}).register('adminReports');
|
||||
|
||||
Template.filesReport.helpers({
|
||||
attachmentFiles() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('attachments:', AttachmentStorage.find());
|
||||
// console.log('attachments.count:', AttachmentStorage.find().count());
|
||||
return AttachmentStorage.find();
|
||||
},
|
||||
|
||||
rulesReport() {
|
||||
const rules = [];
|
||||
|
||||
Rules.find().forEach(rule => {
|
||||
rules.push({
|
||||
_id: rule._id,
|
||||
title: rule.title,
|
||||
boardId: rule.boardId,
|
||||
boardTitle: rule.board().title,
|
||||
action: rule.action().fetch(),
|
||||
trigger: rule.trigger().fetch(),
|
||||
});
|
||||
});
|
||||
|
||||
return rules;
|
||||
},
|
||||
|
||||
resultsCount() {
|
||||
return AttachmentStorage.find().count();
|
||||
},
|
||||
|
||||
fileSize(size) {
|
||||
return Math.round(size / 1024);
|
||||
},
|
||||
|
||||
usageCount(key) {
|
||||
return Attachments.find({ 'copies.attachments.key': key }).count();
|
||||
},
|
||||
});
|
||||
|
||||
Template.orphanedFilesReport.helpers({
|
||||
attachmentFiles() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('attachments:', AttachmentStorage.find());
|
||||
// console.log('attachments.count:', AttachmentStorage.find().count());
|
||||
return AttachmentStorage.find();
|
||||
},
|
||||
|
||||
resultsCount() {
|
||||
return AttachmentStorage.find().count();
|
||||
},
|
||||
|
||||
fileSize(size) {
|
||||
return Math.round(size / 1024);
|
||||
},
|
||||
});
|
||||
|
||||
Template.rulesReport.helpers({
|
||||
rows() {
|
||||
const rules = [];
|
||||
|
||||
Rules.find().forEach(rule => {
|
||||
rules.push({
|
||||
_id: rule._id,
|
||||
title: rule.title,
|
||||
boardId: rule.boardId,
|
||||
boardTitle: rule.board().title,
|
||||
action: rule.action(),
|
||||
trigger: rule.trigger(),
|
||||
});
|
||||
});
|
||||
|
||||
console.log('rows:', rules);
|
||||
return rules;
|
||||
},
|
||||
|
||||
resultsCount() {
|
||||
return Rules.find().count();
|
||||
},
|
||||
});
|
||||
|
||||
class BrokenCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
}
|
||||
}
|
||||
BrokenCardsComponent.register('brokenCardsReport');
|
||||
@@ -110,6 +110,8 @@ template(name="peopleGeneral")
|
||||
th {{_ 'active'}}
|
||||
th {{_ 'authentication-method'}}
|
||||
th {{_ 'import-usernames'}}
|
||||
th {{_ 'organizations'}}
|
||||
th {{_ 'teams'}}
|
||||
th
|
||||
+newUserRow
|
||||
each user in peopleList
|
||||
@@ -132,28 +134,28 @@ template(name="newUserRow")
|
||||
|
||||
template(name="orgRow")
|
||||
tr
|
||||
if orgData.loginDisabled
|
||||
if orgData.orgIsActive
|
||||
td <s>{{ orgData.orgDisplayName }}</s>
|
||||
else
|
||||
td {{ orgData.orgDisplayName }}
|
||||
if orgData.loginDisabled
|
||||
if orgData.orgIsActive
|
||||
td <s>{{ orgData.orgDesc }}</s>
|
||||
else
|
||||
td {{ orgData.orgDesc }}
|
||||
if orgData.loginDisabled
|
||||
td <s>{{ orgData.orgName }}</s>
|
||||
if orgData.orgIsActive
|
||||
td <s>{{ orgData.orgShortName }}</s>
|
||||
else
|
||||
td {{ orgData.orgName }}
|
||||
if orgData.loginDisabled
|
||||
td {{ orgData.orgShortName }}
|
||||
if orgData.orgIsActive
|
||||
td <s>{{ orgData.orgWebsite }}</s>
|
||||
else
|
||||
td {{ orgData.orgWebsite }}
|
||||
if orgData.loginDisabled
|
||||
if orgData.orgIsActive
|
||||
td <s>{{ moment orgData.createdAt 'LLL' }}</s>
|
||||
else
|
||||
td {{ moment orgData.createdAt 'LLL' }}
|
||||
td
|
||||
if orgData.loginDisabled
|
||||
if orgData.orgIsActive
|
||||
| {{_ 'no'}}
|
||||
else
|
||||
| {{_ 'yes'}}
|
||||
@@ -166,28 +168,28 @@ template(name="orgRow")
|
||||
|
||||
template(name="teamRow")
|
||||
tr
|
||||
if teamData.loginDisabled
|
||||
if teamData.teamIsActive
|
||||
td <s>{{ teamData.teamDisplayName }}</s>
|
||||
else
|
||||
td {{ teamData.teamDisplayName }}
|
||||
if teamData.loginDisabled
|
||||
if teamData.teamIsActive
|
||||
td <s>{{ teamData.teamDesc }}</s>
|
||||
else
|
||||
td {{ teamData.teamDesc }}
|
||||
if teamData.loginDisabled
|
||||
td <s>{{ teamData.teamName }}</s>
|
||||
if teamData.teamIsActive
|
||||
td <s>{{ teamData.teamShortName }}</s>
|
||||
else
|
||||
td {{ teamData.teamName }}
|
||||
if teamData.loginDisabled
|
||||
td {{ teamData.teamShortName }}
|
||||
if teamData.teamIsActive
|
||||
td <s>{{ teamData.teamWebsite }}</s>
|
||||
else
|
||||
td {{ teamData.teamWebsite }}
|
||||
if orgData.loginDisabled
|
||||
if teamData.teamIsActive
|
||||
td <s>{{ moment teamData.createdAt 'LLL' }}</s>
|
||||
else
|
||||
td {{ moment teamData.createdAt 'LLL' }}
|
||||
td
|
||||
if teamData.loginDisabled
|
||||
if teamData.teamIsActive
|
||||
| {{_ 'no'}}
|
||||
else
|
||||
| {{_ 'yes'}}
|
||||
@@ -257,6 +259,14 @@ template(name="peopleRow")
|
||||
td <s>{{ userData.importUsernamesString }}</s>
|
||||
else
|
||||
td {{ userData.importUsernamesString }}
|
||||
if userData.loginDisabled
|
||||
td <s>{{ userData.orgsUserBelongs }}</s>
|
||||
else
|
||||
td {{ userData.orgsUserBelongs }}
|
||||
if userData.loginDisabled
|
||||
td <s>{{ userData.teamsUserBelongs }}</s>
|
||||
else
|
||||
td {{ userData.teamsUserBelongs }}
|
||||
td
|
||||
a.edit-user
|
||||
i.fa.fa-edit
|
||||
@@ -269,7 +279,7 @@ template(name="editOrgPopup")
|
||||
label.hide.orgId(type="text" value=org._id)
|
||||
label
|
||||
| {{_ 'displayName'}}
|
||||
input.js-orgDisplayName(type="text" value=org.displayName required)
|
||||
input.js-orgDisplayName(type="text" value=org.orgDisplayName required)
|
||||
span.error.hide.orgname-taken
|
||||
| {{_ 'error-orgname-taken'}}
|
||||
label
|
||||
@@ -285,7 +295,7 @@ template(name="editOrgPopup")
|
||||
| {{_ 'active'}}
|
||||
select.select-active.js-org-isactive
|
||||
option(value="false") {{_ 'yes'}}
|
||||
option(value="true" selected="{{org.loginDisabled}}") {{_ 'no'}}
|
||||
option(value="true" selected="{{org.orgIsActive}}") {{_ 'no'}}
|
||||
hr
|
||||
div.buttonsContainer
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
@@ -311,7 +321,7 @@ template(name="editTeamPopup")
|
||||
| {{_ 'active'}}
|
||||
select.select-active.js-team-isactive
|
||||
option(value="false") {{_ 'yes'}}
|
||||
option(value="true" selected="{{team.loginDisabled}}") {{_ 'no'}}
|
||||
option(value="true" selected="{{team.teamIsActive}}") {{_ 'no'}}
|
||||
hr
|
||||
div.buttonsContainer
|
||||
input.primary.wide(type="submit" value="{{_ 'save'}}")
|
||||
@@ -367,6 +377,27 @@ template(name="editUserPopup")
|
||||
option(value="{{value}}" selected) {{_ value}}
|
||||
else
|
||||
option(value="{{value}}") {{_ value}}
|
||||
label
|
||||
| {{_ 'organizations'}}
|
||||
i.fa.fa-plus-square#addUserOrg
|
||||
i.fa.fa-minus-square#removeUserOrg
|
||||
select.js-orgs#jsOrgs
|
||||
option(value="-1") {{_ 'organizations'}} :
|
||||
each value in orgsDatas
|
||||
option(value="{{value._id}}") {{_ value.orgDisplayName}}
|
||||
input#jsUserOrgsInPut.js-userOrgs(type="text" value=user.orgsUserBelongs, disabled)
|
||||
input#jsUserOrgIdsInPut.js-userOrgIds.hide(type="text" value=user.orgIdsUserBelongs)
|
||||
label
|
||||
| {{_ 'teams'}}
|
||||
i.fa.fa-plus-square#addUserTeam
|
||||
i.fa.fa-minus-square#removeUserTeam
|
||||
select.js-teams#jsTeams
|
||||
option(value="-1") {{_ 'teams'}} :
|
||||
each value in teamsDatas
|
||||
option(value="{{value._id}}") {{_ value.teamDisplayName}}
|
||||
input#jsUserTeamsInPut.js-userteams(type="text" value=user.teamsUserBelongs, disabled)
|
||||
input#jsUserTeamIdsInPut.js-userteamIds.hide(type="text" value=user.teamIdsUserBelongs)
|
||||
|
||||
hr
|
||||
label
|
||||
| {{_ 'password'}}
|
||||
@@ -385,7 +416,7 @@ template(name="newOrgPopup")
|
||||
input.js-orgDesc(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'shortName'}}
|
||||
input.js-orgName(type="text" value="" required)
|
||||
input.js-orgShortName(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'website'}}
|
||||
input.js-orgWebsite(type="text" value="" required)
|
||||
@@ -409,7 +440,7 @@ template(name="newTeamPopup")
|
||||
input.js-teamDesc(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'shortName'}}
|
||||
input.js-teamName(type="text" value="" required)
|
||||
input.js-teamShortName(type="text" value="" required)
|
||||
label
|
||||
| {{_ 'website'}}
|
||||
input.js-teamWebsite(type="text" value="" required)
|
||||
@@ -468,6 +499,27 @@ template(name="newUserPopup")
|
||||
option(value="{{value}}" selected) {{_ value}}
|
||||
else
|
||||
option(value="{{value}}") {{_ value}}
|
||||
label
|
||||
| {{_ 'organizations'}}
|
||||
i.fa.fa-plus-square#addUserOrgNewUser
|
||||
i.fa.fa-minus-square#removeUserOrgNewUser
|
||||
select.js-orgsNewUser#jsOrgsNewUser
|
||||
option(value="-1") {{_ 'organizations'}} :
|
||||
each value in orgsDatas
|
||||
option(value="{{value._id}}") {{_ value.orgDisplayName}}
|
||||
input#jsUserOrgsInPutNewUser.js-userOrgsNewUser(type="text" value=user.orgsUserBelongs, disabled)
|
||||
input#jsUserOrgIdsInPutNewUser.js-userOrgIdsNewUser.hide(type="text" value=user.orgIdsUserBelongs)
|
||||
label
|
||||
| {{_ 'teams'}}
|
||||
i.fa.fa-plus-square#addUserTeamNewUser
|
||||
i.fa.fa-minus-square#removeUserTeamNewUser
|
||||
select.js-teamsNewUser#jsTeamsNewUser
|
||||
option(value="-1") {{_ 'teams'}} :
|
||||
each value in teamsDatas
|
||||
option(value="{{value._id}}") {{_ value.teamDisplayName}}
|
||||
input#jsUserTeamsInPutNewUser.js-userteamsNewUser(type="text" value=user.teamsUserBelongs, disabled)
|
||||
input#jsUserTeamIdsInPutNewUser.js-userteamIdsNewUser.hide(type="text" value=user.teamIdsUserBelongs)
|
||||
|
||||
hr
|
||||
label
|
||||
| {{_ 'password'}}
|
||||
@@ -478,27 +530,34 @@ template(name="newUserPopup")
|
||||
template(name="settingsOrgPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.impersonate-org
|
||||
i.fa.fa-user
|
||||
| {{_ 'impersonate-org'}}
|
||||
// Delete is not enabled yet, because it does leave empty user avatars
|
||||
// to boards: boards members, card members and assignees have
|
||||
// empty users. See:
|
||||
// - wekan/client/components/settings/peopleBody.jade deleteButton
|
||||
// - wekan/client/components/settings/peopleBody.js deleteButton
|
||||
// - wekan/client/components/sidebar/sidebar.js Popup.afterConfirm('removeMember'
|
||||
// that does now remove member from board, card members and assignees correctly,
|
||||
// but that should be used to remove user from all boards similarly
|
||||
// - wekan/models/users.js Delete is not enabled
|
||||
//li
|
||||
// br
|
||||
// br
|
||||
// hr
|
||||
//li
|
||||
// form
|
||||
// label.hide.userId(type="text" value=user._id)
|
||||
// div.buttonsContainer
|
||||
// input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
|
||||
form
|
||||
label
|
||||
| {{_ 'delete-org-confirm-popup'}}
|
||||
br
|
||||
label.hide.orgId(type="text" value=org._id)
|
||||
labeldelete-org-confirm-popup
|
||||
div.buttonsContainer
|
||||
input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
|
||||
// It's not yet possible to impersonate organization. Only impersonate user,
|
||||
// because that changes current user ID. What would it mean in practice
|
||||
// to impersonate organization?
|
||||
// li
|
||||
// a.impersonate-org
|
||||
// i.fa.fa-user
|
||||
// | {{_ 'impersonate-org'}}
|
||||
//
|
||||
//
|
||||
|
||||
template(name="settingsTeamPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
form
|
||||
label
|
||||
| {{_ 'delete-team-confirm-popup'}}
|
||||
br
|
||||
label.hide.teamId(type="text" value=team._id)
|
||||
div.buttonsContainer
|
||||
input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
|
||||
|
||||
template(name="settingsUserPopup")
|
||||
ul.pop-over-list
|
||||
@@ -506,6 +565,12 @@ template(name="settingsUserPopup")
|
||||
a.impersonate-user
|
||||
i.fa.fa-user
|
||||
| {{_ 'impersonate-user'}}
|
||||
//hr
|
||||
//li
|
||||
// form
|
||||
// label.hide.userId(type="text" value=user._id)
|
||||
// div.buttonsContainer
|
||||
// input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
|
||||
// Delete is not enabled yet, because it does leave empty user avatars
|
||||
// to boards: boards members, card members and assignees have
|
||||
// empty users. See:
|
||||
@@ -515,12 +580,3 @@ template(name="settingsUserPopup")
|
||||
// that does now remove member from board, card members and assignees correctly,
|
||||
// but that should be used to remove user from all boards similarly
|
||||
// - wekan/models/users.js Delete is not enabled
|
||||
//li
|
||||
// br
|
||||
// br
|
||||
// hr
|
||||
//li
|
||||
// form
|
||||
// label.hide.userId(type="text" value=user._id)
|
||||
// div.buttonsContainer
|
||||
// input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}")
|
||||
@@ -1,6 +1,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
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
mixins() {
|
||||
@@ -101,9 +102,7 @@ BlazeComponent.extendComponent({
|
||||
];
|
||||
},
|
||||
filterPeople() {
|
||||
const value = $('#searchInput')
|
||||
.first()
|
||||
.val();
|
||||
const value = $('#searchInput').first().val();
|
||||
if (value === '') {
|
||||
this.findUsersOptions.set({});
|
||||
} else {
|
||||
@@ -150,7 +149,7 @@ BlazeComponent.extendComponent({
|
||||
const teams = Team.find(this.findTeamsOptions.get(), {
|
||||
fields: { _id: true },
|
||||
});
|
||||
this.numberTeams.set(team.count(false));
|
||||
this.numberTeams.set(teams.count(false));
|
||||
return teams;
|
||||
},
|
||||
peopleList() {
|
||||
@@ -203,7 +202,7 @@ Template.peopleRow.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editUserPopup.onCreated(function() {
|
||||
Template.editUserPopup.onCreated(function () {
|
||||
this.authenticationMethods = new ReactiveVar([]);
|
||||
this.errorMessage = new ReactiveVar('');
|
||||
|
||||
@@ -215,8 +214,8 @@ Template.editUserPopup.onCreated(function() {
|
||||
{ value: 'password' },
|
||||
// Gets only the authentication methods availables
|
||||
...Object.entries(result)
|
||||
.filter(e => e[1])
|
||||
.map(e => ({ value: e[0] })),
|
||||
.filter((e) => e[1])
|
||||
.map((e) => ({ value: e[0] })),
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -247,6 +246,12 @@ Template.editUserPopup.helpers({
|
||||
authentications() {
|
||||
return Template.instance().authenticationMethods.get();
|
||||
},
|
||||
orgsDatas() {
|
||||
return Org.find({}, {sort: { createdAt: -1 }});
|
||||
},
|
||||
teamsDatas() {
|
||||
return Team.find({}, {sort: { createdAt: -1 }});
|
||||
},
|
||||
isSelected(match) {
|
||||
const userId = Template.instance().data.userId;
|
||||
const selected = Users.findOne(userId).authenticationMethod;
|
||||
@@ -262,15 +267,15 @@ Template.editUserPopup.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
Template.newOrgPopup.onCreated(function() {
|
||||
Template.newOrgPopup.onCreated(function () {
|
||||
this.errorMessage = new ReactiveVar('');
|
||||
});
|
||||
|
||||
Template.newTeamPopup.onCreated(function() {
|
||||
Template.newTeamPopup.onCreated(function () {
|
||||
this.errorMessage = new ReactiveVar('');
|
||||
});
|
||||
|
||||
Template.newUserPopup.onCreated(function() {
|
||||
Template.newUserPopup.onCreated(function () {
|
||||
this.authenticationMethods = new ReactiveVar([]);
|
||||
this.errorMessage = new ReactiveVar('');
|
||||
|
||||
@@ -282,8 +287,8 @@ Template.newUserPopup.onCreated(function() {
|
||||
{ value: 'password' },
|
||||
// Gets only the authentication methods availables
|
||||
...Object.entries(result)
|
||||
.filter(e => e[1])
|
||||
.map(e => ({ value: e[0] })),
|
||||
.filter((e) => e[1])
|
||||
.map((e) => ({ value: e[0] })),
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -314,10 +319,21 @@ Template.newUserPopup.helpers({
|
||||
authentications() {
|
||||
return Template.instance().authenticationMethods.get();
|
||||
},
|
||||
orgsDatas() {
|
||||
return Org.find({}, {sort: { createdAt: -1 }});
|
||||
},
|
||||
teamsDatas() {
|
||||
return Team.find({}, {sort: { createdAt: -1 }});
|
||||
},
|
||||
isSelected(match) {
|
||||
const userId = Template.instance().data.userId;
|
||||
const selected = Users.findOne(userId).authenticationMethod;
|
||||
return selected === match;
|
||||
if(userId){
|
||||
const selected = Users.findOne(userId).authenticationMethod;
|
||||
return selected === match;
|
||||
}
|
||||
else{
|
||||
false;
|
||||
}
|
||||
},
|
||||
isLdap() {
|
||||
const userId = Template.instance().data.userId;
|
||||
@@ -407,7 +423,7 @@ BlazeComponent.extendComponent({
|
||||
Template.editOrgPopup.events({
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const org = Orgs.findOne(this.orgId);
|
||||
const org = Org.findOne(this.orgId);
|
||||
|
||||
const orgDisplayName = templateInstance
|
||||
.find('.js-orgDisplayName')
|
||||
@@ -415,7 +431,8 @@ 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();
|
||||
const orgIsActive =
|
||||
templateInstance.find('.js-org-isactive').value.trim() == 'true';
|
||||
|
||||
const isChangeOrgDisplayName = orgDisplayName !== org.orgDisplayName;
|
||||
const isChangeOrgDesc = orgDesc !== org.orgDesc;
|
||||
@@ -423,20 +440,22 @@ Template.editOrgPopup.events({
|
||||
const isChangeOrgWebsite = orgWebsite !== org.orgWebsite;
|
||||
const isChangeOrgIsActive = orgIsActive !== org.orgIsActive;
|
||||
|
||||
if (isChangeOrgDisplayName) {
|
||||
Meteor.call('setOrgDisplayName', org, orgDisplayName);
|
||||
}
|
||||
|
||||
if (isChangeOrgDesc) {
|
||||
Meteor.call('setOrgDesc', org, orgDesc);
|
||||
}
|
||||
|
||||
if (isChangeOrgShortName) {
|
||||
Meteor.call('setOrgShortName', org, orgShortName);
|
||||
}
|
||||
|
||||
if (isChangeOrgIsActive) {
|
||||
Meteor.call('setOrgIsActive', org, orgIsActive);
|
||||
if (
|
||||
isChangeOrgDisplayName ||
|
||||
isChangeOrgDesc ||
|
||||
isChangeOrgShortName ||
|
||||
isChangeOrgWebsite ||
|
||||
isChangeOrgIsActive
|
||||
) {
|
||||
Meteor.call(
|
||||
'setOrgAllFields',
|
||||
org,
|
||||
orgDisplayName,
|
||||
orgDesc,
|
||||
orgShortName,
|
||||
orgWebsite,
|
||||
orgIsActive,
|
||||
);
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
@@ -446,7 +465,7 @@ Template.editOrgPopup.events({
|
||||
Template.editTeamPopup.events({
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
const team = Teams.findOne(this.teamId);
|
||||
const team = Team.findOne(this.teamId);
|
||||
|
||||
const teamDisplayName = templateInstance
|
||||
.find('.js-teamDisplayName')
|
||||
@@ -456,9 +475,8 @@ Template.editTeamPopup.events({
|
||||
.find('.js-teamShortName')
|
||||
.value.trim();
|
||||
const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim();
|
||||
const teamIsActive = templateInstance
|
||||
.find('.js-team-isactive')
|
||||
.value.trim();
|
||||
const teamIsActive =
|
||||
templateInstance.find('.js-team-isactive').value.trim() == 'true';
|
||||
|
||||
const isChangeTeamDisplayName = teamDisplayName !== team.teamDisplayName;
|
||||
const isChangeTeamDesc = teamDesc !== team.teamDesc;
|
||||
@@ -466,20 +484,22 @@ Template.editTeamPopup.events({
|
||||
const isChangeTeamWebsite = teamWebsite !== team.teamWebsite;
|
||||
const isChangeTeamIsActive = teamIsActive !== team.teamIsActive;
|
||||
|
||||
if (isChangeTeamDisplayName) {
|
||||
Meteor.call('setTeamDisplayName', team, teamDisplayName);
|
||||
}
|
||||
|
||||
if (isChangeTeamDesc) {
|
||||
Meteor.call('setTeamDesc', team, teamDesc);
|
||||
}
|
||||
|
||||
if (isChangeTeamShortName) {
|
||||
Meteor.call('setTeamShortName', team, teamShortName);
|
||||
}
|
||||
|
||||
if (isChangeTeamIsActive) {
|
||||
Meteor.call('setTeamIsActive', team, teamIsActive);
|
||||
if (
|
||||
isChangeTeamDisplayName ||
|
||||
isChangeTeamDesc ||
|
||||
isChangeTeamShortName ||
|
||||
isChangeTeamWebsite ||
|
||||
isChangeTeamIsActive
|
||||
) {
|
||||
Meteor.call(
|
||||
'setTeamAllFields',
|
||||
team,
|
||||
teamDisplayName,
|
||||
teamDesc,
|
||||
teamShortName,
|
||||
teamWebsite,
|
||||
teamIsActive,
|
||||
);
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
@@ -497,15 +517,13 @@ Template.editUserPopup.events({
|
||||
const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
|
||||
const isActive = templateInstance.find('.js-profile-isactive').value.trim();
|
||||
const email = templateInstance.find('.js-profile-email').value.trim();
|
||||
const verified = templateInstance
|
||||
.find('.js-profile-email-verified')
|
||||
.value.trim();
|
||||
const authentication = templateInstance
|
||||
.find('.js-authenticationMethod')
|
||||
.value.trim();
|
||||
const importUsernames = templateInstance
|
||||
.find('.js-import-usernames')
|
||||
.value.trim();
|
||||
const verified = templateInstance.find('.js-profile-email-verified').value.trim();
|
||||
const authentication = templateInstance.find('.js-authenticationMethod').value.trim();
|
||||
const importUsernames = templateInstance.find('.js-import-usernames').value.trim();
|
||||
const userOrgs = templateInstance.find('.js-userOrgs').value.trim();
|
||||
const userOrgsIds = templateInstance.find('.js-userOrgIds').value.trim();
|
||||
const userTeams = templateInstance.find('.js-userteams').value.trim();
|
||||
const userTeamsIds = templateInstance.find('.js-userteamIds').value.trim();
|
||||
|
||||
const isChangePassword = password.length > 0;
|
||||
const isChangeUserName = username !== user.username;
|
||||
@@ -530,6 +548,36 @@ Template.editUserPopup.events({
|
||||
},
|
||||
});
|
||||
|
||||
let userTeamsList = userTeams.split(",");
|
||||
let userTeamsIdsList = userTeamsIds.split(",");
|
||||
let userTms = [];
|
||||
for(let i = 0; i < userTeamsList.length; i++){
|
||||
userTms.push({
|
||||
"teamId": userTeamsIdsList[i],
|
||||
"teamDisplayName": userTeamsList[i],
|
||||
})
|
||||
}
|
||||
Users.update(this.userId, {
|
||||
$set:{
|
||||
teams: userTms
|
||||
}
|
||||
});
|
||||
|
||||
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],
|
||||
})
|
||||
}
|
||||
Users.update(this.userId, {
|
||||
$set:{
|
||||
orgs: userOrganizations
|
||||
}
|
||||
});
|
||||
|
||||
if (isChangePassword) {
|
||||
Meteor.call('setPassword', password, this.userId);
|
||||
}
|
||||
@@ -548,7 +596,7 @@ Template.editUserPopup.events({
|
||||
username,
|
||||
email.toLowerCase(),
|
||||
this.userId,
|
||||
function(error) {
|
||||
function (error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
const emailMessageElement = templateInstance.$('.email-taken');
|
||||
if (error) {
|
||||
@@ -568,7 +616,7 @@ Template.editUserPopup.events({
|
||||
},
|
||||
);
|
||||
} else if (isChangeUserName) {
|
||||
Meteor.call('setUsername', username, this.userId, function(error) {
|
||||
Meteor.call('setUsername', username, this.userId, function (error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
@@ -581,24 +629,138 @@ Template.editUserPopup.events({
|
||||
}
|
||||
});
|
||||
} else if (isChangeEmail) {
|
||||
Meteor.call('setEmail', email.toLowerCase(), this.userId, function(
|
||||
error,
|
||||
) {
|
||||
const emailMessageElement = templateInstance.$('.email-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'email-already-taken') {
|
||||
emailMessageElement.show();
|
||||
Meteor.call(
|
||||
'setEmail',
|
||||
email.toLowerCase(),
|
||||
this.userId,
|
||||
function (error) {
|
||||
const emailMessageElement = templateInstance.$('.email-taken');
|
||||
if (error) {
|
||||
const errorElement = error.error;
|
||||
if (errorElement === 'email-already-taken') {
|
||||
emailMessageElement.show();
|
||||
}
|
||||
} else {
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
}
|
||||
} else {
|
||||
emailMessageElement.hide();
|
||||
Popup.close();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
} else Popup.close();
|
||||
},
|
||||
'click #addUserOrg'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
userOrgsTeamsAction = "addOrg";
|
||||
document.getElementById("jsOrgs").style.display = 'block';
|
||||
document.getElementById("jsTeams").style.display = 'none';
|
||||
},
|
||||
'click #removeUserOrg'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
userOrgsTeamsAction = "removeOrg";
|
||||
document.getElementById("jsOrgs").style.display = 'block';
|
||||
document.getElementById("jsTeams").style.display = 'none';
|
||||
},
|
||||
'click #addUserTeam'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
userOrgsTeamsAction = "addTeam";
|
||||
document.getElementById("jsTeams").style.display = 'block';
|
||||
document.getElementById("jsOrgs").style.display = 'none';
|
||||
},
|
||||
'click #removeUserTeam'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
userOrgsTeamsAction = "removeTeam";
|
||||
document.getElementById("jsTeams").style.display = 'block';
|
||||
document.getElementById("jsOrgs").style.display = 'none';
|
||||
},
|
||||
'change #jsOrgs'(event) {
|
||||
event.preventDefault();
|
||||
UpdateUserOrgsOrTeamsElement();
|
||||
},
|
||||
'change #jsTeams'(event) {
|
||||
event.preventDefault();
|
||||
UpdateUserOrgsOrTeamsElement();
|
||||
},
|
||||
});
|
||||
|
||||
UpdateUserOrgsOrTeamsElement = function(isNewUser = false){
|
||||
let selectedElt;
|
||||
let selectedEltValue;
|
||||
let selectedEltValueId;
|
||||
let inputElt;
|
||||
let inputEltId;
|
||||
let lstInputValues = [];
|
||||
let lstInputValuesIds = [];
|
||||
let index;
|
||||
let indexId;
|
||||
switch(userOrgsTeamsAction)
|
||||
{
|
||||
case "addOrg":
|
||||
case "removeOrg":
|
||||
inputElt = !isNewUser ? document.getElementById("jsUserOrgsInPut") : document.getElementById("jsUserOrgsInPutNewUser");
|
||||
inputEltId = !isNewUser ? document.getElementById("jsUserOrgIdsInPut") : document.getElementById("jsUserOrgIdsInPutNewUser");
|
||||
selectedElt = !isNewUser ? document.getElementById("jsOrgs") : document.getElementById("jsOrgsNewUser");
|
||||
break;
|
||||
case "addTeam":
|
||||
case "removeTeam":
|
||||
inputElt = !isNewUser ? document.getElementById("jsUserTeamsInPut") : document.getElementById("jsUserTeamsInPutNewUser");
|
||||
inputEltId = !isNewUser ? document.getElementById("jsUserTeamIdsInPut") : document.getElementById("jsUserTeamIdsInPutNewUser");
|
||||
selectedElt = !isNewUser ? document.getElementById("jsTeams") : document.getElementById("jsTeamsNewUser");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
selectedEltValue = selectedElt.options[selectedElt.selectedIndex].text;
|
||||
selectedEltValueId = selectedElt.options[selectedElt.selectedIndex].value;
|
||||
lstInputValues = inputElt.value.trim().split(",");
|
||||
if(lstInputValues.length == 1 && lstInputValues[0] == ''){
|
||||
lstInputValues = [];
|
||||
}
|
||||
lstInputValuesIds = inputEltId.value.trim().split(",");
|
||||
if(lstInputValuesIds.length == 1 && lstInputValuesIds[0] == ''){
|
||||
lstInputValuesIds = [];
|
||||
}
|
||||
index = lstInputValues.indexOf(selectedEltValue);
|
||||
indexId = lstInputValuesIds.indexOf(selectedEltValue);
|
||||
if(userOrgsTeamsAction == "addOrg" || userOrgsTeamsAction == "addTeam"){
|
||||
if(index <= -1 && selectedEltValueId != "-1"){
|
||||
lstInputValues.push(selectedEltValue);
|
||||
}
|
||||
|
||||
if(indexId <= -1 && selectedEltValueId != "-1"){
|
||||
lstInputValuesIds.push(selectedEltValueId);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(index > -1 && selectedEltValueId != "-1"){
|
||||
lstInputValues.splice(index, 1);
|
||||
}
|
||||
|
||||
if(indexId > -1 && selectedEltValueId != "-1"){
|
||||
lstInputValuesIds.splice(indexId, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(lstInputValues.length > 0){
|
||||
inputElt.value = lstInputValues.join(",");
|
||||
}
|
||||
else{
|
||||
inputElt.value = "";
|
||||
}
|
||||
|
||||
if(lstInputValuesIds.length > 0){
|
||||
inputEltId.value = lstInputValuesIds.join(",");
|
||||
}
|
||||
else{
|
||||
inputEltId.value = "";
|
||||
}
|
||||
selectedElt.value = "-1";
|
||||
selectedElt.style.display = "none";
|
||||
}
|
||||
|
||||
Template.newOrgPopup.events({
|
||||
submit(event, templateInstance) {
|
||||
event.preventDefault();
|
||||
@@ -608,7 +770,8 @@ Template.newOrgPopup.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();
|
||||
const orgIsActive =
|
||||
templateInstance.find('.js-org-isactive').value.trim() == 'true';
|
||||
|
||||
Meteor.call(
|
||||
'setCreateOrg',
|
||||
@@ -633,9 +796,8 @@ Template.newTeamPopup.events({
|
||||
.find('.js-teamShortName')
|
||||
.value.trim();
|
||||
const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim();
|
||||
const teamIsActive = templateInstance
|
||||
.find('.js-team-isactive')
|
||||
.value.trim();
|
||||
const teamIsActive =
|
||||
templateInstance.find('.js-team-isactive').value.trim() == 'true';
|
||||
|
||||
Meteor.call(
|
||||
'setCreateTeam',
|
||||
@@ -662,6 +824,30 @@ Template.newUserPopup.events({
|
||||
const importUsernames = Users.parseImportUsernames(
|
||||
templateInstance.find('.js-import-usernames').value,
|
||||
);
|
||||
const userOrgs = templateInstance.find('.js-userOrgsNewUser').value.trim();
|
||||
const userOrgsIds = templateInstance.find('.js-userOrgIdsNewUser').value.trim();
|
||||
const userTeams = templateInstance.find('.js-userteamsNewUser').value.trim();
|
||||
const userTeamsIds = templateInstance.find('.js-userteamIdsNewUser').value.trim();
|
||||
|
||||
let userTeamsList = userTeams.split(",");
|
||||
let userTeamsIdsList = userTeamsIds.split(",");
|
||||
let userTms = [];
|
||||
for(let i = 0; i < userTeamsList.length; 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],
|
||||
})
|
||||
}
|
||||
|
||||
Meteor.call(
|
||||
'setCreateUser',
|
||||
@@ -673,6 +859,8 @@ Template.newUserPopup.events({
|
||||
isActive,
|
||||
email.toLowerCase(),
|
||||
importUsernames,
|
||||
userOrganizations,
|
||||
userTms,
|
||||
function(error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
const emailMessageElement = templateInstance.$('.email-taken');
|
||||
@@ -694,13 +882,65 @@ Template.newUserPopup.events({
|
||||
);
|
||||
Popup.close();
|
||||
},
|
||||
'click #addUserOrgNewUser'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
userOrgsTeamsAction = "addOrg";
|
||||
document.getElementById("jsOrgsNewUser").style.display = 'block';
|
||||
document.getElementById("jsTeamsNewUser").style.display = 'none';
|
||||
},
|
||||
'click #removeUserOrgNewUser'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
userOrgsTeamsAction = "removeOrg";
|
||||
document.getElementById("jsOrgsNewUser").style.display = 'block';
|
||||
document.getElementById("jsTeamsNewUser").style.display = 'none';
|
||||
},
|
||||
'click #addUserTeamNewUser'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
userOrgsTeamsAction = "addTeam";
|
||||
document.getElementById("jsTeamsNewUser").style.display = 'block';
|
||||
document.getElementById("jsOrgsNewUser").style.display = 'none';
|
||||
},
|
||||
'click #removeUserTeamNewUser'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
userOrgsTeamsAction = "removeTeam";
|
||||
document.getElementById("jsTeamsNewUser").style.display = 'block';
|
||||
document.getElementById("jsOrgsNewUser").style.display = 'none';
|
||||
},
|
||||
'change #jsOrgsNewUser'(event) {
|
||||
event.preventDefault();
|
||||
UpdateUserOrgsOrTeamsElement(true);
|
||||
},
|
||||
'change #jsTeamsNewUser'(event) {
|
||||
event.preventDefault();
|
||||
UpdateUserOrgsOrTeamsElement(true);
|
||||
},
|
||||
});
|
||||
|
||||
Template.settingsOrgPopup.events({
|
||||
'click #deleteButton'(event) {
|
||||
event.preventDefault();
|
||||
Org.remove(this.orgId);
|
||||
Popup.close();
|
||||
}
|
||||
});
|
||||
|
||||
Template.settingsTeamPopup.events({
|
||||
'click #deleteButton'(event) {
|
||||
event.preventDefault();
|
||||
Team.remove(this.teamId);
|
||||
Popup.close();
|
||||
}
|
||||
});
|
||||
|
||||
Template.settingsUserPopup.events({
|
||||
'click .impersonate-user'(event) {
|
||||
event.preventDefault();
|
||||
|
||||
Meteor.call('impersonate', this.userId, err => {
|
||||
Meteor.call('impersonate', this.userId, (err) => {
|
||||
if (!err) {
|
||||
FlowRouter.go('/');
|
||||
Meteor.connection.setUserId(this.userId);
|
||||
@@ -720,21 +960,6 @@ Template.settingsUserPopup.events({
|
||||
// but that should be used to remove user from all boards similarly
|
||||
// - wekan/models/users.js Delete is not enabled
|
||||
//
|
||||
//console.log('user id: ' + this.userId);
|
||||
//Popup.afterConfirm('userDelete', function(event) {
|
||||
//Boards.find({ members: this.userId }).forEach(board => {
|
||||
// console.log('board id: ' + board._id);
|
||||
//Cards.find({ boardId: board._id, members: this.userId }).forEach(card => {
|
||||
// card.unassignMember(this.userId);
|
||||
//});
|
||||
//Cards.find({ boardId: board._id, members: this.userId }).forEach(card => {
|
||||
// card.unassignMember(this.userId);
|
||||
//});
|
||||
//Cards.find({ boardId: board._id, assignees: this.userId }).forEach(card => {
|
||||
// card.unassignAssignee(this.userId);
|
||||
//});
|
||||
//Boards.findOne({ boardId: board._id }).removeMember(this.userId);
|
||||
//});
|
||||
//Users.remove(this.userId);
|
||||
*/
|
||||
Popup.close();
|
||||
|
||||
@@ -49,3 +49,9 @@ table
|
||||
|
||||
.more-settings-user,.more-settings-team,.more-settings-org
|
||||
margin-left: 10px;
|
||||
|
||||
.js-orgs,.js-orgsNewUser
|
||||
display: none;
|
||||
|
||||
.js-teams,.js-teamsNewUser
|
||||
display: none;
|
||||
@@ -12,6 +12,10 @@ template(name="settingHeaderBar")
|
||||
i.fa(class="fa-users")
|
||||
span {{_ 'people'}}
|
||||
|
||||
a.setting-header-btn.informations(href="{{pathFor 'admin-reports'}}")
|
||||
i.fa(class="fa-list")
|
||||
span {{_ 'reports'}}
|
||||
|
||||
a.setting-header-btn.informations(href="{{pathFor 'information'}}")
|
||||
i.fa(class="fa-info-circle")
|
||||
span {{_ 'info'}}
|
||||
|
||||
@@ -5,10 +5,6 @@ template(name="sidebar")
|
||||
// title="{{showTongueTitle}}")
|
||||
// i.fa.fa-navicon
|
||||
.sidebar-shadow
|
||||
.sidebar-content.sidebar-shortcuts
|
||||
a.board-header-btn.js-shortcuts
|
||||
i.fa.fa-keyboard-o
|
||||
span {{_ 'keyboard-shortcuts' }}
|
||||
.sidebar-content.js-board-sidebar-content
|
||||
//a.hide-btn.js-hide-sidebar
|
||||
// i.fa.fa-navicon
|
||||
@@ -23,7 +19,7 @@ template(name='homeSidebar')
|
||||
hr
|
||||
+labelsWidget
|
||||
ul#cards.label-text-hidden
|
||||
a.flex.js-toggle-minicard-label-text
|
||||
a.flex.js-toggle-minicard-label-text(title="{{_ 'hide-minicard-label-text'}}")
|
||||
span {{_ 'hide-minicard-label-text'}}
|
||||
b
|
||||
.materialCheckBox(class="{{#if hiddenMinicardLabelText}}is-checked{{/if}}")
|
||||
@@ -39,6 +35,12 @@ template(name="membersWidget")
|
||||
h3
|
||||
i.fa.fa-users
|
||||
| {{_ 'members'}}
|
||||
|
||||
.sidebar-shortcuts
|
||||
a.board-header-btn.js-shortcuts(title="{{_ 'keyboard-shortcuts' }}")
|
||||
i.fa.fa-keyboard-o
|
||||
span {{_ 'keyboard-shortcuts' }}
|
||||
|
||||
unless currentUser.isCommentOnly
|
||||
unless currentUser.isWorker
|
||||
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
|
||||
@@ -49,10 +51,10 @@ template(name="membersWidget")
|
||||
+userAvatar(userId=this.userId showStatus=true)
|
||||
if isSandstorm
|
||||
if currentUser.isBoardMember
|
||||
a.member.add-member.sandstorm-powerbox-request-identity
|
||||
a.member.add-member.sandstorm-powerbox-request-identity(title="{{_ 'add-members'}}")
|
||||
i.fa.fa-plus
|
||||
else if currentUser.isBoardAdmin
|
||||
a.member.add-member.js-manage-board-members
|
||||
a.member.add-member.js-manage-board-members(title="{{_ 'add-members'}}")
|
||||
i.fa.fa-plus
|
||||
.clearfix
|
||||
if isInvited
|
||||
@@ -105,6 +107,14 @@ template(name="boardCardSettingsPopup")
|
||||
span
|
||||
i.fa.fa-users
|
||||
| {{_ 'members'}}
|
||||
|
||||
div.check-div
|
||||
a.flex.js-field-has-creator(class="{{#if allowsCreator}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsCreator}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-user
|
||||
| {{_ 'creator'}}
|
||||
|
||||
div.check-div
|
||||
a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")
|
||||
@@ -354,7 +364,7 @@ template(name="labelsWidget")
|
||||
+viewer
|
||||
= name
|
||||
if currentUser.isBoardAdmin
|
||||
a.card-label.add-label.js-add-label
|
||||
a.card-label.add-label.js-add-label(title="{{_ 'label-create'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
template(name="memberPopup")
|
||||
|
||||
@@ -730,6 +730,14 @@ BlazeComponent.extendComponent({
|
||||
return this.currentBoard.allowsSubtasks;
|
||||
},
|
||||
|
||||
allowsCreator() {
|
||||
return (
|
||||
this.currentBoard.allowsCreator === null ||
|
||||
this.currentBoard.allowsCreator === undefined ||
|
||||
this.currentBoard.allowsCreator
|
||||
);
|
||||
},
|
||||
|
||||
allowsMembers() {
|
||||
return this.currentBoard.allowsMembers;
|
||||
},
|
||||
@@ -889,6 +897,19 @@ BlazeComponent.extendComponent({
|
||||
this.currentBoard.allowsSubtasks,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-creator'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsCreator = !this.currentBoard.allowsCreator;
|
||||
this.currentBoard.setAllowsCreator(this.currentBoard.allowsCreator);
|
||||
$(`.js-field-has-creator ${MCB}`).toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsCreator,
|
||||
);
|
||||
$('.js-field-has-creator').toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsCreator,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-members'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
|
||||
|
||||
@@ -100,13 +100,12 @@
|
||||
margin-right: 10px
|
||||
|
||||
.sidebar-shortcuts
|
||||
margin: 0
|
||||
position: absolute
|
||||
margin-left: 40%
|
||||
padding: 0
|
||||
top: auto
|
||||
text-align: center
|
||||
top: 7px
|
||||
font-size: 0.8em
|
||||
line-height: 1.6em
|
||||
vertical-align: middle
|
||||
color: darken(white, 40%)
|
||||
|
||||
.board-sidebar
|
||||
|
||||
@@ -50,6 +50,15 @@ template(name="createCustomFieldPopup")
|
||||
each dropdownItems.get
|
||||
input.js-dropdown-item(type="text" value=name placeholder="")
|
||||
input.js-dropdown-item.last(type="text" value="" placeholder="{{_ 'custom-field-dropdown-options-placeholder'}}")
|
||||
|
||||
div.js-field-settings.js-field-settings-stringtemplate(class="{{#if isTypeNotSelected 'stringtemplate'}}hide{{/if}}")
|
||||
label
|
||||
| {{_ 'custom-field-stringtemplate-format'}}
|
||||
input.js-field-stringtemplate-format(type="text" value=getStringtemplateFormat)
|
||||
label
|
||||
| {{_ 'custom-field-stringtemplate-separator'}}
|
||||
input.js-field-stringtemplate-separator(type="text" value=getStringtemplateSeparator)
|
||||
|
||||
a.flex.js-field-show-on-card(class="{{#if showOnCard}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if showOnCard}}is-checked{{/if}}")
|
||||
|
||||
|
||||
@@ -16,7 +16,15 @@ BlazeComponent.extendComponent({
|
||||
}).register('customFieldsSidebar');
|
||||
|
||||
const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
_types: ['text', 'number', 'date', 'dropdown', 'currency', 'checkbox'],
|
||||
_types: [
|
||||
'text',
|
||||
'number',
|
||||
'date',
|
||||
'dropdown',
|
||||
'currency',
|
||||
'checkbox',
|
||||
'stringtemplate',
|
||||
],
|
||||
|
||||
_currencyList: [
|
||||
{
|
||||
@@ -77,6 +85,18 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
? this.data().settings.dropdownItems
|
||||
: [],
|
||||
);
|
||||
|
||||
this.stringtemplateFormat = new ReactiveVar(
|
||||
this.data().settings && this.data().settings.stringtemplateFormat
|
||||
? this.data().settings.stringtemplateFormat
|
||||
: '',
|
||||
);
|
||||
|
||||
this.stringtemplateSeparator = new ReactiveVar(
|
||||
this.data().settings && this.data().settings.stringtemplateSeparator
|
||||
? this.data().settings.stringtemplateSeparator
|
||||
: '',
|
||||
);
|
||||
},
|
||||
|
||||
types() {
|
||||
@@ -121,6 +141,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
return items;
|
||||
},
|
||||
|
||||
getStringtemplateFormat() {
|
||||
return this.stringtemplateFormat.get();
|
||||
},
|
||||
|
||||
getStringtemplateSeparator() {
|
||||
return this.stringtemplateSeparator.get();
|
||||
},
|
||||
|
||||
getSettings() {
|
||||
const settings = {};
|
||||
switch (this.type.get()) {
|
||||
@@ -136,6 +164,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
settings.dropdownItems = dropdownItems;
|
||||
break;
|
||||
}
|
||||
case 'stringtemplate': {
|
||||
const stringtemplateFormat = this.stringtemplateFormat.get();
|
||||
settings.stringtemplateFormat = stringtemplateFormat;
|
||||
|
||||
const stringtemplateSeparator = this.stringtemplateSeparator.get();
|
||||
settings.stringtemplateSeparator = stringtemplateSeparator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
},
|
||||
@@ -158,6 +194,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
|
||||
evt.target.value = '';
|
||||
}
|
||||
},
|
||||
'input .js-field-stringtemplate-format'(evt) {
|
||||
const value = evt.target.value;
|
||||
this.stringtemplateFormat.set(value);
|
||||
},
|
||||
'input .js-field-stringtemplate-separator'(evt) {
|
||||
const value = evt.target.value;
|
||||
this.stringtemplateSeparator.set(value);
|
||||
},
|
||||
'click .js-field-show-on-card'(evt) {
|
||||
let $target = $(evt.target);
|
||||
if (!$target.hasClass('js-field-show-on-card')) {
|
||||
|
||||
@@ -72,6 +72,43 @@ template(name="filterSidebar")
|
||||
| (<span class="username">{{ username }}</span>)
|
||||
if Filter.assignees.isSelected _id
|
||||
i.fa.fa-check
|
||||
|
||||
hr
|
||||
h3
|
||||
i.fa.fa-list-alt
|
||||
| {{_ 'filter-dates-label' }}
|
||||
ul.sidebar-list
|
||||
li(class="{{#if Filter.dueAt.isSelected 'noDate'}}active{{/if}}")
|
||||
a.name.js-toggle-no-due-date-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-no-due-date' }}
|
||||
if Filter.dueAt.isSelected 'noDate'
|
||||
i.fa.fa-check
|
||||
li(class="{{#if Filter.dueAt.isSelected 'past'}}active{{/if}}")
|
||||
a.name.js-toggle-overdue-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-overdue' }}
|
||||
if Filter.dueAt.isSelected 'past'
|
||||
i.fa.fa-check
|
||||
li(class="{{#if Filter.dueAt.isSelected 'today'}}active{{/if}}")
|
||||
a.name.js-toggle-due-today-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-due-today' }}
|
||||
if Filter.dueAt.isSelected 'today'
|
||||
i.fa.fa-check
|
||||
li(class="{{#if Filter.dueAt.isSelected 'tomorrow'}}active{{/if}}")
|
||||
a.name.js-toggle-due-tomorrow-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-due-tomorrow' }}
|
||||
if Filter.dueAt.isSelected 'tomorrow'
|
||||
i.fa.fa-check
|
||||
li(class="{{#if Filter.dueAt.isSelected 'week'}}active{{/if}}")
|
||||
a.name.js-toggle-due-this-week-filter
|
||||
span.sidebar-list-item-description
|
||||
| {{_ 'filter-due-this-week' }}
|
||||
if Filter.dueAt.isSelected 'week'
|
||||
i.fa.fa-check
|
||||
|
||||
hr
|
||||
h3
|
||||
i.fa.fa-list-alt
|
||||
|
||||
@@ -23,6 +23,31 @@ BlazeComponent.extendComponent({
|
||||
Filter.assignees.toggle(this.currentData()._id);
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-no-due-date-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.noDate();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-overdue-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.past();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-due-today-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.today();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-due-tomorrow-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.tomorrow();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-due-this-week-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.dueAt.thisWeek();
|
||||
Filter.resetExceptions();
|
||||
},
|
||||
'click .js-toggle-archive-filter'(evt) {
|
||||
evt.preventDefault();
|
||||
Filter.archive.toggle(this.currentData()._id);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
template(name="swimlaneHeader")
|
||||
.swimlane-header-wrap.js-swimlane-header(class=colorClass)
|
||||
.swimlane-header-wrap.js-swimlane-header(class=colorClass title="{{_ 'rename'}}")
|
||||
if this.isTemplateContainer
|
||||
+swimlaneFixedHeader(this)
|
||||
else
|
||||
@@ -16,8 +16,8 @@ template(name="swimlaneFixedHeader")
|
||||
.swimlane-header-menu
|
||||
unless currentUser.isCommentOnly
|
||||
if currentUser.isBoardAdmin
|
||||
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon
|
||||
a.fa.fa-navicon.js-open-swimlane-menu
|
||||
a.fa.fa-plus.js-open-add-swimlane-menu.swimlane-header-plus-icon(title="{{_ 'add-swimlane'}}")
|
||||
a.fa.fa-navicon.js-open-swimlane-menu(title="{{_ 'swimlaneActionPopup-title'}}")
|
||||
unless isMiniScreen
|
||||
if showDesktopDragHandles
|
||||
a.swimlane-header-handle.handle.fa.fa-arrows.js-swimlane-header-handle
|
||||
@@ -39,6 +39,8 @@ template(name="swimlaneActionPopup")
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li: a.js-close-swimlane {{_ 'archive-swimlane'}}
|
||||
ul.pop-over-list
|
||||
li: a.js-copy-swimlane {{_ 'copy-swimlane'}}
|
||||
ul.pop-over-list
|
||||
li: a.js-move-swimlane {{_ 'move-swimlane'}}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ Template.swimlaneActionPopup.events({
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-move-swimlane': Popup.open('moveSwimlane'),
|
||||
'click .js-copy-swimlane': Popup.open('copySwimlane'),
|
||||
});
|
||||
|
||||
Template.swimlaneActionPopup.events({
|
||||
|
||||
@@ -58,9 +58,8 @@ template(name="addListForm")
|
||||
| {{_ 'or'}}
|
||||
a.js-list-template {{_ 'template'}}
|
||||
else
|
||||
a.open-list-composer.js-open-inlined-form
|
||||
a.open-list-composer.js-open-inlined-form(title="{{_ 'add-list'}}")
|
||||
i.fa.fa-plus
|
||||
| {{_ 'add-list'}}
|
||||
|
||||
template(name="moveSwimlanePopup")
|
||||
unless currentUser.isWorker
|
||||
@@ -71,3 +70,13 @@ template(name="moveSwimlanePopup")
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
|
||||
template(name="copySwimlanePopup")
|
||||
unless currentUser.isWorker
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards(autofocus)
|
||||
each toBoard in toBoards
|
||||
option(value="{{toBoard._id}}" selected="{{#if $eq toBoard.title board.title}}1{{/if}}") {{toBoard.title}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
@@ -324,45 +324,54 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('listsGroup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
class MoveSwimlaneComponent extends BlazeComponent {
|
||||
serverMethod = 'moveSwimlane';
|
||||
|
||||
onCreated() {
|
||||
this.currentSwimlane = this.currentData();
|
||||
},
|
||||
}
|
||||
|
||||
board() {
|
||||
return Boards.findOne(Session.get('currentBoard'));
|
||||
},
|
||||
}
|
||||
|
||||
toBoardsSelector() {
|
||||
return {
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
type: 'board',
|
||||
_id: { $ne: this.board()._id },
|
||||
};
|
||||
}
|
||||
|
||||
toBoards() {
|
||||
const boards = Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
type: 'board',
|
||||
_id: { $ne: this.board()._id },
|
||||
},
|
||||
{
|
||||
sort: { title: 1 },
|
||||
},
|
||||
);
|
||||
|
||||
return boards;
|
||||
},
|
||||
return Boards.find(this.toBoardsSelector(), { sort: { title: 1 } });
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-done'() {
|
||||
const swimlane = Swimlanes.findOne(this.currentSwimlane._id);
|
||||
// const swimlane = Swimlanes.findOne(this.currentSwimlane._id);
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
let boardId;
|
||||
if (bSelect) {
|
||||
boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
Meteor.call('moveSwimlane', this.currentSwimlane._id, boardId);
|
||||
Meteor.call(this.serverMethod, this.currentSwimlane._id, boardId);
|
||||
}
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('moveSwimlanePopup');
|
||||
}
|
||||
}
|
||||
MoveSwimlaneComponent.register('moveSwimlanePopup');
|
||||
|
||||
(class extends MoveSwimlaneComponent {
|
||||
serverMethod = 'copySwimlane';
|
||||
toBoardsSelector() {
|
||||
const selector = super.toBoardsSelector();
|
||||
delete selector._id;
|
||||
return selector;
|
||||
}
|
||||
}.register('copySwimlanePopup'));
|
||||
@@ -1,5 +1,5 @@
|
||||
template(name="userAvatar")
|
||||
a.member.js-member(title="{{userData.profile.fullname}} ({{userData.username}}) {{_ memberType}}")
|
||||
a.member(class="js-{{#if assignee}}assignee{{else}}member{{/if}}" title="{{userData.profile.fullname}} ({{userData.username}}) {{_ memberType}}")
|
||||
if userData.profile.avatarUrl
|
||||
img.avatar.avatar-image(src="{{userData.profile.avatarUrl}}")
|
||||
else
|
||||
@@ -72,9 +72,10 @@ template(name="cardMemberPopup")
|
||||
h3= user.profile.fullname
|
||||
p.quiet @{{ user.username }}
|
||||
ul.pop-over-list
|
||||
if currentUser.isNotCommentOnly
|
||||
if currentUser.isNotWorker
|
||||
li: a.js-remove-member {{_ 'remove-member-from-card'}}
|
||||
unless noRemove
|
||||
if currentUser.isNotCommentOnly
|
||||
if currentUser.isNotWorker
|
||||
li: a.js-remove-member {{_ 'remove-member-from-card'}}
|
||||
|
||||
if $eq currentUser._id user._id
|
||||
with currentUser
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import Cards from '/models/cards';
|
||||
import Avatars from '/models/avatars';
|
||||
import Users from '/models/users';
|
||||
|
||||
Template.userAvatar.helpers({
|
||||
userData() {
|
||||
// We need to handle a special case for the search results provided by the
|
||||
@@ -30,10 +34,6 @@ Template.userAvatar.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
Template.userAvatar.events({
|
||||
'click .js-change-avatar': Popup.open('changeAvatar'),
|
||||
});
|
||||
|
||||
Template.userAvatarInitials.helpers({
|
||||
initials() {
|
||||
const user = Users.findOne(this.userId);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
template(name="headerUserBar")
|
||||
#header-user-bar
|
||||
a.header-user-bar-name.js-open-header-member-menu
|
||||
a.header-user-bar-name.js-open-header-member-menu(title="{{_ 'memberMenuPopup-title'}}")
|
||||
.header-user-bar-avatar
|
||||
+userAvatar(userId=currentUser._id)
|
||||
unless isMiniScreen
|
||||
@@ -25,10 +25,6 @@ template(name="memberMenuPopup")
|
||||
a.js-global-search(href="{{pathFor 'global-search'}}")
|
||||
i.fa.fa-search
|
||||
| {{_ 'globalSearch-title'}}
|
||||
li
|
||||
a.js-broken-cards(href="{{pathFor 'broken-cards'}}")
|
||||
i.fa.fa-chain-broken
|
||||
| {{_ 'broken-cards'}}
|
||||
li
|
||||
a(href="{{pathFor 'home'}}")
|
||||
span.fa.fa-home
|
||||
|
||||
@@ -174,6 +174,8 @@ Template.changeLanguagePopup.helpers({
|
||||
} else if (lang.name === 'fa-IR') {
|
||||
// fa-IR = Persian (Iran)
|
||||
name = 'فارسی/پارسی (ایران)';
|
||||
} else if (lang.name === 'de-CH') {
|
||||
name = 'Deutsch (Schweiz)';
|
||||
} else if (lang.name === 'fr-BE') {
|
||||
name = 'Français (Belgique)';
|
||||
} else if (lang.name === 'fr-CA') {
|
||||
|
||||
@@ -32,8 +32,11 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
||||
that.searching.set(false);
|
||||
that.hasResults.set(false);
|
||||
that.serverError.set(true);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error.reason:', error.reason);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error.message:', error.message);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error.stack:', error.stack);
|
||||
},
|
||||
};
|
||||
@@ -62,7 +65,6 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
||||
// console.log('getting results');
|
||||
const sessionData = this.getSessionData();
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('selector:', sessionData.getSelector());
|
||||
console.log('session data:', sessionData);
|
||||
const cards = [];
|
||||
sessionData.cards.forEach(cardId => {
|
||||
@@ -72,7 +74,7 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
||||
if (this.queryErrors.length) {
|
||||
// console.log('queryErrors:', this.queryErrorMessages());
|
||||
this.hasQueryErrors.set(true);
|
||||
return null;
|
||||
// return null;
|
||||
}
|
||||
|
||||
if (cards) {
|
||||
@@ -99,10 +101,8 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
||||
}
|
||||
}
|
||||
|
||||
runGlobalSearch(queryParams) {
|
||||
this.searching.set(true);
|
||||
this.stopSubscription();
|
||||
this.subscriptionHandle = Meteor.subscribe(
|
||||
getSubscription(queryParams) {
|
||||
return Meteor.subscribe(
|
||||
'globalSearch',
|
||||
this.sessionId,
|
||||
queryParams.params,
|
||||
@@ -111,6 +111,12 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
||||
);
|
||||
}
|
||||
|
||||
runGlobalSearch(queryParams) {
|
||||
this.searching.set(true);
|
||||
this.stopSubscription();
|
||||
this.subscriptionHandle = this.getSubscription(queryParams);
|
||||
}
|
||||
|
||||
queryErrorMessages() {
|
||||
const messages = [];
|
||||
|
||||
|
||||
+15
-17
@@ -6,17 +6,17 @@ function adjustedTimeFormat() {
|
||||
.replace(/HH/i, 'H');
|
||||
}
|
||||
|
||||
DatePicker = BlazeComponent.extendComponent({
|
||||
export class DatePicker extends BlazeComponent {
|
||||
template() {
|
||||
return 'datepicker';
|
||||
},
|
||||
}
|
||||
|
||||
onCreated(defaultTime = '1970-01-01 08:00:00') {
|
||||
this.error = new ReactiveVar('');
|
||||
this.card = this.data();
|
||||
this.date = new ReactiveVar(moment.invalid());
|
||||
this.defaultTime = defaultTime;
|
||||
},
|
||||
}
|
||||
|
||||
startDayOfWeek() {
|
||||
const currentUser = Meteor.user();
|
||||
@@ -25,7 +25,7 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
onRendered() {
|
||||
const $picker = this.$('.js-datepicker')
|
||||
@@ -42,7 +42,7 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
this.error.set('');
|
||||
const timeInput = this.find('#time');
|
||||
timeInput.focus();
|
||||
if (!timeInput.value) {
|
||||
if (!timeInput.value && this.defaultTime) {
|
||||
const currentHour = evt.date.getHours();
|
||||
const defaultMoment = moment(
|
||||
currentHour > 0 ? evt.date : this.defaultTime,
|
||||
@@ -55,22 +55,22 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
if (this.date.get().isValid()) {
|
||||
$picker.datepicker('update', this.date.get().toDate());
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
showDate() {
|
||||
if (this.date.get().isValid()) return this.date.get().format('L');
|
||||
return '';
|
||||
},
|
||||
}
|
||||
showTime() {
|
||||
if (this.date.get().isValid()) return this.date.get().format('LT');
|
||||
return '';
|
||||
},
|
||||
}
|
||||
dateFormat() {
|
||||
return moment.localeData().longDateFormat('L');
|
||||
},
|
||||
}
|
||||
timeFormat() {
|
||||
return moment.localeData().longDateFormat('LT');
|
||||
},
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
@@ -106,7 +106,7 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
const dateString = `${evt.target.date.value} ${time}`;
|
||||
const newCompleteDate = moment(
|
||||
dateString,
|
||||
'L ' + adjustedTimeFormat(),
|
||||
`L ${adjustedTimeFormat()}`,
|
||||
true,
|
||||
);
|
||||
if (!newTime.isValid()) {
|
||||
@@ -120,10 +120,8 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
if (newCompleteDate.isValid()) {
|
||||
this._storeDate(newCompleteDate.toDate());
|
||||
Popup.close();
|
||||
} else {
|
||||
if (!this.error) {
|
||||
this.error.set('invalid');
|
||||
}
|
||||
} else if (!this.error) {
|
||||
this.error.set('invalid');
|
||||
}
|
||||
},
|
||||
'click .js-delete-date'(evt) {
|
||||
@@ -133,5 +131,5 @@ DatePicker = BlazeComponent.extendComponent({
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,154 @@ function showFilterSidebar() {
|
||||
Sidebar.setView('filter');
|
||||
}
|
||||
|
||||
class DateFilter {
|
||||
constructor() {
|
||||
this._dep = new Tracker.Dependency();
|
||||
this.subField = ''; // Prevent name mangling in Filter
|
||||
this._filter = null;
|
||||
this._filterState = null;
|
||||
}
|
||||
|
||||
_updateState(state) {
|
||||
this._filterState = state;
|
||||
showFilterSidebar();
|
||||
this._dep.changed();
|
||||
}
|
||||
|
||||
// past builds a filter for all dates before now
|
||||
past() {
|
||||
if (this._filterState == 'past') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this._filter = { $lte: moment().toDate() };
|
||||
this._updateState('past');
|
||||
}
|
||||
|
||||
// today is a convenience method for calling relativeDay with 0
|
||||
today() {
|
||||
if (this._filterState == 'today') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this.relativeDay(0);
|
||||
this._updateState('today');
|
||||
}
|
||||
|
||||
// tomorrow is a convenience method for calling relativeDay with 1
|
||||
tomorrow() {
|
||||
if (this._filterState == 'tomorrow') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this.relativeDay(1);
|
||||
this._updateState('tomorrow');
|
||||
}
|
||||
|
||||
// thisWeek is a convenience method for calling relativeWeek with 1
|
||||
thisWeek() {
|
||||
this.relativeWeek(1);
|
||||
}
|
||||
|
||||
// relativeDay builds a filter starting from now and including all
|
||||
// days up to today +/- offset.
|
||||
relativeDay(offset) {
|
||||
if (this._filterState == 'day') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
var startDay = moment()
|
||||
.startOf('day')
|
||||
.toDate(),
|
||||
endDay = moment()
|
||||
.endOf('day')
|
||||
.add(offset, 'day')
|
||||
.toDate();
|
||||
|
||||
if (offset >= 0) {
|
||||
this._filter = { $gte: startDay, $lte: endDay };
|
||||
} else {
|
||||
this._filter = { $lte: startDay, $gte: endDay };
|
||||
}
|
||||
|
||||
this._updateState('day');
|
||||
}
|
||||
|
||||
// relativeWeek builds a filter starting from today and including all
|
||||
// weeks up to today +/- offset. This considers the user's preferred
|
||||
// start of week day (as defined by Meteor).
|
||||
relativeWeek(offset) {
|
||||
if (this._filterState == 'week') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// getStartDayOfWeek returns the offset from Sunday of the user's
|
||||
// preferred starting day of the week. This date should be added
|
||||
// to the moment start of week to get the real start of week date.
|
||||
// The default is 1, meaning Monday.
|
||||
const currentUser = Meteor.user();
|
||||
const weekStartDay = currentUser ? currentUser.getStartDayOfWeek() : 1;
|
||||
|
||||
// Moments are mutable so they must be cloned before modification
|
||||
var thisWeekStart = moment()
|
||||
.startOf('day')
|
||||
.startOf('week')
|
||||
.add(weekStartDay, 'days');
|
||||
var thisWeekEnd = thisWeekStart
|
||||
.clone()
|
||||
.add(offset, 'week')
|
||||
.endOf('day');
|
||||
var startDate = thisWeekStart.toDate();
|
||||
var endDate = thisWeekEnd.toDate();
|
||||
|
||||
if (offset >= 0) {
|
||||
this._filter = { $gte: startDate, $lte: endDate };
|
||||
} else {
|
||||
this._filter = { $lte: startDate, $gte: endDate };
|
||||
}
|
||||
|
||||
this._updateState('week');
|
||||
}
|
||||
|
||||
// noDate builds a filter for items where date is not set
|
||||
noDate() {
|
||||
if (this._filterState == 'noDate') {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
this._filter = null;
|
||||
this._updateState('noDate');
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._filter = null;
|
||||
this._filterState = null;
|
||||
this._dep.changed();
|
||||
}
|
||||
|
||||
isSelected(val) {
|
||||
this._dep.depend();
|
||||
return this._filterState == val;
|
||||
}
|
||||
|
||||
_isActive() {
|
||||
this._dep.depend();
|
||||
return this._filterState !== null;
|
||||
}
|
||||
|
||||
_getMongoSelector() {
|
||||
this._dep.depend();
|
||||
return this._filter;
|
||||
}
|
||||
|
||||
_getEmptySelector() {
|
||||
this._dep.depend();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Use a "set" filter for a field that is a set of documents uniquely
|
||||
// identified. For instance `{ labels: ['labelA', 'labelC', 'labelD'] }`.
|
||||
// use "subField" for searching inside object Fields.
|
||||
@@ -462,6 +610,7 @@ Filter = {
|
||||
assignees: new SetFilter(),
|
||||
archive: new SetFilter(),
|
||||
hideEmpty: new SetFilter(),
|
||||
dueAt: new DateFilter(),
|
||||
customFields: new SetFilter('_id'),
|
||||
advanced: new AdvancedFilter(),
|
||||
lists: new AdvancedFilter(), // we need the ability to filter list by name as well
|
||||
@@ -472,6 +621,7 @@ Filter = {
|
||||
'assignees',
|
||||
'archive',
|
||||
'hideEmpty',
|
||||
'dueAt',
|
||||
'customFields',
|
||||
],
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
export const ALLOWED_BOARD_COLORS = [
|
||||
'belize',
|
||||
'nephritis',
|
||||
'pomegranate',
|
||||
'pumpkin',
|
||||
'wisteria',
|
||||
'moderatepink',
|
||||
'strongcyan',
|
||||
'limegreen',
|
||||
'midnight',
|
||||
'dark',
|
||||
'relax',
|
||||
'corteza',
|
||||
'clearblue',
|
||||
'natural',
|
||||
'modern',
|
||||
'moderndark',
|
||||
];
|
||||
export const ALLOWED_COLORS = [
|
||||
'white',
|
||||
'green',
|
||||
'yellow',
|
||||
'orange',
|
||||
'red',
|
||||
'purple',
|
||||
'blue',
|
||||
'sky',
|
||||
'lime',
|
||||
'pink',
|
||||
'black',
|
||||
'silver',
|
||||
'peachpuff',
|
||||
'crimson',
|
||||
'plum',
|
||||
'darkgreen',
|
||||
'slateblue',
|
||||
'magenta',
|
||||
'gold',
|
||||
'navy',
|
||||
'gray',
|
||||
'saddlebrown',
|
||||
'paleturquoise',
|
||||
'mistyrose',
|
||||
'indigo',
|
||||
];
|
||||
export const TYPE_BOARD = 'board';
|
||||
export const TYPE_CARD = 'cardType-card';
|
||||
export const TYPE_LINKED_BOARD = 'cardType-linkedBoard';
|
||||
export const TYPE_LINKED_CARD = 'cardType-linkedCard';
|
||||
export const TYPE_TEMPLATE_BOARD = 'template-board';
|
||||
export const TYPE_TEMPLATE_CONTAINER = 'template-container';
|
||||
+15
-2
@@ -3,6 +3,7 @@ import {
|
||||
OPERATOR_BOARD,
|
||||
OPERATOR_COMMENT,
|
||||
OPERATOR_CREATED_AT,
|
||||
OPERATOR_CREATOR,
|
||||
OPERATOR_DUE,
|
||||
OPERATOR_HAS,
|
||||
OPERATOR_LABEL,
|
||||
@@ -52,7 +53,9 @@ export class QueryParams {
|
||||
|
||||
hasOperator(operator) {
|
||||
return (
|
||||
this.params[operator] !== undefined && this.params[operator].length > 0
|
||||
this.params[operator] !== undefined &&
|
||||
(this.params[operator].length === undefined ||
|
||||
this.params[operator].length > 0)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,7 +71,11 @@ export class QueryParams {
|
||||
}
|
||||
|
||||
getPredicate(operator) {
|
||||
return this.params[operator][0];
|
||||
if (typeof this.params[operator] === 'object') {
|
||||
return this.params[operator][0];
|
||||
} else {
|
||||
return this.params[operator];
|
||||
}
|
||||
}
|
||||
|
||||
getPredicates(operator) {
|
||||
@@ -107,6 +114,7 @@ export class QueryErrors {
|
||||
[OPERATOR_USER, 'user-username-not-found'],
|
||||
[OPERATOR_ASSIGNEE, 'user-username-not-found'],
|
||||
[OPERATOR_MEMBER, 'user-username-not-found'],
|
||||
[OPERATOR_CREATOR, 'user-username-not-found'],
|
||||
];
|
||||
|
||||
constructor() {
|
||||
@@ -196,6 +204,10 @@ export class Query {
|
||||
return this.queryParams;
|
||||
}
|
||||
|
||||
setQueryParams(queryParams) {
|
||||
this.queryParams = queryParams;
|
||||
}
|
||||
|
||||
addPredicate(operator, predicate) {
|
||||
this.queryParams.addPredicate(operator, predicate);
|
||||
}
|
||||
@@ -238,6 +250,7 @@ export class Query {
|
||||
'operator-member': OPERATOR_MEMBER,
|
||||
'operator-member-abbrev': OPERATOR_MEMBER,
|
||||
'operator-assignee': OPERATOR_ASSIGNEE,
|
||||
'operator-creator': OPERATOR_CREATOR,
|
||||
'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
|
||||
'operator-status': OPERATOR_STATUS,
|
||||
'operator-due': OPERATOR_DUE,
|
||||
|
||||
@@ -291,6 +291,28 @@ FlowRouter.route('/people', {
|
||||
},
|
||||
});
|
||||
|
||||
FlowRouter.route('/admin-reports', {
|
||||
name: 'admin-reports',
|
||||
triggersEnter: [
|
||||
AccountsTemplates.ensureSignedIn,
|
||||
() => {
|
||||
Session.set('currentBoard', null);
|
||||
Session.set('currentList', null);
|
||||
Session.set('currentCard', null);
|
||||
|
||||
Filter.reset();
|
||||
Session.set('sortBy', '');
|
||||
EscapeActions.executeAll();
|
||||
},
|
||||
],
|
||||
action() {
|
||||
BlazeLayout.render('defaultLayout', {
|
||||
headerBar: 'settingHeaderBar',
|
||||
content: 'adminReports',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
FlowRouter.notFound = {
|
||||
action() {
|
||||
BlazeLayout.render('defaultLayout', { content: 'notFound' });
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
export const DEFAULT_LIMIT = 25;
|
||||
export const OPERATOR_ASSIGNEE = 'assignee';
|
||||
export const OPERATOR_ASSIGNEE = 'assignees';
|
||||
export const OPERATOR_COMMENT = 'comment';
|
||||
export const OPERATOR_CREATED_AT = 'createdAt';
|
||||
export const OPERATOR_CREATOR = 'userId';
|
||||
export const OPERATOR_DUE = 'dueAt';
|
||||
export const OPERATOR_BOARD = 'board';
|
||||
export const OPERATOR_HAS = 'has';
|
||||
export const OPERATOR_LABEL = 'label';
|
||||
export const OPERATOR_LIMIT = 'limit';
|
||||
export const OPERATOR_LIST = 'list';
|
||||
export const OPERATOR_MEMBER = 'member';
|
||||
export const OPERATOR_MEMBER = 'members';
|
||||
export const OPERATOR_MODIFIED_AT = 'modifiedAt';
|
||||
export const OPERATOR_SORT = 'sort';
|
||||
export const OPERATOR_STATUS = 'status';
|
||||
|
||||
+6
-6
@@ -68,7 +68,7 @@ version: '2'
|
||||
# docker exec -it wekan-db bash
|
||||
# # 3) and data directory
|
||||
# cd /data
|
||||
# # 4) Remove previos dump
|
||||
# # 4) Remove previous dump
|
||||
# rm -rf dump
|
||||
# # 5) Exit db container
|
||||
# exit
|
||||
@@ -118,7 +118,7 @@ services:
|
||||
#-------------------------------------------------------------------------------------
|
||||
# ==== MONGODB AND METEOR VERSION ====
|
||||
# a) Quay automatic builds do work work, https://quay.io/wekan/wekan
|
||||
image: quay.io/wekan/wekan:stable
|
||||
image: quay.io/wekan/wekan
|
||||
# b) Using specific version tag:
|
||||
# image: quay.io/wekan/wekan:v4.52
|
||||
# c) Docker Hub builds do work https://hub.docker.com/r/wekanteam/wekan
|
||||
@@ -281,7 +281,7 @@ services:
|
||||
#- NOTIFY_DUE_AT_HOUR_OF_DAY=8
|
||||
#-----------------------------------------------------------------
|
||||
# ==== EMAIL NOTIFICATION TIMEOUT, ms =====
|
||||
# Defaut: 30000 ms = 30s
|
||||
# Default: 30000 ms = 30s
|
||||
#- EMAIL_NOTIFICATION_TIMEOUT=30000
|
||||
#-----------------------------------------------------------------
|
||||
# ==== CORS =====
|
||||
@@ -348,7 +348,7 @@ services:
|
||||
#- OAUTH2_USERNAME_MAP=email
|
||||
# The claim name you want to map to the full name field:
|
||||
#- OAUTH2_FULLNAME_MAP=name
|
||||
# Tthe claim name you want to map to the email field:
|
||||
# The claim name you want to map to the email field:
|
||||
#- OAUTH2_EMAIL_MAP=email
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 Nextcloud ====
|
||||
@@ -374,7 +374,7 @@ services:
|
||||
#- OAUTH2_USERNAME_MAP=id
|
||||
# The claim name you want to map to the full name field:
|
||||
#- OAUTH2_FULLNAME_MAP=display-name
|
||||
# Tthe claim name you want to map to the email field:
|
||||
# The claim name you want to map to the email field:
|
||||
#- OAUTH2_EMAIL_MAP=email
|
||||
#-----------------------------------------------------------------
|
||||
# ==== OAUTH2 KEYCLOAK ====
|
||||
@@ -526,7 +526,7 @@ services:
|
||||
# The attribute inside a group object listing its members. Example: member
|
||||
#- LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=
|
||||
#
|
||||
# The format of the value of LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE. Example: 'dn' if the users dn ist saved as value into the attribute.
|
||||
# The format of the value of LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE. Example: 'dn' if the users dn is saved as value into the attribute.
|
||||
#- LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=
|
||||
#
|
||||
# The group name (id) that matches all users.
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
var Meteor = Package.meteor.Meteor;
|
||||
var global = Package.meteor.global;
|
||||
var meteorEnv = Package.meteor.meteorEnv;
|
||||
var FS = Package['cfs:base-package'].FS;
|
||||
var FS = Package['wekan-cfs-base-package'].FS;
|
||||
var check = Package.check.check;
|
||||
var Match = Package.check.Match;
|
||||
var EJSON = Package.ejson.EJSON;
|
||||
var HTTP = Package['cfs:http-methods'].HTTP;
|
||||
var HTTP = Package['wekan-cfs-http-methods'].HTTP;
|
||||
|
||||
/* Package-scope variables */
|
||||
var rootUrlPathPrefix, baseUrl, getHeaders, getHeadersByCollection, _existingMountPoints, mountUrls;
|
||||
@@ -25,7 +25,7 @@ var rootUrlPathPrefix, baseUrl, getHeaders, getHeadersByCollection, _existingMou
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// packages/cfs:access-point/access-point-common.js //
|
||||
// packages/wekan-cfs-access-point/access-point-common.js //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -217,7 +217,7 @@ FS.File.prototype.url = function(options) {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// packages/cfs:access-point/access-point-handlers.js //
|
||||
// packages/wekan-cfs-access-point/access-point-handlers.js //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -537,7 +537,7 @@ FS.HTTP.Handlers.PutUpdate = function httpPutUpdateHandler(ref) {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// packages/cfs:access-point/access-point-server.js //
|
||||
// packages/wekan-cfs-access-point/access-point-server.js //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -909,6 +909,6 @@ Meteor.startup(function () {
|
||||
|
||||
/* Exports */
|
||||
if (typeof Package === 'undefined') Package = {};
|
||||
Package['cfs:access-point'] = {};
|
||||
Package['wekan-cfs-access-point'] = {};
|
||||
|
||||
})();
|
||||
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+51
-1
@@ -82,6 +82,8 @@
|
||||
"add-attachment": "Add Attachment",
|
||||
"add-board": "Add Board",
|
||||
"add-card": "Add Card",
|
||||
"add-card-to-top-of-list": "Add Card to Top of List",
|
||||
"add-card-to-bottom-of-list": "Add Card to Bottom of List",
|
||||
"add-swimlane": "Add Swimlane",
|
||||
"add-subtask": "Add Subtask",
|
||||
"add-checklist": "Add Checklist",
|
||||
@@ -131,6 +133,7 @@
|
||||
"board-not-found": "Board not found",
|
||||
"board-private-info": "This board will be <strong>private</strong>.",
|
||||
"board-public-info": "This board will be <strong>public</strong>.",
|
||||
"board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
|
||||
"boardChangeColorPopup-title": "Change Board Background",
|
||||
"boardChangeTitlePopup-title": "Rename Board",
|
||||
"boardChangeVisibilityPopup-title": "Change Visibility",
|
||||
@@ -178,6 +181,27 @@
|
||||
"vote-against": "against",
|
||||
"deleteVotePopup-title": "Delete vote?",
|
||||
"vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
|
||||
"cardStartPlanningPokerPopup-title": "Start a Planning Poker",
|
||||
"card-edit-planning-poker": "Edit Planning Poker",
|
||||
"editPokerEndDatePopup-title": "Change Planning Poker vote end date",
|
||||
"poker-question": "Planning Poker",
|
||||
"poker-one": "1",
|
||||
"poker-two": "2",
|
||||
"poker-three": "3",
|
||||
"poker-five": "5",
|
||||
"poker-eight": "8",
|
||||
"poker-thirteen": "13",
|
||||
"poker-twenty": "20",
|
||||
"poker-forty": "40",
|
||||
"poker-oneHundred": "100",
|
||||
"poker-unsure": "?",
|
||||
"poker-finish": "Finish",
|
||||
"poker-result-votes": "Votes",
|
||||
"poker-result-who": "Who",
|
||||
"poker-replay": "Replay",
|
||||
"set-estimation": "Set Estimation",
|
||||
"deletePokerPopup-title": "Delete planning poker?",
|
||||
"poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
|
||||
"cardDeletePopup-title": "Delete Card?",
|
||||
"cardDetailsActionsPopup-title": "Card Actions",
|
||||
"cardLabelsPopup-title": "Labels",
|
||||
@@ -186,6 +210,7 @@
|
||||
"cardTemplatePopup-title": "Create template",
|
||||
"cards": "Cards",
|
||||
"cards-count": "Cards",
|
||||
"cards-count-one": "Card",
|
||||
"casSignIn": "Sign In with CAS",
|
||||
"cardType-card": "Card",
|
||||
"cardType-linkedCard": "Linked Card",
|
||||
@@ -208,6 +233,7 @@
|
||||
"close": "Close",
|
||||
"close-board": "Close Board",
|
||||
"close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
|
||||
"close-card": "Close Card",
|
||||
"color-black": "black",
|
||||
"color-blue": "blue",
|
||||
"color-crimson": "crimson",
|
||||
@@ -344,6 +370,12 @@
|
||||
"list-label-short-sort": "(M)",
|
||||
"filter": "Filter",
|
||||
"filter-cards": "Filter Cards or Lists",
|
||||
"filter-dates-label": "Filter by date",
|
||||
"filter-no-due-date": "No due date",
|
||||
"filter-overdue": "Overdue",
|
||||
"filter-due-today": "Due today",
|
||||
"filter-due-this-week": "Due this week",
|
||||
"filter-due-tomorrow": "Due tomorrow",
|
||||
"list-filter-label": "Filter List by Title",
|
||||
"filter-clear": "Clear filter",
|
||||
"filter-labels-label": "Filter by label",
|
||||
@@ -415,6 +447,8 @@
|
||||
"set-color-list": "Set Color",
|
||||
"listActionPopup-title": "List Actions",
|
||||
"settingsUserPopup-title": "User Settings",
|
||||
"settingsTeamPopup-title": "Team Settings",
|
||||
"settingsOrgPopup-title": "Organization Settings",
|
||||
"swimlaneActionPopup-title": "Swimlane Actions",
|
||||
"swimlaneAddPopup-title": "Add a Swimlane below",
|
||||
"listImportCardPopup-title": "Import a Trello card",
|
||||
@@ -811,6 +845,8 @@
|
||||
"act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
|
||||
"act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
|
||||
"delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
|
||||
"delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
|
||||
"delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
|
||||
"accounts-allowUserDelete": "Allow users to self delete their account",
|
||||
"hide-minicard-label-text": "Hide minicard label text",
|
||||
"show-desktop-drag-handles": "Show desktop drag handles",
|
||||
@@ -901,6 +937,7 @@
|
||||
"operator-member-abbrev": "m",
|
||||
"operator-assignee": "assignee",
|
||||
"operator-assignee-abbrev": "a",
|
||||
"operator-creator": "creator",
|
||||
"operator-status": "status",
|
||||
"operator-due": "due",
|
||||
"operator-created": "created",
|
||||
@@ -952,6 +989,7 @@
|
||||
"globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:<username>`",
|
||||
"globalSearch-instructions-operator-member": "`__operator_member__:<username>` - cards where *<username>* is a *member*",
|
||||
"globalSearch-instructions-operator-assignee": "`__operator_assignee__:<username>` - cards where *<username>* is an *assignee*",
|
||||
"globalSearch-instructions-operator-creator": "`__operator_creator__:<username>` - cards where *<username>* is the card's creator",
|
||||
"globalSearch-instructions-operator-due": "`__operator_due__:<n>` - cards which are due up to *<n>* days from now. `__operator_due__:__predicate_overdue__ lists all cards past their due date.",
|
||||
"globalSearch-instructions-operator-created": "`__operator_created__:<n>` - cards which were created *<n>* days ago or less",
|
||||
"globalSearch-instructions-operator-modified": "`__operator_modified__:<n>` - cards which were modified *<n>* days ago or less",
|
||||
@@ -988,5 +1026,17 @@
|
||||
"hide-system-messages-of-all-users": "Hide system messages of all users",
|
||||
"now-system-messages-of-all-users-are-hidden": "Now system messages of all users are hidden",
|
||||
"move-swimlane": "Move Swimlane",
|
||||
"moveSwimlanePopup-title": "Move Swimlane"
|
||||
"moveSwimlanePopup-title": "Move Swimlane",
|
||||
"custom-field-stringtemplate": "String Template",
|
||||
"custom-field-stringtemplate-format": "Format (use %{value} as placeholder)",
|
||||
"custom-field-stringtemplate-separator": "Separator (use   or for a space)",
|
||||
"custom-field-stringtemplate-item-placeholder": "Press enter to add more items",
|
||||
"creator": "Creator",
|
||||
"filesReportTitle": "Files Report",
|
||||
"orphanedFilesReportTitle": "Orphaned Files Report",
|
||||
"reports": "Reports",
|
||||
"rulesReportTitle": "Rules Report",
|
||||
"copy-swimlane": "Copy Swimlane",
|
||||
"copySwimlanePopup-title": "Copy Swimlane",
|
||||
"display-card-creator": "Display Card Creator"
|
||||
}
|
||||
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
+1040
-990
File diff suppressed because it is too large.
Load diff
Loaded 100 of 450 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user