Browse Source

Merge branch 'master' of https://github.com/wekan/wekan

John Supplee 3 years ago
parent
commit
0ce2f9ea43
100 changed files with 5088 additions and 765 deletions
  1. 8 1
      .babelrc
  2. 63 18
      .devcontainer/Dockerfile
  3. 12 7
      .devcontainer/docker-compose.yml
  4. 36 0
      .dockerignore
  5. 1 1
      .future-snap/broken-snapcraft.yaml
  6. 1 1
      .future-snap/snapcraft.yaml
  7. 3 0
      .github/ISSUE_TEMPLATE.md
  8. 160 0
      .github/workflows/test_suite.yml
  9. 2 0
      .gitignore
  10. 4 2
      .meteor/packages
  11. 16 12
      .meteor/versions
  12. 1 1
      .travis.yml
  13. 251 0
      CHANGELOG.md
  14. 3 2
      Dockerfile
  15. 2 2
      Dockerfile.arm64v8
  16. 5 0
      README.md
  17. 1 1
      Stackerfile.yml
  18. 48 8
      api.py
  19. 57 18
      client/components/boards/boardColors.styl
  20. 9 3
      client/components/boards/boardHeader.jade
  21. 68 12
      client/components/boards/boardHeader.js
  22. 86 42
      client/components/boards/boardsList.jade
  23. 10 0
      client/components/boards/boardsList.js
  24. 3 0
      client/components/boards/boardsList.styl
  25. 1 2
      client/components/cards/attachments.jade
  26. 1 1
      client/components/cards/cardCustomFields.jade
  27. 27 0
      client/components/cards/cardDate.js
  28. 0 1
      client/components/cards/cardDescription.styl
  29. 563 295
      client/components/cards/cardDetails.jade
  30. 590 26
      client/components/cards/cardDetails.js
  31. 179 4
      client/components/cards/cardDetails.styl
  32. 4 6
      client/components/cards/checklists.jade
  33. 11 0
      client/components/cards/labels.styl
  34. 9 0
      client/components/cards/minicard.jade
  35. 1 2
      client/components/cards/subtasks.jade
  36. 1 1
      client/components/forms/forms.styl
  37. 4 9
      client/components/lists/listBody.jade
  38. 40 29
      client/components/lists/listBody.js
  39. 4 4
      client/components/lists/listHeader.jade
  40. 2 2
      client/components/main/editor.js
  41. 33 31
      client/components/main/globalSearch.js
  42. 3 3
      client/components/main/header.jade
  43. 5 0
      client/components/main/spinner.jade
  44. 11 0
      client/components/main/spinner.js
  45. 0 6
      client/components/main/spinner.tpl.jade
  46. 11 0
      client/components/main/spinner_bounce.jade
  47. 44 0
      client/components/main/spinner_bounce.styl
  48. 8 0
      client/components/main/spinner_cube.jade
  49. 52 0
      client/components/main/spinner_cube.styl
  50. 14 0
      client/components/main/spinner_cube_grid.jade
  51. 64 0
      client/components/main/spinner_cube_grid.styl
  52. 7 0
      client/components/main/spinner_dot.jade
  53. 51 0
      client/components/main/spinner_dot.styl
  54. 7 0
      client/components/main/spinner_double_bounce.jade
  55. 44 0
      client/components/main/spinner_double_bounce.styl
  56. 6 0
      client/components/main/spinner_rotateplane.jade
  57. 38 0
      client/components/main/spinner_rotateplane.styl
  58. 6 0
      client/components/main/spinner_scaleout.jade
  59. 40 0
      client/components/main/spinner_scaleout.styl
  60. 15 0
      client/components/main/spinner_wave.jade
  61. 2 16
      client/components/main/spinner_wave.styl
  62. 1 1
      client/components/notifications/notifications.jade
  63. 116 53
      client/components/settings/peopleBody.jade
  64. 343 92
      client/components/settings/peopleBody.js
  65. 6 0
      client/components/settings/peopleBody.styl
  66. 11 0
      client/components/settings/settingBody.jade
  67. 14 0
      client/components/settings/settingBody.js
  68. 94 8
      client/components/sidebar/sidebar.jade
  69. 299 0
      client/components/sidebar/sidebar.js
  70. 13 4
      client/components/sidebar/sidebar.styl
  71. 12 5
      client/components/swimlanes/swimlaneHeader.jade
  72. 1 2
      client/components/swimlanes/swimlanes.jade
  73. 38 0
      client/components/users/userAvatar.jade
  74. 128 0
      client/components/users/userAvatar.js
  75. 1 1
      client/components/users/userHeader.jade
  76. 27 0
      client/lib/keyboard.js
  77. 197 0
      client/lib/tests/Utils.tests.js
  78. 1 0
      client/lib/tests/index.js
  79. 14 7
      client/lib/utils.js
  80. 11 1
      config/const.js
  81. 3 0
      docker-compose.yml
  82. 53 1
      i18n/ar-EG.i18n.json
  83. 53 1
      i18n/ar.i18n.json
  84. 53 1
      i18n/bg.i18n.json
  85. 53 1
      i18n/br.i18n.json
  86. 53 1
      i18n/ca.i18n.json
  87. 53 1
      i18n/cs.i18n.json
  88. 53 1
      i18n/da.i18n.json
  89. 56 4
      i18n/de-CH.i18n.json
  90. 53 1
      i18n/de.i18n.json
  91. 53 1
      i18n/el.i18n.json
  92. 53 1
      i18n/en-GB.i18n.json
  93. 53 1
      i18n/en.i18n.json
  94. 53 1
      i18n/eo.i18n.json
  95. 53 1
      i18n/es-AR.i18n.json
  96. 53 1
      i18n/es-CL.i18n.json
  97. 53 1
      i18n/es-LA.i18n.json
  98. 53 1
      i18n/es-MX.i18n.json
  99. 53 1
      i18n/es-PE.i18n.json
  100. 53 1
      i18n/es-PY.i18n.json

+ 8 - 1
.babelrc

@@ -1,5 +1,12 @@
 { 
   "presets": [ 
     "@babel/preset-stage-3" 
-  ]
+  ],
+  "env": {
+    "COVERAGE": {
+      "plugins": [
+        "istanbul"
+      ]
+    }
+  }
 }

+ 63 - 18
.devcontainer/Dockerfile

@@ -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.2 \
     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
 
+USER wekan
+
+# Copy source dir
 RUN set -o xtrace \
-  && mkdir /home/wekan/app
+  && 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 ${SRC_PATH} /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"]

+ 12 - 7
.devcontainer/docker-compose.yml

@@ -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:

+ 36 - 0
.dockerignore

@@ -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

+ 1 - 1
.future-snap/broken-snapcraft.yaml

@@ -81,7 +81,7 @@ parts:
     wekan:
         source: .
         plugin: nodejs
-        node-engine: 12.22.1
+        node-engine: 12.22.2
         node-packages:
             - node-gyp
             - node-pre-gyp

+ 1 - 1
.future-snap/snapcraft.yaml

@@ -83,7 +83,7 @@ parts:
     wekan:
         source: .
         plugin: nodejs
-        node-engine: 12.22.1
+        node-engine: 12.22.2
         node-packages:
             - node-gyp
             - node-pre-gyp

+ 3 - 0
.github/ISSUE_TEMPLATE.md

@@ -1,5 +1,8 @@
 ## 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
 

+ 160 - 0
.github/workflows/test_suite.yml

@@ -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!

+ 2 - 0
.gitignore

@@ -32,3 +32,5 @@ ehthumbs.db
 .eslintcache
 .meteor/local
 .devcontainer/docker-compose.extend.yml
+.devcontainer/volumes*/
+.coverage

+ 4 - 2
.meteor/packages

@@ -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
@@ -92,7 +91,6 @@ wekan-markdown
 konecty:mongo-counter
 percolate:synced-cron
 wekan-cfs-filesystem
-tmeasday:check-npm-versions
 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

+ 16 - 12
.meteor/versions

@@ -15,8 +15,8 @@ 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.1
+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
@@ -42,8 +42,8 @@ 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
@@ -53,8 +53,8 @@ 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.1
-htmljs@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
@@ -69,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
@@ -83,6 +83,9 @@ meteorhacks:collection-utils@1.2.0
 meteorhacks:picker@1.0.3
 meteorhacks:subs-manager@1.6.4
 meteorspark:util@0.2.0
+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
@@ -107,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
@@ -193,7 +197,7 @@ simple:rest-bearer-token-parser@1.0.1
 simple:rest-json-error-handler@1.0.1
 socket-stream-client@0.3.3
 softwarerero:accounts-t9n@1.3.11
-spacebars@1.1.0
+spacebars@1.2.0
 spacebars-compiler@1.2.1
 srp@1.1.0
 standard-minifier-css@1.7.2
@@ -204,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
@@ -220,6 +223,7 @@ 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

+ 1 - 1
.travis.yml

@@ -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.2
   TRAVIS_NPM_VERSION: latest
 
 before_install:

+ 251 - 0
CHANGELOG.md

@@ -1,5 +1,256 @@
 [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
+
+# Upcoming 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.
+
+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:

+ 3 - 2
Dockerfile

@@ -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.2 \
     METEOR_RELEASE=1.10.2 \
     USE_EDGE=false \
     METEOR_EDGE=1.5-beta.17 \
@@ -137,7 +137,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
Dockerfile.arm64v8

@@ -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.2 \
     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.2 \
     NODE_ENV=production \
     NPM_VERSION=latest \
     WITH_API=true \

+ 5 - 0
README.md

@@ -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
Stackerfile.yml

@@ -1,5 +1,5 @@
 appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928
-appVersion: "v5.29.0"
+appVersion: "v5.37.0"
 files:
   userUploads:
     - README.md

+ 48 - 8
api.py

@@ -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)

+ 57 - 18
client/components/boards/boardColors.styl

@@ -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
 

+ 9 - 3
client/components/boards/boardHeader.jade

@@ -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'}}

+ 68 - 12
client/components/boards/boardHeader.js

@@ -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'));
+
+      this.boardId.set(
+        Boards.insert({
+            // title: TAPi18n.__('templates'),
+            title: title,
+            permission: 'private',
+            type: 'template-container',
+          }),
+       );
 
-    Swimlanes.insert({
-      title: 'Default',
-      boardId: 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());
+      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,
       },
     ];
   },

+ 86 - 42
client/components/boards/boardsList.jade

@@ -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 }}

+ 10 - 0
client/components/boards/boardsList.js

@@ -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() {
@@ -79,6 +88,7 @@ BlazeComponent.extendComponent({
   boards() {
     const query = {
       archived: false,
+      //type: { $in: ['board','template-container'] },
       type: 'board',
     };
     if (FlowRouter.getRouteName() === 'home')

+ 3 - 0
client/components/boards/boardsList.styl

@@ -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

+ 1 - 2
client/components/cards/attachments.jade

@@ -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' }}

+ 1 - 1
client/components/cards/cardCustomFields.jade

@@ -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

+ 27 - 0
client/components/cards/cardDate.js

@@ -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');

+ 0 - 1
client/components/cards/cardDescription.styl

@@ -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

+ 563 - 295
client/components/cards/cardDetails.jade

@@ -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
 
-      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
+      .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-received-date
+                a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
                   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.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.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.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.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.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
 
-      hr
-      if currentBoard.allowsCreator
-        .card-details-item.card-details-item-creator
-          h3.card-details-item-title
-            i.fa.fa-user
-            | {{_ 'creator'}}
+        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
 
-          +userAvatar(userId=userId noRemove=true)
-          | {{! XXX Hack to hide syntaxic coloration /// }}
+        hr
+        if currentBoard.allowsCreator
+          .card-details-item.card-details-item-creator
+            h3.card-details-item-title
+              i.fa.fa-user
+              | {{_ 'creator'}}
 
-      //.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
+        //.card-details-items
+        if currentBoard.allowsRequestedBy
+          .card-details-item.card-details-item-name
             h3.card-details-item-title
-              | {{_ 'overtime-hours'}}
-          else
+              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
-              | {{_ '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
+              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
+        //.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
+
+      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'}}
+
+      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-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 getAssignedBy
+                  if getDescription
                     +viewer
-                      = getAssignedBy
+                      = getDescription
                   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
-
-    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
+                    | {{_ '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
-
-    .card-checklist-attachmentGalerys
-      .card-checklist-attachmentGalery.card-checklists
-        if currentBoard.allowsChecklists
+          +viewer
+            = getDescription
+
+      .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'}}
+        a.js-move-card
+          i.fa.fa-arrow-right
+          | {{_ 'moveCardPopup-title'}}
+    unless currentUser.isWorker
       li
-        a.js-move-card-to-bottom
-          i.fa.fa-arrow-down
-          | {{_ 'moveCardToBottom-title'}}
-      hr
+        a.js-copy-card
+          i.fa.fa-copy
+          | {{_ 'copyCardPopup-title'}}
+  unless currentUser.isWorker
+    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'}}
-    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-archive
-              i.fa.fa-arrow-right
-              i.fa.fa-archive
-              | {{_ 'archive-card'}}
+      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-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'}}

+ 590 - 26
client/components/cards/cardDetails.js

@@ -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'),
@@ -492,7 +634,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 +643,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 +662,7 @@ Template.cardDetailsActionsPopup.events({
   },
 });
 
-Template.editCardTitleForm.onRendered(function() {
+Template.editCardTitleForm.onRendered(function () {
   autosize(this.$('.js-edit-card-title'));
 });
 
@@ -534,7 +676,7 @@ Template.editCardTitleForm.events({
   },
 });
 
-Template.editCardRequesterForm.onRendered(function() {
+Template.editCardRequesterForm.onRendered(function () {
   autosize(this.$('.js-edit-card-requester'));
 });
 
@@ -547,7 +689,7 @@ Template.editCardRequesterForm.events({
   },
 });
 
-Template.editCardAssignerForm.onRendered(function() {
+Template.editCardAssignerForm.onRendered(function () {
   autosize(this.$('.js-edit-card-assigner'));
 });
 
@@ -632,9 +774,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;
@@ -665,9 +805,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 +822,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 +837,7 @@ Template.copyChecklistToManyCardsPopup.events({
         });
 
         // copy card comments
-        CardComments.find({ cardId: oldId }).forEach(cmt => {
+        CardComments.find({ cardId: oldId }).forEach((cmt) => {
           cmt.copy(_id);
         });
       }
@@ -715,7 +853,7 @@ BlazeComponent.extendComponent({
   },
 
   colors() {
-    return ALLOWED_COLORS.map(color => ({ color, name: '' }));
+    return ALLOWED_COLORS.map((color) => ({ color, name: '' }));
   },
 
   isSelected(color) {
@@ -838,7 +976,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 +1083,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 +1095,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 +1269,277 @@ BlazeComponent.extendComponent({
   }
 }.register('editVoteEndDatePopup'));
 
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.currentCard = this.currentData();
+    this.pokerQuestion = new ReactiveVar(this.currentCard.pokerQuestion);
+  },
+
+  events() {
+    return [
+      {
+        'click .js-end-date': Popup.open('editPokerEndDate'),
+        'submit .edit-poker-question'(evt) {
+          evt.preventDefault();
+          const pokerQuestion = true;
+          const allowNonBoardMembers = $('#poker-allow-non-members').hasClass(
+            'is-checked',
+          );
+          const endString = this.currentCard.getPokerEnd();
+
+          this.currentCard.setPokerQuestion(
+            pokerQuestion,
+            allowNonBoardMembers,
+          );
+          if (endString) {
+            this.currentCard.setPokerEnd(endString);
+          }
+          Popup.close();
+        },
+        'click .js-remove-poker': Popup.afterConfirm('deletePoker', (event) => {
+          event.preventDefault();
+          this.currentCard.unsetPoker();
+          Popup.close();
+        }),
+        'click a.js-toggle-poker-allow-non-members'(event) {
+          event.preventDefault();
+          $('#poker-allow-non-members').toggleClass('is-checked');
+        },
+      },
+    ];
+  },
+}).register('cardStartPlanningPokerPopup');
+
+// editPokerEndDatePopup
+(class extends DatePicker {
+  onCreated() {
+    super.onCreated(moment().format('YYYY-MM-DD HH:mm'));
+    this.data().getPokerEnd() &&
+      this.date.set(moment(this.data().getPokerEnd()));
+  }
+
+  /*
+  Tried to use dateFormat and timeFormat from client/components/lib/datepicker.js
+  to make detecting all date formats not necessary,
+  but got error "language mk does not exist".
+  Maybe client/components/lib/datepicker.jade could have hidden input field for
+  datepicker format that could be used to detect date format?
+
+  dateFormat() {
+    return moment.localeData().longDateFormat('L');
+  }
+
+  timeFormat() {
+    return moment.localeData().longDateFormat('LT');
+  }
+
+  const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true);
+  */
+
+  events() {
+    return [
+      {
+        'submit .edit-date'(evt) {
+          evt.preventDefault();
+
+          // if no time was given, init with 12:00
+          const time =
+            evt.target.time.value ||
+            moment(new Date().setHours(12, 0, 0)).format('LT');
+
+          const dateString = `${evt.target.date.value} ${time}`;
+
+          /*
+          Tried to use dateFormat and timeFormat from client/components/lib/datepicker.js
+          to make detecting all date formats not necessary,
+          but got error "language mk does not exist".
+          Maybe client/components/lib/datepicker.jade could have hidden input field for
+          datepicker format that could be used to detect date format?
+
+          const newDate = moment(dateString, dateformat() + ' ' + timeformat(), true);
+
+          if (newDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(newDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: newDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          */
+
+          // Try to parse different date formats of all languages.
+          // This code is same for vote and planning poker.
+          const usaDate = moment(dateString, 'L LT', true);
+          const euroAmDate = moment(dateString, 'DD.MM.YYYY LT', true);
+          const euro24hDate = moment(dateString, 'DD.MM.YYYY HH.mm', true);
+          const eurodotDate = moment(dateString, 'DD.MM.YYYY HH:mm', true);
+          const minusDate = moment(dateString, 'YYYY-MM-DD HH:mm', true);
+          const slashDate = moment(dateString, 'DD/MM/YYYY HH.mm', true);
+          const dotDate = moment(dateString, 'DD/MM/YYYY HH:mm', true);
+          const brezhonegDate = moment(dateString, 'DD/MM/YYYY h[e]mm A', true);
+          const hrvatskiDate = moment(dateString, 'DD. MM. YYYY H:mm', true);
+          const latviaDate = moment(dateString, 'YYYY.MM.DD. H:mm', true);
+          const nederlandsDate = moment(dateString, 'DD-MM-YYYY HH:mm', true);
+          // greekDate does not work: el Greek Ελληνικά ,
+          // it has date format DD/MM/YYYY h:mm MM like 20/06/2021 11:15 MM
+          // where MM is maybe some text like AM/PM ?
+          // Also some other languages that have non-ascii characters in dates
+          // do not work.
+          const greekDate = moment(dateString, 'DD/MM/YYYY h:mm A', true);
+          const macedonianDate = moment(dateString, 'D.MM.YYYY H:mm', true);
+
+          if (usaDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(usaDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: usaDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (euroAmDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(euroAmDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: euroAmDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (euro24hDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(euro24hDate.toDate());
+              this.card.setPokerEnd(euro24hDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: euro24hDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (eurodotDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(eurodotDate.toDate());
+              this.card.setPokerEnd(eurodotDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: eurodotDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (minusDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(minusDate.toDate());
+              this.card.setPokerEnd(minusDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: minusDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (slashDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(slashDate.toDate());
+              this.card.setPokerEnd(slashDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: slashDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (dotDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(dotDate.toDate());
+              this.card.setPokerEnd(dotDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: dotDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (brezhonegDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(brezhonegDate.toDate());
+              this.card.setPokerEnd(brezhonegDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: brezhonegDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (hrvatskiDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(hrvatskiDate.toDate());
+              this.card.setPokerEnd(hrvatskiDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: hrvatskiDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (latviaDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(latviaDate.toDate());
+              this.card.setPokerEnd(latviaDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: latviaDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (nederlandsDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(nederlandsDate.toDate());
+              this.card.setPokerEnd(nederlandsDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: nederlandsDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (greekDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(greekDate.toDate());
+              this.card.setPokerEnd(greekDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: greekDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else if (macedonianDate.isValid()) {
+            // if active poker -  store it
+            if (this.currentData().getPokerQuestion()) {
+              this._storeDate(macedonianDate.toDate());
+              this.card.setPokerEnd(macedonianDate.toDate());
+              Popup.close();
+            } else {
+              this.currentData().poker = { end: macedonianDate.toDate() }; // set poker end temp
+              Popup.back();
+            }
+          } else {
+            // this.error.set('invalid-date);
+            this.error.set('invalid-date' + ' ' + dateString);
+            evt.target.date.focus();
+          }
+        },
+        'click .js-delete-date'(evt) {
+          evt.preventDefault();
+          this._deleteDate();
+          Popup.close();
+        },
+      },
+    ];
+  }
+  _storeDate(newDate) {
+    this.card.setPokerEnd(newDate);
+  }
+  _deleteDate() {
+    this.card.unsetPokerEnd();
+  }
+}.register('editPokerEndDatePopup'));
+
 // Close the card details pane by pressing escape
 EscapeActions.register(
   'detailsPane',

+ 179 - 4
client/components/cards/cardDetails.styl

@@ -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 - 6
client/components/cards/checklists.jade

@@ -4,8 +4,8 @@ template(name="checklists")
       i.fa.fa-check
       | {{_ 'checklists'}}
     if currentUser.isBoardMember
-      .material-toggle-switch
-        span.toggle-switch-title {{_ 'hide-checked-items'}}
+      .material-toggle-switch(title="{{_ 'hide-checked-items'}}")
+        //span.toggle-switch-title
         if hideCheckedItems
           input.toggle-switch(type="checkbox" id="toggleHideCheckedItemsButton" checked="checked")
         else
@@ -25,9 +25,8 @@ template(name="checklists")
     +inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
       +addChecklistItemForm
     else
-      a.js-open-inlined-form
+      a.js-open-inlined-form(title="{{_ 'add-checklist'}}")
         i.fa.fa-plus
-        | {{_ 'add-checklist'}}...
 
 template(name="checklistDetail")
   .js-checklist.checklist
@@ -93,9 +92,8 @@ template(name="checklistItems")
       +inlinedForm(autoclose=false classNames="js-add-checklist-item" checklist = checklist)
         +addChecklistItemForm
       else
-        a.add-checklist-item.js-open-inlined-form
+        a.add-checklist-item.js-open-inlined-form(title="{{_ 'add-checklist-item'}}")
           i.fa.fa-plus
-          | {{_ 'add-checklist-item'}}...
 
 template(name='checklistItemDetail')
   .js-checklist-item.checklist-item(class="{{#if item.isFinished }}is-checked{{#if hideCheckedItems}} invisible{{/if}}{{/if}}")

+ 11 - 0
client/components/cards/labels.styl

@@ -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

+ 9 - 0
client/components/cards/minicard.jade

@@ -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
@@ -134,3 +139,7 @@ template(name="minicard")
           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 }}

+ 1 - 2
client/components/cards/subtasks.jade

@@ -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

+ 1 - 1
client/components/forms/forms.styl

@@ -86,7 +86,7 @@ select
   margin-bottom: 8px
 
   &.inline
-  	width: 100%
+    width: 100%
 
 option[disabled]
   color: #8c8c8c

+ 4 - 9
client/components/lists/listBody.jade

@@ -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

+ 40 - 29
client/components/lists/listBody.js

@@ -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() {
+    // spinner deleted
+    if (!this.spinner.offsetTop) {
+      return false;
+    }
+
     const parentViewHeight = this.container.clientHeight;
     const bottomViewPosition = this.container.scrollTop + parentViewHeight;
 
-    const threshold = this.spinner.offsetTop;
+    let spinnerOffsetTop = this.spinner.offsetTop;
 
-    // spinner deleted
-    if (!this.spinner.offsetTop) {
-      return false;
+    const addCard = $(this.container).find("a.open-minicard-composer").first()[0];
+    if (addCard !== undefined) {
+      spinnerOffsetTop -= addCard.clientHeight;
     }
 
-    return bottomViewPosition > threshold;
-  },
-}).register('spinnerList');
+    return bottomViewPosition > spinnerOffsetTop;
+  }
+
+  getSkSpinnerName() {
+    return "sk-spinner-" + super.getSpinnerName().toLowerCase();
+  }
+}.register('spinnerList'));

+ 4 - 4
client/components/lists/listHeader.jade

@@ -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

+ 2 - 2
client/components/main/editor.js

@@ -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 = [
@@ -229,7 +229,7 @@ Template.editor.onRendered(() => {
                 // (and multiplies by pasting more) by changing paste "p" to "br".
                 // Fixes https://github.com/wekan/wekan/2890 .
                 // == Fix Start ==
-                //someNote.execCommand('defaultParagraphSeparator', false, 'br');
+                someNote.execCommand('defaultParagraphSeparator', false, 'br');
                 // == Fix End ==
                 const original = someNote.summernote('code');
                 const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML.

+ 33 - 31
client/components/main/globalSearch.js

@@ -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;
   }
 

+ 3 - 3
client/components/main/header.jade

@@ -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

+ 5 - 0
client/components/main/spinner.jade

@@ -0,0 +1,5 @@
+template(name="spinner")
+  +Template.dynamic(template=getSpinnerTemplate)
+
+template(name="spinnerRaw")
+  +Template.dynamic(template=getSpinnerTemplateRaw)

+ 11 - 0
client/components/main/spinner.js

@@ -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'));

+ 0 - 6
client/components/main/spinner.tpl.jade

@@ -1,6 +0,0 @@
-.sk-spinner.sk-spinner-wave(class=currentBoard.colorClass)
-  .sk-rect1
-  .sk-rect2
-  .sk-rect3
-  .sk-rect4
-  .sk-rect5

+ 11 - 0
client/components/main/spinner_bounce.jade

@@ -0,0 +1,11 @@
+template(name="spinnerBounce")
+  .sk-spinner.sk-spinner-bounce(class=currentBoard.colorClass)
+    +spinnerBounceRaw
+
+template(name="spinnerBounceRaw")
+  .sk-bounce1
+  | &nbsp;
+  .sk-bounce2
+  | &nbsp;
+  .sk-bounce3
+  | &nbsp;

+ 44 - 0
client/components/main/spinner_bounce.styl

@@ -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);
+  }
+}

+ 8 - 0
client/components/main/spinner_cube.jade

@@ -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

+ 52 - 0
client/components/main/spinner_cube.styl

@@ -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);
+  }
+}

+ 14 - 0
client/components/main/spinner_cube_grid.jade

@@ -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

+ 64 - 0
client/components/main/spinner_cube_grid.styl

@@ -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);
+  }
+}

+ 7 - 0
client/components/main/spinner_dot.jade

@@ -0,0 +1,7 @@
+template(name="spinnerDot")
+  .sk-spinner.sk-spinner-dot(class=currentBoard.colorClass)
+    +spinnerDotRaw
+
+template(name="spinnerDotRaw")
+  .sk-dot1
+  .sk-dot2

+ 51 - 0
client/components/main/spinner_dot.styl

@@ -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);
+  }
+}

+ 7 - 0
client/components/main/spinner_double_bounce.jade

@@ -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

+ 44 - 0
client/components/main/spinner_double_bounce.styl

@@ -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);
+  }
+}

+ 6 - 0
client/components/main/spinner_rotateplane.jade

@@ -0,0 +1,6 @@
+template(name="spinnerRotateplane")
+  .sk-spinner.sk-spinner-rotateplane(class=currentBoard.colorClass)
+    +spinnerRotateplaneRaw
+
+template(name="spinnerRotateplaneRaw")
+  .sk-rotateplane1

+ 38 - 0
client/components/main/spinner_rotateplane.styl

@@ -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);
+  }
+}

+ 6 - 0
client/components/main/spinner_scaleout.jade

@@ -0,0 +1,6 @@
+template(name="spinnerScaleout")
+  .sk-spinner.sk-spinner-scaleout(class=currentBoard.colorClass)
+    +spinnerScaleoutRaw
+
+template(name="spinnerScaleoutRaw")
+  .sk-scaleout1

+ 40 - 0
client/components/main/spinner_scaleout.styl

@@ -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;
+  }
+}

+ 15 - 0
client/components/main/spinner_wave.jade

@@ -0,0 +1,15 @@
+template(name="spinnerWave")
+  .sk-spinner.sk-spinner-wave(class=currentBoard.colorClass)
+    +spinnerWaveRaw
+
+template(name="spinnerWaveRaw")
+  .sk-rect1
+  | &nbsp;
+  .sk-rect2
+  | &nbsp;
+  .sk-rect3
+  | &nbsp;
+  .sk-rect4
+  | &nbsp;
+  .sk-rect5
+  | &nbsp;

+ 2 - 16
client/components/main/spinner.styl → client/components/main/spinner_wave.styl

@@ -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 - 1
client/components/notifications/notifications.jade

@@ -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)

+ 116 - 53
client/components/settings/peopleBody.jade

@@ -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'}}")

+ 343 - 92
client/components/settings/peopleBody.js

@@ -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();

+ 6 - 0
client/components/settings/peopleBody.styl

@@ -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;

+ 11 - 0
client/components/settings/settingBody.jade

@@ -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}}

+ 14 - 0
client/components/settings/settingBody.js

@@ -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;
+  },
+});

+ 94 - 8
client/components/sidebar/sidebar.jade

@@ -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 &nbsp;
       .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")

+ 299 - 0
client/components/sidebar/sidebar.js

@@ -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,

+ 13 - 4
client/components/sidebar/sidebar.styl

@@ -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

+ 12 - 5
client/components/swimlanes/swimlaneHeader.jade

@@ -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

+ 1 - 2
client/components/swimlanes/swimlanes.jade

@@ -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

+ 38 - 0
client/components/users/userAvatar.jade

@@ -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

+ 128 - 0
client/components/users/userAvatar.js

@@ -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 - 1
client/components/users/userHeader.jade

@@ -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

+ 27 - 0
client/lib/keyboard.js

@@ -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(/-/, '');
+  }
+}

+ 197 - 0
client/lib/tests/Utils.tests.js

@@ -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');
+  });
+});

+ 1 - 0
client/lib/tests/index.js

@@ -0,0 +1 @@
+import './Utils.tests';

+ 14 - 7
client/lib/utils.js

@@ -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

+ 11 - 1
config/const.js

@@ -55,5 +55,15 @@ export const CARD_TYPES = [
   TYPE_CARD,
   TYPE_LINKED_CARD,
   TYPE_LINKED_BOARD,
-  TYPE_TEMPLATE_CARD,
+  TYPE_TEMPLATE_CARD
+];
+export const ALLOWED_WAIT_SPINNERS = [
+  'Bounce',
+  'Cube',
+  'Cube-Grid',
+  'Dot',
+  'Double-Bounce',
+  'Rotateplane',
+  'Scaleout',
+  'Wave'
 ];

+ 3 - 0
docker-compose.yml

@@ -618,6 +618,9 @@ services:
       #- SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE=
       #- SAML_ATTRIBUTES=
       #---------------------------------------------------------------------
+      # Wait spinner to use
+      # - WAIT_SPINNER=Bounce
+      #---------------------------------------------------------------------
     depends_on:
       - wekandb
 

+ 53 - 1
i18n/ar-EG.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Add Attachment",
     "add-board": "Add Board",
+    "add-template": "Add Template",
     "add-card": "Add Card",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Add Swimlane",
     "add-subtask": "Add Subtask",
     "add-checklist": "Add Checklist",
@@ -117,6 +120,8 @@
     "archives": "Archive",
     "template": "Template",
     "templates": "Templates",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Assign member",
     "attached": "attached",
     "attachment": "Attachment",
@@ -131,6 +136,7 @@
     "board-not-found": "Board not found",
     "board-private-info": "This board will be <strong>private</strong>.",
     "board-public-info": "This board will be <strong>public</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Change Board Background",
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
@@ -178,6 +184,27 @@
     "vote-against": "against",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Delete Card?",
     "cardDetailsActionsPopup-title": "Card Actions",
     "cardLabelsPopup-title": "Labels",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "Cards",
     "cards-count": "Cards",
+    "cards-count-one": "Card",
     "casSignIn": "Sign In with CAS",
     "cardType-card": "Card",
     "cardType-linkedCard": "Linked Card",
@@ -208,6 +236,7 @@
     "close": "Close",
     "close-board": "Close Board",
     "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+    "close-card": "Close Card",
     "color-black": "black",
     "color-blue": "blue",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Export board",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "List Actions",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Swimlane Actions",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Import a Trello card",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Choose a color",
     "assigned-by": "Assigned By",
     "requested-by": "Requested By",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/ar.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "إضافة مرفق",
     "add-board": "إضافة لوحة",
+    "add-template": "Add Template",
     "add-card": "إضافة بطاقة",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Add Swimlane",
     "add-subtask": "إضافة مهمة فرعية",
     "add-checklist": "إضافة قائمة تدقيق",
@@ -117,6 +120,8 @@
     "archives": "أرشيف",
     "template": "نموذج",
     "templates": "نماذج",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "تعيين عضو",
     "attached": "أُرفق)",
     "attachment": "مرفق",
@@ -131,6 +136,7 @@
     "board-not-found": "لوحة مفقودة",
     "board-private-info": "سوف تصبح هذه اللوحة <strong>خاصة</strong>",
     "board-public-info": "سوف تصبح هذه اللوحة <strong>عامّة</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "تعديل خلفية الشاشة",
     "boardChangeTitlePopup-title": "إعادة تسمية اللوحة",
     "boardChangeVisibilityPopup-title": "تعديل وضوح الرؤية",
@@ -178,6 +184,27 @@
     "vote-against": "ضد",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "حذف البطاقة ?",
     "cardDetailsActionsPopup-title": "إجراءات على البطاقة",
     "cardLabelsPopup-title": "علامات",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "بطاقات",
     "cards-count": "بطاقات",
+    "cards-count-one": "بطاقة",
     "casSignIn": "تسجيل الدخول مع  CAS",
     "cardType-card": "بطاقة",
     "cardType-linkedCard": "البطاقة المرتبطة",
@@ -208,6 +236,7 @@
     "close": "غلق",
     "close-board": "غلق اللوحة",
     "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+    "close-card": "Close Card",
     "color-black": "black",
     "color-blue": "blue",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Export board",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "قائمة الإجراءات",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Swimlane Actions",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Import a Trello card",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "اختر لوناً",
     "assigned-by": "Assigned By",
     "requested-by": "Requested By",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "تقارير",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/bg.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Добави прикачен файл",
     "add-board": "Добави Табло",
+    "add-template": "Add Template",
     "add-card": "Добави карта",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Добави коридор",
     "add-subtask": "Добави подзадача",
     "add-checklist": "Добави списък със задачи",
@@ -117,6 +120,8 @@
     "archives": "Архив",
     "template": "Шаблон",
     "templates": "Шаблони",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Възложи на член от екипа",
     "attached": "прикачен",
     "attachment": "Прикаченн файл",
@@ -131,6 +136,7 @@
     "board-not-found": "Таблото не е намерено",
     "board-private-info": "Това табло ще бъде <strong>лично</strong>",
     "board-public-info": "Това табло ще бъде <strong>публично</strong>",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Променете фона на таблото",
     "boardChangeTitlePopup-title": "Промени името на Таблото",
     "boardChangeVisibilityPopup-title": "Променете видимостта",
@@ -178,6 +184,27 @@
     "vote-against": "против",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Желаете да изтриете картата?",
     "cardDetailsActionsPopup-title": "Опции",
     "cardLabelsPopup-title": "Етикети",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "Карти",
     "cards-count": "Карти",
+    "cards-count-one": "Карта",
     "casSignIn": "Sign In with CAS",
     "cardType-card": "Карта",
     "cardType-linkedCard": "Свързана карта",
@@ -208,6 +236,7 @@
     "close": "Затвори",
     "close-board": "Затвори Таблото",
     "close-board-pop": "Ще можете да възстановите Таблото като натиснете на бутона \"Архив\" в началото на хедъра.",
+    "close-card": "Close Card",
     "color-black": "черно",
     "color-blue": "синьо",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Експортиране на Табло",
+    "exportCardPopup-title": "Export card",
     "sort": "Сортирай",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Изберете цвят",
     "listActionPopup-title": "List Actions",
     "settingsUserPopup-title": "Потребителски настройки",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Swimlane Actions",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Импорт на карта от Trello",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Избери цвят",
     "assigned-by": "Разпределена от",
     "requested-by": "Поискан от",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Изтриването е перманентно. Ще загубите всички списъци, карти и действия асоциирани с тази дъска.",
     "delete-board-confirm-popup": "Всички списъци, карти, имена и действия ще бъдат изтрити и няма да можете да възстановите съдържанието на дъската. Няма връщане назад.",
     "boardDeletePopup-title": "Изтриване на Таблото?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/br.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Add Attachment",
     "add-board": "Add Board",
+    "add-template": "Add Template",
     "add-card": "Add Card",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Add Swimlane",
     "add-subtask": "Add Subtask",
     "add-checklist": "Add Checklist",
@@ -117,6 +120,8 @@
     "archives": "Archive",
     "template": "Template",
     "templates": "Templates",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Assign member",
     "attached": "attached",
     "attachment": "Attachment",
@@ -131,6 +136,7 @@
     "board-not-found": "Board not found",
     "board-private-info": "This board will be <strong>private</strong>.",
     "board-public-info": "This board will be <strong>public</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Change Board Background",
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
@@ -178,6 +184,27 @@
     "vote-against": "against",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Diverkañ ar gartenn ?",
     "cardDetailsActionsPopup-title": "Card Actions",
     "cardLabelsPopup-title": "Labels",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "Kartennoù",
     "cards-count": "Kartennoù",
+    "cards-count-one": "Card",
     "casSignIn": "Sign In with CAS",
     "cardType-card": "Card",
     "cardType-linkedCard": "Linked Card",
@@ -208,6 +236,7 @@
     "close": "Close",
     "close-board": "Close Board",
     "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+    "close-card": "Close Card",
     "color-black": "du",
     "color-blue": "glas",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Export board",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "List Actions",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Swimlane Actions",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Import a Trello card",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Choose a color",
     "assigned-by": "Assigned By",
     "requested-by": "Requested By",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/ca.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Afegeix adjunt",
     "add-board": "Afegeix Tauler",
+    "add-template": "Add Template",
     "add-card": "Afegeix Fitxa",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Afegeix carril de natació",
     "add-subtask": "Afegir Subtasca",
     "add-checklist": "Afegeix checklist",
@@ -117,6 +120,8 @@
     "archives": "Desa",
     "template": "Plantilla",
     "templates": "Plantilles",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Assignar membre",
     "attached": "adjuntat",
     "attachment": "Adjunt",
@@ -131,6 +136,7 @@
     "board-not-found": "No s'ha trobat el tauler",
     "board-private-info": "Aquest tauler serà <strong> privat.",
     "board-public-info": "Aquest tauler serà <strong> públic.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Canvia fons del tauler",
     "boardChangeTitlePopup-title": "Canvia el nom tauler",
     "boardChangeVisibilityPopup-title": "Canvia visibilitat",
@@ -178,6 +184,27 @@
     "vote-against": "against",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Esborrar fitxa?",
     "cardDetailsActionsPopup-title": "Accions de fitxes",
     "cardLabelsPopup-title": "Etiquetes",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "Fitxes",
     "cards-count": "Fitxes",
+    "cards-count-one": "Fitxa",
     "casSignIn": "Sign In with CAS",
     "cardType-card": "Fitxa",
     "cardType-linkedCard": "Linked Card",
@@ -208,6 +236,7 @@
     "close": "Tanca",
     "close-board": "Tanca tauler",
     "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+    "close-card": "Close Card",
     "color-black": "negre",
     "color-blue": "blau",
     "color-crimson": "carmesí",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Exporta tauler",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "Accions de la llista",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Accions de Carril de Natació",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "importa una fitxa de Trello",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Choose a color",
     "assigned-by": "Assignat Per",
     "requested-by": "Demanat Per",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/cs.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "editoval(a) datum ukončení na %s z %s",
     "add-attachment": "Přidat přílohu",
     "add-board": "Přidat tablo",
+    "add-template": "Add Template",
     "add-card": "Přidat kartu",
+    "add-card-to-top-of-list": "Přidat kartu na začátek seznamu",
+    "add-card-to-bottom-of-list": "Přidat kartu na konec seznamu",
     "add-swimlane": "Přidat Swimlane",
     "add-subtask": "Přidat Podúkol",
     "add-checklist": "Přidat zaškrtávací seznam",
@@ -117,6 +120,8 @@
     "archives": "Archiv",
     "template": "Šablona",
     "templates": "Šablony",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Přiřadit člena",
     "attached": "přiloženo",
     "attachment": "Příloha",
@@ -131,6 +136,7 @@
     "board-not-found": "Tablo nenalezeno",
     "board-private-info": "Toto tablo bude <strong>soukromé</strong>.",
     "board-public-info": "Toto tablo bude <strong>veřejné</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Přetažením změníte pořadí ikon tabel. Kliknutím otevřete tablo.",
     "boardChangeColorPopup-title": "Změnit pozadí tabla",
     "boardChangeTitlePopup-title": "Přejmenovat tablo",
     "boardChangeVisibilityPopup-title": "Upravit viditelnost",
@@ -178,6 +184,27 @@
     "vote-against": "proti",
     "deleteVotePopup-title": "Smazat hlas?",
     "vote-delete-pop": "Smazání je nevratné. Ztratíte vše spojené s tímto hlasováním.",
+    "cardStartPlanningPokerPopup-title": "Zahájit plánovací poker",
+    "card-edit-planning-poker": "Upravit plánovací poker",
+    "editPokerEndDatePopup-title": "Změnit datum konce plánovacího pokeru",
+    "poker-question": "Plánovací poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Hotovo",
+    "poker-result-votes": "Hlasů",
+    "poker-result-who": "Kdo",
+    "poker-replay": "Zopakovat",
+    "set-estimation": "Nastavit odhad",
+    "deletePokerPopup-title": "Smazat plánovací poker?",
+    "poker-delete-pop": "Smazání je trvalé. Přijdete o všechny akce asociované s tímto plánovacím pokerem.",
     "cardDeletePopup-title": "Smazat kartu?",
     "cardDetailsActionsPopup-title": "Akce karty",
     "cardLabelsPopup-title": "Štítky",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Vytvořit šablonu",
     "cards": "Karty",
     "cards-count": "Karty",
+    "cards-count-one": "Karta",
     "casSignIn": "Přihlásit pomocí CAS",
     "cardType-card": "Karta",
     "cardType-linkedCard": "Propojená karta",
@@ -208,6 +236,7 @@
     "close": "Zavřít",
     "close-board": "Zavřít tablo",
     "close-board-pop": "Budete moci obnovit tablo kliknutím na tlačítko \"Archiv\" v hlavním menu.",
+    "close-card": "Zavřít kartu",
     "color-black": "černá",
     "color-blue": "modrá",
     "color-crimson": "karmínová",
@@ -332,7 +361,11 @@
     "export-board-excel": "Exportovat tablo do Excelu",
     "user-can-not-export-excel": "Uživatel nemůže exportovat do Excelu",
     "export-board-html": "Exportovat tablo do HTML",
+    "export-card": "Exportovat kartu",
+    "export-card-pdf": "Exportovat kartu do PDF",
+    "user-can-not-export-card-to-pdf": "Uživatel nemůže exportovat kartu do PDF",
     "exportBoardPopup-title": "Exportovat tablo",
+    "exportCardPopup-title": "Exportovat kartu",
     "sort": "řadit",
     "sort-desc": "Kliknout pro třídění seznamu",
     "list-sort-by": "řadit seznam podle",
@@ -421,6 +454,8 @@
     "set-color-list": "Nastavit barvu",
     "listActionPopup-title": "Vypsat akce",
     "settingsUserPopup-title": "Nastavení uživatele",
+    "settingsTeamPopup-title": "Nastavení týmu",
+    "settingsOrgPopup-title": "Nastavení organizace",
     "swimlaneActionPopup-title": "Akce swimlane",
     "swimlaneAddPopup-title": "Přidat swimlane dolů",
     "listImportCardPopup-title": "Importovat Trello kartu",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Vyber barvu",
     "assigned-by": "Přidělil(a)",
     "requested-by": "Vyžádal(a)",
+    "card-sorting-by-number": "Řazení karet podle čísla",
     "board-delete-notice": "Smazání je trvalé. Přijdete o všechny sloupce, karty a akce spojené s tímto tablem.",
     "delete-board-confirm-popup": "Všechny sloupce, štítky a aktivity budou smazány a obsah tabla nebude možné obnovit. Toto nelze vrátit zpět.",
     "boardDeletePopup-title": "Smazat tablo?",
@@ -817,6 +853,8 @@
     "act-duenow": "připomínal(a), že stávající termín dokončení (__timeValue__)  __card__ je teď",
     "act-atUserComment": "Byli jste zmíněni v [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Jste si jisti, že chcete smazat tento účet? Tuto akci nelze vrátit zpět.",
+    "delete-team-confirm-popup": "Jste si jisti, že chcete smazat tento tým? Tuto akci nelze vrátit zpět.",
+    "delete-org-confirm-popup": "Jste si jisti, že chcete smazat tuto organizaci? Tuto akci nelze vrátit zpět.",
     "accounts-allowUserDelete": "Dovolit uživatelům smazat vlastní účet",
     "hide-minicard-label-text": "Skrýt text popisku minikarty",
     "show-desktop-drag-handles": "Zobrazit okraje pro přesun plochy",
@@ -1006,5 +1044,19 @@
     "reports": "Reporty",
     "rulesReportTitle": "Report pravidel",
     "copy-swimlane": "Kopírovat dráhu",
-    "copySwimlanePopup-title": "Kopírovat dráhu"
+    "copySwimlanePopup-title": "Kopírovat dráhu",
+    "display-card-creator": "Zobrazovat tvůrce karet",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximalizovat kartu",
+    "minimize-card": "Minimalizovat kartu",
+    "delete-org-warning-message": "Tuto organizaci není možné smazat, protože do ní patří uživatel(é)",
+    "delete-team-warning-message": "Tento tým není možné smazat, protože do nej patří uživatel(é)"
 }

+ 53 - 1
i18n/da.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Tilføj vedhæftning",
     "add-board": "Tilføj tavle",
+    "add-template": "Add Template",
     "add-card": "Tilføj kort",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Tilføj svømmebane",
     "add-subtask": "Tilføj delopgave",
     "add-checklist": "Tilføj tjekliste",
@@ -117,6 +120,8 @@
     "archives": "Arkiv",
     "template": "Skabelon",
     "templates": "Skabeloner",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Tilknyt medlem",
     "attached": "vedhæftet",
     "attachment": "Vedhæftning",
@@ -131,6 +136,7 @@
     "board-not-found": "Fandt ikke tavle ",
     "board-private-info": "Denne tavle vil være <strong>privat</strong>.",
     "board-public-info": "Denne tavle vil være <strong>offentlig</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Skift tavlens baggrund",
     "boardChangeTitlePopup-title": "Omdøb tavle",
     "boardChangeVisibilityPopup-title": "Tilpas synlighed",
@@ -178,6 +184,27 @@
     "vote-against": "går imod",
     "deleteVotePopup-title": "Slet afstemning?",
     "vote-delete-pop": "Sletning er permanent. Du mister alle handlinger knyttet til denne afstemning.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Slet kort?",
     "cardDetailsActionsPopup-title": "Handlinger for kort",
     "cardLabelsPopup-title": "Etiketter",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Opret skabelon",
     "cards": "Kort",
     "cards-count": "Kort",
+    "cards-count-one": "Kort",
     "casSignIn": "Log ind med CAS",
     "cardType-card": "Kort",
     "cardType-linkedCard": "Sammenkædet kort",
@@ -208,6 +236,7 @@
     "close": "Luk",
     "close-board": "Luk tavle",
     "close-board-pop": "Du har mulighed for at genskabe tavlen ved at klikke på \"Arkiv\"-knappen fra overskriften hjem.",
+    "close-card": "Close Card",
     "color-black": "sort",
     "color-blue": "blå",
     "color-crimson": "crimsonrød",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Eksportér tavle til HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Eksportér tavle",
+    "exportCardPopup-title": "Export card",
     "sort": "Sortér",
     "sort-desc": "Klik for at sortere listen",
     "list-sort-by": "Sortér listen efter:",
@@ -421,6 +454,8 @@
     "set-color-list": "Angiv farve",
     "listActionPopup-title": "Handlinger for liste",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Handlinger for svømmebane",
     "swimlaneAddPopup-title": "Tilføj en svømmebane nedenfor",
     "listImportCardPopup-title": "Importér et Trello-kort",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Vælg en farve",
     "assigned-by": "Tildelt af",
     "requested-by": "Anmodet af",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Sletning er permanent. Du vil miste alle lister, kort og handlinger knyttet til denne tavle.",
     "delete-board-confirm-popup": "Alle lister, kort, etiketter og aktiviteter vil blive slettet og du får ikke mulighed for at genskabe tavlens indhold. Dette kan ikke fortrydes.",
     "boardDeletePopup-title": "Slet tavle?",
@@ -817,6 +853,8 @@
     "act-duenow": "påmindede om at aktuelt forfald (__timeValue__) of __card__ er nu",
     "act-atUserComment": "Du blev nævnt i [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Er du sikker på du vil slette denne konto? Det er ikke muligt at fortryde.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Tillad brugere at slette deres egen konto",
     "hide-minicard-label-text": "Skjul etiketteteksten for minikort",
     "show-desktop-drag-handles": "Vis trække-håndtag for skrivebord",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 56 - 4
i18n/de-CH.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "hat Enddatum zu %s geändert auf %s",
     "add-attachment": "Datei anhängen",
     "add-board": "Board hinzufügen",
+    "add-template": "Vorlage hinzufügen",
     "add-card": "Karte hinzufügen",
+    "add-card-to-top-of-list": "Karte am Anfang der Liste hinzufügen",
+    "add-card-to-bottom-of-list": "Karte am Ende der Liste hinzufügen",
     "add-swimlane": "Swimlane hinzufügen",
     "add-subtask": "Teilaufgabe hinzufügen",
     "add-checklist": "Checkliste hinzufügen",
@@ -117,6 +120,8 @@
     "archives": "Archiv",
     "template": "Vorlage",
     "templates": "Vorlagen",
+    "template-container": "Vorlagen-Container",
+    "add-template-container": "Vorlagen-Container hinzufügen",
     "assign-member": "Mitglied zuweisen",
     "attached": "angehängt",
     "attachment": "Anhang",
@@ -131,6 +136,7 @@
     "board-not-found": "Board nicht gefunden",
     "board-private-info": "Dieses Board wird <strong>privat</strong> sein.",
     "board-public-info": "Dieses Board wird <strong>öffentlich zugänglich</strong> sein.",
+    "board-drag-drop-reorder-or-click-open": "Ziehen und Fallenlassen um die Board-Icons neu anzuordnen. Ein Klick auf das Board-Icon öffnet das zugehörige Board.",
     "boardChangeColorPopup-title": "Farbe des Boards ändern",
     "boardChangeTitlePopup-title": "Board umbenennen",
     "boardChangeVisibilityPopup-title": "Sichtbarkeit ändern",
@@ -156,7 +162,7 @@
     "card-due-on": "fällig am",
     "card-spent": "Aufgewendete Zeit",
     "card-edit-attachments": "Anhänge ändern",
-    "card-edit-custom-fields": "Benutzerdefinierte Felder editieren",
+    "card-edit-custom-fields": "Benutzerdefinierte Felder bearbeiten",
     "card-edit-labels": "Labels ändern",
     "card-edit-members": "Mitglieder ändern",
     "card-labels-title": "Labels für diese Karte ändern.",
@@ -165,11 +171,11 @@
     "card-start-on": "Start am",
     "cardAttachmentsPopup-title": "Anhängen von",
     "cardCustomField-datePopup-title": "Datum ändern",
-    "cardCustomFieldsPopup-title": "Benutzerdefinierte Felder editieren",
+    "cardCustomFieldsPopup-title": "Benutzerdefinierte Felder bearbeiten",
     "cardStartVotingPopup-title": "Abstimmung starten",
     "positiveVoteMembersPopup-title": "Befürworter",
     "negativeVoteMembersPopup-title": "Gegner",
-    "card-edit-voting": "Abstimmung editieren",
+    "card-edit-voting": "Abstimmung bearbeiten",
     "editVoteEndDatePopup-title": "Enddatum der Abstimmung ändern",
     "allowNonBoardMembers": "Alle eingeloggte Nutzer erlauben",
     "vote-question": "Abstimmen über",
@@ -178,6 +184,27 @@
     "vote-against": "Dagegen",
     "deleteVotePopup-title": "Wahl löschen?",
     "vote-delete-pop": "Löschen ist unwiderruflich. Alle Aktionen die dieser Karte zugeordnet sind werden ebenfalls gelöscht.",
+    "cardStartPlanningPokerPopup-title": "Planning Poker starten",
+    "card-edit-planning-poker": "Planning Poker bearbeiten",
+    "editPokerEndDatePopup-title": "Ende-Datum dieses Planning Poker ändern",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Beenden",
+    "poker-result-votes": "Werte",
+    "poker-result-who": "Wer",
+    "poker-replay": "Wiederholen",
+    "set-estimation": "Schätzung vornehmen",
+    "deletePokerPopup-title": "Planning Poker löschen ?",
+    "poker-delete-pop": "Die Löschung ist permanent. Sie werden alles im Zusammenhang mit diesem Planning Poker verlieren.",
     "cardDeletePopup-title": "Karte löschen?",
     "cardDetailsActionsPopup-title": "Kartenaktionen",
     "cardLabelsPopup-title": "Labels",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Vorlage erstellen",
     "cards": "Karten",
     "cards-count": "Karten",
+    "cards-count-one": "Karte",
     "casSignIn": "Mit CAS anmelden",
     "cardType-card": "Karte",
     "cardType-linkedCard": "Verknüpfte Karte",
@@ -208,6 +236,7 @@
     "close": "Schliessen",
     "close-board": "Board schliessen",
     "close-board-pop": "Sie können das Board wiederherstellen, indem Sie die Schaltfläche \"Archiv\" in der Kopfzeile der Startseite anklicken.",
+    "close-card": "Karte schliessen",
     "color-black": "schwarz",
     "color-blue": "blau",
     "color-crimson": "Karminrot",
@@ -332,7 +361,11 @@
     "export-board-excel": "Board nach Excel exportieren",
     "user-can-not-export-excel": "Benutzer kann nicht nach Excel exportieren",
     "export-board-html": "Board als HTML exportieren",
+    "export-card": "Karte exportieren",
+    "export-card-pdf": "Karte als PDF exportieren",
+    "user-can-not-export-card-to-pdf": "Der Benutzer kann die Karte nicht als PDF exportieren",
     "exportBoardPopup-title": "Board exportieren",
+    "exportCardPopup-title": "Karte exportieren",
     "sort": "Sortieren",
     "sort-desc": "Zum Sortieren der Liste klicken",
     "list-sort-by": "Sortieren der Liste nach:",
@@ -421,6 +454,8 @@
     "set-color-list": "Lege Farbe fest",
     "listActionPopup-title": "Listenaktionen",
     "settingsUserPopup-title": "Benutzereinstellungen",
+    "settingsTeamPopup-title": "Team-Einstellungen",
+    "settingsOrgPopup-title": "Organisations-Einstellungen",
     "swimlaneActionPopup-title": "Swimlaneaktionen",
     "swimlaneAddPopup-title": "Swimlane unterhalb einfügen",
     "listImportCardPopup-title": "Eine Trello-Karte importieren",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Farbe wählen",
     "assigned-by": "Zugewiesen von",
     "requested-by": "Angefordert von",
+    "card-sorting-by-number": "Karten nach Nummer sortieren",
     "board-delete-notice": "Löschen kann nicht rückgängig gemacht werden. Sie werden alle Listen, Karten und Aktionen, die mit diesem Board verbunden sind, verlieren.",
     "delete-board-confirm-popup": "Alle Listen, Karten, Labels und Akivitäten werden gelöscht und Sie können die Inhalte des Boards nicht wiederherstellen! Die Aktion kann nicht rückgängig gemacht werden.",
     "boardDeletePopup-title": "Board löschen?",
@@ -817,6 +853,8 @@
     "act-duenow": "erinnernd an das aktuelle Fälligkeitszeitpunkt (__timeValue__) von __card__ ist jetzt",
     "act-atUserComment": "Sie wurden in [__board__] __list__/__card__ erwähnt",
     "delete-user-confirm-popup": "Sind Sie sicher, dass Sie diesen Account löschen wollen? Die Aktion kann nicht rückgängig gemacht werden.",
+    "delete-team-confirm-popup": "Sind Sie sicher, dass Sie dieses Team löschen möchten? Es gibt kein Zurück!",
+    "delete-org-confirm-popup": "Sind Sie sicher, dass Sie diese Organisation löschen möchten? Es gibt kein Zurück!",
     "accounts-allowUserDelete": "Erlaube Benutzern ihren eigenen Account zu löschen",
     "hide-minicard-label-text": "Labeltext auf Minikarte ausblenden",
     "show-desktop-drag-handles": "Desktop-Ziehpunkte anzeigen",
@@ -1006,5 +1044,19 @@
     "reports": "Berichte",
     "rulesReportTitle": "Regeln-Bericht",
     "copy-swimlane": "Swimlane kopieren",
-    "copySwimlanePopup-title": "Swimlane kopieren"
+    "copySwimlanePopup-title": "Swimlane kopieren",
+    "display-card-creator": "Zeige Karten-Erstellung",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Karte maximieren",
+    "minimize-card": "Karte minimieren",
+    "delete-org-warning-message": "Diese Organisation kann nicht gelöscht werden. Zumindest ein Benutzer ist ihr noch zugehörig.",
+    "delete-team-warning-message": "Dieses Team kann nicht gelöscht werden. Zumindest ein Benutzer ist ihm noch zugehörig."
 }

+ 53 - 1
i18n/de.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "hat Enddatum zu %s geändert auf %s",
     "add-attachment": "Datei anhängen",
     "add-board": "neues Board",
+    "add-template": "Vorlage hinzufügen",
     "add-card": "Karte hinzufügen",
+    "add-card-to-top-of-list": "Karte am Anfang der Liste hinzufügen",
+    "add-card-to-bottom-of-list": "Karte am Ende der Liste hinzufügen",
     "add-swimlane": "Swimlane hinzufügen",
     "add-subtask": "Teilaufgabe hinzufügen",
     "add-checklist": "Checkliste hinzufügen",
@@ -117,6 +120,8 @@
     "archives": "Archiv",
     "template": "Vorlage",
     "templates": "Vorlagen",
+    "template-container": "Vorlagen-Container",
+    "add-template-container": "Vorlagen-Container hinzufügen",
     "assign-member": "Mitglied zuweisen",
     "attached": "angehängt",
     "attachment": "Anhang",
@@ -131,6 +136,7 @@
     "board-not-found": "Board nicht gefunden",
     "board-private-info": "Dieses Board wird <strong>privat</strong> sein.",
     "board-public-info": "Dieses Board wird <strong>öffentlich</strong> sein.",
+    "board-drag-drop-reorder-or-click-open": "Benutze Drag-and-Drop, um Board-Icons neu anzuordnen. Klicke auf ein Board-Icon, um das Board zu öffnen.",
     "boardChangeColorPopup-title": "Farbe des Boards ändern",
     "boardChangeTitlePopup-title": "Board umbenennen",
     "boardChangeVisibilityPopup-title": "Sichtbarkeit ändern",
@@ -178,6 +184,27 @@
     "vote-against": "Dagegen",
     "deleteVotePopup-title": "Wahl löschen?",
     "vote-delete-pop": "Löschen ist unwiderruflich. Alle Aktionen die dieser Karte zugeordnet sind werden ebenfalls gelöscht.",
+    "cardStartPlanningPokerPopup-title": "Planungspoker starten",
+    "card-edit-planning-poker": "Planungspoker ändern",
+    "editPokerEndDatePopup-title": "Enddatum für Planungspoker-Stimme ändern",
+    "poker-question": "Planungspoker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Beenden",
+    "poker-result-votes": "Werte",
+    "poker-result-who": "Wer",
+    "poker-replay": "Wiederholen",
+    "set-estimation": "Schätzung vornehmen",
+    "deletePokerPopup-title": "Planungspoker löschen?",
+    "poker-delete-pop": "Die Löschung ist permanent. Sie werden alles im Zusammenhang mit diesem Planungspoker verlieren.",
     "cardDeletePopup-title": "Karte löschen?",
     "cardDetailsActionsPopup-title": "Kartenaktionen",
     "cardLabelsPopup-title": "Labels",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Vorlage erstellen",
     "cards": "Karten",
     "cards-count": "Karten",
+    "cards-count-one": "Karte",
     "casSignIn": "Mit CAS anmelden",
     "cardType-card": "Karte",
     "cardType-linkedCard": "Verknüpfte Karte",
@@ -208,6 +236,7 @@
     "close": "Schließen",
     "close-board": "Board schließen",
     "close-board-pop": "Sie können das Board wiederherstellen, indem Sie die Schaltfläche \"Archiv\" in der Kopfzeile der Startseite anklicken.",
+    "close-card": "Karte schließen",
     "color-black": "schwarz",
     "color-blue": "blau",
     "color-crimson": "Karminrot",
@@ -332,7 +361,11 @@
     "export-board-excel": "Board nach Excel exportieren",
     "user-can-not-export-excel": "Benutzer kann nicht nach Excel exportieren",
     "export-board-html": "Board als HTML exportieren",
+    "export-card": "Karte exportieren",
+    "export-card-pdf": "Karte in ein PDF exportieren",
+    "user-can-not-export-card-to-pdf": "Benutzer kann Karte nicht in ein PDF exportieren",
     "exportBoardPopup-title": "Board exportieren",
+    "exportCardPopup-title": "Karte exportieren",
     "sort": "Sortieren",
     "sort-desc": "Zum Sortieren der Liste klicken",
     "list-sort-by": "Sortieren der Liste nach:",
@@ -421,6 +454,8 @@
     "set-color-list": "Lege Farbe fest",
     "listActionPopup-title": "Listenaktionen",
     "settingsUserPopup-title": "Benutzereinstellungen",
+    "settingsTeamPopup-title": "Team-Einstellungen",
+    "settingsOrgPopup-title": "Organisations-Einstellungen",
     "swimlaneActionPopup-title": "Swimlaneaktionen",
     "swimlaneAddPopup-title": "Swimlane unterhalb einfügen",
     "listImportCardPopup-title": "Eine Trello-Karte importieren",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Farbe wählen",
     "assigned-by": "Zugewiesen von",
     "requested-by": "Angefordert von",
+    "card-sorting-by-number": "Kartensortierung nach Nummer",
     "board-delete-notice": "Löschen kann nicht rückgängig gemacht werden. Sie werden alle Listen, Karten und Aktionen, die mit diesem Board verbunden sind, verlieren.",
     "delete-board-confirm-popup": "Alle Listen, Karten, Labels und Akivitäten werden gelöscht und Sie können die Inhalte des Boards nicht wiederherstellen! Die Aktion kann nicht rückgängig gemacht werden.",
     "boardDeletePopup-title": "Board löschen?",
@@ -817,6 +853,8 @@
     "act-duenow": "erinnernd an das aktuelle Fälligkeitszeitpunkt (__timeValue__) von __card__ ist jetzt",
     "act-atUserComment": "Sie wurden in [__board__] __list__/__card__ erwähnt",
     "delete-user-confirm-popup": "Sind Sie sicher, dass Sie diesen Account löschen wollen? Die Aktion kann nicht rückgängig gemacht werden.",
+    "delete-team-confirm-popup": "Sind Sie sicher, daß Sie dieses Team löschen wollen? Es gibt keine Möglichkeit, das rückgängig zu machen.",
+    "delete-org-confirm-popup": "Sind Sie sicher, daß Sie diese Organisation löschen wollen? Es gibt keine Möglichkeit, das rückgängig zu machen.",
     "accounts-allowUserDelete": "Erlaube Benutzern ihren eigenen Account zu löschen",
     "hide-minicard-label-text": "Labeltext auf Minikarte ausblenden",
     "show-desktop-drag-handles": "Desktop-Ziehpunkte anzeigen",
@@ -1006,5 +1044,19 @@
     "reports": "Berichte",
     "rulesReportTitle": "Regeln-Bericht",
     "copy-swimlane": "Kopiere Swimlane",
-    "copySwimlanePopup-title": "Swimlane kopieren"
+    "copySwimlanePopup-title": "Swimlane kopieren",
+    "display-card-creator": "Karten-Ersteller anzeigen",
+    "wait-spinner": "Warte-Symbol",
+    "Bounce": "Puls-Warte-Symbol",
+    "Cube": "Würfel-Warte-Symbol",
+    "Cube-Grid": "Würfel-Gitter-Warte-Symbol",
+    "Dot": "Punkt-Warte-Symbol",
+    "Double-Bounce": "Doppelpuls-Warte-Symbol",
+    "Rotateplane": "Drehscheibe-Warte-Symbol",
+    "Scaleout": "Scaleout-Warte-Symbol",
+    "Wave": "Wellen-Warte-Symbol",
+    "maximize-card": "Karte maximieren",
+    "minimize-card": "Karte minimieren",
+    "delete-org-warning-message": "Diese Organisation kann nicht gelöscht werden, da wenigstens ein Nutzer dazu gehört.",
+    "delete-team-warning-message": "Dieses Team kann nicht gelöscht werden, da wenigstens ein Nutzer dazu gehört."
 }

+ 53 - 1
i18n/el.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "η ημερομηνία λήξης άλλαξε σε %s από %s",
     "add-attachment": "Προσθήκη Συνημμένου",
     "add-board": "Προσθήκη Πίνακα",
+    "add-template": "Add Template",
     "add-card": "Προσθήκη Κάρτας",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Προσθήκη Λωρίδας",
     "add-subtask": "Προσθήκη Υποεργασίας",
     "add-checklist": "Προσθήκη Λίστας ελέγχου",
@@ -117,6 +120,8 @@
     "archives": "Αρχείο",
     "template": "Πρότυπο",
     "templates": "Πρότυπα",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Ανάθεση μέλους",
     "attached": "επισυνάφθηκε",
     "attachment": "Συνημμένο",
@@ -131,6 +136,7 @@
     "board-not-found": "Ο πίνακας δε βρέθηκε",
     "board-private-info": "Αυτός ο πίνακας θα είναι <strong>κρυφός</strong>.",
     "board-public-info": "Αυτός ο πίνακας θα είναι <strong>δημόσιος</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Αλλαγή Φόντου Πίνακα",
     "boardChangeTitlePopup-title": "Μετονομασία Πίνακα",
     "boardChangeVisibilityPopup-title": "Αλλαγή Ορατότητας",
@@ -178,6 +184,27 @@
     "vote-against": "εναντίον",
     "deleteVotePopup-title": "Διαγραφή ψήφου;",
     "vote-delete-pop": "Μόνιμη Διαγραφή. Θα χάσετε όλες τις ενέργειες που σχετίζονται με αυτή την ψήφο.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Διαγραφή Κάρτας;",
     "cardDetailsActionsPopup-title": "Ενέργειες κάρτας",
     "cardLabelsPopup-title": "Ετικέτες",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Δημιουργία προτύπου",
     "cards": "Κάρτες",
     "cards-count": "Κάρτες",
+    "cards-count-one": "Κάρτα",
     "casSignIn": "Σύνδεση χρήστη με CAS",
     "cardType-card": "Κάρτα",
     "cardType-linkedCard": "Συνδεδεμένη Κάρτα",
@@ -208,6 +236,7 @@
     "close": "Κλείσιμο",
     "close-board": "Κλείσιμο Πίνακα",
     "close-board-pop": "Μελλοντικά μπορείτε να επαναφέρετε τον πίνακα κάνοντας κλικ στο κουμπί \"Αρχειοθέτηση\" από την αρχική επικεφαλίδα.  ",
+    "close-card": "Close Card",
     "color-black": "μαύρο",
     "color-blue": "μπλε",
     "color-crimson": "βυσσινί",
@@ -332,7 +361,11 @@
     "export-board-excel": "Εξαγωγή πίνακα σε Excel",
     "user-can-not-export-excel": "Ο χρήστης δε μπορεί να εξάγει σε Excel",
     "export-board-html": "Εξαγωγή πίνακα σε HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Εξαγωγή πίνακα",
+    "exportCardPopup-title": "Export card",
     "sort": "Ταξινόμηση",
     "sort-desc": "Κάντε κλικ για να ταξινομήστε τη λίστα",
     "list-sort-by": "Ταξινόμηση λίστας βάσει:",
@@ -421,6 +454,8 @@
     "set-color-list": "Ρύθμιση Χρώματος",
     "listActionPopup-title": "Ενέργειες Λίστας",
     "settingsUserPopup-title": "Ρυθμίσεις Χρήστη",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Ενέργειες λωρίδας",
     "swimlaneAddPopup-title": "Προσθήκη μιας Λωρίδας παρακάτω",
     "listImportCardPopup-title": "Εισαγωγή μιας κάρτας Trello",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Επιλέξτε ένα χρώμα",
     "assigned-by": "Ανατέθηκε Από",
     "requested-by": "Αιτήθηκε Από",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Διαγραφή Πίνακα;",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/en-GB.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Add Attachment",
     "add-board": "Add Board",
+    "add-template": "Add Template",
     "add-card": "Add Card",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Add Swimlane",
     "add-subtask": "Add Subtask",
     "add-checklist": "Add Checklist",
@@ -117,6 +120,8 @@
     "archives": "Archive",
     "template": "Template",
     "templates": "Templates",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Assign member",
     "attached": "attached",
     "attachment": "Attachment",
@@ -131,6 +136,7 @@
     "board-not-found": "Board not found",
     "board-private-info": "This board will be <strong>private</strong>.",
     "board-public-info": "This board will be <strong>public</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Change Board Background",
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
@@ -178,6 +184,27 @@
     "vote-against": "against",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Delete Card?",
     "cardDetailsActionsPopup-title": "Card Actions",
     "cardLabelsPopup-title": "Labels",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "Cards",
     "cards-count": "Cards",
+    "cards-count-one": "Card",
     "casSignIn": "Sign In with CAS",
     "cardType-card": "Card",
     "cardType-linkedCard": "Linked Card",
@@ -208,6 +236,7 @@
     "close": "Close",
     "close-board": "Close Board",
     "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+    "close-card": "Close Card",
     "color-black": "black",
     "color-blue": "blue",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Export board",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "List Actions",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Swimlane Actions",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Import a Trello card",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Choose a color",
     "assigned-by": "Assigned By",
     "requested-by": "Requested By",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/en.i18n.json

@@ -81,7 +81,10 @@
   "activity-endDate": "edited end date to %s of %s",
   "add-attachment": "Add Attachment",
   "add-board": "Add Board",
+  "add-template": "Add Template",
   "add-card": "Add Card",
+  "add-card-to-top-of-list": "Add Card to Top of List",
+  "add-card-to-bottom-of-list": "Add Card to Bottom of List",
   "add-swimlane": "Add Swimlane",
   "add-subtask": "Add Subtask",
   "add-checklist": "Add Checklist",
@@ -117,6 +120,8 @@
   "archives": "Archive",
   "template": "Template",
   "templates": "Templates",
+  "template-container": "Template Container",
+  "add-template-container": "Add Template Container",
   "assign-member": "Assign member",
   "attached": "attached",
   "attachment": "Attachment",
@@ -131,6 +136,7 @@
   "board-not-found": "Board not found",
   "board-private-info": "This board will be <strong>private</strong>.",
   "board-public-info": "This board will be <strong>public</strong>.",
+  "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
   "boardChangeColorPopup-title": "Change Board Background",
   "boardChangeTitlePopup-title": "Rename Board",
   "boardChangeVisibilityPopup-title": "Change Visibility",
@@ -178,6 +184,27 @@
   "vote-against": "against",
   "deleteVotePopup-title": "Delete vote?",
   "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+  "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+  "card-edit-planning-poker": "Edit Planning Poker",
+  "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+  "poker-question": "Planning Poker",
+  "poker-one": "1",
+  "poker-two": "2",
+  "poker-three": "3",
+  "poker-five": "5",
+  "poker-eight": "8",
+  "poker-thirteen": "13",
+  "poker-twenty": "20",
+  "poker-forty": "40",
+  "poker-oneHundred": "100",
+  "poker-unsure": "?",
+  "poker-finish": "Finish",
+  "poker-result-votes": "Votes",
+  "poker-result-who": "Who",
+  "poker-replay": "Replay",
+  "set-estimation": "Set Estimation",
+  "deletePokerPopup-title": "Delete planning poker?",
+  "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
   "cardDeletePopup-title": "Delete Card?",
   "cardDetailsActionsPopup-title": "Card Actions",
   "cardLabelsPopup-title": "Labels",
@@ -186,6 +213,7 @@
   "cardTemplatePopup-title": "Create template",
   "cards": "Cards",
   "cards-count": "Cards",
+  "cards-count-one": "Card",
   "casSignIn": "Sign In with CAS",
   "cardType-card": "Card",
   "cardType-linkedCard": "Linked Card",
@@ -208,6 +236,7 @@
   "close": "Close",
   "close-board": "Close Board",
   "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+  "close-card": "Close Card",
   "color-black": "black",
   "color-blue": "blue",
   "color-crimson": "crimson",
@@ -332,7 +361,11 @@
   "export-board-excel": "Export board to Excel",
   "user-can-not-export-excel": "User can not export Excel",
   "export-board-html": "Export board to HTML",
+  "export-card": "Export card",
+  "export-card-pdf": "Export card to PDF",
+  "user-can-not-export-card-to-pdf": "User can not export card to PDF",
   "exportBoardPopup-title": "Export board",
+  "exportCardPopup-title": "Export card",
   "sort": "Sort",
   "sort-desc": "Click to Sort List",
   "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
   "set-color-list": "Set Color",
   "listActionPopup-title": "List Actions",
   "settingsUserPopup-title": "User Settings",
+  "settingsTeamPopup-title": "Team Settings",
+  "settingsOrgPopup-title": "Organization Settings",
   "swimlaneActionPopup-title": "Swimlane Actions",
   "swimlaneAddPopup-title": "Add a Swimlane below",
   "listImportCardPopup-title": "Import a Trello card",
@@ -636,6 +671,7 @@
   "setListColorPopup-title": "Choose a color",
   "assigned-by": "Assigned By",
   "requested-by": "Requested By",
+  "card-sorting-by-number": "Card sorting by number",
   "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
   "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
   "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
   "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
   "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
   "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+  "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+  "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
   "accounts-allowUserDelete": "Allow users to self delete their account",
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1008,5 +1046,19 @@
   "rulesReportTitle": "Rules Report",
   "cardsReportTitle": "Cards Report",
   "copy-swimlane": "Copy Swimlane",
-  "copySwimlanePopup-title": "Copy Swimlane"
+  "copySwimlanePopup-title": "Copy Swimlane",
+  "display-card-creator": "Display Card Creator",
+  "wait-spinner": "Wait Spinner",
+  "Bounce": "Bounce Wait Spinner",
+  "Cube": "Cube Wait Spinner",
+  "Cube-Grid": "Cube-Grid Wait Spinner",
+  "Dot": "Dot Wait Spinner",
+  "Double-Bounce": "Double Bounce Wait Spinner",
+  "Rotateplane": "Rotateplane Wait Spinner",
+  "Scaleout": "Scaleout Wait Spinner",
+  "Wave": "Wave Wait Spinner",
+  "maximize-card": "Maximize Card",
+  "minimize-card": "Minimize Card",
+  "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+  "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/eo.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Add Attachment",
     "add-board": "Add Board",
+    "add-template": "Add Template",
     "add-card": "Add Card",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Add Swimlane",
     "add-subtask": "Add Subtask",
     "add-checklist": "Add Checklist",
@@ -117,6 +120,8 @@
     "archives": "Arkivi",
     "template": "Template",
     "templates": "Templates",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Assign member",
     "attached": "attached",
     "attachment": "Attachment",
@@ -131,6 +136,7 @@
     "board-not-found": "Board not found",
     "board-private-info": "This board will be <strong>private</strong>.",
     "board-public-info": "This board will be <strong>public</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Change Board Background",
     "boardChangeTitlePopup-title": "Renomi tavolon",
     "boardChangeVisibilityPopup-title": "Change Visibility",
@@ -178,6 +184,27 @@
     "vote-against": "against",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Delete Card?",
     "cardDetailsActionsPopup-title": "Card Actions",
     "cardLabelsPopup-title": "Etikedoj",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "Kartoj",
     "cards-count": "Kartoj",
+    "cards-count-one": "Card",
     "casSignIn": "Sign In with CAS",
     "cardType-card": "Card",
     "cardType-linkedCard": "Linked Card",
@@ -208,6 +236,7 @@
     "close": "Fermi",
     "close-board": "Close Board",
     "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+    "close-card": "Close Card",
     "color-black": "nigra",
     "color-blue": "blua",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Export board",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "List Actions",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Swimlane Actions",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Import a Trello card",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Choose a color",
     "assigned-by": "Assigned By",
     "requested-by": "Requested By",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/es-AR.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Agregar Adjunto",
     "add-board": "Agregar Tablero",
+    "add-template": "Add Template",
     "add-card": "Agregar Tarjeta",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Agregar Calle",
     "add-subtask": "Agregar Subtarea",
     "add-checklist": "Agregar Lista de Tareas",
@@ -117,6 +120,8 @@
     "archives": "Archivar",
     "template": "Plantilla",
     "templates": "Plantillas",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Asignar miembro",
     "attached": "adjunto(s)",
     "attachment": "Adjunto",
@@ -131,6 +136,7 @@
     "board-not-found": "Tablero no encontrado",
     "board-private-info": "Este tablero va a ser <strong>privado</strong>.",
     "board-public-info": "Este tablero va a ser <strong>público</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Cambiar Fondo del Tablero",
     "boardChangeTitlePopup-title": "Renombrar Tablero",
     "boardChangeVisibilityPopup-title": "Cambiar Visibilidad",
@@ -178,6 +184,27 @@
     "vote-against": "against",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "¿Borrar Tarjeta?",
     "cardDetailsActionsPopup-title": "Acciones de la Tarjeta",
     "cardLabelsPopup-title": "Etiquetas",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Crear plantilla",
     "cards": "Tarjetas",
     "cards-count": "Tarjetas",
+    "cards-count-one": "Tarjeta",
     "casSignIn": "Ingresar con CAS",
     "cardType-card": "Tarjeta",
     "cardType-linkedCard": "Tarjeta Vinculada",
@@ -208,6 +236,7 @@
     "close": "Cerrar",
     "close-board": "Cerrar Tablero",
     "close-board-pop": "Podrás restaurar el tablero clickeando el \"Archivo\" desde el encabesado de inicio.",
+    "close-card": "Close Card",
     "color-black": "negro",
     "color-blue": "azul",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Exportar tablero",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "Listar Acciones",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Acciones de la Calle",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Importar una tarjeta Trello",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Choose a color",
     "assigned-by": "Assigned By",
     "requested-by": "Requested By",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/es-CL.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Añadir adjunto",
     "add-board": "Añadir tablero",
+    "add-template": "Add Template",
     "add-card": "Añadir una tarjeta",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Añadir un carril de flujo",
     "add-subtask": "Añadir subtarea",
     "add-checklist": "Añadir una lista de verificación",
@@ -117,6 +120,8 @@
     "archives": "Archivo",
     "template": "Plantilla",
     "templates": "Plantillas",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Asignar miembros",
     "attached": "adjuntado",
     "attachment": "Adjunto",
@@ -131,6 +136,7 @@
     "board-not-found": "Tablero no encontrado",
     "board-private-info": "Este tablero será <strong>privado</strong>.",
     "board-public-info": "Este tablero será <strong>público</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Cambiar el fondo del tablero",
     "boardChangeTitlePopup-title": "Renombrar el tablero",
     "boardChangeVisibilityPopup-title": "Cambiar visibilidad",
@@ -178,6 +184,27 @@
     "vote-against": "contrarios",
     "deleteVotePopup-title": "¿Borrar voto?",
     "vote-delete-pop": "El Borrado es permanente. Perderá todas las acciones asociadas con este voto.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "¿Eliminar la tarjeta?",
     "cardDetailsActionsPopup-title": "Acciones de la tarjeta",
     "cardLabelsPopup-title": "Etiquetas",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Crear plantilla",
     "cards": "Tarjetas",
     "cards-count": "Tarjetas",
+    "cards-count-one": "Tarjeta",
     "casSignIn": "Iniciar sesión con CAS",
     "cardType-card": "Tarjeta",
     "cardType-linkedCard": "Tarjeta enlazada",
@@ -208,6 +236,7 @@
     "close": "Cerrar",
     "close-board": "Cerrar el tablero",
     "close-board-pop": "Podrás restaurar el tablero haciendo clic en el botón \"Archivo\" del encabezado de la pantalla inicial.",
+    "close-card": "Close Card",
     "color-black": "negra",
     "color-blue": "azul",
     "color-crimson": "carmesí",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Exportar el tablero",
+    "exportCardPopup-title": "Export card",
     "sort": "Ordenar",
     "sort-desc": "Click para ordenar la lista",
     "list-sort-by": "Ordenar la lista por:",
@@ -421,6 +454,8 @@
     "set-color-list": "Cambiar el color",
     "listActionPopup-title": "Acciones de la lista",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Acciones del carril de flujo",
     "swimlaneAddPopup-title": "Añadir un carril de flujo debajo",
     "listImportCardPopup-title": "Importar una tarjeta de Trello",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Elegir un color",
     "assigned-by": "Asignado por",
     "requested-by": "Solicitado por",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Se eliminarán todas las listas, tarjetas y acciones asociadas a este tablero. Esta acción no puede deshacerse.",
     "delete-board-confirm-popup": "Se eliminarán todas las listas, tarjetas, etiquetas y actividades, y no podrás recuperar los contenidos del tablero. Esta acción no puede deshacerse.",
     "boardDeletePopup-title": "¿Eliminar el tablero?",
@@ -817,6 +853,8 @@
     "act-duenow": "se ha notificado que el vencimiento actual (__timeValue__) de __card__ es ahora",
     "act-atUserComment": "Se te mencionó en [__board__] __list__/__card__",
     "delete-user-confirm-popup": "¿Seguro que quieres eliminar esta cuenta? Esta acción no puede deshacerse.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Permitir a los usuarios eliminar su cuenta",
     "hide-minicard-label-text": "Ocultar el texto de la etiqueta de la minitarjeta",
     "show-desktop-drag-handles": "Mostrar los controles de arrastre del escritorio",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/es-LA.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Add Attachment",
     "add-board": "Add Board",
+    "add-template": "Add Template",
     "add-card": "Add Card",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Add Swimlane",
     "add-subtask": "Add Subtask",
     "add-checklist": "Add Checklist",
@@ -117,6 +120,8 @@
     "archives": "Archive",
     "template": "Template",
     "templates": "Templates",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Assign member",
     "attached": "attached",
     "attachment": "Attachment",
@@ -131,6 +136,7 @@
     "board-not-found": "Board not found",
     "board-private-info": "This board will be <strong>private</strong>.",
     "board-public-info": "This board will be <strong>public</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Change Board Background",
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
@@ -178,6 +184,27 @@
     "vote-against": "against",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Delete Card?",
     "cardDetailsActionsPopup-title": "Card Actions",
     "cardLabelsPopup-title": "Labels",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "Cards",
     "cards-count": "Cards",
+    "cards-count-one": "Card",
     "casSignIn": "Sign In with CAS",
     "cardType-card": "Card",
     "cardType-linkedCard": "Linked Card",
@@ -208,6 +236,7 @@
     "close": "Close",
     "close-board": "Close Board",
     "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+    "close-card": "Close Card",
     "color-black": "black",
     "color-blue": "blue",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Export board",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "List Actions",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Swimlane Actions",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Import a Trello card",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Choose a color",
     "assigned-by": "Assigned By",
     "requested-by": "Requested By",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/es-MX.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Add Attachment",
     "add-board": "Add Board",
+    "add-template": "Add Template",
     "add-card": "Add Card",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Add Swimlane",
     "add-subtask": "Add Subtask",
     "add-checklist": "Add Checklist",
@@ -117,6 +120,8 @@
     "archives": "Archive",
     "template": "Template",
     "templates": "Templates",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Assign member",
     "attached": "attached",
     "attachment": "Attachment",
@@ -131,6 +136,7 @@
     "board-not-found": "Board not found",
     "board-private-info": "This board will be <strong>private</strong>.",
     "board-public-info": "This board will be <strong>public</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Change Board Background",
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
@@ -178,6 +184,27 @@
     "vote-against": "against",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Delete Card?",
     "cardDetailsActionsPopup-title": "Card Actions",
     "cardLabelsPopup-title": "Labels",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "Cards",
     "cards-count": "Cards",
+    "cards-count-one": "Card",
     "casSignIn": "Sign In with CAS",
     "cardType-card": "Card",
     "cardType-linkedCard": "Linked Card",
@@ -208,6 +236,7 @@
     "close": "Close",
     "close-board": "Close Board",
     "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+    "close-card": "Close Card",
     "color-black": "black",
     "color-blue": "blue",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Export board",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "List Actions",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Swimlane Actions",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Import a Trello card",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Choose a color",
     "assigned-by": "Assigned By",
     "requested-by": "Requested By",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/es-PE.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "editada la fecha de finalización a %s de %s",
     "add-attachment": "Agregar adjunto",
     "add-board": "Agregar tablero",
+    "add-template": "Add Template",
     "add-card": "Agregar una tarjeta",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Agregar un carril «swimlane»",
     "add-subtask": "Agregar subtarea",
     "add-checklist": "Agregar lista de comprobación",
@@ -117,6 +120,8 @@
     "archives": "Archivo",
     "template": "Plantilla",
     "templates": "Plantillas",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Asignar miembros",
     "attached": "adjuntado",
     "attachment": "Adjunto",
@@ -131,6 +136,7 @@
     "board-not-found": "Tablero no encontrado",
     "board-private-info": "Este tablero será <strong>privado</strong>.",
     "board-public-info": "Este tablero será <strong>público</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Cambiar el fondo del tablero",
     "boardChangeTitlePopup-title": "Renombrar el tablero",
     "boardChangeVisibilityPopup-title": "Cambiar visibilidad",
@@ -178,6 +184,27 @@
     "vote-against": "contrarios",
     "deleteVotePopup-title": "¿Borrar voto?",
     "vote-delete-pop": "El borrado es permanente. Perderá todas las acciones asociadas con este voto.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "¿Eliminar la tarjeta?",
     "cardDetailsActionsPopup-title": "Acciones de la tarjeta",
     "cardLabelsPopup-title": "Etiquetas",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Crear plantilla",
     "cards": "Tarjetas",
     "cards-count": "Tarjetas",
+    "cards-count-one": "Tarjeta",
     "casSignIn": "Iniciar sesión con CAS",
     "cardType-card": "Tarjeta",
     "cardType-linkedCard": "Tarjeta enlazada",
@@ -208,6 +236,7 @@
     "close": "Cerrar",
     "close-board": "Cerrar el tablero",
     "close-board-pop": "Podrá restaurar el tablero haciendo clic en el botón «Archivo» de la cabecera de inicio.",
+    "close-card": "Close Card",
     "color-black": "negro",
     "color-blue": "azul",
     "color-crimson": "carmesí",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Exportar tablero a HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Exportar tablero",
+    "exportCardPopup-title": "Export card",
     "sort": "Ordenar",
     "sort-desc": "Clic para ordenar lista",
     "list-sort-by": "Ordenar la lista por:",
@@ -421,6 +454,8 @@
     "set-color-list": "Cambiar el color",
     "listActionPopup-title": "Acciones de la lista",
     "settingsUserPopup-title": "Configuración de usuario",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Acciones del carril «swimlane»",
     "swimlaneAddPopup-title": "Agregar un carril «swimlane» debajo",
     "listImportCardPopup-title": "Importar una tarjeta de Trello",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Elegir un color",
     "assigned-by": "Asignado por",
     "requested-by": "Solicitado por",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Se eliminarán todas las listas, tarjetas y acciones asociadas a este tablero. Esta acción no puede deshacerse.",
     "delete-board-confirm-popup": "Se eliminarán todas las listas, tarjetas, etiquetas y actividades, y no podrás recuperar los contenidos del tablero. Esta acción no puede deshacerse.",
     "boardDeletePopup-title": "¿Eliminar el tablero?",
@@ -817,6 +853,8 @@
     "act-duenow": "se ha notificado que el vencimiento actual (__timeValue__) de __card__ es ahora",
     "act-atUserComment": "Se le mencionó en [__board__] __list__/__card__",
     "delete-user-confirm-popup": "¿Seguro que desea eliminar esta cuenta? Esta acción no puede deshacerse.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Permitir a los usuarios eliminar su cuenta",
     "hide-minicard-label-text": "Ocultar el texto de la etiqueta de la minitarjeta",
     "show-desktop-drag-handles": "Mostrar los controles de arrastre del escritorio",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

+ 53 - 1
i18n/es-PY.i18n.json

@@ -81,7 +81,10 @@
     "activity-endDate": "edited end date to %s of %s",
     "add-attachment": "Add Attachment",
     "add-board": "Add Board",
+    "add-template": "Add Template",
     "add-card": "Add Card",
+    "add-card-to-top-of-list": "Add Card to Top of List",
+    "add-card-to-bottom-of-list": "Add Card to Bottom of List",
     "add-swimlane": "Add Swimlane",
     "add-subtask": "Add Subtask",
     "add-checklist": "Add Checklist",
@@ -117,6 +120,8 @@
     "archives": "Archive",
     "template": "Template",
     "templates": "Templates",
+    "template-container": "Template Container",
+    "add-template-container": "Add Template Container",
     "assign-member": "Assign member",
     "attached": "attached",
     "attachment": "Attachment",
@@ -131,6 +136,7 @@
     "board-not-found": "Board not found",
     "board-private-info": "This board will be <strong>private</strong>.",
     "board-public-info": "This board will be <strong>public</strong>.",
+    "board-drag-drop-reorder-or-click-open": "Drag and drop to reorder board icons. Click board icon to open board.",
     "boardChangeColorPopup-title": "Change Board Background",
     "boardChangeTitlePopup-title": "Rename Board",
     "boardChangeVisibilityPopup-title": "Change Visibility",
@@ -178,6 +184,27 @@
     "vote-against": "against",
     "deleteVotePopup-title": "Delete vote?",
     "vote-delete-pop": "Deleting is permanent. You will lose all actions associated with this vote.",
+    "cardStartPlanningPokerPopup-title": "Start a Planning Poker",
+    "card-edit-planning-poker": "Edit Planning Poker",
+    "editPokerEndDatePopup-title": "Change Planning Poker vote end date",
+    "poker-question": "Planning Poker",
+    "poker-one": "1",
+    "poker-two": "2",
+    "poker-three": "3",
+    "poker-five": "5",
+    "poker-eight": "8",
+    "poker-thirteen": "13",
+    "poker-twenty": "20",
+    "poker-forty": "40",
+    "poker-oneHundred": "100",
+    "poker-unsure": "?",
+    "poker-finish": "Finish",
+    "poker-result-votes": "Votes",
+    "poker-result-who": "Who",
+    "poker-replay": "Replay",
+    "set-estimation": "Set Estimation",
+    "deletePokerPopup-title": "Delete planning poker?",
+    "poker-delete-pop": "Deleting is permanent. You will lose all actions associated with this planning poker.",
     "cardDeletePopup-title": "Delete Card?",
     "cardDetailsActionsPopup-title": "Card Actions",
     "cardLabelsPopup-title": "Labels",
@@ -186,6 +213,7 @@
     "cardTemplatePopup-title": "Create template",
     "cards": "Cards",
     "cards-count": "Cards",
+    "cards-count-one": "Card",
     "casSignIn": "Sign In with CAS",
     "cardType-card": "Card",
     "cardType-linkedCard": "Linked Card",
@@ -208,6 +236,7 @@
     "close": "Close",
     "close-board": "Close Board",
     "close-board-pop": "You will be able to restore the board by clicking the “Archive” button from the home header.",
+    "close-card": "Close Card",
     "color-black": "black",
     "color-blue": "blue",
     "color-crimson": "crimson",
@@ -332,7 +361,11 @@
     "export-board-excel": "Export board to Excel",
     "user-can-not-export-excel": "User can not export Excel",
     "export-board-html": "Export board to HTML",
+    "export-card": "Export card",
+    "export-card-pdf": "Export card to PDF",
+    "user-can-not-export-card-to-pdf": "User can not export card to PDF",
     "exportBoardPopup-title": "Export board",
+    "exportCardPopup-title": "Export card",
     "sort": "Sort",
     "sort-desc": "Click to Sort List",
     "list-sort-by": "Sort the List By:",
@@ -421,6 +454,8 @@
     "set-color-list": "Set Color",
     "listActionPopup-title": "List Actions",
     "settingsUserPopup-title": "User Settings",
+    "settingsTeamPopup-title": "Team Settings",
+    "settingsOrgPopup-title": "Organization Settings",
     "swimlaneActionPopup-title": "Swimlane Actions",
     "swimlaneAddPopup-title": "Add a Swimlane below",
     "listImportCardPopup-title": "Import a Trello card",
@@ -636,6 +671,7 @@
     "setListColorPopup-title": "Choose a color",
     "assigned-by": "Assigned By",
     "requested-by": "Requested By",
+    "card-sorting-by-number": "Card sorting by number",
     "board-delete-notice": "Deleting is permanent. You will lose all lists, cards and actions associated with this board.",
     "delete-board-confirm-popup": "All lists, cards, labels, and activities will be deleted and you won't be able to recover the board contents. There is no undo.",
     "boardDeletePopup-title": "Delete Board?",
@@ -817,6 +853,8 @@
     "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now",
     "act-atUserComment": "You were mentioned in [__board__] __list__/__card__",
     "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.",
+    "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.",
+    "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.",
     "accounts-allowUserDelete": "Allow users to self delete their account",
     "hide-minicard-label-text": "Hide minicard label text",
     "show-desktop-drag-handles": "Show desktop drag handles",
@@ -1006,5 +1044,19 @@
     "reports": "Reports",
     "rulesReportTitle": "Rules Report",
     "copy-swimlane": "Copy Swimlane",
-    "copySwimlanePopup-title": "Copy Swimlane"
+    "copySwimlanePopup-title": "Copy Swimlane",
+    "display-card-creator": "Display Card Creator",
+    "wait-spinner": "Wait Spinner",
+    "Bounce": "Bounce Wait Spinner",
+    "Cube": "Cube Wait Spinner",
+    "Cube-Grid": "Cube-Grid Wait Spinner",
+    "Dot": "Dot Wait Spinner",
+    "Double-Bounce": "Double Bounce Wait Spinner",
+    "Rotateplane": "Rotateplane Wait Spinner",
+    "Scaleout": "Scaleout Wait Spinner",
+    "Wave": "Wave Wait Spinner",
+    "maximize-card": "Maximize Card",
+    "minimize-card": "Minimize Card",
+    "delete-org-warning-message": "Can not delete this organization, there is at least one user that belongs to it",
+    "delete-team-warning-message": "Can not delete this team, there is at least one user that belongs to it"
 }

Some files were not shown because too many files changed in this diff