mirror of
https://github.com/wekan/wekan.git
synced 2026-11-06 10:45:03 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36c2cb9573 | ||
|
|
88a47780a8 | ||
|
|
d30ce3c5cf | ||
|
|
0fa8fc8b50 | ||
|
|
6d73618a48 | ||
|
|
db493f1571 | ||
|
|
2b51afaedf | ||
|
|
18b6381d79 | ||
|
|
c65d8ed6a9 | ||
|
|
b3ba156d20 | ||
|
|
e1de787f19 | ||
|
|
e1f08b157b | ||
|
|
508915462d | ||
|
|
7c2ef6fbd5 | ||
|
|
ae73029dda | ||
|
|
542972fc5c | ||
|
|
d7cb143b0a | ||
|
|
d594555227 | ||
|
|
fe25ed5202 | ||
|
|
f6c36d6edd | ||
|
|
fe40f35d6d | ||
|
|
789d1d1d1c | ||
|
|
70265deb47 | ||
|
|
c0d1ed714e | ||
|
|
0555b7e34c | ||
|
|
c1c2eb22ba | ||
|
|
697d7613f2 | ||
|
|
33836af1f9 | ||
|
|
4f0c2dafca | ||
|
|
84801010d5 | ||
|
|
cb418f5e23 | ||
|
|
f7d6ac9401 | ||
|
|
d939ab0d76 | ||
|
|
d538a01d19 | ||
|
|
8770326ed3 | ||
|
|
b2e96746db | ||
|
|
6ce0be635b | ||
|
|
1001e95a87 | ||
|
|
609adcdf10 | ||
|
|
9de2580e30 | ||
|
|
9eb9081c6d | ||
|
|
f551b03dc7 | ||
|
|
1d5dd5e60f | ||
|
|
35a7036d4a | ||
|
|
7024929881 | ||
|
|
90058f25bf | ||
|
|
24ec9018fb | ||
|
|
ec01e5182d | ||
|
|
06d2880a7e | ||
|
|
2a611a8935 | ||
|
|
b236d95636 | ||
|
|
b1abfc768a | ||
|
|
9b97810109 | ||
|
|
4feffd90e3 | ||
|
|
dd8d2ea276 | ||
|
|
4c29b72242 | ||
|
|
ebe39771a6 | ||
|
|
63605ee42e | ||
|
|
b7f047db88 | ||
|
|
96289210d7 | ||
|
|
3b4a44abb1 | ||
|
|
e0797c83f8 | ||
|
|
30ffcc9246 | ||
|
|
a0945d2a2e | ||
|
|
c4cea9e71b | ||
|
|
35ec61726d | ||
|
|
f18a57b059 | ||
|
|
4ba180fbc4 | ||
|
|
0f34d407a4 | ||
|
|
4b29e9fbcb | ||
|
|
7f648720af | ||
|
|
58081c13cf | ||
|
|
78555f57a7 | ||
|
|
1ad40134dd | ||
|
|
9e16a405d8 | ||
|
|
9122a339cb | ||
|
|
56197274b6 | ||
|
|
6cf65ba82a | ||
|
|
aa59007409 | ||
|
|
66729e4c05 | ||
|
|
6387d32c2f | ||
|
|
48dbeb1e14 | ||
|
|
20452d7296 | ||
|
|
7daa67c792 | ||
|
|
4d9ba8d73b | ||
|
|
3650ef4805 | ||
|
|
be9154a015 | ||
|
|
0e00ca2350 | ||
|
|
2c0549a67a | ||
|
|
59e23e4296 | ||
|
|
3aa7a6e244 | ||
|
|
838c74a83b | ||
|
|
7f17bc9fb0 | ||
|
|
2dca6e8e7c | ||
|
|
a1bb3f9031 | ||
|
|
d1d4453120 | ||
|
|
48dcb02328 | ||
|
|
0a0cec6ef0 | ||
|
|
c3dd9f47fb | ||
|
|
5417f9b190 | ||
|
|
f03c873815 | ||
|
|
80adca5134 | ||
|
|
6efcac2a0e | ||
|
|
5162317298 | ||
|
|
90139b978c | ||
|
|
080fcb9c62 | ||
|
|
c437c2048d | ||
|
|
f80fcfd7c0 | ||
|
|
e9670aa5cb | ||
|
|
164b6e9070 | ||
|
|
5af18809a7 | ||
|
|
a719e8fda1 | ||
|
|
50fa8a0743 | ||
|
|
250f95deb6 | ||
|
|
bc98d82acf | ||
|
|
9e160d2431 | ||
|
|
77cff95c07 | ||
|
|
ecd780962f | ||
|
|
ab06597b7c | ||
|
|
b98a46b78c | ||
|
|
49e31707e5 | ||
|
|
92122e1fdd | ||
|
|
7238a65a6f | ||
|
|
e68ccf8532 | ||
|
|
220c52373a | ||
|
|
362fd86dbf | ||
|
|
8aba58188d | ||
|
|
41848dd99e | ||
|
|
f2fcfd6a47 | ||
|
|
33cdb906e2 | ||
|
|
19e73009b1 | ||
|
|
401e256d84 | ||
|
|
e1cd87433a | ||
|
|
23e5e1e3bd | ||
|
|
8f19f043a1 | ||
|
|
b1f07149c0 | ||
|
|
17acf18848 | ||
|
|
f52f589f7d | ||
|
|
e3e25bc233 | ||
|
|
a2f2ce1135 | ||
|
|
90152ab9f5 | ||
|
|
7a25f542e8 | ||
|
|
8c57250243 | ||
|
|
2e1eb1e224 | ||
|
|
b9a15e68de | ||
|
|
a9d2e03b65 | ||
|
|
d84b04dc35 | ||
|
|
7f3f082557 | ||
|
|
c810c68923 | ||
|
|
865a585c7f | ||
|
|
8703dd4229 | ||
|
|
d0c417b17d | ||
|
|
d09151d8f3 | ||
|
|
01a28860fe | ||
|
|
488b765f95 | ||
|
|
8a21b69fb0 | ||
|
|
eda3ed59df | ||
|
|
611ac0ab57 | ||
|
|
d485154a12 | ||
|
|
0caff7d5d5 | ||
|
|
6df7657c8e | ||
|
|
55d0f0bacd | ||
|
|
a74fac4cbf | ||
|
|
8aa58c0b11 | ||
|
|
4b9f8ab2b1 | ||
|
|
ba41533128 | ||
|
|
38069c6d63 | ||
|
|
1d66058240 | ||
|
|
43ac328e8a | ||
|
|
7519abf3fe | ||
|
|
849deae11e | ||
|
|
06a2b08780 | ||
|
|
4f9d3d8be5 | ||
|
|
ac3d658cb1 | ||
|
|
8f00191617 | ||
|
|
762391965e | ||
|
|
0fa3a2729d | ||
|
|
54640bb125 | ||
|
|
57d35a294d | ||
|
|
39069b322d | ||
|
|
540ce3a6ed | ||
|
|
ab22fb27ce | ||
|
|
0f564365f4 | ||
|
|
cccb7e4a39 | ||
|
|
1e837dec11 | ||
|
|
be3325ce35 | ||
|
|
c042d54714 | ||
|
|
77e2f95c66 | ||
|
|
403b416b2a | ||
|
|
b0901ccd01 | ||
|
|
6d2f8a97af | ||
|
|
bbd3324b79 | ||
|
|
5697b89817 | ||
|
|
6bf6bcb192 | ||
|
|
df74b31541 | ||
|
|
0b34bcdfa8 | ||
|
|
09a060a57b | ||
|
|
2a5147a0c1 | ||
|
|
4caa0e2f2e | ||
|
|
8f5c0af88e | ||
|
|
665d7eb831 | ||
|
|
d08b208bc6 | ||
|
|
c5d8117052 | ||
|
|
b769d18f73 | ||
|
|
b17760d228 | ||
|
|
2983c51db1 | ||
|
|
d6e11940cc | ||
|
|
17f15f546f | ||
|
|
688ef08cd5 | ||
|
|
8607a18a26 | ||
|
|
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 | ||
|
|
fe7962e10a |
No files matched your search
@@ -1,5 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-stage-3"
|
||||
]
|
||||
],
|
||||
"env": {
|
||||
"COVERAGE": {
|
||||
"plugins": [
|
||||
"istanbul"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
-19
@@ -1,16 +1,12 @@
|
||||
FROM quay.io/wekan/ubuntu:groovy-20201125.2
|
||||
FROM quay.io/wekan/ubuntu:groovy-20210115
|
||||
LABEL maintainer="sgr"
|
||||
|
||||
# 2020-12-03:
|
||||
# - Above Ubuntu base image copied from Docker Hub ubuntu:groovy-20201125.2
|
||||
# to Quay to avoid Docker Hub rate limits.
|
||||
|
||||
ENV BUILD_DEPS="gnupg gosu bsdtar wget curl bzip2 g++ build-essential python git ca-certificates iproute2"
|
||||
ENV BUILD_DEPS="gnupg gosu libarchive-tools wget curl bzip2 g++ build-essential python git ca-certificates iproute2"
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ENV \
|
||||
DEBUG=false \
|
||||
NODE_VERSION=v12.22.1 \
|
||||
NODE_VERSION=v12.22.3 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
@@ -134,23 +130,24 @@ ENV \
|
||||
SAML_PUBLIC_CERTFILE="" \
|
||||
SAML_IDENTIFIER_FORMAT="" \
|
||||
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
|
||||
SAML_ATTRIBUTES=""
|
||||
SAML_ATTRIBUTES="" \
|
||||
DEFAULT_WAIT_SPINNER=""
|
||||
|
||||
# Install OS
|
||||
RUN set -o xtrace \
|
||||
&& useradd --user-group -m --system --home-dir /home/wekan wekan \
|
||||
&& apt-get update \
|
||||
&& apt-get install --assume-yes --no-install-recommends apt-utils apt-transport-https ca-certificates 2>&1 \
|
||||
&& apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
|
||||
&& apt-get install --assume-yes --no-install-recommends apt-utils apt-transport-https ca-certificates 2>&1 \
|
||||
&& apt-get install --assume-yes --no-install-recommends ${BUILD_DEPS}
|
||||
|
||||
# Install NodeJS
|
||||
RUN set -o xtrace \
|
||||
&& cd /tmp \
|
||||
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-$ARCHITECTURE.tar.xz" \
|
||||
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
|
||||
&& grep " node-v$NODE_VERSION-$ARCHITECTURE.tar.xz\$" SHASUMS256.txt.asc | sha256sum -c - \
|
||||
&& tar -xJf "node-v$NODE_VERSION-$ARCHITECTURE.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
|
||||
&& rm "node-v$NODE_VERSION-$ARCHITECTURE.tar.xz" SHASUMS256.txt.asc \
|
||||
&& curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-$ARCHITECTURE.tar.xz" \
|
||||
&& curl -fsSLO --compressed "https://nodejs.org/dist/$NODE_VERSION/SHASUMS256.txt.asc" \
|
||||
&& grep " node-$NODE_VERSION-$ARCHITECTURE.tar.xz\$" SHASUMS256.txt.asc | sha256sum -c - \
|
||||
&& tar -xJf "node-$NODE_VERSION-$ARCHITECTURE.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
|
||||
&& rm "node-$NODE_VERSION-$ARCHITECTURE.tar.xz" SHASUMS256.txt.asc \
|
||||
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
|
||||
&& mkdir -p /usr/local/lib/node_modules/fibers/.node-gyp /root/.node-gyp/${NODE_VERSION} /home/wekan/.config \
|
||||
&& npm install -g npm@${NPM_VERSION} \
|
||||
@@ -172,17 +169,65 @@ RUN set -o xtrace \
|
||||
|
||||
ENV PATH=$PATH:/home/wekan/.meteor/
|
||||
|
||||
# Copy source dir
|
||||
USER root
|
||||
|
||||
RUN echo "export PATH=$PATH" >> /etc/environment
|
||||
|
||||
RUN set -o xtrace \
|
||||
&& mkdir /home/wekan/app
|
||||
USER wekan
|
||||
|
||||
COPY ${SRC_PATH} /home/wekan/app/
|
||||
# Copy source dir
|
||||
RUN set -o xtrace \
|
||||
&& mkdir -p /home/wekan/app/.meteor \
|
||||
&& mkdir -p /home/wekan/app/packages
|
||||
|
||||
COPY \
|
||||
.meteor/.finished-upgraders \
|
||||
.meteor/.id \
|
||||
.meteor/cordova-plugins \
|
||||
.meteor/packages \
|
||||
.meteor/platforms \
|
||||
.meteor/release \
|
||||
.meteor/versions \
|
||||
/home/wekan/app/.meteor/
|
||||
|
||||
COPY \
|
||||
package.json \
|
||||
settings.json \
|
||||
/home/wekan/app/
|
||||
|
||||
COPY \
|
||||
packages \
|
||||
/home/wekan/app/packages/
|
||||
|
||||
USER root
|
||||
|
||||
RUN set -o xtrace \
|
||||
&& chown -R wekan:wekan /home/wekan/app /home/wekan/.meteor
|
||||
|
||||
USER wekan
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \
|
||||
cd /home/wekan/.meteor && \
|
||||
/home/wekan/.meteor/meteor -- help;
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
# Build app
|
||||
cd /home/wekan/app && \
|
||||
/home/wekan/.meteor/meteor add standard-minifier-js && \
|
||||
/home/wekan/.meteor/meteor npm install && \
|
||||
/home/wekan/.meteor/meteor build --directory /home/wekan/app_build
|
||||
|
||||
RUN \
|
||||
set -o xtrace && \
|
||||
cd /home/wekan/app_build/bundle/programs/server/ && \
|
||||
chmod u+w package.json npm-shrinkwrap.json && \
|
||||
npm install
|
||||
|
||||
ENV PORT=3000
|
||||
EXPOSE $PORT
|
||||
WORKDIR /home/wekan/app
|
||||
|
||||
CMD ["/home/wekan/.meteor/meteor", "run", "--verbose", "--settings", "settings.json"]
|
||||
@@ -3,17 +3,18 @@ version: '3.7'
|
||||
services:
|
||||
|
||||
wekandb-dev:
|
||||
image: mongo:4.0.12
|
||||
image: mongo:4.4
|
||||
container_name: wekan-dev-db
|
||||
restart: unless-stopped
|
||||
command: mongod --smallfiles --oplogSize 128
|
||||
command: mongod --oplogSize 128
|
||||
networks:
|
||||
- wekan-dev-tier
|
||||
expose:
|
||||
- 27017
|
||||
volumes:
|
||||
- wekan-dev-db:/data/db
|
||||
- wekan-dev-db-dump:/dump
|
||||
- ./volumes/wekan-db:/data/db
|
||||
- ./volumes/wekan-db-dump:/dump
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
|
||||
wekan-dev:
|
||||
container_name: wekan-dev-app
|
||||
@@ -35,9 +36,13 @@ services:
|
||||
depends_on:
|
||||
- wekandb-dev
|
||||
volumes:
|
||||
- ..:/app:delegated
|
||||
command:
|
||||
sleep infinity
|
||||
- ../client:/home/wekan/app/client
|
||||
- ../models:/home/wekan/app/models
|
||||
- ../config:/home/wekan/app/config
|
||||
- ../i18n:/home/wekan/app/i18n
|
||||
- ../server:/home/wekan/app/server
|
||||
- ../public:/home/wekan/app/public
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
|
||||
volumes:
|
||||
wekan-dev-db:
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
*~
|
||||
*.swp
|
||||
.meteor-spk
|
||||
*.sublime-workspace
|
||||
tmp/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
.gitmodules
|
||||
.vscode/
|
||||
.idea/
|
||||
.build/*
|
||||
**/parts/
|
||||
**/stage
|
||||
**/prime
|
||||
**/*.snap
|
||||
snap/.snapcraft/
|
||||
.idea
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
.build*
|
||||
*.browserify.js.cached
|
||||
*.browserify.js.map
|
||||
.build*
|
||||
versions.json
|
||||
.versions
|
||||
.npm
|
||||
.build*
|
||||
._*
|
||||
.Trashes
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
.eslintcache
|
||||
.meteor/local
|
||||
.devcontainer/docker-compose.extend.yml
|
||||
.devcontainer/volumes*/
|
||||
.git
|
||||
@@ -81,7 +81,7 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.22.1
|
||||
node-engine: 12.22.3
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
|
||||
@@ -83,7 +83,7 @@ parts:
|
||||
wekan:
|
||||
source: .
|
||||
plugin: nodejs
|
||||
node-engine: 12.22.1
|
||||
node-engine: 12.22.3
|
||||
node-packages:
|
||||
- node-gyp
|
||||
- node-pre-gyp
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
## Issue
|
||||
|
||||
Note: With Docker, please don't use latest tag. Only use release tags.
|
||||
See https://github.com/wekan/wekan/issues/3874
|
||||
|
||||
If you can not login for any reason:
|
||||
- https://github.com/wekan/wekan/wiki/Forgot-Password
|
||||
|
||||
Email settings:
|
||||
- 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'
|
||||
@@ -0,0 +1,160 @@
|
||||
name: Test suite
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
# the following are optional jobs and need to be configured according
|
||||
# to this project's settings:
|
||||
#
|
||||
# lintcode:
|
||||
# name: Javascript lint
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: checkout
|
||||
# uses: actions/checkout@v2
|
||||
#
|
||||
# - name: setup node
|
||||
# uses: actions/setup-node@v1
|
||||
# with:
|
||||
# node-version: '12.x'
|
||||
#
|
||||
# - name: cache dependencies
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ~/.npm
|
||||
# key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-node-
|
||||
#
|
||||
# - run: npm install
|
||||
# - run: npm run lint:code
|
||||
#
|
||||
# lintstyle:
|
||||
# name: SCSS lint
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: [lintcode]
|
||||
# steps:
|
||||
# - name: checkout
|
||||
# uses: actions/checkout@v2
|
||||
#
|
||||
# - name: setup node
|
||||
# uses: actions/setup-node@v1
|
||||
# with:
|
||||
# node-version: '12.x'
|
||||
#
|
||||
# - name: cache dependencies
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ~/.npm
|
||||
# key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-node-
|
||||
# - run: npm install
|
||||
# - run: npm run lint:style
|
||||
#
|
||||
# lintdocs:
|
||||
# name: documentation lint
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: [lintcode,lintstyle]
|
||||
# steps:
|
||||
# - name: checkout
|
||||
# uses: actions/checkout@v2
|
||||
#
|
||||
# - name: setup node
|
||||
# uses: actions/setup-node@v1
|
||||
# with:
|
||||
# node-version: '12.x'
|
||||
#
|
||||
# - name: cache dependencies
|
||||
# uses: actions/cache@v1
|
||||
# with:
|
||||
# path: ~/.npm
|
||||
# key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-node-
|
||||
#
|
||||
# - run: npm install
|
||||
# - run: npm run lint:markdown
|
||||
|
||||
tests:
|
||||
name: Meteor ${{ matrix.meteor }} tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
# CHECKOUTS
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# CACHING
|
||||
- name: Install Meteor
|
||||
id: cache-meteor-install
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.meteor
|
||||
key: v1-meteor-${{ hashFiles('.meteor/versions') }}
|
||||
restore-keys: |
|
||||
v1-meteor-
|
||||
|
||||
- name: Cache NPM dependencies
|
||||
id: cache-meteor-npm
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: v1-npm-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
v1-npm-
|
||||
|
||||
- name: Cache Meteor build
|
||||
id: cache-meteor-build
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
.meteor/local/resolver-result-cache.json
|
||||
.meteor/local/plugin-cache
|
||||
.meteor/local/isopacks
|
||||
.meteor/local/bundler-cache/scanner
|
||||
key: v1-meteor_build_cache-${{ github.ref }}-${{ github.sha }}
|
||||
restore-key: |
|
||||
v1-meteor_build_cache-
|
||||
|
||||
- name: Setup meteor
|
||||
uses: meteorengineer/setup-meteor@v1
|
||||
with:
|
||||
meteor-release: '2.2'
|
||||
|
||||
- name: Install NPM Dependencies
|
||||
run: meteor npm ci
|
||||
|
||||
- name: Run Tests
|
||||
run: sh ./test-wekan.sh -cv
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: coverage-folder
|
||||
path: .coverage/
|
||||
|
||||
coverage:
|
||||
name: Coverage report
|
||||
runs-on: ubuntu-latest
|
||||
needs: [tests]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Download coverage
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: coverage-folder
|
||||
path: .coverage/
|
||||
|
||||
|
||||
- name: Coverage Report
|
||||
uses: VeryGoodOpenSource/very_good_coverage@v1.1.1
|
||||
with:
|
||||
path: ".coverage/lcov.info"
|
||||
min_coverage: 1 # TODO add tests and increase to 95!
|
||||
@@ -32,3 +32,5 @@ ehthumbs.db
|
||||
.eslintcache
|
||||
.meteor/local
|
||||
.devcontainer/docker-compose.extend.yml
|
||||
.devcontainer/volumes*/
|
||||
.coverage
|
||||
+7
-5
@@ -17,7 +17,7 @@ es5-shim@4.8.0
|
||||
|
||||
# Collections
|
||||
aldeed:collection2
|
||||
cfs:standard-packages
|
||||
wekan-cfs-standard-packages
|
||||
cottz:publish-relations
|
||||
dburles:collection-helpers
|
||||
idmontie:migrations
|
||||
@@ -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
|
||||
@@ -83,7 +83,6 @@ mquandalle:moment
|
||||
msavin:usercache
|
||||
# Keep stylus in 1.1.0, because building v2 takes extra 52 minutes.
|
||||
coagmano:stylus@1.1.0!
|
||||
lucasantoniassi:accounts-lockout
|
||||
meteorhacks:subs-manager
|
||||
meteorhacks:picker
|
||||
lamhieu:unblock
|
||||
@@ -91,8 +90,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
|
||||
@@ -145,3 +143,7 @@ rajit:bootstrap3-datepicker-zh-tw
|
||||
staringatlights:fast-render
|
||||
spacebars
|
||||
easylogic:summernote
|
||||
pascoual:pdfkit
|
||||
wekan-accounts-lockout
|
||||
lmieulet:meteor-coverage
|
||||
meteortesting:mocha
|
||||
+45
-40
@@ -1,7 +1,7 @@
|
||||
3stack:presence@1.1.2
|
||||
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
|
||||
@@ -15,32 +15,15 @@ babel-compiler@7.6.1
|
||||
babel-runtime@1.5.0
|
||||
base64@1.0.12
|
||||
binary-heap@1.0.11
|
||||
blaze@2.4.0
|
||||
blaze-tools@1.1.0
|
||||
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.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,18 +32,18 @@ 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.1
|
||||
ecmascript-runtime@0.7.0
|
||||
ecmascript-runtime-client@0.11.0
|
||||
ecmascript-runtime-server@0.10.0
|
||||
ecmascript-runtime-client@0.11.1
|
||||
ecmascript-runtime-server@0.10.1
|
||||
ejson@1.1.1
|
||||
email@2.0.0
|
||||
es5-shim@4.8.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.1.0
|
||||
htmljs@1.1.0
|
||||
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
|
||||
@@ -86,9 +69,9 @@ lamhieu:meteorx@2.1.1
|
||||
lamhieu:unblock@1.0.0
|
||||
launch-screen@1.2.1
|
||||
livedata@1.0.18
|
||||
lmieulet:meteor-coverage@3.2.0
|
||||
localstorage@1.2.0
|
||||
logging@1.2.0
|
||||
lucasantoniassi:accounts-lockout@1.0.0
|
||||
matb33:collection-hooks@0.9.1
|
||||
matteodem:easy-search@1.6.4
|
||||
mdg:validation-error@0.5.1
|
||||
@@ -100,7 +83,10 @@ 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
|
||||
meteortesting:browser-tests@1.3.4
|
||||
meteortesting:mocha@2.0.1
|
||||
meteortesting:mocha-core@8.0.1
|
||||
minifier-css@1.5.4
|
||||
minifier-js@2.6.0
|
||||
minifiers@1.1.8-faster-rebuild.0
|
||||
minimongo@1.6.2
|
||||
@@ -109,10 +95,10 @@ modern-browsers@0.1.5
|
||||
modules@0.16.0
|
||||
modules-runtime@0.12.0
|
||||
momentjs:moment@2.29.1
|
||||
mongo@1.11.0
|
||||
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
|
||||
@@ -124,13 +110,14 @@ mquandalle:jquery-ui-drag-drop-sort@0.2.0
|
||||
mquandalle:moment@1.0.1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
msavin:usercache@1.8.0
|
||||
npm-bcrypt@0.9.3
|
||||
npm-bcrypt@0.9.4
|
||||
npm-mongo@3.9.0
|
||||
oauth@1.3.2
|
||||
oauth2@1.3.0
|
||||
observe-sequence@1.0.16
|
||||
ongoworks:speakingurl@1.1.0
|
||||
ordered-dict@1.1.0
|
||||
pascoual:pdfkit@1.0.7
|
||||
peerlibrary:assert@0.3.0
|
||||
peerlibrary:base-component@0.16.0
|
||||
peerlibrary:blaze-components@0.15.1
|
||||
@@ -191,7 +178,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 +195,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.1.0
|
||||
spacebars-compiler@1.2.0
|
||||
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
|
||||
@@ -221,15 +208,14 @@ steffo:meteor-accounts-saml@0.0.18
|
||||
tap:i18n@1.8.2
|
||||
templates:tabs@2.3.0
|
||||
templating@1.4.0
|
||||
templating-compiler@1.4.0
|
||||
templating-compiler@1.4.1
|
||||
templating-runtime@1.4.0
|
||||
templating-tools@1.2.0
|
||||
tmeasday:check-npm-versions@0.3.2
|
||||
tracker@1.2.0
|
||||
twbs:bootstrap@3.3.6
|
||||
ui@1.0.13
|
||||
underscore@1.0.10
|
||||
url@1.3.1
|
||||
url@1.3.2
|
||||
useraccounts:core@1.14.2
|
||||
useraccounts:flow-routing@1.14.2
|
||||
useraccounts:unstyled@1.14.2
|
||||
@@ -237,7 +223,26 @@ verron:autosize@3.0.8
|
||||
webapp@1.10.1
|
||||
webapp-hashing@1.1.0
|
||||
wekan-accounts-cas@0.1.0
|
||||
wekan-accounts-lockout@1.0.0
|
||||
wekan-accounts-oidc@1.0.10
|
||||
wekan-cfs-access-point@0.1.50
|
||||
wekan-cfs-base-package@0.0.30
|
||||
wekan-cfs-collection@0.5.5
|
||||
wekan-cfs-collection-filters@0.2.4
|
||||
wekan-cfs-data-man@0.0.6
|
||||
wekan-cfs-file@0.1.17
|
||||
wekan-cfs-filesystem@0.1.2
|
||||
wekan-cfs-gridfs@0.0.34
|
||||
wekan-cfs-http-methods@0.0.32
|
||||
wekan-cfs-http-publish@0.0.13
|
||||
wekan-cfs-power-queue@0.9.11
|
||||
wekan-cfs-reactive-list@0.0.9
|
||||
wekan-cfs-reactive-property@0.0.4
|
||||
wekan-cfs-standard-packages@0.5.10
|
||||
wekan-cfs-storage-adapter@0.2.4
|
||||
wekan-cfs-tempstore@0.1.6
|
||||
wekan-cfs-upload-http@0.0.21
|
||||
wekan-cfs-worker@0.1.5
|
||||
wekan-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.1
|
||||
TRAVIS_NODE_VERSION: 12.22.3
|
||||
TRAVIS_NPM_VERSION: latest
|
||||
|
||||
before_install:
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ host = https://www.transifex.com
|
||||
# tap:i18n requires us to use `-` separator in the language identifiers whereas
|
||||
# Transifex uses a `_` separator, without an option to customize it on one side
|
||||
# or the other, so we need to do a Manual mapping.
|
||||
lang_map = ar_EG:ar-EG, bg_BG:bg, de_CH:de-CH, en_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_IT:en-IT, en_GB:en-GB, es_AR:es-AR, es_CL:es-CL, es_419:es-LA, es_PE:es-PE, es_MX:es-MX, es_TX:es-TX, es_PY:es-PY, el_GR:el, fa_IR:fa-IR, fi_FI:fi, hu_HU:hu, id_ID:id, mn_MN:mn, lv_LV:lv, pt_BR:pt-BR, ro_RO:ro, sl_SI:sl, zh_CN:zh-CN, zh_TW:zh-TW, zh_HK:zh-HK
|
||||
|
||||
[wekan.application]
|
||||
file_filter = i18n/<lang>.i18n.json
|
||||
|
||||
+447
-3
@@ -1,10 +1,454 @@
|
||||
[Mac ChangeLog](https://github.com/wekan/wekan/wiki/Mac)
|
||||
|
||||
Note: With Docker, please don't use latest tag. Only use release tags.
|
||||
See https://github.com/wekan/wekan/issues/3874
|
||||
|
||||
# v5.41 2021-07-26 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fixed Wekan JSON import fails](https://github.com/wekan/wekan/commit/0fa8fc8b506831a60649d100ce93c96f8f7b76f5).
|
||||
Thanks to BartoszBroda and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.40 2021-07-25 Wekan release
|
||||
|
||||
This release fixes the following bugs:
|
||||
|
||||
- [Fixed Delete of Planning Poker doesn't work](https://github.com/wekan/wekan/commit/18b6381d790848062752b211d48c84a2de268bd0).
|
||||
Thanks to xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.39 2021-07-24 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [LDAP AD Simple Auth](https://github.com/wekan/wekan/pull/3909).
|
||||
Thanks to indika-dev.
|
||||
- [LDAP AD Simple Auth: Added settings for all remaining Wekan Standalone (non-Sandstorm)
|
||||
platforms](https://github.com/wekan/wekan/commit/fe40f35d6d9b6293f3bdbf5bc0f3e8e708c59518)
|
||||
and Docs to https://github.com/wekan/wekan/wiki/LDAP-AD-Simple-Auth .
|
||||
Thanks to xet7.
|
||||
- [Convert Checklist Item to Card](https://github.com/wekan/wekan/pull/3910).
|
||||
Thanks to helioguardabaxo.
|
||||
- [Organizations and Teams are taken into account when displaying board at
|
||||
`All Boards` page](https://github.com/wekan/wekan/pull/3912).
|
||||
Thanks to Emile840.
|
||||
|
||||
and adds the following new translations:
|
||||
|
||||
- [English (Italy)](https://github.com/wekan/wekan/commit/ae73029dda63db361925e378202e6f8c137cf5bd).
|
||||
Thanks to translators.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.38 2021-07-18 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Added Code View `</>` button when `RICHER_CARD_COMMENT_EDITOR=true` and in desktop view
|
||||
where is enough screen space for buttons (not added to mobile
|
||||
view)](https://github.com/wekan/wekan/commit/ec01e5182d6b8c848d752540887a8113472b0226).
|
||||
Thanks to xet7.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- Updated dependencies
|
||||
[Part 1](https://github.com/wekan/wekan/commit/7024929881c05cad472de74c86517cf80c8e240c),
|
||||
[Part 2](https://github.com/wekan/wekan/commit/609adcdf100db226c5f310577195afa4b1a4aead).
|
||||
Thanks to developers of dependencies.
|
||||
- [Updated to Node.js v12.22.3](https://github.com/wekan/wekan/commit/d538a01d1962464cf4cb001462669150eeafaa99).
|
||||
Thanks to Node.js developers.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fixed Line break which is wrongly added in Cards description and Cards
|
||||
comments](https://github.com/wekan/wekan/commit/ec01e5182d6b8c848d752540887a8113472b0226).
|
||||
Thanks to Emile840 and xet7.
|
||||
- [Fixed rebuild-wekan.sh](https://github.com/wekan/wekan/commit/1d5dd5e60fec151de6c7dce7ef4e758b562923b9).
|
||||
Thanks to xet7.
|
||||
- [Small fixes for ModernDark theme](https://github.com/wekan/wekan/pull/3902).
|
||||
Thanks to jghaanstra.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.37 2021-07-04 Wekan release
|
||||
|
||||
This release adds the following CRITICAL SECURITY UPDATES:
|
||||
|
||||
- [Updated to Node.js v12.22.2](https://github.com/wekan/wekan/commit/4feffd90e3f466609e09524e0ddccdafa2faef32).
|
||||
Thanks to Node.js developers.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Building OpenAPI docs is broken in Wekan v3.56](https://github.com/wekan/wekan/pull/3889).
|
||||
Thanks to bentiss.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.36 2021-06-29 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Added some controls and warning messages when user try to delete an organization or team that has
|
||||
at least one user belongs to it](https://github.com/wekan/wekan/pull/3865).
|
||||
Thanks to Emile840.
|
||||
- Shared Templates. In Progress.
|
||||
[Part 1](https://github.com/wekan/wekan/commit/0a0cec6ef0eb55391608aade897004db430ba10a).
|
||||
Template Containers visible at All Boards page, with white border around board icon.
|
||||
[Part 2](https://github.com/wekan/wekan/commit/d1d4453120005de61eaf2cbadc6a7b9d80e75fc1).
|
||||
Ablity to Add Template Container, checkbox in Create Board popup.
|
||||
Do not create Template Container by default, when creating user.
|
||||
[Part 3](https://github.com/wekan/wekan/commit/7f17bc9fb03d6f4b43a2cd71ecc372e0f1b0f491).
|
||||
Template container titles "Card/List/Board Templates" automatically translated.
|
||||
Thanks to xet7.
|
||||
[Part 4](https://github.com/wekan/wekan/commit/3b4a44abb1c1c4339c3d1b00dfac1c69ec3684cd).
|
||||
Hide this Shared Templates feature while it's not finished yet.
|
||||
Added back creating Template Container by default, when creating user.
|
||||
Thanks to xet7.
|
||||
- [Added testsuite](https://github.com/wekan/wekan/pull/3872).
|
||||
Thanks to jankapunkt.
|
||||
- [Delete user at REST API and `Admin Panel/People/People`](https://github.com/wekan/wekan/commit/9e16a405d8ca32a4e1be9cf89f8f978a2985593c).
|
||||
There is still bug of leaving empty user avatars to boards: boards members, card members and assignees have
|
||||
empty users. So it would be better to delete user from all boards before deleting user.
|
||||
Thanks to darren-teo and xet7.
|
||||
|
||||
and adds the following improvements:
|
||||
|
||||
- [Removed unused exceljs from client bundle](https://github.com/wekan/wekan/pull/3871).
|
||||
This decreased Wekan browserside frontend amount of Javascript from 5.4 MB to 4.3 MB.
|
||||
Thanks to jankapunkt.
|
||||
- Added note: With Docker, please don't use latest tag. Only use release tags.
|
||||
See https://github.com/wekan/wekan/issues/3874 .
|
||||
[Part 1](https://github.com/wekan/wekan/commit/f18a57b059994b8a6a3588a69cf095fe599b3a90),
|
||||
[Part 2](https://github.com/wekan/wekan/commit/c4cea9e71b467731fd8290538dd039b7691097af).
|
||||
Thanks to xet7.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fixed tests, that need to be in tests directory to not get build
|
||||
errors](https://github.com/wekan/wekan/commit/56197274b6c4782fa20c7d9b5b9d58255d1f830a).
|
||||
Thanks to xet7.
|
||||
- Try to fix tests.
|
||||
[Part 1](https://github.com/wekan/wekan/commit/78555f57a7c2ba0fb3e3986608bcf11509af9a21),
|
||||
[Part 2](https://github.com/wekan/wekan/commit/7f648720afa42a2b53bfdee7e709fd891eb33373),
|
||||
[Part 3](https://github.com/wekan/wekan/commit/0f34d407a43c8a63d882e69ea64ea17fc4b22c7b).
|
||||
Thanks to xet7.
|
||||
- [Fixed "Search All Boards" instructions are gone](https://github.com/wekan/wekan/commit/30ffcc924663f39406b250d93b14384a2f38ab6a).
|
||||
Thanks to ClaudiaK21 and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.35 2021-06-14 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- Wait Spinners can now be translated
|
||||
[Part 1](https://github.com/wekan/wekan/commit/8703dd42296d531450eb21a3d3adea17558a8500),
|
||||
[Part 1](https://github.com/wekan/wekan/commit/7f3f0825573b1f8a7b0388e4bacbb0bd2525e886).
|
||||
Added Wait Spinners docs: https://github.com/wekan/wekan/wiki/Wait-Spinners .
|
||||
Thanks to xet7.
|
||||
- Maximize Card.
|
||||
[Part 1](https://github.com/wekan/wekan/commit/8c572502436a2eb22bd1eb1e4069c1c9145e2070),
|
||||
[Part 2](https://github.com/wekan/wekan/pull/3863).
|
||||
Thanks to mfilser and xet7.
|
||||
- Export Card to PDF. In Progress, does not work yet.
|
||||
[Part 1](https://github.com/wekan/wekan/commit/a2f2ce11354a8dbfdd6759e3b65797e4be4cc6ec),
|
||||
[Part 2](https://github.com/wekan/wekan/commit/17acf1884850d8d95ae79493289adf18966df652).
|
||||
Thanks to xet7.
|
||||
|
||||
and removes some not needed files:
|
||||
|
||||
- [Reduced Wekan bundle size from 636 MB to 467 MB by deleting all dependencies of lucasantoniassi:accounts-lockout and including
|
||||
only required 10 files](https://github.com/wekan/wekan/commit/23e5e1e3bd081699ce39ce5887db7e612616014d).
|
||||
Wekan Docker image size changed from 269.6 MB to 165.1 MB.
|
||||
Thanks to xet7.
|
||||
|
||||
and adds the following improvements:
|
||||
|
||||
- [Add border and update label colors for better visibility](https://github.com/wekan/wekan/commit/2e1eb1e224c83f16a384316626d7a4183639d4cd).
|
||||
Thanks to xet7.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
- [Updated dependencies](https://github.com/wekan/wekan/commit/f80fcfd7c0a83f4181c7a0b8beb52da9ba1446d3).
|
||||
Thanks to developers of dependencies.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Manual sort number 0 accepted](https://github.com/wekan/wekan/pull/3861).
|
||||
Thanks to mfilser.
|
||||
- Allow board members to use more of API. Please add issue (or pull request) if this allows too much.
|
||||
[Part 1](https://github.com/wekan/wekan/commit/a719e8fda1f78bcbf9af6e7b4341f8be1d141e90),
|
||||
[Part 2](https://github.com/wekan/wekan/commit/164b6e9070199dca36d12fa3048d6b22bf6850b0).
|
||||
Thanks to JayVii and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# v5.34 2021-06-11 Wekan release
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [View and change card sort number](https://github.com/wekan/wekan/pull/3857).
|
||||
Thanks to mfilser.
|
||||
- [More spinners + configureable in admin panel](https://github.com/wekan/wekan/pull/3858).
|
||||
Thanks to mfilser.
|
||||
- [Added remaining spinner settings](https://github.com/wekan/wekan/commit/488b765f95ad67b19630cd125543836c04eaa24f).
|
||||
Thanks to xet7.
|
||||
|
||||
and adds the following new improvements:
|
||||
|
||||
- [Card Description has now the same color on view and editing](https://github.com/wekan/wekan/pull/3851).
|
||||
Thanks to mfilser.
|
||||
- [Development in docker container](https://github.com/wekan/wekan/pull/3852).
|
||||
Thanks to mfilser.
|
||||
|
||||
and fixes the following bugs:
|
||||
|
||||
- [Fix Google SSO to access Wekan has not been working by reverting Wekan v5.31 not-working fixes
|
||||
to OAUTH2_LOGIN_STYLE=redirect Has No Effect](https://github.com/wekan/wekan/commit/1e837dec11dc5cb266b83efcff4f462aa02d733d).
|
||||
Thanks to unpokitodxfavor and xet7.
|
||||
- [CustomFields were not created after adding 1 card](https://github.com/wekan/wekan/pull/3856).
|
||||
Thanks to mfilser.
|
||||
- [Try to fix BUG: Database error attempting to change a account](https://github.com/wekan/wekan/commit/762391965e6ae3cd5682d5b164131500e7d92338).
|
||||
Thanks to bbyszio and xet7.
|
||||
|
||||
Thanks to above GitHub users for their contributions and translators for their translations.
|
||||
|
||||
# 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 2](https://github.com/wekan/wekan/commit/dce99c00be80cceba686fd73b4b78b6c778d78a6),
|
||||
[Part 3](https://github.com/wekan/wekan/commit/6ff9c5b58d25ba52b11e5429c9cfe6ed6a97000e).
|
||||
Thanks to translators.
|
||||
|
||||
@@ -19,11 +463,11 @@ Thanks to above GitHub users for their contributions and translators for their t
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
- [Summernote Rich Text Editor:
|
||||
- [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](https://github.com/wekan/wekan/pull/3720).
|
||||
4) Allow client side grammerly extension.
|
||||
Thanks to ryanMushy.
|
||||
|
||||
and adds the following updates:
|
||||
|
||||
+6
-4
@@ -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.1 \
|
||||
NODE_VERSION=v12.22.3 \
|
||||
METEOR_RELEASE=1.10.2 \
|
||||
USE_EDGE=false \
|
||||
METEOR_EDGE=1.5-beta.17 \
|
||||
@@ -64,6 +64,9 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
LDAP_ENABLE=false \
|
||||
LDAP_PORT=389 \
|
||||
LDAP_HOST="" \
|
||||
LDAP_AD_SIMPLE_AUTH="" \
|
||||
LDAP_USER_AUTHENTICATION=false \
|
||||
LDAP_USER_AUTHENTICATION_FIELD=uid \
|
||||
LDAP_BASEDN="" \
|
||||
LDAP_LOGIN_FALLBACK=false \
|
||||
LDAP_RECONNECT=true \
|
||||
@@ -81,8 +84,6 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
LDAP_ENCRYPTION=false \
|
||||
LDAP_CA_CERT="" \
|
||||
LDAP_REJECT_UNAUTHORIZED=false \
|
||||
LDAP_USER_AUTHENTICATION=false \
|
||||
LDAP_USER_AUTHENTICATION_FIELD=uid \
|
||||
LDAP_USER_SEARCH_FILTER="" \
|
||||
LDAP_USER_SEARCH_SCOPE="" \
|
||||
LDAP_USER_SEARCH_FIELD="" \
|
||||
@@ -137,7 +138,8 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
|
||||
SAML_IDENTIFIER_FORMAT="" \
|
||||
SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
|
||||
SAML_ATTRIBUTES="" \
|
||||
ORACLE_OIM_ENABLED=false
|
||||
ORACLE_OIM_ENABLED=false \
|
||||
WAIT_SPINNER=""
|
||||
|
||||
# Copy the app to the image
|
||||
COPY ${SRC_PATH} /home/wekan/app
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ 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.1 \
|
||||
NODE_VERSION=v12.22.3 \
|
||||
WEKAN_VERSION=latest \
|
||||
WEKAN_ARCHITECTURE=arm64
|
||||
|
||||
@@ -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.1 \
|
||||
NODE_VERSION=v12.22.3 \
|
||||
NODE_ENV=production \
|
||||
NPM_VERSION=latest \
|
||||
WITH_API=true \
|
||||
|
||||
@@ -30,6 +30,11 @@ It's better than at chat where details get lost when chat scrolls up.
|
||||
|
||||
[Wekan IRC FAQ](https://github.com/wekan/wekan/wiki/IRC-FAQ)
|
||||
|
||||
## Docker: Please only use Docker release tags
|
||||
|
||||
Note: With Docker, please don't use latest tag. Only use release tags.
|
||||
See https://github.com/wekan/wekan/issues/3874
|
||||
|
||||
## FAQ
|
||||
|
||||
**NOTE**:
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
|
||||
appVersion: "v5.22.0"
|
||||
appVersion: "v5.41.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 }),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -766,7 +766,15 @@ setBoardClear(color1,color2)
|
||||
.toggle-label:after, .board-color-modern .toggle-switch:checked~.toggle-label:after
|
||||
background-color: #819C5D !important
|
||||
|
||||
button, input:not([type=file]), select, textarea
|
||||
border-radius: 2px
|
||||
|
||||
/* Headers */
|
||||
&#header
|
||||
background-color: #262626
|
||||
border-bottom: 1px solid #555555;
|
||||
border-top: 1px solid #555555;
|
||||
|
||||
&#header-quick-access, .background-box, #header
|
||||
background-color: #333333
|
||||
|
||||
@@ -774,14 +782,16 @@ setBoardClear(color1,color2)
|
||||
padding: 4px
|
||||
font-size: 14px
|
||||
|
||||
&#header-quick-access .allBoards
|
||||
padding: 5px 10px 0 10px;
|
||||
|
||||
&#header-quick-access ul.header-quick-access-list
|
||||
margin: -5px 0 -5px 0
|
||||
|
||||
&#header #header-main-bar
|
||||
height: 30px
|
||||
padding-top: 3px
|
||||
padding-bottom: 3px
|
||||
|
||||
&#header
|
||||
box-shadow: 0 6px 6px -6px rgba(0,0,0,0.8)
|
||||
|
||||
&#header-quick-access ul
|
||||
overflow: visible
|
||||
|
||||
@@ -812,18 +822,23 @@ setBoardClear(color1,color2)
|
||||
/* Content */
|
||||
.board-canvas
|
||||
background: #2a2a2a
|
||||
padding: 10px 10px 0
|
||||
|
||||
/* Swimlanes */
|
||||
.swimlane .swimlane-header-wrap
|
||||
background-color: #666666
|
||||
background-color: #494949
|
||||
color: #cccccc
|
||||
padding: 4px 0
|
||||
margin-bottom: 10px
|
||||
|
||||
.swimlane .swimlane-header-wrap .swimlane-header
|
||||
font-family: Poppins
|
||||
|
||||
.swimlane .swimlane-header-wrap .swimlane-header-menu
|
||||
padding: 6px
|
||||
font-size: 16px
|
||||
|
||||
.swimlane .swimlane-header-wrap .swimlane-header-plus-icon
|
||||
font-size: 16px
|
||||
|
||||
.swimlane
|
||||
background: #2a2a2a
|
||||
line-height: 15px
|
||||
@@ -836,13 +851,19 @@ setBoardClear(color1,color2)
|
||||
border: 0px solid #666666
|
||||
flex: 0 0 265px;
|
||||
|
||||
.swimlane .list:first-child
|
||||
margin-left: 0
|
||||
|
||||
.swimlane .list:nth-child(even)
|
||||
background: #5f5f5f
|
||||
|
||||
.swimlane .list:nth-child(odd) .list-header
|
||||
background: #3b3b3b
|
||||
|
||||
.list-header
|
||||
background: #333333
|
||||
padding-top: 10px
|
||||
border-bottom: 6px solid #333333
|
||||
padding: 10px
|
||||
border-bottom: 0
|
||||
|
||||
.list-header .viewer
|
||||
padding-left: 10px
|
||||
@@ -852,7 +873,11 @@ setBoardClear(color1,color2)
|
||||
color: #eeeeee
|
||||
|
||||
.list-header .list-header-menu
|
||||
padding-top: 17px
|
||||
padding: 10px
|
||||
top: 0
|
||||
|
||||
.list-header .list-header-plus-icon
|
||||
color: #a6a6a6
|
||||
|
||||
.list-body
|
||||
scrollbar-width: thin
|
||||
@@ -883,35 +908,43 @@ setBoardClear(color1,color2)
|
||||
color: #ffffff
|
||||
|
||||
/* Mini Card */
|
||||
.minicard-wrapper
|
||||
margin-bottom: 12px
|
||||
|
||||
.minicard
|
||||
background-color: #444444
|
||||
color: #cccccc
|
||||
border-radius: 2px
|
||||
font-size: 0.9em
|
||||
font-size: 0.95em
|
||||
padding: 10px
|
||||
box-shadow: 0 4px 3px -3px rgba(0,0,0,0.8)
|
||||
border-bottom: 1px solid #666666
|
||||
|
||||
.minicard:hover
|
||||
background-color: #555555 !important
|
||||
background-color: #494949 !important
|
||||
|
||||
.minicard .minicard-labels
|
||||
margin-bottom: 4px
|
||||
|
||||
.minicard .card-label
|
||||
font-size: 11px
|
||||
font-weight: 400
|
||||
padding: 2px 6px 0
|
||||
padding: 1px 6px 0
|
||||
border-radius: 2px
|
||||
|
||||
.minicard .badges
|
||||
color: #bbbbbb
|
||||
|
||||
.minicard .date
|
||||
margin-top: 7px
|
||||
margin-top: 10px
|
||||
font-size: 11px
|
||||
|
||||
.card-date
|
||||
color: #555555
|
||||
color: #444444
|
||||
border-radius: 2px
|
||||
|
||||
.card-date.almost-due
|
||||
color: #666666
|
||||
color: #444444
|
||||
|
||||
.minicard.minicard-composer textarea.minicard-composer-textarea:focus
|
||||
background-color: #eeeeee
|
||||
@@ -970,11 +1003,17 @@ setBoardClear(color1,color2)
|
||||
|
||||
.card-details .checklist-item
|
||||
background-color: rgba(255,255,255,0.1)
|
||||
padding: 2px 8px
|
||||
border-radius: 3px
|
||||
padding: 4px 8px
|
||||
border-radius: 2px
|
||||
font-size: 13px
|
||||
margin-top: 5px
|
||||
|
||||
.card-details .checklist-item:hover
|
||||
background-color: rgba(255,255,255,0.2)
|
||||
|
||||
.card-details .checklist-item .item-title .viewer p
|
||||
max-width: auto
|
||||
|
||||
.card-details .check-box.materialCheckBox
|
||||
border-color: #ffffff
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
template(name="boardHeaderBar")
|
||||
h1.header-board-menu
|
||||
with currentBoard
|
||||
+viewer
|
||||
= title
|
||||
if $eq title 'Templates'
|
||||
| {{_ 'templates'}}
|
||||
else
|
||||
+viewer
|
||||
= title
|
||||
|
||||
.board-header-btns.left
|
||||
unless isMiniScreen
|
||||
@@ -131,7 +134,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")
|
||||
@@ -225,6 +228,9 @@ template(name="createBoard")
|
||||
= " "
|
||||
| {{{_ 'board-private-info'}}}
|
||||
a.js-change-visibility {{_ 'change'}}.
|
||||
//a.flex.js-toggle-add-template-container
|
||||
// .materialCheckBox#add-template-container
|
||||
// span {{_ 'add-template-container'}}
|
||||
input.primary.wide(type="submit" value="{{_ 'create'}}")
|
||||
span.quiet
|
||||
| {{_ 'or'}}
|
||||
|
||||
@@ -209,24 +209,79 @@ const CreateBoard = BlazeComponent.extendComponent({
|
||||
this.visibilityMenuIsOpen.set(!this.visibilityMenuIsOpen.get());
|
||||
},
|
||||
|
||||
toggleAddTemplateContainer() {
|
||||
$('#add-template-container').toggleClass('is-checked');
|
||||
},
|
||||
|
||||
onSubmit(event) {
|
||||
event.preventDefault();
|
||||
const title = this.find('.js-new-board-title').value;
|
||||
const visibility = this.visibility.get();
|
||||
|
||||
this.boardId.set(
|
||||
Boards.insert({
|
||||
title,
|
||||
permission: visibility,
|
||||
}),
|
||||
);
|
||||
const addTemplateContainer = $('#add-template-container.is-checked').length > 0;
|
||||
if (addTemplateContainer) {
|
||||
//const templateContainerId = Meteor.call('setCreateTemplateContainer');
|
||||
//Utils.goBoardId(templateContainerId);
|
||||
//alert('niinku template ' + Meteor.call('setCreateTemplateContainer'));
|
||||
|
||||
Swimlanes.insert({
|
||||
title: 'Default',
|
||||
boardId: this.boardId.get(),
|
||||
});
|
||||
this.boardId.set(
|
||||
Boards.insert({
|
||||
// title: TAPi18n.__('templates'),
|
||||
title: title,
|
||||
permission: 'private',
|
||||
type: 'template-container',
|
||||
}),
|
||||
);
|
||||
|
||||
Utils.goBoardId(this.boardId.get());
|
||||
// Insert the card templates swimlane
|
||||
Swimlanes.insert({
|
||||
// title: TAPi18n.__('card-templates-swimlane'),
|
||||
title: 'Card Templates',
|
||||
boardId: this.boardId.get(),
|
||||
sort: 1,
|
||||
type: 'template-container',
|
||||
}),
|
||||
|
||||
// Insert the list templates swimlane
|
||||
Swimlanes.insert(
|
||||
{
|
||||
// title: TAPi18n.__('list-templates-swimlane'),
|
||||
title: 'List Templates',
|
||||
boardId: this.boardId.get(),
|
||||
sort: 2,
|
||||
type: 'template-container',
|
||||
},
|
||||
);
|
||||
|
||||
// Insert the board templates swimlane
|
||||
Swimlanes.insert(
|
||||
{
|
||||
//title: TAPi18n.__('board-templates-swimlane'),
|
||||
title: 'Board Templates',
|
||||
boardId: this.boardId.get(),
|
||||
sort: 3,
|
||||
type: 'template-container',
|
||||
},
|
||||
);
|
||||
|
||||
Utils.goBoardId(this.boardId.get());
|
||||
|
||||
} else {
|
||||
const visibility = this.visibility.get();
|
||||
|
||||
this.boardId.set(
|
||||
Boards.insert({
|
||||
title,
|
||||
permission: visibility,
|
||||
}),
|
||||
);
|
||||
|
||||
Swimlanes.insert({
|
||||
title: 'Default',
|
||||
boardId: this.boardId.get(),
|
||||
});
|
||||
|
||||
Utils.goBoardId(this.boardId.get());
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
@@ -240,6 +295,7 @@ const CreateBoard = BlazeComponent.extendComponent({
|
||||
submit: this.onSubmit,
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
'click .js-board-template': Popup.open('searchElement'),
|
||||
'click .js-toggle-add-template-container': this.toggleAddTemplateContainer,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
@@ -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
|
||||
@@ -16,47 +17,90 @@ template(name="boardList")
|
||||
button.js-accept-invite.primary {{_ 'accept'}}
|
||||
button.js-decline-invite {{_ 'decline'}}
|
||||
else
|
||||
a.js-open-board.board-list-item(href="{{pathFor 'board' id=_id slug=slug}}")
|
||||
span.details
|
||||
span.board-list-item-name
|
||||
+viewer
|
||||
= title
|
||||
i.fa.js-star-board(
|
||||
class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
|
||||
title="{{_ 'star-board-title'}}")
|
||||
p.board-list-item-desc
|
||||
+viewer
|
||||
= description
|
||||
if hasSpentTimeCards
|
||||
i.fa.js-has-spenttime-cards(
|
||||
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
|
||||
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
|
||||
if isMiniScreen
|
||||
i.fa.board-handle(
|
||||
class="fa-arrows"
|
||||
title="{{_ 'Drag board'}}")
|
||||
unless isMiniScreen
|
||||
if isSandstorm
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else if isAdministrable
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else if currentUser.isAdmin
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
if $eq type "template-container"
|
||||
a.js-open-board.template-container.board-list-item(href="{{pathFor 'board' id=_id slug=slug}}")
|
||||
span.details
|
||||
span.board-list-item-name(title="{{_ 'template-container'}}")
|
||||
+viewer
|
||||
= title
|
||||
i.fa.js-star-board(
|
||||
class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
|
||||
title="{{_ 'star-board-title'}}")
|
||||
p.board-list-item-desc
|
||||
+viewer
|
||||
= description
|
||||
if hasSpentTimeCards
|
||||
i.fa.js-has-spenttime-cards(
|
||||
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
|
||||
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
|
||||
if isMiniScreen
|
||||
i.fa.board-handle(
|
||||
class="fa-arrows"
|
||||
title="{{_ 'Drag board'}}")
|
||||
unless isMiniScreen
|
||||
if isSandstorm
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else if isAdministrable
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else if currentUser.isAdmin
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else
|
||||
a.js-open-board.board-list-item(href="{{pathFor 'board' id=_id slug=slug}}")
|
||||
span.details
|
||||
span.board-list-item-name(title="{{_ 'board-drag-drop-reorder-or-click-open'}}")
|
||||
+viewer
|
||||
= title
|
||||
i.fa.js-star-board(
|
||||
class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
|
||||
title="{{_ 'star-board-title'}}")
|
||||
p.board-list-item-desc
|
||||
+viewer
|
||||
= description
|
||||
if hasSpentTimeCards
|
||||
i.fa.js-has-spenttime-cards(
|
||||
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
|
||||
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
|
||||
if isMiniScreen
|
||||
i.fa.board-handle(
|
||||
class="fa-arrows"
|
||||
title="{{_ 'Drag board'}}")
|
||||
unless isMiniScreen
|
||||
if isSandstorm
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else if isAdministrable
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
else if currentUser.isAdmin
|
||||
i.fa.js-clone-board(
|
||||
class="fa-clone"
|
||||
title="{{_ 'duplicate-board'}}")
|
||||
i.fa.js-archive-board(
|
||||
class="fa-archive"
|
||||
title="{{_ 'archive-board'}}")
|
||||
|
||||
template(name="boardListHeaderBar")
|
||||
h1 {{_ title }}
|
||||
|
||||
@@ -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() {
|
||||
@@ -78,15 +87,52 @@ BlazeComponent.extendComponent({
|
||||
|
||||
boards() {
|
||||
const query = {
|
||||
archived: false,
|
||||
type: 'board',
|
||||
//archived: false,
|
||||
////type: { $in: ['board','template-container'] },
|
||||
//type: 'board',
|
||||
$and: [
|
||||
{ archived: false },
|
||||
{ type: 'board' },
|
||||
{ $or:[] }
|
||||
]
|
||||
};
|
||||
if (FlowRouter.getRouteName() === 'home')
|
||||
query['members.userId'] = Meteor.userId();
|
||||
if (FlowRouter.getRouteName() === 'home'){
|
||||
query.$and[2].$or.push({'members.userId': Meteor.userId()});
|
||||
|
||||
const currUser = Users.findOne(Meteor.userId());
|
||||
|
||||
// const currUser = Users.findOne(Meteor.userId(), {
|
||||
// fields: {
|
||||
// orgs: 1,
|
||||
// teams: 1,
|
||||
// },
|
||||
// });
|
||||
|
||||
let orgIdsUserBelongs = currUser.teams !== 'undefined' ? currUser.orgIdsUserBelongs() : '';
|
||||
if(orgIdsUserBelongs && orgIdsUserBelongs != ''){
|
||||
let orgsIds = orgIdsUserBelongs.split(',');
|
||||
// for(let i = 0; i < orgsIds.length; i++){
|
||||
// query.$and[2].$or.push({'orgs.orgId': orgsIds[i]});
|
||||
// }
|
||||
|
||||
//query.$and[2].$or.push({'orgs': {$elemMatch : {orgId: orgsIds[0]}}});
|
||||
query.$and[2].$or.push({'orgs.orgId': {$in : orgsIds}});
|
||||
}
|
||||
|
||||
let teamIdsUserBelongs = currUser.teams !== 'undefined' ? currUser.teamIdsUserBelongs() : '';
|
||||
if(teamIdsUserBelongs && teamIdsUserBelongs != ''){
|
||||
let teamsIds = teamIdsUserBelongs.split(',');
|
||||
// for(let i = 0; i < teamsIds.length; i++){
|
||||
// query.$or[2].$or.push({'teams.teamId': teamsIds[i]});
|
||||
// }
|
||||
//query.$and[2].$or.push({'teams': { $elemMatch : {teamId: teamsIds[0]}}});
|
||||
query.$and[2].$or.push({'teams.teamId': {$in : teamsIds}});
|
||||
}
|
||||
}
|
||||
else query.permission = 'public';
|
||||
|
||||
return Boards.find(query, {
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
//sort: { sort: 1 /* boards default sorting */ },
|
||||
});
|
||||
},
|
||||
isStarred() {
|
||||
|
||||
@@ -45,6 +45,9 @@ $spaceBetweenTiles = 16px
|
||||
text-decoration: none
|
||||
word-wrap: break-word
|
||||
|
||||
&.template-container
|
||||
border: 4px solid #fff
|
||||
|
||||
&.tile
|
||||
background-size: auto
|
||||
background-repeat: repeat
|
||||
|
||||
@@ -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' }}
|
||||
@@ -125,7 +125,7 @@ template(name="cardCustomField-stringtemplate")
|
||||
+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'}}")
|
||||
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
|
||||
|
||||
@@ -187,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;
|
||||
}
|
||||
@@ -354,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');
|
||||
@@ -23,7 +23,6 @@
|
||||
background-color: #fff
|
||||
border: 0
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
|
||||
color: #8c8c8c
|
||||
height: 36px
|
||||
margin: 4px 4px 6px 0
|
||||
padding: 9px 11px
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
template(name="cardDetails")
|
||||
section.card-details.js-card-details: .card-details-canvas
|
||||
section.card-details.js-card-details(class='{{#if cardMaximized}}card-details-maximized{{/if}}'): .card-details-canvas
|
||||
.card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}')
|
||||
+inlinedForm(classNames="js-card-details-title")
|
||||
+editCardTitleForm
|
||||
else
|
||||
unless isMiniScreen
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details
|
||||
a.fa.fa-times-thin.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
|
||||
unless cardMaximized
|
||||
a.fa.fa-window-maximize.maximize-card-details.js-maximize-card-details(title="{{_ 'maximize-card'}}")
|
||||
if cardMaximized
|
||||
a.fa.fa-window-minimize.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
|
||||
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}}")
|
||||
@@ -41,286 +45,501 @@ template(name="cardDetails")
|
||||
else
|
||||
p.warning {{_ 'card-archived'}}
|
||||
|
||||
.card-details-items
|
||||
if currentBoard.allowsLabels
|
||||
.card-details-item.card-details-item-labels
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-tags
|
||||
| {{_ 'labels'}}
|
||||
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
|
||||
each labels
|
||||
span.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
|
||||
i.fa.fa-plus
|
||||
.card-details-left
|
||||
|
||||
.card-details-items
|
||||
if currentBoard.allowsLabels
|
||||
.card-details-item.card-details-item-labels
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-tags
|
||||
| {{_ 'labels'}}
|
||||
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
|
||||
each labels
|
||||
span.card-label(class="card-label-{{color}}" title=name)
|
||||
+viewer
|
||||
= name
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
if currentBoard.allowsReceivedDate
|
||||
hr
|
||||
.card-details-item.card-details-item-received
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-sign-out
|
||||
| {{_ 'card-received'}}
|
||||
if getReceived
|
||||
+cardReceivedDate
|
||||
else
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-received-date
|
||||
i.fa.fa-plus
|
||||
|
||||
if currentBoard.allowsStartDate
|
||||
.card-details-item.card-details-item-start
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-hourglass-start
|
||||
| {{_ 'card-start'}}
|
||||
if getStart
|
||||
+cardStartDate
|
||||
else
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-start-date
|
||||
i.fa.fa-plus
|
||||
|
||||
if currentBoard.allowsDueDate
|
||||
.card-details-item.card-details-item-due
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-sign-in
|
||||
| {{_ 'card-due'}}
|
||||
if getDue
|
||||
+cardDueDate
|
||||
else
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-due-date
|
||||
i.fa.fa-plus
|
||||
|
||||
if currentBoard.allowsEndDate
|
||||
.card-details-item.card-details-item-end
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-hourglass-end
|
||||
| {{_ 'card-end'}}
|
||||
if getEnd
|
||||
+cardEndDate
|
||||
else
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-end-date
|
||||
i.fa.fa-plus
|
||||
|
||||
if currentBoard.allowsReceivedDate
|
||||
hr
|
||||
.card-details-item.card-details-item-received
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-sign-out
|
||||
| {{_ 'card-received'}}
|
||||
if getReceived
|
||||
+cardReceivedDate
|
||||
else
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-received-date
|
||||
i.fa.fa-plus
|
||||
if currentBoard.allowsCreator
|
||||
.card-details-item.card-details-item-creator
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-user
|
||||
| {{_ 'creator'}}
|
||||
|
||||
if currentBoard.allowsStartDate
|
||||
.card-details-item.card-details-item-start
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-hourglass-start
|
||||
| {{_ 'card-start'}}
|
||||
if getStart
|
||||
+cardStartDate
|
||||
else
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-start-date
|
||||
i.fa.fa-plus
|
||||
|
||||
if currentBoard.allowsDueDate
|
||||
.card-details-item.card-details-item-due
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-sign-in
|
||||
| {{_ 'card-due'}}
|
||||
if getDue
|
||||
+cardDueDate
|
||||
else
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.card-label.add-label.js-due-date
|
||||
i.fa.fa-plus
|
||||
|
||||
if currentBoard.allowsEndDate
|
||||
.card-details-item.card-details-item-end
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-hourglass-end
|
||||
| {{_ 'card-end'}}
|
||||
if getEnd
|
||||
+cardEndDate
|
||||
else
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
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
|
||||
.card-details-item.card-details-item-members
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-users
|
||||
| {{_ 'members'}}
|
||||
each userId in getMembers
|
||||
+userAvatar(userId=userId cardId=_id)
|
||||
+userAvatar(userId=userId noRemove=true)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
//if assigneeSelected
|
||||
if currentBoard.allowsAssignee
|
||||
.card-details-item.card-details-item-assignees
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-user
|
||||
| {{_ 'assignee'}}
|
||||
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'}}")
|
||||
i.fa.fa-plus
|
||||
if currentUser.isWorker
|
||||
unless assigneeSelected
|
||||
//.card-details-items
|
||||
if currentBoard.allowsMembers
|
||||
.card-details-item.card-details-item-members
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-users
|
||||
| {{_ 'members'}}
|
||||
each userId in getMembers
|
||||
+userAvatar(userId=userId cardId=_id)
|
||||
| {{! XXX Hack to hide syntaxic coloration /// }}
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
//if assigneeSelected
|
||||
if currentBoard.allowsAssignee
|
||||
.card-details-item.card-details-item-assignees
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-user
|
||||
| {{_ 'assignee'}}
|
||||
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'}}")
|
||||
i.fa.fa-plus
|
||||
if currentUser.isWorker
|
||||
unless assigneeSelected
|
||||
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
|
||||
i.fa.fa-plus
|
||||
|
||||
//.card-details-items
|
||||
if getSpentTime
|
||||
.card-details-item.card-details-item-spent
|
||||
if getIsOvertime
|
||||
h3.card-details-item-title
|
||||
| {{_ 'overtime-hours'}}
|
||||
else
|
||||
h3.card-details-item-title
|
||||
| {{_ 'spent-time-hours'}}
|
||||
+cardSpentTime
|
||||
//.card-details-items
|
||||
if getSpentTime
|
||||
.card-details-item.card-details-item-spent
|
||||
if getIsOvertime
|
||||
h3.card-details-item-title
|
||||
| {{_ 'overtime-hours'}}
|
||||
else
|
||||
h3.card-details-item-title
|
||||
| {{_ 'spent-time-hours'}}
|
||||
+cardSpentTime
|
||||
|
||||
//.card-details-items
|
||||
if currentBoard.allowsRequestedBy
|
||||
.card-details-item.card-details-item-name
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-shopping-cart
|
||||
| {{_ 'requested-by'}}
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
+inlinedForm(classNames="js-card-details-requester")
|
||||
+editCardRequesterForm
|
||||
//.card-details-items
|
||||
if currentBoard.allowsRequestedBy
|
||||
.card-details-item.card-details-item-name
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-shopping-cart
|
||||
| {{_ 'requested-by'}}
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
+inlinedForm(classNames="js-card-details-requester")
|
||||
+editCardRequesterForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getRequestedBy
|
||||
+viewer
|
||||
= getRequestedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
else if getRequestedBy
|
||||
+viewer
|
||||
= getRequestedBy
|
||||
|
||||
if currentBoard.allowsAssignedBy
|
||||
.card-details-item.card-details-item-name
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-user-plus
|
||||
| {{_ 'assigned-by'}}
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
+inlinedForm(classNames="js-card-details-assigner")
|
||||
+editCardAssignerForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getAssignedBy
|
||||
+viewer
|
||||
= getAssignedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
else if getRequestedBy
|
||||
+viewer
|
||||
= getAssignedBy
|
||||
|
||||
if currentBoard.allowsCardSortingByNumber
|
||||
.card-details-item.card-details-sort-order
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-sort
|
||||
| {{_ 'sort'}}
|
||||
if canModifyCard
|
||||
+inlinedForm(classNames="js-card-details-sort")
|
||||
+editCardSortOrderForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getRequestedBy
|
||||
+viewer
|
||||
= getRequestedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
else if getRequestedBy
|
||||
+viewer
|
||||
= getRequestedBy
|
||||
+viewer
|
||||
= sort
|
||||
|
||||
if currentBoard.allowsAssignedBy
|
||||
.card-details-item.card-details-item-name
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-user-plus
|
||||
| {{_ 'assigned-by'}}
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
+inlinedForm(classNames="js-card-details-assigner")
|
||||
+editCardAssignerForm
|
||||
else
|
||||
a.js-open-inlined-form
|
||||
if getAssignedBy
|
||||
+viewer
|
||||
= getAssignedBy
|
||||
else
|
||||
| {{_ 'add'}}
|
||||
else if getRequestedBy
|
||||
+viewer
|
||||
= getAssignedBy
|
||||
//.card-details-items
|
||||
if customFieldsWD
|
||||
hr
|
||||
each customFieldsWD
|
||||
.card-details-item.card-details-item-customfield
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-list-alt
|
||||
= definition.name
|
||||
+cardCustomField
|
||||
|
||||
//.card-details-items
|
||||
if customFieldsWD
|
||||
if getVoteQuestion
|
||||
hr
|
||||
each customFieldsWD
|
||||
.card-details-item.card-details-item-customfield
|
||||
.vote-title
|
||||
div.flex
|
||||
h3
|
||||
i.fa.fa-thumbs-up
|
||||
| {{_ 'vote-question'}}
|
||||
if getVoteEnd
|
||||
+voteEndDate
|
||||
.vote-result
|
||||
if votePublic
|
||||
a.card-label.card-label-green.js-show-positive-votes {{ voteCountPositive }}
|
||||
a.card-label.card-label-red.js-show-negative-votes {{ voteCountNegative }}
|
||||
else
|
||||
.card-label.card-label-green {{ voteCountPositive }}
|
||||
.card-label.card-label-red {{ voteCountNegative }}
|
||||
unless ($and currentBoard.isPublic voteAllowNonBoardMembers )
|
||||
.card-label.card-label-gray {{ voteCount }} {{_ 'r-of' }} {{ currentBoard.activeMembers.length }}
|
||||
+viewer
|
||||
= getVoteQuestion
|
||||
if showVotingButtons
|
||||
button.card-details-green.js-vote.js-vote-positive(class="{{#if voteState}}voted{{/if}}")
|
||||
if voteState
|
||||
i.fa.fa-thumbs-up
|
||||
| {{_ 'vote-for-it'}}
|
||||
button.card-details-red.js-vote.js-vote-negative(class="{{#if $eq voteState false}}voted{{/if}}")
|
||||
if $eq voteState false
|
||||
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
|
||||
if currentBoard.allowsDescriptionTitle
|
||||
hr
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-list-alt
|
||||
= definition.name
|
||||
+cardCustomField
|
||||
|
||||
if getVoteQuestion
|
||||
hr
|
||||
.vote-title
|
||||
div.flex
|
||||
h3
|
||||
i.fa.fa-thumbs-up
|
||||
| {{_ 'vote-question'}}
|
||||
if getVoteEnd
|
||||
+voteEndDate
|
||||
.vote-result
|
||||
if votePublic
|
||||
a.card-label.card-label-green.js-show-positive-votes {{ voteCountPositive }}
|
||||
a.card-label.card-label-red.js-show-negative-votes {{ voteCountNegative }}
|
||||
else
|
||||
.card-label.card-label-green {{ voteCountPositive }}
|
||||
.card-label.card-label-red {{ voteCountNegative }}
|
||||
unless ($and currentBoard.isPublic voteAllowNonBoardMembers )
|
||||
.card-label.card-label-gray {{ voteCount }} {{_ 'r-of' }} {{ currentBoard.activeMembers.length }}
|
||||
+viewer
|
||||
= getVoteQuestion
|
||||
if showVotingButtons
|
||||
button.card-details-green.js-vote.js-vote-positive(class="{{#if voteState}}voted{{/if}}")
|
||||
if voteState
|
||||
i.fa.fa-thumbs-up
|
||||
| {{_ 'vote-for-it'}}
|
||||
button.card-details-red.js-vote.js-vote-negative(class="{{#if $eq voteState false}}voted{{/if}}")
|
||||
if $eq voteState false
|
||||
i.fa.fa-thumbs-down
|
||||
| {{_ 'vote-against'}}
|
||||
|
||||
//- XXX We should use "editable" to avoid repetiting ourselves
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
i.fa.fa-align-left
|
||||
| {{_ 'description'}}
|
||||
if currentBoard.allowsDescriptionText
|
||||
+inlinedCardDescription(classNames="card-description js-card-description")
|
||||
+descriptionForm
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
if currentBoard.allowsDescriptionText
|
||||
a.js-open-inlined-form
|
||||
if getDescription
|
||||
+viewer
|
||||
= getDescription
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
if (hasUnsavedValue 'cardDescription' _id)
|
||||
p.quiet
|
||||
| {{_ 'unsaved-description'}}
|
||||
a.js-open-inlined-form {{_ 'view-it'}}
|
||||
= ' - '
|
||||
a.js-close-inlined-form {{_ 'discard'}}
|
||||
else if getDescription
|
||||
if currentBoard.allowsDescriptionTitle
|
||||
hr
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-align-left
|
||||
| {{_ 'description'}}
|
||||
h3.card-details-item-title {{_ 'description'}}
|
||||
if currentBoard.allowsDescriptionText
|
||||
+inlinedCardDescription(classNames="card-description js-card-description")
|
||||
+descriptionForm
|
||||
.edit-controls.clearfix
|
||||
button.primary(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
else
|
||||
if currentBoard.allowsDescriptionText
|
||||
a.js-open-inlined-form
|
||||
if getDescription
|
||||
+viewer
|
||||
= getDescription
|
||||
else
|
||||
| {{_ 'edit'}}
|
||||
if (hasUnsavedValue 'cardDescription' _id)
|
||||
p.quiet
|
||||
| {{_ 'unsaved-description'}}
|
||||
a.js-open-inlined-form {{_ 'view-it'}}
|
||||
= ' - '
|
||||
a.js-close-inlined-form {{_ 'discard'}}
|
||||
else if getDescription
|
||||
if currentBoard.allowsDescriptionTitle
|
||||
hr
|
||||
h3.card-details-item-title {{_ 'description'}}
|
||||
if currentBoard.allowsDescriptionText
|
||||
+viewer
|
||||
= getDescription
|
||||
+viewer
|
||||
= getDescription
|
||||
|
||||
.card-checklist-attachmentGalerys
|
||||
.card-checklist-attachmentGalery.card-checklists
|
||||
if currentBoard.allowsChecklists
|
||||
.card-checklist-attachmentGalerys
|
||||
.card-checklist-attachmentGalery.card-checklists
|
||||
if currentBoard.allowsChecklists
|
||||
hr
|
||||
+checklists(cardId = _id)
|
||||
if currentBoard.allowsSubtasks
|
||||
hr
|
||||
+subtasks(cardId = _id)
|
||||
if currentBoard.allowsAttachments
|
||||
hr
|
||||
+checklists(cardId = _id)
|
||||
if currentBoard.allowsSubtasks
|
||||
hr
|
||||
+subtasks(cardId = _id)
|
||||
if currentBoard.allowsAttachments
|
||||
hr
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'attachments'}}
|
||||
.card-checklist-attachmentGalery.card-attachmentGalery
|
||||
+attachmentsGalery
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-paperclip
|
||||
| {{_ 'attachments'}}
|
||||
.card-checklist-attachmentGalery.card-attachmentGalery
|
||||
+attachmentsGalery
|
||||
|
||||
hr
|
||||
unless currentUser.isNoComments
|
||||
.activity-title
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-history
|
||||
| {{ _ 'activity'}}
|
||||
.card-details-right
|
||||
|
||||
unless currentUser.isNoComments
|
||||
.activity-title
|
||||
h3.card-details-item-title
|
||||
i.fa.fa-history
|
||||
| {{ _ 'activity'}}
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch(title="{{_ 'hide-system-messages'}}")
|
||||
//span.toggle-switch-title
|
||||
if hiddenSystemMessages
|
||||
input.toggle-switch(type="checkbox" id="toggleButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleButton")
|
||||
label.toggle-label(for="toggleButton")
|
||||
if currentBoard.allowsComments
|
||||
if currentUser.isBoardMember
|
||||
.material-toggle-switch
|
||||
span.toggle-switch-title {{_ 'hide-system-messages'}}
|
||||
if hiddenSystemMessages
|
||||
input.toggle-switch(type="checkbox" id="toggleButton" checked="checked")
|
||||
else
|
||||
input.toggle-switch(type="checkbox" id="toggleButton")
|
||||
label.toggle-label(for="toggleButton")
|
||||
if currentBoard.allowsComments
|
||||
if currentUser.isBoardMember
|
||||
unless currentUser.isNoComments
|
||||
+commentForm
|
||||
unless currentUser.isNoComments
|
||||
if isLoaded.get
|
||||
if isLinkedCard
|
||||
+activities(card=this mode="linkedcard")
|
||||
else if isLinkedBoard
|
||||
+activities(card=this mode="linkedboard")
|
||||
else
|
||||
+activities(card=this mode="card")
|
||||
unless currentUser.isNoComments
|
||||
+commentForm
|
||||
unless currentUser.isNoComments
|
||||
if isLoaded.get
|
||||
if isLinkedCard
|
||||
+activities(card=this mode="linkedcard")
|
||||
else if isLinkedBoard
|
||||
+activities(card=this mode="linkedboard")
|
||||
else
|
||||
+activities(card=this mode="card")
|
||||
|
||||
template(name="editCardTitleForm")
|
||||
textarea.js-edit-card-title(rows='1' autofocus dir="auto")
|
||||
@@ -341,6 +560,12 @@ template(name="editCardAssignerForm")
|
||||
button.primary.confirm.js-submit-edit-card-assigner-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="editCardSortOrderForm")
|
||||
input.js-edit-card-sort(type='text' autofocus value=sort dir="auto")
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-sort-form(type="submit") {{_ 'save'}}
|
||||
a.fa.fa-times-thin.js-close-inlined-form
|
||||
|
||||
template(name="cardDetailsActionsPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
@@ -351,9 +576,9 @@ template(name="cardDetailsActionsPopup")
|
||||
else
|
||||
i.fa.fa-eye-slash
|
||||
| {{_ 'watch'}}
|
||||
hr
|
||||
if canModifyCard
|
||||
unless currentUser.isWorker
|
||||
hr
|
||||
ul.pop-over-list
|
||||
//li: a.js-members {{_ 'card-edit-members'}}
|
||||
//li: a.js-labels {{_ 'card-edit-labels'}}
|
||||
@@ -362,6 +587,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
|
||||
@@ -379,50 +608,63 @@ template(name="cardDetailsActionsPopup")
|
||||
a.js-set-card-color
|
||||
i.fa.fa-paint-brush
|
||||
| {{_ 'setCardColorPopup-title'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-export-card
|
||||
i.fa.fa-share-alt
|
||||
| {{_ 'export-card'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-move-card-to-top
|
||||
i.fa.fa-arrow-up
|
||||
| {{_ 'moveCardToTop-title'}}
|
||||
li
|
||||
a.js-move-card-to-bottom
|
||||
i.fa.fa-arrow-down
|
||||
| {{_ 'moveCardToBottom-title'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
if currentUser.isBoardAdmin
|
||||
li
|
||||
a.js-move-card-to-top
|
||||
i.fa.fa-arrow-up
|
||||
| {{_ 'moveCardToTop-title'}}
|
||||
li
|
||||
a.js-move-card-to-bottom
|
||||
i.fa.fa-arrow-down
|
||||
| {{_ 'moveCardToBottom-title'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
if currentUser.isBoardAdmin
|
||||
li
|
||||
a.js-move-card
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ 'moveCardPopup-title'}}
|
||||
unless currentUser.isWorker
|
||||
li
|
||||
a.js-copy-card
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyCardPopup-title'}}
|
||||
a.js-move-card
|
||||
i.fa.fa-arrow-right
|
||||
| {{_ 'moveCardPopup-title'}}
|
||||
unless currentUser.isWorker
|
||||
li
|
||||
a.js-copy-card
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyCardPopup-title'}}
|
||||
unless currentUser.isWorker
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-copy-checklist-cards
|
||||
i.fa.fa-list
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyChecklistToManyCardsPopup-title'}}
|
||||
unless archived
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-copy-checklist-cards
|
||||
i.fa.fa-list
|
||||
i.fa.fa-copy
|
||||
| {{_ 'copyChecklistToManyCardsPopup-title'}}
|
||||
unless archived
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-archive
|
||||
i.fa.fa-arrow-right
|
||||
i.fa.fa-archive
|
||||
| {{_ 'archive-card'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-more
|
||||
i.fa.fa-link
|
||||
| {{_ 'cardMorePopup-title'}}
|
||||
a.js-archive
|
||||
i.fa.fa-arrow-right
|
||||
i.fa.fa-archive
|
||||
| {{_ 'archive-card'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-more
|
||||
i.fa.fa-link
|
||||
| {{_ 'cardMorePopup-title'}}
|
||||
|
||||
template(name="exportCardPopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
a(href="{{exportUrlCardPDF}}",, download="{{exportFilenameCardPDF}}")
|
||||
i.fa.fa-share-alt
|
||||
| {{_ 'export-card-pdf'}}
|
||||
|
||||
template(name="moveCardPopup")
|
||||
+boardsAndLists
|
||||
@@ -621,3 +863,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'}}
|
||||
@@ -54,6 +54,10 @@ BlazeComponent.extendComponent({
|
||||
return Meteor.user().hasHiddenSystemMessages();
|
||||
},
|
||||
|
||||
cardMaximized() {
|
||||
return Meteor.user().hasCardMaximized();
|
||||
},
|
||||
|
||||
canModifyCard() {
|
||||
return (
|
||||
Meteor.user() &&
|
||||
@@ -140,6 +144,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 ---
|
||||
@@ -161,7 +174,7 @@ BlazeComponent.extendComponent({
|
||||
}).fetch();
|
||||
|
||||
if (integrations.length > 0) {
|
||||
integrations.forEach(integration => {
|
||||
integrations.forEach((integration) => {
|
||||
Meteor.call(
|
||||
'outgoingWebhooks',
|
||||
integration,
|
||||
@@ -327,9 +340,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 {
|
||||
@@ -338,9 +349,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 {
|
||||
@@ -349,15 +358,23 @@ 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 {
|
||||
this.data().setRequestedBy('');
|
||||
}
|
||||
},
|
||||
'submit .js-card-details-sort'(event) {
|
||||
event.preventDefault();
|
||||
const sort = parseFloat(this.currentComponent()
|
||||
.getValue()
|
||||
.trim());
|
||||
if (!Number.isNaN(sort)) {
|
||||
let card = this.data();
|
||||
card.move(card.boardId, card.swimlaneId, card.listId, sort);
|
||||
}
|
||||
},
|
||||
'click .js-go-to-linked-card'() {
|
||||
Utils.goCardId(this.data().linkedId);
|
||||
},
|
||||
@@ -395,6 +412,14 @@ BlazeComponent.extendComponent({
|
||||
'click #toggleButton'() {
|
||||
Meteor.call('toggleSystemMessages');
|
||||
},
|
||||
'click .js-maximize-card-details'() {
|
||||
Meteor.call('toggleCardMaximized');
|
||||
autosize($('.card-details'));
|
||||
},
|
||||
'click .js-minimize-card-details'() {
|
||||
Meteor.call('toggleCardMaximized');
|
||||
autosize($('.card-details'));
|
||||
},
|
||||
'click .js-vote'(e) {
|
||||
const forIt = $(e.target).hasClass('js-vote-positive');
|
||||
let newState = null;
|
||||
@@ -407,11 +432,126 @@ 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');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
template() {
|
||||
return 'exportCard';
|
||||
},
|
||||
withApi() {
|
||||
return Template.instance().apiEnabled.get();
|
||||
},
|
||||
exportUrlCardPDF() {
|
||||
const params = {
|
||||
boardId: Session.get('currentBoard'),
|
||||
listId: this.listId,
|
||||
cardId: this.cardId,
|
||||
};
|
||||
const queryParams = {
|
||||
authToken: Accounts._storedLoginToken(),
|
||||
};
|
||||
return FlowRouter.path(
|
||||
'/api/boards/:boardId/lists/:listId/cards/:cardId/exportPDF',
|
||||
params,
|
||||
queryParams,
|
||||
);
|
||||
},
|
||||
exportFilenameCardPDF() {
|
||||
//const boardId = Session.get('currentBoard');
|
||||
//return `export-card-pdf-${boardId}.xlsx`;
|
||||
return `export-card.pdf`;
|
||||
},
|
||||
}).register('exportCardPopup');
|
||||
|
||||
// only allow number input
|
||||
Template.editCardSortOrderForm.onRendered(function() {
|
||||
this.$('input').on("keypress paste", function(event) {
|
||||
let keyCode = event.keyCode;
|
||||
let charCode = String.fromCharCode(keyCode);
|
||||
let regex = new RegExp('[-0-9.]');
|
||||
let ret = regex.test(charCode);
|
||||
// only working here, defining in events() doesn't handle the return value correctly
|
||||
return ret;
|
||||
});
|
||||
});
|
||||
|
||||
// We extends the normal InlinedForm component to support UnsavedEdits draft
|
||||
// feature.
|
||||
(class extends InlinedForm {
|
||||
@@ -472,11 +612,13 @@ Template.cardDetailsActionsPopup.helpers({
|
||||
});
|
||||
|
||||
Template.cardDetailsActionsPopup.events({
|
||||
'click .js-export-card': Popup.open('exportCard'),
|
||||
'click .js-members': Popup.open('cardMembers'),
|
||||
'click .js-assignees': Popup.open('cardAssignees'),
|
||||
'click .js-labels': Popup.open('cardLabels'),
|
||||
'click .js-attachments': Popup.open('cardAttachments'),
|
||||
'click .js-start-voting': Popup.open('cardStartVoting'),
|
||||
'click .js-start-planning-poker': Popup.open('cardStartPlanningPoker'),
|
||||
'click .js-custom-fields': Popup.open('cardCustomFields'),
|
||||
'click .js-received-date': Popup.open('editCardReceivedDate'),
|
||||
'click .js-start-date': Popup.open('editCardStartDate'),
|
||||
@@ -485,6 +627,7 @@ Template.cardDetailsActionsPopup.events({
|
||||
'click .js-spent-time': Popup.open('editCardSpentTime'),
|
||||
'click .js-move-card': Popup.open('moveCard'),
|
||||
'click .js-copy-card': Popup.open('copyCard'),
|
||||
'click .js-convert-checklist-item-to-card': Popup.open('convertChecklistItemToCard'),
|
||||
'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
|
||||
'click .js-set-card-color': Popup.open('setCardColor'),
|
||||
'click .js-move-card-to-top'(event) {
|
||||
@@ -492,7 +635,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);
|
||||
},
|
||||
@@ -501,7 +644,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);
|
||||
},
|
||||
@@ -520,7 +663,7 @@ Template.cardDetailsActionsPopup.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardTitleForm.onRendered(function() {
|
||||
Template.editCardTitleForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-title'));
|
||||
});
|
||||
|
||||
@@ -534,7 +677,7 @@ Template.editCardTitleForm.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardRequesterForm.onRendered(function() {
|
||||
Template.editCardRequesterForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-requester'));
|
||||
});
|
||||
|
||||
@@ -547,7 +690,7 @@ Template.editCardRequesterForm.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.editCardAssignerForm.onRendered(function() {
|
||||
Template.editCardAssignerForm.onRendered(function () {
|
||||
autosize(this.$('.js-edit-card-assigner'));
|
||||
});
|
||||
|
||||
@@ -632,9 +775,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;
|
||||
@@ -651,6 +792,34 @@ Template.copyCardPopup.events({
|
||||
},
|
||||
});
|
||||
|
||||
Template.convertChecklistItemToCardPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
const lSelect = $('.js-select-lists')[0];
|
||||
const listId = lSelect.options[lSelect.selectedIndex].value;
|
||||
const slSelect = $('.js-select-swimlanes')[0];
|
||||
const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
|
||||
const bSelect = $('.js-select-boards')[0];
|
||||
const boardId = bSelect.options[bSelect.selectedIndex].value;
|
||||
const textarea = $('#copy-card-title');
|
||||
const title = textarea.val().trim();
|
||||
|
||||
if (title) {
|
||||
const _id = Cards.insert({
|
||||
title: title,
|
||||
listId: listId,
|
||||
boardId: boardId,
|
||||
swimlaneId: swimlaneId,
|
||||
sort: 0,
|
||||
});
|
||||
Filter.addException(_id);
|
||||
|
||||
Popup.close();
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.copyChecklistToManyCardsPopup.events({
|
||||
'click .js-done'() {
|
||||
const card = Cards.findOne(Session.get('currentCard'));
|
||||
@@ -665,9 +834,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);
|
||||
@@ -684,13 +851,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
|
||||
const cursor = Cards.find({ parentId: oldId });
|
||||
cursor.forEach(function() {
|
||||
cursor.forEach(function () {
|
||||
'use strict';
|
||||
const subtask = arguments[0];
|
||||
subtask.parentId = _id;
|
||||
@@ -699,7 +866,7 @@ Template.copyChecklistToManyCardsPopup.events({
|
||||
});
|
||||
|
||||
// copy card comments
|
||||
CardComments.find({ cardId: oldId }).forEach(cmt => {
|
||||
CardComments.find({ cardId: oldId }).forEach((cmt) => {
|
||||
cmt.copy(_id);
|
||||
});
|
||||
}
|
||||
@@ -715,7 +882,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
|
||||
colors() {
|
||||
return ALLOWED_COLORS.map(color => ({ color, name: '' }));
|
||||
return ALLOWED_COLORS.map((color) => ({ color, name: '' }));
|
||||
},
|
||||
|
||||
isSelected(color) {
|
||||
@@ -838,7 +1005,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) {
|
||||
@@ -945,6 +1112,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
|
||||
@@ -955,6 +1124,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();
|
||||
@@ -976,6 +1298,276 @@ 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) => {
|
||||
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',
|
||||
|
||||
@@ -110,11 +110,13 @@ avatar-radius = 50%
|
||||
|
||||
.card-details-header
|
||||
margin: 0 -20px 5px
|
||||
padding 7px 20px
|
||||
padding: 7px 20px
|
||||
background: darken(white, 7%)
|
||||
border-bottom: 1px solid darken(white, 14%)
|
||||
|
||||
.close-card-details,
|
||||
.maximize-card-details,
|
||||
.minimize-card-details,
|
||||
.card-details-menu,
|
||||
.card-copy-button,
|
||||
.card-copy-mobile-button,
|
||||
@@ -122,9 +124,11 @@ avatar-radius = 50%
|
||||
.card-details-menu-mobile-web
|
||||
float: right
|
||||
|
||||
.close-card-details
|
||||
.close-card-details,
|
||||
.maximize-card-details,
|
||||
.minimize-card-details
|
||||
font-size: 24px
|
||||
padding: 5px
|
||||
padding: 5px 10px 5px 10px
|
||||
margin-right: -8px
|
||||
|
||||
.close-card-details-mobile-web
|
||||
@@ -233,6 +237,43 @@ avatar-radius = 50%
|
||||
.activities
|
||||
padding-top: 10px
|
||||
|
||||
.card-details-maximized
|
||||
padding: 0
|
||||
flex-shrink: 0
|
||||
flex-basis: calc(100% - 20px)
|
||||
will-change: flex-basis
|
||||
overflow-y: scroll
|
||||
overflow-x: scroll
|
||||
background: darken(white, 3%)
|
||||
border-radius: bottom 3px
|
||||
z-index: 1000 !important
|
||||
animation: flexGrowIn 0.1s
|
||||
box-shadow: 0 0 7px 0 darken(white, 30%)
|
||||
transition: flex-basis 0.1s
|
||||
box-sizing: border-box
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
height: calc(100% - 20px)
|
||||
width: calc(100% - 20px)
|
||||
float: left
|
||||
|
||||
.card-details-left
|
||||
position: absolute
|
||||
float: left
|
||||
top: 60px
|
||||
left: 20px
|
||||
width: 47%
|
||||
|
||||
.card-details-right
|
||||
position: absolute
|
||||
float: right
|
||||
top: 20px
|
||||
left: 50%
|
||||
|
||||
.card-details-header
|
||||
width: 47%
|
||||
|
||||
input[type="text"].attachment-add-link-input
|
||||
float: left
|
||||
margin: 0 0 8px
|
||||
@@ -260,7 +301,13 @@ input[type="submit"].attachment-add-link-submit
|
||||
margin-right: 0px
|
||||
|
||||
.card-details-menu
|
||||
margin-right: 10px
|
||||
margin-right: 40px
|
||||
|
||||
.maximize-card-details
|
||||
margin-right: 40px
|
||||
|
||||
.minimize-card-details
|
||||
margin-right: 40px
|
||||
|
||||
card-details-color(background, color...)
|
||||
background: background !important
|
||||
@@ -357,3 +404,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
|
||||
@@ -81,6 +80,9 @@ template(name="editChecklistItemForm")
|
||||
span(title=createdAt) {{ moment createdAt }}
|
||||
if canModifyCard
|
||||
a.js-delete-checklist-item {{_ "delete"}}...
|
||||
a.js-convert-checklist-item-to-card
|
||||
i.fa.fa-copy
|
||||
| {{_ 'convertChecklistItemToCardPopup-title'}}
|
||||
|
||||
template(name="checklistItems")
|
||||
.checklist-items.js-checklist-items
|
||||
@@ -93,9 +95,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}}")
|
||||
@@ -112,3 +113,32 @@ template(name='checklistItemDetail')
|
||||
.item-title(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||
+viewer
|
||||
= item.title
|
||||
|
||||
template(name="convertChecklistItemToCardPopup")
|
||||
label(for='convert-checklist-item-to-card-title') {{_ 'title'}}:
|
||||
textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
|
||||
= item.title
|
||||
+boardsSwimlanesAndLists
|
||||
|
||||
template(name="boardsSwimlanesAndLists")
|
||||
unless currentUser.isWorker
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards(autofocus)
|
||||
each boards
|
||||
if $eq _id currentBoard._id
|
||||
option(value="{{_id}}" selected) {{_ 'current'}}
|
||||
else
|
||||
option(value="{{_id}}") {{title}}
|
||||
|
||||
label {{_ 'swimlanes'}}:
|
||||
select.js-select-swimlanes
|
||||
each swimlanes
|
||||
option(value="{{_id}}") {{title}}
|
||||
|
||||
label {{_ 'lists'}}:
|
||||
select.js-select-lists
|
||||
each aBoardLists
|
||||
option(value="{{_id}}") {{title}}
|
||||
|
||||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
@@ -1,3 +1,7 @@
|
||||
import Cards from '/models/cards';
|
||||
import Boards from '/models/boards';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const { calculateIndexData, capitalize } = Utils;
|
||||
|
||||
function initSorting(items) {
|
||||
@@ -206,6 +210,7 @@ BlazeComponent.extendComponent({
|
||||
'submit .js-edit-checklist-title': this.editChecklist,
|
||||
'submit .js-add-checklist-item': this.addChecklistItem,
|
||||
'submit .js-edit-checklist-item': this.editChecklistItem,
|
||||
'click .js-convert-checklist-item-to-card': Popup.open('convertChecklistItemToCard'),
|
||||
'click .js-delete-checklist-item': this.deleteItem,
|
||||
'click .confirm-checklist-delete': this.deleteChecklist,
|
||||
'focus .js-add-checklist-item': this.focusChecklistItem,
|
||||
@@ -215,6 +220,47 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('checklists');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
subManager.subscribe('board', Session.get('currentBoard'), false);
|
||||
this.selectedBoardId = new ReactiveVar(Session.get('currentBoard'));
|
||||
},
|
||||
|
||||
boards() {
|
||||
return Boards.find(
|
||||
{
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: { $ne: Meteor.user().getTemplatesBoardId() },
|
||||
},
|
||||
{
|
||||
sort: { sort: 1 /* boards default sorting */ },
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
swimlanes() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
return board.swimlanes();
|
||||
},
|
||||
|
||||
aBoardLists() {
|
||||
const board = Boards.findOne(this.selectedBoardId.get());
|
||||
return board.lists();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'change .js-select-boards'(event) {
|
||||
this.selectedBoardId.set($(event.currentTarget).val());
|
||||
subManager.subscribe('board', this.selectedBoardId.get(), false);
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('boardsSwimlanesAndLists');
|
||||
|
||||
Template.checklists.helpers({
|
||||
hideCheckedItems() {
|
||||
const currentUser = Meteor.user();
|
||||
|
||||
@@ -159,7 +159,8 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||
padding-top: 2px
|
||||
padding-right: 10px;
|
||||
|
||||
.js-delete-checklist-item
|
||||
.js-delete-checklist-item,
|
||||
.js-convert-checklist-item-to-card
|
||||
margin: 0 0 0.5em 1.33em
|
||||
@extends .delete-text
|
||||
padding: 12px 0 0 0
|
||||
|
||||
@@ -44,9 +44,20 @@
|
||||
align-items: center
|
||||
justify-content: center
|
||||
|
||||
.card-label-white
|
||||
background-color: #ffffff
|
||||
color: #000000 //Black text for better visibility
|
||||
border: 1px solid #c0c0c0
|
||||
|
||||
.card-label-white:hover
|
||||
color: #aaaaaa //grey text for better visibility
|
||||
|
||||
.card-label-green
|
||||
background-color: #3cb500
|
||||
|
||||
.card-label-green:hover
|
||||
color: #000000 //Black hover text for better visibility
|
||||
|
||||
.card-label-yellow
|
||||
background-color: #fad900
|
||||
color: #000000 //Black text for better visibility
|
||||
|
||||
@@ -121,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
|
||||
@@ -129,3 +134,12 @@ 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
|
||||
if currentBoard.allowsCardSortingByNumber
|
||||
.badge
|
||||
span.badge-icon.fa.fa-sort
|
||||
span.badge-text {{ sort }}
|
||||
@@ -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
|
||||
|
||||
@@ -86,7 +86,7 @@ select
|
||||
margin-bottom: 8px
|
||||
|
||||
&.inline
|
||||
width: 100%
|
||||
width: 100%
|
||||
|
||||
option[disabled]
|
||||
color: #8c8c8c
|
||||
|
||||
@@ -19,19 +19,14 @@ 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(
|
||||
class=currentBoard.colorClass
|
||||
.sk-spinner.sk-spinner-list(
|
||||
class="{{currentBoard.colorClass}} {{getSkSpinnerName}}"
|
||||
id="showMoreResults")
|
||||
.sk-rect1
|
||||
.sk-rect2
|
||||
.sk-rect3
|
||||
.sk-rect4
|
||||
.sk-rect5
|
||||
+spinnerRaw
|
||||
|
||||
template(name="addCardForm")
|
||||
.minicard.minicard-composer.js-composer
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Spinner } from '/client/lib/spinner';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
const InfiniteScrollIter = 10;
|
||||
|
||||
@@ -116,8 +118,6 @@ BlazeComponent.extendComponent({
|
||||
if (position === 'bottom') {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
formComponent.reset();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -552,7 +552,7 @@ BlazeComponent.extendComponent({
|
||||
board = Boards.findOne((Meteor.user().profile || {}).templatesBoardId);
|
||||
} else {
|
||||
// Prefetch first non-current board id
|
||||
board = Boards.findOne({
|
||||
board = Boards.find({
|
||||
archived: false,
|
||||
'members.userId': Meteor.userId(),
|
||||
_id: {
|
||||
@@ -698,7 +698,7 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('searchElementPopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
(class extends Spinner {
|
||||
onCreated() {
|
||||
this.cardlimit = this.parentComponent().cardlimit;
|
||||
|
||||
@@ -726,7 +726,7 @@ BlazeComponent.extendComponent({
|
||||
.parentComponent()
|
||||
.data()._id;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
onRendered() {
|
||||
this.spinner = this.find('.sk-spinner-list');
|
||||
@@ -741,47 +741,58 @@ BlazeComponent.extendComponent({
|
||||
);
|
||||
|
||||
this.updateList();
|
||||
},
|
||||
}
|
||||
|
||||
onDestroyed() {
|
||||
$(this.container).off(`scroll.spinner_${this.swimlaneId}_${this.listId}`);
|
||||
$(window).off(`resize.spinner_${this.swimlaneId}_${this.listId}`);
|
||||
},
|
||||
}
|
||||
|
||||
checkIdleTime() {
|
||||
return window.requestIdleCallback ||
|
||||
function(handler) {
|
||||
const startTime = Date.now();
|
||||
return setTimeout(function() {
|
||||
handler({
|
||||
didTimeout: false,
|
||||
timeRemaining() {
|
||||
return Math.max(0, 50.0 - (Date.now() - startTime));
|
||||
},
|
||||
});
|
||||
}, 1);
|
||||
};
|
||||
}
|
||||
|
||||
updateList() {
|
||||
// Use fallback when requestIdleCallback is not available on iOS and Safari
|
||||
// https://www.afasterweb.com/2017/11/20/utilizing-idle-moments/
|
||||
checkIdleTime =
|
||||
window.requestIdleCallback ||
|
||||
function(handler) {
|
||||
const startTime = Date.now();
|
||||
return setTimeout(function() {
|
||||
handler({
|
||||
didTimeout: false,
|
||||
timeRemaining() {
|
||||
return Math.max(0, 50.0 - (Date.now() - startTime));
|
||||
},
|
||||
});
|
||||
}, 1);
|
||||
};
|
||||
|
||||
if (this.spinnerInView()) {
|
||||
this.cardlimit.set(this.cardlimit.get() + InfiniteScrollIter);
|
||||
checkIdleTime(() => this.updateList());
|
||||
this.checkIdleTime(() => this.updateList());
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
spinnerInView() {
|
||||
const parentViewHeight = this.container.clientHeight;
|
||||
const bottomViewPosition = this.container.scrollTop + parentViewHeight;
|
||||
|
||||
const threshold = this.spinner.offsetTop;
|
||||
|
||||
// spinner deleted
|
||||
if (!this.spinner.offsetTop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return bottomViewPosition > threshold;
|
||||
},
|
||||
}).register('spinnerList');
|
||||
const parentViewHeight = this.container.clientHeight;
|
||||
const bottomViewPosition = this.container.scrollTop + parentViewHeight;
|
||||
|
||||
let spinnerOffsetTop = this.spinner.offsetTop;
|
||||
|
||||
const addCard = $(this.container).find("a.open-minicard-composer").first()[0];
|
||||
if (addCard !== undefined) {
|
||||
spinnerOffsetTop -= addCard.clientHeight;
|
||||
}
|
||||
|
||||
return bottomViewPosition > spinnerOffsetTop;
|
||||
}
|
||||
|
||||
getSkSpinnerName() {
|
||||
return "sk-spinner-" + super.getSpinnerName().toLowerCase();
|
||||
}
|
||||
}.register('spinnerList'));
|
||||
@@ -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() {
|
||||
|
||||
@@ -50,7 +50,7 @@ Template.editor.onRendered(() => {
|
||||
['table', ['table']],
|
||||
//['insert', ['link', 'picture', 'video']], // iframe tag will be sanitized TODO if iframe[class=note-video-clip] can be added into safe list, insert video can be enabled
|
||||
['insert', ['link']], //, 'picture']], // modal popup has issue somehow :(
|
||||
['view', ['fullscreen', 'help']],
|
||||
['view', ['fullscreen', 'codeview', 'help']],
|
||||
];
|
||||
const cleanPastedHTML = function(input) {
|
||||
const badTags = [
|
||||
@@ -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) {
|
||||
@@ -236,14 +246,11 @@ Template.editor.onRendered(() => {
|
||||
dialogsInBody: true,
|
||||
spellCheck: true,
|
||||
disableGrammar: false,
|
||||
disableDragAndDrop: true,
|
||||
disableDragAndDrop: false,
|
||||
toolbar,
|
||||
popover: {
|
||||
image: [
|
||||
[
|
||||
'image',
|
||||
['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone'],
|
||||
],
|
||||
['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
|
||||
['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||
['remove', ['removeMedia']],
|
||||
],
|
||||
@@ -266,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
|
||||
@@ -295,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
|
||||
@@ -310,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) {
|
||||
@@ -354,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 }),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -158,40 +158,42 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
['# ', 'globalSearch-instructions-heading'],
|
||||
['\n', 'globalSearch-instructions-description'],
|
||||
['\n\n', 'globalSearch-instructions-operators'],
|
||||
['\n* ', 'globalSearch-instructions-operator-board'],
|
||||
['\n* ', 'globalSearch-instructions-operator-list'],
|
||||
['\n* ', 'globalSearch-instructions-operator-swimlane'],
|
||||
['\n* ', 'globalSearch-instructions-operator-comment'],
|
||||
['\n* ', 'globalSearch-instructions-operator-label'],
|
||||
['\n* ', 'globalSearch-instructions-operator-hash'],
|
||||
['\n* ', 'globalSearch-instructions-operator-user'],
|
||||
['\n* ', 'globalSearch-instructions-operator-at'],
|
||||
['\n* ', 'globalSearch-instructions-operator-member'],
|
||||
['\n* ', 'globalSearch-instructions-operator-assignee'],
|
||||
['\n* ', 'globalSearch-instructions-operator-creator'],
|
||||
['\n* ', 'globalSearch-instructions-operator-due'],
|
||||
['\n* ', 'globalSearch-instructions-operator-created'],
|
||||
['\n* ', 'globalSearch-instructions-operator-modified'],
|
||||
['\n* ', 'globalSearch-instructions-operator-status'],
|
||||
['\n * ', 'globalSearch-instructions-status-archived'],
|
||||
['\n * ', 'globalSearch-instructions-status-public'],
|
||||
['\n * ', 'globalSearch-instructions-status-private'],
|
||||
['\n * ', 'globalSearch-instructions-status-all'],
|
||||
['\n * ', 'globalSearch-instructions-status-ended'],
|
||||
['\n* ', 'globalSearch-instructions-operator-has'],
|
||||
['\n* ', 'globalSearch-instructions-operator-sort'],
|
||||
['\n* ', 'globalSearch-instructions-operator-limit'],
|
||||
['\n- ', 'globalSearch-instructions-operator-board'],
|
||||
['\n- ', 'globalSearch-instructions-operator-list'],
|
||||
['\n- ', 'globalSearch-instructions-operator-swimlane'],
|
||||
['\n- ', 'globalSearch-instructions-operator-comment'],
|
||||
['\n- ', 'globalSearch-instructions-operator-label'],
|
||||
['\n- ', 'globalSearch-instructions-operator-hash'],
|
||||
['\n- ', 'globalSearch-instructions-operator-user'],
|
||||
['\n- ', 'globalSearch-instructions-operator-at'],
|
||||
['\n- ', 'globalSearch-instructions-operator-member'],
|
||||
['\n- ', 'globalSearch-instructions-operator-assignee'],
|
||||
['\n- ', 'globalSearch-instructions-operator-creator'],
|
||||
['\n- ', 'globalSearch-instructions-operator-due'],
|
||||
['\n- ', 'globalSearch-instructions-operator-created'],
|
||||
['\n- ', 'globalSearch-instructions-operator-modified'],
|
||||
['\n- ', 'globalSearch-instructions-operator-status'],
|
||||
['\n - ', 'globalSearch-instructions-status-archived'],
|
||||
['\n - ', 'globalSearch-instructions-status-public'],
|
||||
['\n - ', 'globalSearch-instructions-status-private'],
|
||||
['\n - ', 'globalSearch-instructions-status-all'],
|
||||
['\n - ', 'globalSearch-instructions-status-ended'],
|
||||
['\n- ', 'globalSearch-instructions-operator-has'],
|
||||
['\n- ', 'globalSearch-instructions-operator-sort'],
|
||||
['\n- ', 'globalSearch-instructions-operator-limit'],
|
||||
['\n## ', 'heading-notes'],
|
||||
['\n* ', 'globalSearch-instructions-notes-1'],
|
||||
['\n* ', 'globalSearch-instructions-notes-2'],
|
||||
['\n* ', 'globalSearch-instructions-notes-3'],
|
||||
['\n* ', 'globalSearch-instructions-notes-3-2'],
|
||||
['\n* ', 'globalSearch-instructions-notes-4'],
|
||||
['\n* ', 'globalSearch-instructions-notes-5'],
|
||||
['\n- ', 'globalSearch-instructions-notes-1'],
|
||||
['\n- ', 'globalSearch-instructions-notes-2'],
|
||||
['\n- ', 'globalSearch-instructions-notes-3'],
|
||||
['\n- ', 'globalSearch-instructions-notes-3-2'],
|
||||
['\n- ', 'globalSearch-instructions-notes-4'],
|
||||
['\n- ', 'globalSearch-instructions-notes-5'],
|
||||
].forEach(([prefix, instruction]) => {
|
||||
text += `${prefix}${TAPi18n.__(instruction, tags)}`;
|
||||
text += `${prefix}${TAPi18n.__(instruction, tags)}`
|
||||
// Replace *<text>* with `<text>` so markdown shows correctly
|
||||
.replace(/\*\</, '`<')
|
||||
.replace(/\>\*/, '\>\`')
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -78,7 +78,7 @@ Template.userFormsLayout.helpers({
|
||||
// ar-EG = Arabic (Egypt), simply Masri (مَصرى, [ˈmɑsˤɾi], Egyptian, Masr refers to Cairo)
|
||||
name = 'مَصرى';
|
||||
} else if (lang.name === 'de-CH') {
|
||||
name = 'Schwiizerdütsch';
|
||||
name = 'Deutsch (Schweiz)';
|
||||
} else if (lang.name === 'fa-IR') {
|
||||
// fa-IR = Persian (Iran)
|
||||
name = 'فارسی/پارسی (ایران)';
|
||||
@@ -92,6 +92,8 @@ Template.userFormsLayout.helpers({
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'latviešu valoda') {
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'en-IT') {
|
||||
name = 'English (Italy)';
|
||||
} else if (lang.name === 'Español') {
|
||||
name = 'español';
|
||||
} else if (lang.name === 'es_419') {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
template(name="spinner")
|
||||
+Template.dynamic(template=getSpinnerTemplate)
|
||||
|
||||
template(name="spinnerRaw")
|
||||
+Template.dynamic(template=getSpinnerTemplateRaw)
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Spinner } from '/client/lib/spinner';
|
||||
|
||||
(class extends Spinner {
|
||||
}.register('spinner'));
|
||||
|
||||
(class extends Spinner {
|
||||
getSpinnerTemplateRaw() {
|
||||
let ret = super.getSpinnerTemplate() + 'Raw';
|
||||
return ret;
|
||||
}
|
||||
}.register('spinnerRaw'));
|
||||
@@ -1,6 +0,0 @@
|
||||
.sk-spinner.sk-spinner-wave(class=currentBoard.colorClass)
|
||||
.sk-rect1
|
||||
.sk-rect2
|
||||
.sk-rect3
|
||||
.sk-rect4
|
||||
.sk-rect5
|
||||
@@ -0,0 +1,11 @@
|
||||
template(name="spinnerBounce")
|
||||
.sk-spinner.sk-spinner-bounce(class=currentBoard.colorClass)
|
||||
+spinnerBounceRaw
|
||||
|
||||
template(name="spinnerBounceRaw")
|
||||
.sk-bounce1
|
||||
|
|
||||
.sk-bounce2
|
||||
|
|
||||
.sk-bounce3
|
||||
|
|
||||
@@ -0,0 +1,44 @@
|
||||
@import 'nib'
|
||||
|
||||
// From https://github.com/tobiasahlin/SpinKit
|
||||
.sk-spinner-bounce {
|
||||
margin: 100px auto 0;
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
|
||||
div {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #333;
|
||||
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.sk-bounce1 {
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.sk-bounce2 {
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% { -webkit-transform: scale(0) }
|
||||
40% { -webkit-transform: scale(1.0) }
|
||||
}
|
||||
|
||||
@keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
} 40% {
|
||||
-webkit-transform: scale(1.0);
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
template(name="spinnerCube")
|
||||
.sk-spinner.sk-spinner-cube(class=currentBoard.colorClass)
|
||||
+spinnerCubeRaw
|
||||
|
||||
template(name="spinnerCubeRaw")
|
||||
.sk-cube1
|
||||
.sk-cube2
|
||||
.sk-cube3
|
||||
@@ -0,0 +1,52 @@
|
||||
@import 'nib'
|
||||
|
||||
// From https://github.com/tobiasahlin/SpinKit
|
||||
.sk-spinner-cube {
|
||||
margin: 100px auto;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sk-cube1, .sk-cube2 {
|
||||
background-color: #333;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
-webkit-animation: sk-cubemove 1.8s infinite ease-in-out;
|
||||
animation: sk-cubemove 1.8s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.sk-cube2 {
|
||||
-webkit-animation-delay: -0.9s;
|
||||
animation-delay: -0.9s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-cubemove {
|
||||
25% { -webkit-transform: translateX(35px) rotate(-90deg) scale(0.5) }
|
||||
50% { -webkit-transform: translateX(35px) translateY(35px) rotate(-180deg) }
|
||||
75% { -webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5) }
|
||||
100% { -webkit-transform: rotate(-360deg) }
|
||||
}
|
||||
|
||||
@keyframes sk-cubemove {
|
||||
25% {
|
||||
transform: translateX(35px) rotate(-90deg) scale(0.5);
|
||||
-webkit-transform: translateX(35px) rotate(-90deg) scale(0.5);
|
||||
} 50% {
|
||||
transform: translateX(35px) translateY(35px) rotate(-179deg);
|
||||
-webkit-transform: translateX(35px) translateY(35px) rotate(-179deg);
|
||||
} 50.1% {
|
||||
transform: translateX(35px) translateY(35px) rotate(-180deg);
|
||||
-webkit-transform: translateX(35px) translateY(35px) rotate(-180deg);
|
||||
} 75% {
|
||||
transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
||||
-webkit-transform: translateX(0px) translateY(35px) rotate(-270deg) scale(0.5);
|
||||
} 100% {
|
||||
transform: rotate(-360deg);
|
||||
-webkit-transform: rotate(-360deg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
template(name="spinnerCubeGrid")
|
||||
.sk-spinner.sk-spinner-cube-grid(class=currentBoard.colorClass)
|
||||
+spinnerCubeGridRaw
|
||||
|
||||
template(name="spinnerCubeGridRaw")
|
||||
.sk-cube-grid.sk-cube-grid1
|
||||
.sk-cube-grid.sk-cube-grid2
|
||||
.sk-cube-grid.sk-cube-grid3
|
||||
.sk-cube-grid.sk-cube-grid4
|
||||
.sk-cube-grid.sk-cube-grid5
|
||||
.sk-cube-grid.sk-cube-grid6
|
||||
.sk-cube-grid.sk-cube-grid7
|
||||
.sk-cube-grid.sk-cube-grid8
|
||||
.sk-cube-grid.sk-cube-grid9
|
||||
@@ -0,0 +1,64 @@
|
||||
@import 'nib'
|
||||
|
||||
// From https://github.com/tobiasahlin/SpinKit
|
||||
.sk-spinner-cube-grid {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 100px auto;
|
||||
}
|
||||
|
||||
.sk-spinner-cube-grid .sk-cube-grid {
|
||||
width: 33%;
|
||||
height: 33%;
|
||||
background-color: #333;
|
||||
float: left;
|
||||
-webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
|
||||
animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
|
||||
}
|
||||
.sk-spinner-cube-grid .sk-cube-grid1 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s; }
|
||||
.sk-spinner-cube-grid .sk-cube-grid2 {
|
||||
-webkit-animation-delay: 0.3s;
|
||||
animation-delay: 0.3s; }
|
||||
.sk-spinner-cube-grid .sk-cube-grid3 {
|
||||
-webkit-animation-delay: 0.4s;
|
||||
animation-delay: 0.4s; }
|
||||
.sk-spinner-cube-grid .sk-cube-grid4 {
|
||||
-webkit-animation-delay: 0.1s;
|
||||
animation-delay: 0.1s; }
|
||||
.sk-spinner-cube-grid .sk-cube-grid5 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s; }
|
||||
.sk-spinner-cube-grid .sk-cube-grid6 {
|
||||
-webkit-animation-delay: 0.3s;
|
||||
animation-delay: 0.3s; }
|
||||
.sk-spinner-cube-grid .sk-cube-grid7 {
|
||||
-webkit-animation-delay: 0s;
|
||||
animation-delay: 0s; }
|
||||
.sk-spinner-cube-grid .sk-cube-grid8 {
|
||||
-webkit-animation-delay: 0.1s;
|
||||
animation-delay: 0.1s; }
|
||||
.sk-spinner-cube-grid .sk-cube-grid9 {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s; }
|
||||
|
||||
@-webkit-keyframes sk-cubeGridScaleDelay {
|
||||
0%, 70%, 100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
} 35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-cubeGridScaleDelay {
|
||||
0%, 70%, 100% {
|
||||
-webkit-transform: scale3D(1, 1, 1);
|
||||
transform: scale3D(1, 1, 1);
|
||||
} 35% {
|
||||
-webkit-transform: scale3D(0, 0, 1);
|
||||
transform: scale3D(0, 0, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
template(name="spinnerDot")
|
||||
.sk-spinner.sk-spinner-dot(class=currentBoard.colorClass)
|
||||
+spinnerDotRaw
|
||||
|
||||
template(name="spinnerDotRaw")
|
||||
.sk-dot1
|
||||
.sk-dot2
|
||||
@@ -0,0 +1,51 @@
|
||||
@import 'nib'
|
||||
|
||||
// From https://github.com/tobiasahlin/SpinKit
|
||||
.sk-spinner-dot {
|
||||
margin: 100px auto;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
-webkit-animation: sk-rotate 2.0s infinite linear;
|
||||
animation: sk-rotate 2.0s infinite linear;
|
||||
}
|
||||
|
||||
.sk-dot1, .sk-dot2 {
|
||||
width: 40%;
|
||||
height: 40%;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
background-color: #333;
|
||||
border-radius: 100%;
|
||||
|
||||
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.sk-dot2 {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
-webkit-animation-delay: -1.0s;
|
||||
animation-delay: -1.0s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-rotate { 100% { -webkit-transform: rotate(360deg) }}
|
||||
@keyframes sk-rotate { 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg) }}
|
||||
|
||||
@-webkit-keyframes sk-bounce {
|
||||
0%, 100% { -webkit-transform: scale(0.0) }
|
||||
50% { -webkit-transform: scale(1.0) }
|
||||
}
|
||||
|
||||
@keyframes sk-bounce {
|
||||
0%, 100% {
|
||||
transform: scale(0.0);
|
||||
-webkit-transform: scale(0.0);
|
||||
} 50% {
|
||||
transform: scale(1.0);
|
||||
-webkit-transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
template(name="spinnerDoubleBounce")
|
||||
.sk-spinner.sk-spinner-double-bounce(class=currentBoard.colorClass)
|
||||
+spinnerDoubleBounceRaw
|
||||
|
||||
template(name="spinnerDoubleBounceRaw")
|
||||
.sk-double-bounce1
|
||||
.sk-double-bounce2
|
||||
@@ -0,0 +1,44 @@
|
||||
@import 'nib'
|
||||
|
||||
// From https://github.com/tobiasahlin/SpinKit
|
||||
.sk-spinner-double-bounce {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
position: relative;
|
||||
margin: 100px auto;
|
||||
}
|
||||
|
||||
.sk-double-bounce1, .sk-double-bounce2 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: #333;
|
||||
opacity: 0.6;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.sk-double-bounce2 {
|
||||
-webkit-animation-delay: -1.0s;
|
||||
animation-delay: -1.0s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-bounce {
|
||||
0%, 100% { -webkit-transform: scale(0.0) }
|
||||
50% { -webkit-transform: scale(1.0) }
|
||||
}
|
||||
|
||||
@keyframes sk-bounce {
|
||||
0%, 100% {
|
||||
transform: scale(0.0);
|
||||
-webkit-transform: scale(0.0);
|
||||
} 50% {
|
||||
transform: scale(1.0);
|
||||
-webkit-transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
template(name="spinnerRotateplane")
|
||||
.sk-spinner.sk-spinner-rotateplane(class=currentBoard.colorClass)
|
||||
+spinnerRotateplaneRaw
|
||||
|
||||
template(name="spinnerRotateplaneRaw")
|
||||
.sk-rotateplane1
|
||||
@@ -0,0 +1,38 @@
|
||||
@import 'nib'
|
||||
|
||||
// From https://github.com/tobiasahlin/SpinKit
|
||||
.sk-spinner-rotateplane {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
|
||||
margin: 100px auto;
|
||||
-webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
|
||||
animation: sk-rotateplane 1.2s infinite ease-in-out;
|
||||
|
||||
div {
|
||||
background-color: #333;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-rotateplane {
|
||||
0% { -webkit-transform: perspective(120px) }
|
||||
50% { -webkit-transform: perspective(120px) rotateY(180deg) }
|
||||
100% { -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg) }
|
||||
}
|
||||
|
||||
@keyframes sk-rotateplane {
|
||||
0% {
|
||||
transform: perspective(120px) rotateX(0deg) rotateY(0deg);
|
||||
-webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
|
||||
} 50% {
|
||||
transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
|
||||
-webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)
|
||||
} 100% {
|
||||
transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
|
||||
-webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
template(name="spinnerScaleout")
|
||||
.sk-spinner.sk-spinner-scaleout(class=currentBoard.colorClass)
|
||||
+spinnerScaleoutRaw
|
||||
|
||||
template(name="spinnerScaleoutRaw")
|
||||
.sk-scaleout1
|
||||
@@ -0,0 +1,40 @@
|
||||
@import 'nib'
|
||||
|
||||
// From https://github.com/tobiasahlin/SpinKit
|
||||
.sk-spinner-scaleout {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
|
||||
margin: 100px auto;
|
||||
|
||||
border-radius: 100%;
|
||||
-webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
|
||||
animation: sk-scaleout 1.0s infinite ease-in-out;
|
||||
|
||||
div {
|
||||
background-color: #333;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-scaleout {
|
||||
0% { -webkit-transform: scale(0) }
|
||||
100% {
|
||||
-webkit-transform: scale(1.0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-scaleout {
|
||||
0% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
} 100% {
|
||||
-webkit-transform: scale(1.0);
|
||||
transform: scale(1.0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
template(name="spinnerWave")
|
||||
.sk-spinner.sk-spinner-wave(class=currentBoard.colorClass)
|
||||
+spinnerWaveRaw
|
||||
|
||||
template(name="spinnerWaveRaw")
|
||||
.sk-rect1
|
||||
|
|
||||
.sk-rect2
|
||||
|
|
||||
.sk-rect3
|
||||
|
|
||||
.sk-rect4
|
||||
|
|
||||
.sk-rect5
|
||||
|
|
||||
@@ -1,21 +1,7 @@
|
||||
@import 'nib'
|
||||
|
||||
/*
|
||||
* From https://github.com/tobiasahlin/SpinKit
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* <div class="sk-spinner sk-spinner-wave">
|
||||
* <div class="sk-rect1"></div>
|
||||
* <div class="sk-rect2"></div>
|
||||
* <div class="sk-rect3"></div>
|
||||
* <div class="sk-rect4"></div>
|
||||
* <div class="sk-rect5"></div>
|
||||
* </div>
|
||||
*
|
||||
*/
|
||||
|
||||
.sk-spinner {
|
||||
// From https://github.com/tobiasahlin/SpinKit
|
||||
.sk-spinner-wave {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: auto;
|
||||
@@ -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)
|
||||
@@ -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,40 @@ 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#deleteOrgWarningMessage.hide
|
||||
| {{_ 'delete-org-warning-message'}}
|
||||
br
|
||||
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#deleteTeamWarningMessage.hide
|
||||
| {{_ 'delete-team-warning-message'}}
|
||||
br
|
||||
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,21 +571,19 @@ template(name="settingsUserPopup")
|
||||
a.impersonate-user
|
||||
i.fa.fa-user
|
||||
| {{_ 'impersonate-user'}}
|
||||
// Delete is not enabled yet, because it does leave empty user avatars
|
||||
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 enabled, but there is still bug of leaving empty user avatars
|
||||
// to boards: boards members, card members and assignees have
|
||||
// empty users. See:
|
||||
// empty users. So it is better to remove user from all boards before removing user.
|
||||
// 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'}}")
|
||||
@@ -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,42 @@ Template.editUserPopup.events({
|
||||
},
|
||||
});
|
||||
|
||||
let userTeamsList = userTeams.split(",");
|
||||
let userTeamsIdsList = userTeamsIds.split(",");
|
||||
let userTms = [];
|
||||
if(userTeams != ''){
|
||||
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 = [];
|
||||
if(userOrgs != ''){
|
||||
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 +602,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 +622,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 +635,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(selectedEltValueId);
|
||||
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 +776,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 +802,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 +830,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 +865,8 @@ Template.newUserPopup.events({
|
||||
isActive,
|
||||
email.toLowerCase(),
|
||||
importUsernames,
|
||||
userOrganizations,
|
||||
userTms,
|
||||
function(error) {
|
||||
const usernameMessageElement = templateInstance.$('.username-taken');
|
||||
const emailMessageElement = templateInstance.$('.email-taken');
|
||||
@@ -694,13 +888,85 @@ 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();
|
||||
if(Users.find({"orgs.orgId": this.orgId}).count() > 0)
|
||||
{
|
||||
let orgClassList = document.getElementById("deleteOrgWarningMessage").classList;
|
||||
if(orgClassList.contains('hide'))
|
||||
{
|
||||
orgClassList.remove('hide');
|
||||
document.getElementById("deleteOrgWarningMessage").style.color = "red";
|
||||
}
|
||||
return;
|
||||
}
|
||||
Org.remove(this.orgId);
|
||||
Popup.close();
|
||||
}
|
||||
});
|
||||
|
||||
Template.settingsTeamPopup.events({
|
||||
'click #deleteButton'(event) {
|
||||
event.preventDefault();
|
||||
if(Users.find({"teams.teamId": this.teamId}).count() > 0)
|
||||
{
|
||||
let teamClassList = document.getElementById("deleteTeamWarningMessage").classList;
|
||||
if(teamClassList.contains('hide'))
|
||||
{
|
||||
teamClassList.remove('hide');
|
||||
document.getElementById("deleteTeamWarningMessage").style.color = "red";
|
||||
}
|
||||
return;
|
||||
}
|
||||
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 +986,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;
|
||||
@@ -173,6 +173,9 @@ template(name='layoutSettings')
|
||||
li.layout-form
|
||||
.title {{_ 'default-authentication-method'}}
|
||||
+selectAuthenticationMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
|
||||
li.layout-form
|
||||
.title {{_ 'wait-spinner'}}
|
||||
+selectSpinnerName(spinnerName=currentSetting.spinnerName)
|
||||
li.layout-form
|
||||
.title {{_ 'custom-product-name'}}
|
||||
.form-group
|
||||
@@ -222,3 +225,11 @@ template(name='selectAuthenticationMethod')
|
||||
option(value="{{value}}" selected) {{_ value}}
|
||||
else
|
||||
option(value="{{value}}") {{_ value}}
|
||||
|
||||
template(name='selectSpinnerName')
|
||||
select#spinnerName
|
||||
each spinner in spinners
|
||||
if isSelected spinner
|
||||
option(value="{{spinner}}" selected) {{_ spinner}}
|
||||
else
|
||||
option(value="{{spinner}}") {{_ spinner}}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ALLOWED_WAIT_SPINNERS } from '/config/const';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
@@ -199,6 +201,8 @@ BlazeComponent.extendComponent({
|
||||
$('input[name=displayAuthenticationMethod]:checked').val() === 'true';
|
||||
const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
|
||||
|
||||
const spinnerName = $('#spinnerName').val();
|
||||
|
||||
try {
|
||||
Settings.update(Settings.findOne()._id, {
|
||||
$set: {
|
||||
@@ -213,6 +217,7 @@ BlazeComponent.extendComponent({
|
||||
displayAuthenticationMethod,
|
||||
defaultAuthenticationMethod,
|
||||
automaticLinkedUrlSchemes,
|
||||
spinnerName,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -384,3 +389,12 @@ Template.selectAuthenticationMethod.helpers({
|
||||
return Template.instance().data.authenticationMethod === match;
|
||||
},
|
||||
});
|
||||
|
||||
Template.selectSpinnerName.helpers({
|
||||
spinners() {
|
||||
return ALLOWED_WAIT_SPINNERS;
|
||||
},
|
||||
isSelected(match) {
|
||||
return Template.instance().data.spinnerName === match;
|
||||
},
|
||||
});
|
||||
@@ -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}}")
|
||||
@@ -35,10 +31,36 @@ template(name='homeSidebar')
|
||||
+activities(mode="board")
|
||||
|
||||
template(name="membersWidget")
|
||||
.board-widget.board-widget-members
|
||||
h3
|
||||
i.fa.fa-users
|
||||
| {{_ 'organizations'}}
|
||||
|
||||
.board-widget-content
|
||||
+boardOrgGeneral
|
||||
.clearfix
|
||||
br
|
||||
hr
|
||||
.board-widget.board-widget-members
|
||||
h3
|
||||
i.fa.fa-users
|
||||
| {{_ 'teams'}}
|
||||
|
||||
.board-widget-content
|
||||
+boardTeamGeneral
|
||||
.clearfix
|
||||
br
|
||||
hr
|
||||
.board-widget.board-widget-members
|
||||
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 +71,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
|
||||
@@ -63,6 +85,30 @@ template(name="membersWidget")
|
||||
button.js-member-invite-accept.primary {{_ 'accept'}}
|
||||
button.js-member-invite-decline {{_ 'decline'}}
|
||||
|
||||
template(name="boardOrgGeneral")
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
th {{_ 'displayName'}}
|
||||
th
|
||||
if currentUser.isBoardAdmin
|
||||
a.member.orgOrTeamMember.add-member.js-manage-board-addOrg(title="{{_ 'add-members'}}")
|
||||
i.fa.fa-plus
|
||||
each org in currentBoard.activeOrgs
|
||||
+boardOrgRow(orgId=org.orgId)
|
||||
|
||||
template(name="boardTeamGeneral")
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
th {{_ 'displayName'}}
|
||||
th
|
||||
if currentUser.isBoardAdmin
|
||||
a.member.orgOrTeamMember.add-member.js-manage-board-addTeam(title="{{_ 'add-members'}}")
|
||||
i.fa.fa-plus
|
||||
each currentBoard.activeTeams
|
||||
+boardTeamRow(teamId=this.teamId)
|
||||
|
||||
template(name="boardChangeColorPopup")
|
||||
.board-backgrounds-list.clearfix
|
||||
each backgroundColors
|
||||
@@ -131,6 +177,12 @@ template(name="boardCardSettingsPopup")
|
||||
span
|
||||
i.fa.fa-user-plus
|
||||
| {{_ 'requested-by'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-card-sorting-by-number(class="{{#if allowsCardSortingByNumber}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsCardSortingByNumber}}is-checked{{/if}}")
|
||||
span
|
||||
i.fa.fa-sort
|
||||
| {{_ 'card-sorting-by-number'}}
|
||||
div.check-div
|
||||
a.flex.js-field-has-labels(class="{{#if allowsLabels}}is-checked{{/if}}")
|
||||
.materialCheckBox(class="{{#if allowsLabels}}is-checked{{/if}}")
|
||||
@@ -362,7 +414,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")
|
||||
@@ -401,6 +453,40 @@ template(name="leaveBoardPopup")
|
||||
p {{_ 'leave-board-pop' boardTitle=board.title}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'leave-board'}}
|
||||
|
||||
template(name="addBoardOrgPopup")
|
||||
select.js-boardOrgs#jsBoardOrgs
|
||||
option(value="-1") {{_ 'organizations'}} :
|
||||
each value in orgsDatas
|
||||
option(value="{{value._id}}") {{_ value.orgDisplayName}}
|
||||
|
||||
template(name="removeBoardOrgPopup")
|
||||
form
|
||||
input.hide#hideOrgId(type="text" value=org._id)
|
||||
label
|
||||
| {{_ 'leave-board'}} ?
|
||||
br
|
||||
hr
|
||||
div.buttonsContainer
|
||||
input.primary.wide.leaveBoardBtn#leaveBoardBtn(type="submit" value="{{_ 'leave-board'}}")
|
||||
input.primary.wide.cancelLeaveBoardBtn#cancelLeaveBoardBtn(type="submit" value="{{_ 'Cancel'}}")
|
||||
|
||||
template(name="addBoardTeamPopup")
|
||||
select.js-boardTeams#jsBoardTeams
|
||||
option(value="-1") {{_ 'teams'}} :
|
||||
each value in teamsDatas
|
||||
option(value="{{value._id}}") {{_ value.teamDisplayName}}
|
||||
|
||||
template(name="removeBoardTeamPopup")
|
||||
form
|
||||
input.hide#hideTeamId(type="text" value=team._id)
|
||||
label
|
||||
| {{_ 'leave-board'}} ?
|
||||
br
|
||||
hr
|
||||
div.buttonsContainer
|
||||
input.primary.wide.leaveBoardBtn#leaveBoardTeamBtn(type="submit" value="{{_ 'leave-board'}}")
|
||||
input.primary.wide.cancelLeaveBoardBtn#cancelLeaveBoardTeamBtn(type="submit" value="{{_ 'Cancel'}}")
|
||||
|
||||
template(name="addMemberPopup")
|
||||
.js-search-member
|
||||
+esInput(index="users")
|
||||
|
||||
@@ -313,6 +313,8 @@ Template.membersWidget.events({
|
||||
'click .js-member': Popup.open('member'),
|
||||
'click .js-open-board-menu': Popup.open('boardMenu'),
|
||||
'click .js-manage-board-members': Popup.open('addMember'),
|
||||
'click .js-manage-board-addOrg': Popup.open('addBoardOrg'),
|
||||
'click .js-manage-board-addTeam': Popup.open('addBoardTeam'),
|
||||
'click .js-import': Popup.open('boardImportBoard'),
|
||||
submit: this.onSubmit,
|
||||
'click .js-import-board': Popup.open('chooseBoardSource'),
|
||||
@@ -754,6 +756,10 @@ BlazeComponent.extendComponent({
|
||||
return this.currentBoard.allowsRequestedBy;
|
||||
},
|
||||
|
||||
allowsCardSortingByNumber() {
|
||||
return this.currentBoard.allowsCardSortingByNumber;
|
||||
},
|
||||
|
||||
allowsLabels() {
|
||||
return this.currentBoard.allowsLabels;
|
||||
},
|
||||
@@ -968,6 +974,22 @@ BlazeComponent.extendComponent({
|
||||
this.currentBoard.allowsRequestedBy,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-card-sorting-by-number'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsCardSortingByNumber = !this.currentBoard
|
||||
.allowsCardSortingByNumber;
|
||||
this.currentBoard.setAllowsCardSortingByNumber(
|
||||
this.currentBoard.allowsCardSortingByNumber,
|
||||
);
|
||||
$(`.js-field-has-card-sorting-by-number ${MCB}`).toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsCardSortingByNumber,
|
||||
);
|
||||
$('.js-field-has-card-sorting-by-number').toggleClass(
|
||||
CKCLS,
|
||||
this.currentBoard.allowsCardSortingByNumber,
|
||||
);
|
||||
},
|
||||
'click .js-field-has-labels'(evt) {
|
||||
evt.preventDefault();
|
||||
this.currentBoard.allowsLabels = !this.currentBoard.allowsLabels;
|
||||
@@ -1148,6 +1170,283 @@ BlazeComponent.extendComponent({
|
||||
},
|
||||
}).register('addMemberPopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.findOrgsOptions = new ReactiveVar({});
|
||||
|
||||
this.page = new ReactiveVar(1);
|
||||
this.autorun(() => {
|
||||
const limitOrgs = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => {});
|
||||
});
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
this.setLoading(false);
|
||||
},
|
||||
|
||||
setError(error) {
|
||||
this.error.set(error);
|
||||
},
|
||||
|
||||
setLoading(w) {
|
||||
this.loading.set(w);
|
||||
},
|
||||
|
||||
isLoading() {
|
||||
return this.loading.get();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keyup input'() {
|
||||
this.setError('');
|
||||
},
|
||||
'change #jsBoardOrgs'() {
|
||||
let currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let selectElt = document.getElementById("jsBoardOrgs");
|
||||
let selectedOrgId = selectElt.options[selectElt.selectedIndex].value;
|
||||
let selectedOrgDisplayName = selectElt.options[selectElt.selectedIndex].text;
|
||||
let boardOrganizations = [];
|
||||
if(currentBoard.orgs !== undefined){
|
||||
for(let i = 0; i < currentBoard.orgs.length; i++){
|
||||
boardOrganizations.push(currentBoard.orgs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(!boardOrganizations.some((org) => org.orgDisplayName == selectedOrgDisplayName)){
|
||||
boardOrganizations.push({
|
||||
"orgId": selectedOrgId,
|
||||
"orgDisplayName": selectedOrgDisplayName,
|
||||
"isActive" : true,
|
||||
})
|
||||
|
||||
if (selectedOrgId != "-1") {
|
||||
Meteor.call('setBoardOrgs', boardOrganizations, currentBoard._id);
|
||||
}
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('addBoardOrgPopup');
|
||||
|
||||
Template.addBoardOrgPopup.helpers({
|
||||
orgsDatas() {
|
||||
// return Org.find({}, {sort: { createdAt: -1 }});
|
||||
let orgs = Org.find({}, {sort: { createdAt: -1 }});
|
||||
return orgs;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.findOrgsOptions = new ReactiveVar({});
|
||||
|
||||
this.page = new ReactiveVar(1);
|
||||
this.autorun(() => {
|
||||
const limitOrgs = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => {});
|
||||
});
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
this.setLoading(false);
|
||||
},
|
||||
|
||||
setError(error) {
|
||||
this.error.set(error);
|
||||
},
|
||||
|
||||
setLoading(w) {
|
||||
this.loading.set(w);
|
||||
},
|
||||
|
||||
isLoading() {
|
||||
return this.loading.get();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keyup input'() {
|
||||
this.setError('');
|
||||
},
|
||||
'click #leaveBoardBtn'(){
|
||||
let stringOrgId = document.getElementById('hideOrgId').value;
|
||||
let currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let boardOrganizations = [];
|
||||
if(currentBoard.orgs !== undefined){
|
||||
for(let i = 0; i < currentBoard.orgs.length; i++){
|
||||
if(currentBoard.orgs[i].orgId != stringOrgId){
|
||||
boardOrganizations.push(currentBoard.orgs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Meteor.call('setBoardOrgs', boardOrganizations, currentBoard._id);
|
||||
|
||||
Popup.close();
|
||||
},
|
||||
'click #cancelLeaveBoardBtn'(){
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('removeBoardOrgPopup');
|
||||
|
||||
Template.removeBoardOrgPopup.helpers({
|
||||
org() {
|
||||
return Org.findOne(this.orgId);
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.findOrgsOptions = new ReactiveVar({});
|
||||
|
||||
this.page = new ReactiveVar(1);
|
||||
this.autorun(() => {
|
||||
const limitTeams = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('team', this.findOrgsOptions.get(), limitTeams, () => {});
|
||||
});
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
this.setLoading(false);
|
||||
},
|
||||
|
||||
setError(error) {
|
||||
this.error.set(error);
|
||||
},
|
||||
|
||||
setLoading(w) {
|
||||
this.loading.set(w);
|
||||
},
|
||||
|
||||
isLoading() {
|
||||
return this.loading.get();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keyup input'() {
|
||||
this.setError('');
|
||||
},
|
||||
'change #jsBoardTeams'() {
|
||||
let currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let selectElt = document.getElementById("jsBoardTeams");
|
||||
let selectedTeamId = selectElt.options[selectElt.selectedIndex].value;
|
||||
let selectedTeamDisplayName = selectElt.options[selectElt.selectedIndex].text;
|
||||
let boardTeams = [];
|
||||
if(currentBoard.teams !== undefined){
|
||||
for(let i = 0; i < currentBoard.teams.length; i++){
|
||||
boardTeams.push(currentBoard.teams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(!boardTeams.some((team) => team.teamDisplayName == selectedTeamDisplayName)){
|
||||
boardTeams.push({
|
||||
"teamId": selectedTeamId,
|
||||
"teamDisplayName": selectedTeamDisplayName,
|
||||
"isActive" : true,
|
||||
})
|
||||
|
||||
if (selectedTeamId != "-1") {
|
||||
Meteor.call('setBoardTeams', boardTeams, currentBoard._id);
|
||||
}
|
||||
}
|
||||
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('addBoardTeamPopup');
|
||||
|
||||
Template.addBoardTeamPopup.helpers({
|
||||
teamsDatas() {
|
||||
let teams = Team.find({}, {sort: { createdAt: -1 }});
|
||||
return teams;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.findOrgsOptions = new ReactiveVar({});
|
||||
|
||||
this.page = new ReactiveVar(1);
|
||||
this.autorun(() => {
|
||||
const limitTeams = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('team', this.findOrgsOptions.get(), limitTeams, () => {});
|
||||
});
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
this.setLoading(false);
|
||||
},
|
||||
|
||||
setError(error) {
|
||||
this.error.set(error);
|
||||
},
|
||||
|
||||
setLoading(w) {
|
||||
this.loading.set(w);
|
||||
},
|
||||
|
||||
isLoading() {
|
||||
return this.loading.get();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keyup input'() {
|
||||
this.setError('');
|
||||
},
|
||||
'click #leaveBoardTeamBtn'(){
|
||||
let stringTeamId = document.getElementById('hideTeamId').value;
|
||||
let currentBoard = Boards.findOne(Session.get('currentBoard'));
|
||||
let boardTeams = [];
|
||||
if(currentBoard.teams !== undefined){
|
||||
for(let i = 0; i < currentBoard.teams.length; i++){
|
||||
if(currentBoard.teams[i].teamId != stringTeamId){
|
||||
boardTeams.push(currentBoard.teams[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Meteor.call('setBoardTeams', boardTeams, currentBoard._id);
|
||||
|
||||
Popup.close();
|
||||
},
|
||||
'click #cancelLeaveBoardTeamBtn'(){
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('removeBoardTeamPopup');
|
||||
|
||||
Template.removeBoardTeamPopup.helpers({
|
||||
team() {
|
||||
return Team.findOne(this.teamId);
|
||||
},
|
||||
});
|
||||
|
||||
Template.changePermissionsPopup.events({
|
||||
'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only, click .js-set-worker'(
|
||||
event,
|
||||
|
||||
@@ -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
|
||||
@@ -215,3 +214,13 @@
|
||||
i.fa
|
||||
padding: 8px 0px 8px 16px
|
||||
font-weight: bold
|
||||
|
||||
#jsBoardOrgs, #jsBoardTeams
|
||||
width: 90%
|
||||
|
||||
.leaveBoardBtn
|
||||
background-color: green !important
|
||||
|
||||
.cancelLeaveBoardBtn
|
||||
margin-left: 5% !important
|
||||
background-color: red !important
|
||||
@@ -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
|
||||
@@ -11,13 +11,20 @@ template(name="swimlaneHeader")
|
||||
template(name="swimlaneFixedHeader")
|
||||
.swimlane-header(
|
||||
class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}")
|
||||
+viewer
|
||||
= title
|
||||
if $eq title 'Card Templates'
|
||||
| {{_ 'card-templates-swimlane'}}
|
||||
else if $eq title 'List Templates'
|
||||
| {{_ 'list-templates-swimlane'}}
|
||||
else if $eq title 'Board Templates'
|
||||
| {{_ 'board-templates-swimlane'}}
|
||||
else
|
||||
+viewer
|
||||
= title
|
||||
.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 +46,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'));
|
||||
@@ -19,6 +19,44 @@ template(name="userAvatarInitials")
|
||||
svg.avatar.avatar-initials(viewBox="0 0 {{viewPortWidth}} 15")
|
||||
text(x="50%" y="13" text-anchor="middle")= initials
|
||||
|
||||
template(name="orgAvatar")
|
||||
a.member.orgOrTeamMember(class="js-member" title="{{orgData.orgDisplayName}}")
|
||||
+boardOrgName(orgId=orgData._id)
|
||||
|
||||
template(name="boardOrgRow")
|
||||
tr
|
||||
if orgData.orgIsActive
|
||||
td <s>{{ orgData.orgDisplayName }}</s>
|
||||
else
|
||||
td {{ orgData.orgDisplayName }}
|
||||
td
|
||||
if currentUser.isBoardAdmin
|
||||
a.member.orgOrTeamMember.add-member.js-manage-board-removeOrg(title="{{_ 'remove-from-board'}}")
|
||||
i.fa.fa-minus
|
||||
|
||||
template(name="boardTeamRow")
|
||||
tr
|
||||
if teamData.teamIsActive
|
||||
td <s>{{ teamData.teamDisplayName }}</s>
|
||||
else
|
||||
td {{ teamData.teamDisplayName }}
|
||||
td
|
||||
if currentUser.isBoardAdmin
|
||||
a.member.orgOrTeamMember.add-member.js-manage-board-removeTeam(title="{{_ 'remove-from-board'}}")
|
||||
i.fa.fa-minus
|
||||
|
||||
template(name="boardOrgName")
|
||||
svg.avatar.avatar-initials(viewBox="0 0 {{orgViewPortWidth}} 15")
|
||||
text(x="50%" y="13" text-anchor="middle")= orgName
|
||||
|
||||
template(name="teamAvatar")
|
||||
a.member.orgOrTeamMember(class="js-member" title="{{teamData.teamDisplayName}}")
|
||||
+boardTeamName(orgId=orgData._id)
|
||||
|
||||
template(name="boardTeamName")
|
||||
svg.avatar.avatar-initials(viewBox="0 0 {{teamViewPortWidth}} 15")
|
||||
text(x="50%" y="13" text-anchor="middle")= teamName
|
||||
|
||||
template(name="userPopup")
|
||||
.board-member-menu
|
||||
.mini-profile-info
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import Cards from '/models/cards';
|
||||
import Avatars from '/models/avatars';
|
||||
import Users from '/models/users';
|
||||
import Org from '/models/org';
|
||||
import Team from '/models/team';
|
||||
|
||||
Template.userAvatar.helpers({
|
||||
userData() {
|
||||
@@ -46,6 +48,132 @@ Template.userAvatarInitials.helpers({
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.findOrgsOptions = new ReactiveVar({});
|
||||
|
||||
this.page = new ReactiveVar(1);
|
||||
this.autorun(() => {
|
||||
const limitOrgs = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => {});
|
||||
});
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
this.setLoading(false);
|
||||
},
|
||||
|
||||
setError(error) {
|
||||
this.error.set(error);
|
||||
},
|
||||
|
||||
setLoading(w) {
|
||||
this.loading.set(w);
|
||||
},
|
||||
|
||||
isLoading() {
|
||||
return this.loading.get();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keyup input'() {
|
||||
this.setError('');
|
||||
},
|
||||
'click .js-manage-board-removeOrg': Popup.open('removeBoardOrg'),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('boardOrgRow');
|
||||
|
||||
Template.boardOrgRow.helpers({
|
||||
orgData() {
|
||||
const orgCollection = this.esSearch ? ESSearchResults : Org;
|
||||
return orgCollection.findOne(this.orgId);
|
||||
},
|
||||
currentUser(){
|
||||
return Meteor.user();
|
||||
},
|
||||
});
|
||||
|
||||
Template.boardOrgName.helpers({
|
||||
orgName() {
|
||||
const org = Org.findOne(this.orgId);
|
||||
return org && org.orgDisplayName;
|
||||
},
|
||||
|
||||
orgViewPortWidth() {
|
||||
const org = Org.findOne(this.orgId);
|
||||
return ((org && org.orgDisplayName.length) || 1) * 12;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
this.findOrgsOptions = new ReactiveVar({});
|
||||
|
||||
this.page = new ReactiveVar(1);
|
||||
this.autorun(() => {
|
||||
const limitTeams = this.page.get() * Number.MAX_SAFE_INTEGER;
|
||||
this.subscribe('team', this.findOrgsOptions.get(), limitTeams, () => {});
|
||||
});
|
||||
},
|
||||
|
||||
onRendered() {
|
||||
this.setLoading(false);
|
||||
},
|
||||
|
||||
setError(error) {
|
||||
this.error.set(error);
|
||||
},
|
||||
|
||||
setLoading(w) {
|
||||
this.loading.set(w);
|
||||
},
|
||||
|
||||
isLoading() {
|
||||
return this.loading.get();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'keyup input'() {
|
||||
this.setError('');
|
||||
},
|
||||
'click .js-manage-board-removeTeam': Popup.open('removeBoardTeam'),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('boardTeamRow');
|
||||
|
||||
Template.boardTeamRow.helpers({
|
||||
teamData() {
|
||||
const teamCollection = this.esSearch ? ESSearchResults : Team;
|
||||
return teamCollection.findOne(this.teamId);
|
||||
},
|
||||
currentUser(){
|
||||
return Meteor.user();
|
||||
},
|
||||
});
|
||||
|
||||
Template.boardTeamName.helpers({
|
||||
teamName() {
|
||||
const team = Team.findOne(this.teamId);
|
||||
return team && team.teamDisplayName;
|
||||
},
|
||||
|
||||
teamViewPortWidth() {
|
||||
const team = Team.findOne(this.teamId);
|
||||
return ((team && team.teamDisplayName.length) || 1) * 12;
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -175,7 +175,7 @@ Template.changeLanguagePopup.helpers({
|
||||
// fa-IR = Persian (Iran)
|
||||
name = 'فارسی/پارسی (ایران)';
|
||||
} else if (lang.name === 'de-CH') {
|
||||
name = 'Schwiizerdütsch';
|
||||
name = 'Deutsch (Schweiz)';
|
||||
} else if (lang.name === 'fr-BE') {
|
||||
name = 'Français (Belgique)';
|
||||
} else if (lang.name === 'fr-CA') {
|
||||
@@ -186,6 +186,8 @@ Template.changeLanguagePopup.helpers({
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'latviešu valoda') {
|
||||
name = 'Latviešu';
|
||||
} else if (lang.name === 'en-IT') {
|
||||
name = 'English (Italy)';
|
||||
} else if (lang.name === 'Español') {
|
||||
name = 'español';
|
||||
} else if (lang.name === 'es_419') {
|
||||
|
||||
@@ -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',
|
||||
],
|
||||
|
||||
|
||||
Executable → Regular
File mode changed.
@@ -0,0 +1,27 @@
|
||||
Meteor.subscribe('setting');
|
||||
|
||||
import { ALLOWED_WAIT_SPINNERS } from '/config/const';
|
||||
|
||||
export class Spinner extends BlazeComponent {
|
||||
currentSettings() {
|
||||
return Settings.findOne();
|
||||
}
|
||||
|
||||
getSpinnerName() {
|
||||
let ret = 'Bounce';
|
||||
let defaultWaitSpinner = Meteor.settings.public.WAIT_SPINNER;
|
||||
if (defaultWaitSpinner && ALLOWED_WAIT_SPINNERS.includes(defaultWaitSpinner)) {
|
||||
ret = defaultWaitSpinner;
|
||||
}
|
||||
let settings = this.currentSettings();
|
||||
|
||||
if (settings && settings.spinnerName) {
|
||||
ret = settings.spinnerName;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
getSpinnerTemplate() {
|
||||
return 'spinner' + this.getSpinnerName().replace(/-/, '');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/* eslint-env mocha */
|
||||
import sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
import { Random } from 'meteor/random';
|
||||
import '../utils';
|
||||
|
||||
|
||||
describe('Utils', function() {
|
||||
beforeEach(function() {
|
||||
sinon.stub(Utils, 'reload').callsFake(() => {});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
window.localStorage.removeItem(boardView);
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
const boardView = 'boardView';
|
||||
|
||||
describe(Utils.setBoardView.name, function() {
|
||||
it('sets the board view if the user exists', function(done) {
|
||||
const viewId = Random.id();
|
||||
const user = {
|
||||
setBoardView: (view) => {
|
||||
expect(view).to.equal(viewId);
|
||||
done();
|
||||
},
|
||||
};
|
||||
sinon.stub(Meteor, 'user').callsFake(() => user);
|
||||
Utils.setBoardView(viewId);
|
||||
|
||||
expect(window.localStorage.getItem(boardView)).to.equal(viewId);
|
||||
});
|
||||
|
||||
it('sets a specific view if no user exists but a view is defined', function() {
|
||||
const views = [
|
||||
'board-view-swimlanes',
|
||||
'board-view-lists',
|
||||
'board-view-cal'
|
||||
];
|
||||
|
||||
sinon.stub(Meteor, 'user').callsFake(() => {});
|
||||
|
||||
views.forEach(viewName => {
|
||||
Utils.setBoardView(viewName);
|
||||
expect(window.localStorage.getItem(boardView)).to.equal(viewName);
|
||||
});
|
||||
});
|
||||
|
||||
it('sets a default view if no user and no view are given', function() {
|
||||
sinon.stub(Meteor, 'user').callsFake(() => {});
|
||||
Utils.setBoardView();
|
||||
expect(window.localStorage.getItem(boardView)).to.equal('board-view-swimlanes');
|
||||
});
|
||||
});
|
||||
|
||||
describe(Utils.unsetBoardView.name, function() {
|
||||
it('removes the boardview from localStoage', function() {
|
||||
window.localStorage.setItem(boardView, Random.id());
|
||||
window.localStorage.setItem('collapseSwimlane', Random.id());
|
||||
|
||||
Utils.unsetBoardView();
|
||||
|
||||
expect(window.localStorage.getItem(boardView)).to.equal(null);
|
||||
expect(window.localStorage.getItem('collapseSwimlane')).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe(Utils.boardView.name, function() {
|
||||
it('returns the user\'s board view if a user exists', function() {
|
||||
const viewId = Random.id();
|
||||
const user = {};
|
||||
sinon.stub(Meteor, 'user').callsFake(() => user);
|
||||
expect(Utils.boardView()).to.equal(undefined);
|
||||
|
||||
const boardView = Random.id();
|
||||
user.profile = { boardView };
|
||||
|
||||
expect(Utils.boardView()).to.equal(boardView);
|
||||
});
|
||||
it('returns the current defined view', function() {
|
||||
const views = [
|
||||
'board-view-swimlanes',
|
||||
'board-view-lists',
|
||||
'board-view-cal'
|
||||
];
|
||||
|
||||
sinon.stub(Meteor, 'user').callsFake(() => {});
|
||||
|
||||
views.forEach(viewName => {
|
||||
window.localStorage.setItem(boardView, viewName);
|
||||
expect(Utils.boardView()).to.equal(viewName);
|
||||
});
|
||||
});
|
||||
it('returns a default if nothing is set', function() {
|
||||
sinon.stub(Meteor, 'user').callsFake(() => {});
|
||||
expect(Utils.boardView()).to.equal('board-view-swimlanes');
|
||||
expect(window.localStorage.getItem(boardView)).to.equal('board-view-swimlanes');
|
||||
});
|
||||
});
|
||||
|
||||
describe(Utils.myCardsSort.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.myCardsSortToggle.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.setMyCardsSort.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.archivedBoardIds.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.dueCardsView.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.setDueCardsView.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.goBoardId.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.goCardId.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.processUploadedAttachment.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.shrinkImage.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.capitalize.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.isMiniScreen.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.isShowDesktopDragHandles.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.isMiniScreenOrShowDesktopDragHandles.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.calculateIndexData.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.calculateIndex.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.isTouchDevice.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.calculateTouchDistance.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.enableClickOnTouch.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.manageCustomUI.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.setCustomUI.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.setMatomo.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.manageMatomo.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
|
||||
describe(Utils.getTriggerActionDesc.name, function() {
|
||||
it('has no tests yet');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
import './Utils.tests';
|
||||
+14
-7
@@ -1,20 +1,27 @@
|
||||
Utils = {
|
||||
reload () {
|
||||
// we move all window.location.reload calls into this function
|
||||
// so we can disable it when running tests.
|
||||
// This is because we are not allowed to override location.reload but
|
||||
// we can override Utils.reload to prevent reload during tests.
|
||||
window.location.reload();
|
||||
},
|
||||
setBoardView(view) {
|
||||
currentUser = Meteor.user();
|
||||
if (currentUser) {
|
||||
Meteor.user().setBoardView(view);
|
||||
} else if (view === 'board-view-swimlanes') {
|
||||
window.localStorage.setItem('boardView', 'board-view-swimlanes'); //true
|
||||
location.reload();
|
||||
Utils.reload();
|
||||
} else if (view === 'board-view-lists') {
|
||||
window.localStorage.setItem('boardView', 'board-view-lists'); //true
|
||||
location.reload();
|
||||
Utils.reload();
|
||||
} else if (view === 'board-view-cal') {
|
||||
window.localStorage.setItem('boardView', 'board-view-cal'); //true
|
||||
location.reload();
|
||||
Utils.reload();
|
||||
} else {
|
||||
window.localStorage.setItem('boardView', 'board-view-swimlanes'); //true
|
||||
location.reload();
|
||||
Utils.reload();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -39,7 +46,7 @@ Utils = {
|
||||
return 'board-view-cal';
|
||||
} else {
|
||||
window.localStorage.setItem('boardView', 'board-view-swimlanes'); //true
|
||||
location.reload();
|
||||
Utils.reload();
|
||||
return 'board-view-swimlanes';
|
||||
}
|
||||
},
|
||||
@@ -64,7 +71,7 @@ Utils = {
|
||||
|
||||
setMyCardsSort(sort) {
|
||||
window.localStorage.setItem('myCardsSort', sort);
|
||||
location.reload();
|
||||
Utils.reload();
|
||||
},
|
||||
|
||||
archivedBoardIds() {
|
||||
@@ -87,7 +94,7 @@ Utils = {
|
||||
|
||||
setDueCardsView(view) {
|
||||
window.localStorage.setItem('dueCardsView', view);
|
||||
location.reload();
|
||||
Utils.reload();
|
||||
},
|
||||
|
||||
// XXX We should remove these two methods
|
||||
|
||||
@@ -49,3 +49,13 @@ 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';
|
||||
export const ALLOWED_WAIT_SPINNERS = [
|
||||
'Bounce',
|
||||
'Cube',
|
||||
'Cube-Grid',
|
||||
'Dot',
|
||||
'Double-Bounce',
|
||||
'Rotateplane',
|
||||
'Scaleout',
|
||||
'Wave'
|
||||
];
|
||||
+48
-14
@@ -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
|
||||
@@ -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 ====
|
||||
@@ -440,9 +440,47 @@ services:
|
||||
# The host server for the LDAP server
|
||||
#- LDAP_HOST=localhost
|
||||
#
|
||||
#-----------------------------------------------------------------
|
||||
# ==== LDAP AD Simple Auth ====
|
||||
#
|
||||
# Set to true, if you want to connect with Active Directory by Simple Authentication.
|
||||
# When using AD Simple Auth, LDAP_BASEDN is not needed.
|
||||
#
|
||||
# Example:
|
||||
#- LDAP_AD_SIMPLE_AUTH=true
|
||||
#
|
||||
# === LDAP User Authentication ===
|
||||
#
|
||||
# a) Option to login to the LDAP server with the user's own username and password, instead of
|
||||
# an administrator key. Default: false (use administrator key).
|
||||
#
|
||||
# b) When using AD Simple Auth, set to true, when login user is used for binding,
|
||||
# and LDAP_BASEDN is not needed.
|
||||
#
|
||||
# Example:
|
||||
#- LDAP_USER_AUTHENTICATION=true
|
||||
#
|
||||
# Which field is used to find the user for the user authentication. Default: uid.
|
||||
#- LDAP_USER_AUTHENTICATION_FIELD=uid
|
||||
#
|
||||
# === LDAP Default Domain ===
|
||||
#
|
||||
# a) In case AD SimpleAuth is configured, the default domain is appended to the given
|
||||
# loginname for creating the correct username for the bind request to AD.
|
||||
#
|
||||
# b) The default domain of the ldap it is used to create email if the field is not map
|
||||
# correctly with the LDAP_SYNC_USER_DATA_FIELDMAP
|
||||
#
|
||||
# Example :
|
||||
#- LDAP_DEFAULT_DOMAIN=mydomain.com
|
||||
#
|
||||
#-----------------------------------------------------------------
|
||||
# ==== LDAP BASEDN Auth ====
|
||||
#
|
||||
# The base DN for the LDAP Tree
|
||||
#- LDAP_BASEDN=ou=user,dc=example,dc=org
|
||||
#
|
||||
#-----------------------------------------------------------------
|
||||
# Fallback on the default authentication method
|
||||
#- LDAP_LOGIN_FALLBACK=false
|
||||
#
|
||||
@@ -493,12 +531,6 @@ services:
|
||||
# Reject Unauthorized Certificate
|
||||
#- LDAP_REJECT_UNAUTHORIZED=false
|
||||
#
|
||||
# Option to login to the LDAP server with the user's own username and password, instead of an administrator key. Default: false (use administrator key).
|
||||
#- LDAP_USER_AUTHENTICATION=true
|
||||
#
|
||||
# Which field is used to find the user for the user authentication. Default: uid.
|
||||
#- LDAP_USER_AUTHENTICATION_FIELD=uid
|
||||
#
|
||||
# Optional extra LDAP filters. Don't forget the outmost enclosing parentheses if needed
|
||||
#- LDAP_USER_SEARCH_FILTER=
|
||||
#
|
||||
@@ -526,7 +558,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.
|
||||
@@ -564,9 +596,8 @@ services:
|
||||
#
|
||||
#- LDAP_SYNC_GROUP_ROLES=
|
||||
#
|
||||
# The default domain of the ldap it is used to create email if the field is not map correctly with the LDAP_SYNC_USER_DATA_FIELDMAP
|
||||
# example :
|
||||
#- LDAP_DEFAULT_DOMAIN=mydomain.com
|
||||
# The default domain of the ldap it is used to create email if the field is not map correctly
|
||||
# with the LDAP_SYNC_USER_DATA_FIELDMAP is defined in setting LDAP_DEFAULT_DOMAIN above.
|
||||
#
|
||||
# Enable/Disable syncing of admin status based on ldap groups:
|
||||
#- LDAP_SYNC_ADMIN_STATUS=true
|
||||
@@ -618,6 +649,9 @@ services:
|
||||
#- SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE=
|
||||
#- SAML_ATTRIBUTES=
|
||||
#---------------------------------------------------------------------
|
||||
# Wait spinner to use
|
||||
# - WAIT_SPINNER=Bounce
|
||||
#---------------------------------------------------------------------
|
||||
depends_on:
|
||||
- wekandb
|
||||
|
||||
|
||||
@@ -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'] = {};
|
||||
|
||||
})();
|
||||
+1062
-1001
File diff suppressed because it is too large.
Load diff
+1062
-1001
File diff suppressed because it is too large.
Load diff
+1062
-1001
File diff suppressed because it is too large.
Load diff
+1062
-1001
File diff suppressed because it is too large.
Load diff
Loaded 100 of 498 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user