Browse Source

Merge branch 'master' of https://github.com/wekan/wekan into lib-change

Romulus Urakagi Tsai 5 years ago
parent
commit
4b196d5378
100 changed files with 11396 additions and 914 deletions
  1. 1 1
      .devcontainer/Dockerfile
  2. 1 0
      .eslintignore
  3. 1 0
      .eslintrc.json
  4. 2 0
      .github/ISSUE_TEMPLATE.md
  5. 17 0
      .github/workflows/dockerimage.yml
  6. 1 0
      .meteor/.finished-upgraders
  7. 6 5
      .meteor/packages
  8. 1 1
      .meteor/release
  9. 27 27
      .meteor/versions
  10. 1 0
      .prettierignore
  11. 20 0
      .snap-meteor-1.8/.meteor/.finished-upgraders
  12. 2 0
      .snap-meteor-1.8/.meteor/.gitignore
  13. 7 0
      .snap-meteor-1.8/.meteor/.id
  14. 0 0
      .snap-meteor-1.8/.meteor/cordova-plugins
  15. 99 0
      .snap-meteor-1.8/.meteor/packages
  16. 2 0
      .snap-meteor-1.8/.meteor/platforms
  17. 1 0
      .snap-meteor-1.8/.meteor/release
  18. 198 0
      .snap-meteor-1.8/.meteor/versions
  19. 914 0
      .snap-meteor-1.8/cfs_access-point.txt
  20. 238 0
      .snap-meteor-1.8/export.js
  21. 155 0
      .snap-meteor-1.8/future/snapcraft.yaml
  22. 584 0
      .snap-meteor-1.8/ldap.js
  23. 149 0
      .snap-meteor-1.8/oidc_server.js
  24. 5184 0
      .snap-meteor-1.8/package-lock.json
  25. 73 0
      .snap-meteor-1.8/package.json
  26. 47 65
      .snap-meteor-1.8/snapcraft.yaml
  27. 853 0
      .snap-meteor-1.8/wekanCreator.js
  28. 3 3
      .travis.yml
  29. 444 5
      CHANGELOG.md
  30. 5 5
      Dockerfile
  31. 1 1
      Stackerfile.yml
  32. 3 3
      client/components/activities/activities.jade
  33. 1 1
      client/components/activities/activities.styl
  34. 0 3
      client/components/activities/comments.js
  35. 20 0
      client/components/activities/comments.styl
  36. 52 13
      client/components/boards/boardBody.js
  37. 64 28
      client/components/boards/boardHeader.jade
  38. 32 21
      client/components/boards/boardHeader.js
  39. 16 13
      client/components/cards/attachments.jade
  40. 2 1
      client/components/cards/cardDate.js
  41. 301 174
      client/components/cards/cardDetails.jade
  42. 101 1
      client/components/cards/cardDetails.js
  43. 31 1
      client/components/cards/cardDetails.styl
  44. 3 1
      client/components/cards/checklists.jade
  45. 9 3
      client/components/cards/checklists.js
  46. 9 8
      client/components/cards/minicard.jade
  47. 23 3
      client/components/cards/minicard.js
  48. 3 1
      client/components/cards/subtasks.jade
  49. 6 3
      client/components/cards/subtasks.js
  50. 56 20
      client/components/lists/list.js
  51. 16 22
      client/components/lists/listBody.js
  52. 40 17
      client/components/lists/listHeader.jade
  53. 21 7
      client/components/lists/listHeader.js
  54. 3 0
      client/components/main/header.styl
  55. 25 12
      client/components/rules/actions/boardActions.jade
  56. 25 5
      client/components/rules/actions/boardActions.js
  57. 6 2
      client/components/settings/informationBody.jade
  58. 54 18
      client/components/settings/peopleBody.jade
  59. 1 1
      client/components/settings/peopleBody.styl
  60. 19 12
      client/components/settings/settingBody.jade
  61. 0 8
      client/components/settings/settingBody.js
  62. 4 1
      client/components/settings/settingBody.styl
  63. 188 26
      client/components/sidebar/sidebar.jade
  64. 399 4
      client/components/sidebar/sidebar.js
  65. 1 1
      client/components/sidebar/sidebar.styl
  66. 30 24
      client/components/sidebar/sidebarArchives.jade
  67. 9 0
      client/components/sidebar/sidebarArchives.js
  68. 8 7
      client/components/sidebar/sidebarFilters.jade
  69. 10 1
      client/components/swimlanes/swimlaneHeader.js
  70. 32 30
      client/components/swimlanes/swimlanes.jade
  71. 75 20
      client/components/swimlanes/swimlanes.js
  72. 49 13
      client/components/swimlanes/swimlanes.styl
  73. 1 0
      client/components/users/userAvatar.jade
  74. 47 18
      client/components/users/userHeader.jade
  75. 82 11
      client/components/users/userHeader.js
  76. 28 0
      client/lib/keyboard.js
  77. 8 3
      client/lib/textComplete.js
  78. 66 0
      client/lib/utils.js
  79. 7 3
      config/accounts.js
  80. 32 8
      docker-compose.yml
  81. 4 4
      fix-download-unicode/cfs_access-point.txt
  82. 1 1
      helm/wekan/requirements.yaml
  83. 1 1
      helm/wekan/templates/_helpers.tpl
  84. 1 1
      helm/wekan/templates/ingress.yaml
  85. 9 2
      i18n/ar.i18n.json
  86. 9 2
      i18n/bg.i18n.json
  87. 9 2
      i18n/br.i18n.json
  88. 10 3
      i18n/ca.i18n.json
  89. 15 8
      i18n/cs.i18n.json
  90. 9 2
      i18n/da.i18n.json
  91. 13 6
      i18n/de.i18n.json
  92. 9 2
      i18n/el.i18n.json
  93. 9 2
      i18n/en-GB.i18n.json
  94. 9 2
      i18n/en.i18n.json
  95. 37 30
      i18n/eo.i18n.json
  96. 9 2
      i18n/es-AR.i18n.json
  97. 10 3
      i18n/es.i18n.json
  98. 9 2
      i18n/eu.i18n.json
  99. 128 121
      i18n/fa.i18n.json
  100. 40 33
      i18n/fi.i18n.json

+ 1 - 1
.devcontainer/Dockerfile

@@ -6,7 +6,7 @@ ENV DEBIAN_FRONTEND=noninteractive
 
 ENV \
     DEBUG=false \
-    NODE_VERSION=8.16.1 \
+    NODE_VERSION=8.17.0 \
     METEOR_RELEASE=1.8.1 \
     USE_EDGE=false \
     METEOR_EDGE=1.5-beta.17 \

+ 1 - 0
.eslintignore

@@ -1 +1,2 @@
 packages/*
+.snap-meteor-1.8/*

+ 1 - 0
.eslintrc.json

@@ -145,6 +145,7 @@
     "allowIsBoardMemberByCard": true,
     "allowIsBoardMemberCommentOnly": true,
     "allowIsBoardMemberNoComments": true,
+    "allowIsBoardMemberWorker": true,
     "Emoji": true,
     "Checklists": true,
     "Settings": true,

+ 2 - 0
.github/ISSUE_TEMPLATE.md

@@ -2,6 +2,8 @@
 
 Add these issues to elsewhere:
 - Snap: https://github.com/wekan/wekan-snap/issues
+- LDAP: https://github.com/wekan/wekan-ldap/issues
+- UCS: https://github.com/wekan/univention/issues
 
 Other Wekan issues can be added here.
 

+ 17 - 0
.github/workflows/dockerimage.yml

@@ -0,0 +1,17 @@
+name: Docker Image CI
+
+on:
+  push:
+    branches:
+      - master
+
+jobs:
+
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v1
+    - name: Build the Docker image
+      run: docker build . --file Dockerfile --tag wekan:$(date +%s)

+ 1 - 0
.meteor/.finished-upgraders

@@ -17,3 +17,4 @@ notices-for-facebook-graph-api-2
 1.4.3-split-account-service-packages
 1.5-add-dynamic-import-package
 1.7-split-underscore-from-meteor-base
+1.8.3-split-jquery-from-blaze

+ 6 - 5
.meteor/packages

@@ -6,9 +6,9 @@
 meteor-base@1.4.0
 
 # Build system
-ecmascript@0.12.4
-standard-minifier-css@1.5.3
-standard-minifier-js@2.4.1
+ecmascript@0.14.0
+standard-minifier-css@1.6.0
+standard-minifier-js@2.6.0
 mquandalle:jade
 
 # Polyfills
@@ -22,7 +22,7 @@ dburles:collection-helpers
 idmontie:migrations
 matb33:collection-hooks
 matteodem:easy-search
-mongo@1.6.2
+mongo@1.8.0
 mquandalle:collection-mutations
 
 # Account system
@@ -75,7 +75,7 @@ horka:swipebox
 dynamic-import@0.5.1
 staringatlights:fast-render
 
-accounts-password@1.5.1
+accounts-password@1.5.2
 cfs:gridfs
 rzymek:fullcalendar
 momentjs:moment@2.22.2
@@ -97,3 +97,4 @@ percolate:synced-cron
 easylogic:summernote
 cfs:filesystem
 ostrio:files
+ostrio:cookies

+ 1 - 1
.meteor/release

@@ -1 +1 @@
-METEOR@1.8.1
+METEOR@1.9

+ 27 - 27
.meteor/versions

@@ -1,7 +1,7 @@
 3stack:presence@1.1.2
-accounts-base@1.4.4
+accounts-base@1.5.0
 accounts-oauth@1.1.16
-accounts-password@1.5.1
+accounts-password@1.5.3
 aldeed:collection2@2.10.0
 aldeed:collection2-core@1.2.0
 aldeed:schema-deny@1.1.0
@@ -12,18 +12,18 @@ allow-deny@1.1.0
 arillo:flow-router-helpers@0.5.2
 audit-argument-checks@1.0.7
 autoupdate@1.6.0
-babel-compiler@7.3.4
-babel-runtime@1.3.0
+babel-compiler@7.5.1
+babel-runtime@1.5.0
 base64@1.0.12
 binary-heap@1.0.11
-blaze@2.3.3
+blaze@2.3.4
 blaze-tools@1.0.10
 boilerplate-generator@1.6.0
 browser-policy-common@1.0.11
 browser-policy-framing@1.1.0
 caching-compiler@1.2.1
 caching-html-compiler@1.1.3
-callback-hook@1.1.0
+callback-hook@1.3.0
 cfs:access-point@0.1.49
 cfs:base-package@0.0.30
 cfs:collection@0.5.5
@@ -52,16 +52,16 @@ ddp@1.4.0
 ddp-client@2.3.3
 ddp-common@1.4.0
 ddp-rate-limiter@1.0.7
-ddp-server@2.3.0
+ddp-server@2.3.1
 deps@1.0.12
 diff-sequence@1.1.1
 dynamic-import@0.5.1
 easylogic:summernote@0.8.8
-ecmascript@0.12.7
+ecmascript@0.14.1
 ecmascript-runtime@0.7.0
-ecmascript-runtime-client@0.8.0
-ecmascript-runtime-server@0.7.1
-ejson@1.1.0
+ecmascript-runtime-client@0.10.0
+ecmascript-runtime-server@0.9.0
+ejson@1.1.1
 email@1.2.3
 es5-shim@4.8.0
 fastclick@1.0.13
@@ -82,7 +82,7 @@ kadira:dochead@1.5.0
 kadira:flow-router@2.12.1
 kenton:accounts-sandstorm@0.7.0
 konecty:mongo-counter@0.0.5_3
-lamhieu:meteorx@2.0.1
+lamhieu:meteorx@2.1.1
 lamhieu:unblock@1.0.0
 launch-screen@1.1.1
 livedata@1.0.18
@@ -91,7 +91,7 @@ logging@1.1.20
 lucasantoniassi:accounts-lockout@1.0.0
 matb33:collection-hooks@0.9.1
 matteodem:easy-search@1.6.4
-mdg:meteor-apm-agent@3.2.3
+mdg:meteor-apm-agent@3.2.5
 mdg:validation-error@0.5.1
 meteor@1.9.3
 meteor-base@1.4.0
@@ -101,16 +101,16 @@ meteorhacks:collection-utils@1.2.0
 meteorhacks:picker@1.0.3
 meteorhacks:subs-manager@1.6.4
 meteorspark:util@0.2.0
-minifier-css@1.4.2
-minifier-js@2.4.1
+minifier-css@1.5.0
+minifier-js@2.6.0
 minifiers@1.1.8-faster-rebuild.0
 minimongo@1.4.5
 mobile-status-bar@1.0.14
-modern-browsers@0.1.4
-modules@0.13.0
-modules-runtime@0.10.3
+modern-browsers@0.1.5
+modules@0.15.0
+modules-runtime@0.12.0
 momentjs:moment@2.24.0
-mongo@1.6.3
+mongo@1.8.1
 mongo-decimal@0.1.1
 mongo-dev-server@1.1.0
 mongo-id@1.0.7
@@ -127,7 +127,7 @@ mquandalle:mousetrap-bindglobal@0.0.1
 mquandalle:perfect-scrollbar@0.6.5_2
 msavin:usercache@1.8.0
 npm-bcrypt@0.9.3
-npm-mongo@3.1.2
+npm-mongo@3.3.0
 oauth@1.2.8
 oauth2@1.2.1
 observe-sequence@1.0.16
@@ -135,11 +135,11 @@ ongoworks:speakingurl@1.1.0
 ordered-dict@1.1.0
 ostrio:cookies@2.5.0
 ostrio:files@1.13.0
-peerlibrary:assert@0.2.5
+peerlibrary:assert@0.3.0
 peerlibrary:base-component@0.16.0
 peerlibrary:blaze-components@0.15.1
-peerlibrary:computed-field@0.9.0
-peerlibrary:reactive-field@0.5.0
+peerlibrary:computed-field@0.10.0
+peerlibrary:reactive-field@0.6.0
 percolate:synced-cron@1.3.2
 promise@0.11.2
 raix:eventemitter@0.1.3
@@ -163,13 +163,13 @@ simple:json-routes@2.1.0
 simple:rest-accounts-password@1.1.2
 simple:rest-bearer-token-parser@1.0.1
 simple:rest-json-error-handler@1.0.1
-socket-stream-client@0.2.2
+socket-stream-client@0.2.3
 softwarerero:accounts-t9n@1.3.11
 spacebars@1.0.15
 spacebars-compiler@1.1.3
 srp@1.0.12
-standard-minifier-css@1.5.3
-standard-minifier-js@2.4.1
+standard-minifier-css@1.6.0
+standard-minifier-js@2.6.0
 staringatlights:fast-render@3.2.0
 staringatlights:inject-data@2.3.0
 tap:i18n@1.8.2
@@ -187,7 +187,7 @@ useraccounts:core@1.14.2
 useraccounts:flow-routing@1.14.2
 useraccounts:unstyled@1.14.2
 verron:autosize@3.0.8
-webapp@1.7.4
+webapp@1.8.2
 webapp-hashing@1.0.9
 wekan-accounts-cas@0.1.0
 wekan-accounts-oidc@1.0.10

+ 1 - 0
.prettierignore

@@ -5,3 +5,4 @@ node_modules/
 .vscode/
 .tx/
 .github/
+.snap-meteor-1.8/

+ 20 - 0
.snap-meteor-1.8/.meteor/.finished-upgraders

@@ -0,0 +1,20 @@
+# This file contains information which helps Meteor properly upgrade your
+# app when you run 'meteor update'. You should check it into version control
+# with your project.
+
+notices-for-0.9.0
+notices-for-0.9.1
+0.9.4-platform-file
+notices-for-facebook-graph-api-2
+1.2.0-standard-minifiers-package
+1.2.0-meteor-platform-split
+1.2.0-cordova-changes
+1.2.0-breaking-changes
+1.3.0-split-minifiers-package
+1.3.5-remove-old-dev-bundle-link
+1.4.0-remove-old-dev-bundle-link
+1.4.1-add-shell-server-package
+1.4.3-split-account-service-packages
+1.5-add-dynamic-import-package
+1.7-split-underscore-from-meteor-base
+1.8.3-split-jquery-from-blaze

+ 2 - 0
.snap-meteor-1.8/.meteor/.gitignore

@@ -0,0 +1,2 @@
+dev_bundle
+local

+ 7 - 0
.snap-meteor-1.8/.meteor/.id

@@ -0,0 +1,7 @@
+# This file contains a token that is unique to your project.
+# Check it into your repository along with the rest of this directory.
+# It can be used for purposes such as:
+#   - ensuring you don't accidentally deploy one app on top of another
+#   - providing package authors with aggregated statistics
+
+dvyihgykyzec6y1dpg

+ 0 - 0
.snap-meteor-1.8/.meteor/cordova-plugins


+ 99 - 0
.snap-meteor-1.8/.meteor/packages

@@ -0,0 +1,99 @@
+# Meteor packages used by this project, one per line.
+#
+# 'meteor add' and 'meteor remove' will edit this file for you,
+# but you can also edit it by hand.
+
+meteor-base@1.4.0
+
+# Build system
+ecmascript@0.13.2
+standard-minifier-css@1.5.4
+standard-minifier-js@2.5.2
+mquandalle:jade
+
+# Polyfills
+es5-shim@4.8.0
+
+# Collections
+aldeed:collection2
+cfs:standard-packages
+cottz:publish-relations
+dburles:collection-helpers
+idmontie:migrations
+matb33:collection-hooks
+matteodem:easy-search
+mongo@1.7.0
+mquandalle:collection-mutations
+
+# Account system
+kenton:accounts-sandstorm
+service-configuration@1.0.11
+useraccounts:unstyled
+useraccounts:flow-routing
+wekan-ldap
+wekan-accounts-cas
+wekan-accounts-oidc
+
+# Utilities
+check@1.3.1
+jquery@1.11.10
+random@1.1.0
+reactive-dict@1.3.0
+session@1.2.0
+tracker@1.2.0
+underscore@1.0.10
+3stack:presence
+alethes:pages
+arillo:flow-router-helpers
+audit-argument-checks@1.0.7
+kadira:blaze-layout
+kadira:dochead
+mquandalle:autofocus
+ongoworks:speakingurl
+raix:handlebar-helpers
+tap:i18n
+http@1.4.2
+
+# UI components
+blaze
+reactive-var@1.0.11
+fortawesome:fontawesome
+mousetrap:mousetrap
+mquandalle:jquery-textcomplete
+mquandalle:jquery-ui-drag-drop-sort
+mquandalle:mousetrap-bindglobal
+peerlibrary:blaze-components@=0.15.1
+templates:tabs
+verron:autosize
+simple:json-routes
+rajit:bootstrap3-datepicker
+shell-server@0.4.0
+simple:rest-accounts-password
+useraccounts:core
+email@1.2.3
+horka:swipebox
+dynamic-import@0.5.1
+staringatlights:fast-render
+
+accounts-password@1.5.2
+cfs:gridfs
+rzymek:fullcalendar
+momentjs:moment@2.22.2
+browser-policy-framing@1.1.0
+mquandalle:moment
+msavin:usercache
+wekan-scrollbar
+mquandalle:perfect-scrollbar
+mdg:meteor-apm-agent@3.2.0-rc.0!
+coagmano:stylus
+lucasantoniassi:accounts-lockout
+meteorhacks:subs-manager
+meteorhacks:picker
+lamhieu:unblock
+meteorhacks:aggregate@1.3.0
+wekan-markdown
+konecty:mongo-counter
+percolate:synced-cron
+easylogic:summernote
+cfs:filesystem
+ostrio:cookies

+ 2 - 0
.snap-meteor-1.8/.meteor/platforms

@@ -0,0 +1,2 @@
+server
+browser

+ 1 - 0
.snap-meteor-1.8/.meteor/release

@@ -0,0 +1 @@
+METEOR@1.8.3

+ 198 - 0
.snap-meteor-1.8/.meteor/versions

@@ -0,0 +1,198 @@
+3stack:presence@1.1.2
+accounts-base@1.4.5
+accounts-oauth@1.1.16
+accounts-password@1.5.2
+aldeed:collection2@2.10.0
+aldeed:collection2-core@1.2.0
+aldeed:schema-deny@1.1.0
+aldeed:schema-index@1.1.1
+aldeed:simple-schema@1.5.4
+alethes:pages@1.8.6
+allow-deny@1.1.0
+arillo:flow-router-helpers@0.5.2
+audit-argument-checks@1.0.7
+autoupdate@1.6.0
+babel-compiler@7.4.2
+babel-runtime@1.4.0
+base64@1.0.12
+binary-heap@1.0.11
+blaze@2.3.4
+blaze-tools@1.0.10
+boilerplate-generator@1.6.0
+browser-policy-common@1.0.11
+browser-policy-framing@1.1.0
+caching-compiler@1.2.1
+caching-html-compiler@1.1.3
+callback-hook@1.2.0
+cfs:access-point@0.1.49
+cfs:base-package@0.0.30
+cfs:collection@0.5.5
+cfs:collection-filters@0.2.4
+cfs:data-man@0.0.6
+cfs:file@0.1.17
+cfs:filesystem@0.1.2
+cfs:gridfs@0.0.34
+cfs:http-methods@0.0.32
+cfs:http-publish@0.0.13
+cfs:power-queue@0.9.11
+cfs:reactive-list@0.0.9
+cfs:reactive-property@0.0.4
+cfs:standard-packages@0.5.10
+cfs:storage-adapter@0.2.4
+cfs:tempstore@0.1.6
+cfs:upload-http@0.0.20
+cfs:worker@0.1.5
+check@1.3.1
+chuangbo:cookie@1.1.0
+coagmano:stylus@2.0.0
+coffeescript@1.0.17
+cottz:publish-relations@2.0.8
+dburles:collection-helpers@1.1.0
+ddp@1.4.0
+ddp-client@2.3.3
+ddp-common@1.4.0
+ddp-rate-limiter@1.0.7
+ddp-server@2.3.0
+deps@1.0.12
+diff-sequence@1.1.1
+dynamic-import@0.5.1
+easylogic:summernote@0.8.8
+ecmascript@0.13.2
+ecmascript-runtime@0.7.0
+ecmascript-runtime-client@0.9.0
+ecmascript-runtime-server@0.8.0
+ejson@1.1.1
+email@1.2.3
+es5-shim@4.8.0
+fastclick@1.0.13
+fetch@0.1.1
+fortawesome:fontawesome@4.7.0
+geojson-utils@1.0.10
+horka:swipebox@1.0.2
+hot-code-push@1.0.4
+html-tools@1.0.11
+htmljs@1.0.11
+http@1.4.2
+id-map@1.1.0
+idmontie:migrations@1.0.3
+inter-process-messaging@0.1.0
+jquery@1.11.11
+kadira:blaze-layout@2.3.0
+kadira:dochead@1.5.0
+kadira:flow-router@2.12.1
+kenton:accounts-sandstorm@0.7.0
+konecty:mongo-counter@0.0.5_3
+lamhieu:meteorx@2.1.1
+lamhieu:unblock@1.0.0
+launch-screen@1.1.1
+livedata@1.0.18
+localstorage@1.2.0
+logging@1.1.20
+lucasantoniassi:accounts-lockout@1.0.0
+matb33:collection-hooks@0.9.1
+matteodem:easy-search@1.6.4
+mdg:meteor-apm-agent@3.2.5
+mdg:validation-error@0.5.1
+meteor@1.9.3
+meteor-base@1.4.0
+meteor-platform@1.2.6
+meteorhacks:aggregate@1.3.0
+meteorhacks:collection-utils@1.2.0
+meteorhacks:picker@1.0.3
+meteorhacks:subs-manager@1.6.4
+meteorspark:util@0.2.0
+minifier-css@1.4.3
+minifier-js@2.5.1
+minifiers@1.1.8-faster-rebuild.0
+minimongo@1.4.5
+mobile-status-bar@1.0.14
+modern-browsers@0.1.4
+modules@0.14.0
+modules-runtime@0.11.0
+momentjs:moment@2.24.0
+mongo@1.7.0
+mongo-decimal@0.1.1
+mongo-dev-server@1.1.0
+mongo-id@1.0.7
+mongo-livedata@1.0.12
+mousetrap:mousetrap@1.4.6_1
+mquandalle:autofocus@1.0.0
+mquandalle:collection-mutations@0.1.0
+mquandalle:jade@0.4.9
+mquandalle:jade-compiler@0.4.5
+mquandalle:jquery-textcomplete@0.8.0_1
+mquandalle:jquery-ui-drag-drop-sort@0.2.0
+mquandalle:moment@1.0.1
+mquandalle:mousetrap-bindglobal@0.0.1
+mquandalle:perfect-scrollbar@0.6.5_2
+msavin:usercache@1.8.0
+npm-bcrypt@0.9.3
+npm-mongo@3.2.0
+oauth@1.2.8
+oauth2@1.2.1
+observe-sequence@1.0.16
+ongoworks:speakingurl@1.1.0
+ordered-dict@1.1.0
+ostrio:cookies@2.5.0
+peerlibrary:assert@0.3.0
+peerlibrary:base-component@0.16.0
+peerlibrary:blaze-components@0.15.1
+peerlibrary:computed-field@0.10.0
+peerlibrary:reactive-field@0.6.0
+percolate:synced-cron@1.3.2
+promise@0.11.2
+raix:eventemitter@0.1.3
+raix:handlebar-helpers@0.2.5
+rajit:bootstrap3-datepicker@1.7.1_1
+random@1.1.0
+rate-limit@1.0.9
+reactive-dict@1.3.0
+reactive-var@1.0.11
+reload@1.3.0
+retry@1.1.0
+routepolicy@1.1.0
+rzymek:fullcalendar@3.8.0
+server-render@0.3.1
+service-configuration@1.0.11
+session@1.2.0
+sha@1.0.9
+shell-server@0.4.0
+simple:authenticate-user-by-token@1.0.1
+simple:json-routes@2.1.0
+simple:rest-accounts-password@1.1.2
+simple:rest-bearer-token-parser@1.0.1
+simple:rest-json-error-handler@1.0.1
+socket-stream-client@0.2.2
+softwarerero:accounts-t9n@1.3.11
+spacebars@1.0.15
+spacebars-compiler@1.1.3
+srp@1.0.12
+standard-minifier-css@1.5.4
+standard-minifier-js@2.5.2
+staringatlights:fast-render@3.2.0
+staringatlights:inject-data@2.3.0
+tap:i18n@1.8.2
+templates:tabs@2.3.0
+templating@1.3.2
+templating-compiler@1.3.3
+templating-runtime@1.3.2
+templating-tools@1.1.2
+tracker@1.2.0
+twbs:bootstrap@3.3.6
+ui@1.0.13
+underscore@1.0.10
+url@1.2.0
+useraccounts:core@1.14.2
+useraccounts:flow-routing@1.14.2
+useraccounts:unstyled@1.14.2
+verron:autosize@3.0.8
+webapp@1.7.5
+webapp-hashing@1.0.9
+wekan-accounts-cas@0.1.0
+wekan-accounts-oidc@1.0.10
+wekan-ldap@0.0.2
+wekan-markdown@1.0.7
+wekan-oidc@1.0.12
+wekan-scrollbar@3.1.3
+yasaricli:slugify@0.0.7
+zimme:active-route@2.3.2

+ 914 - 0
.snap-meteor-1.8/cfs_access-point.txt

@@ -0,0 +1,914 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var global = Package.meteor.global;
+var meteorEnv = Package.meteor.meteorEnv;
+var FS = Package['cfs:base-package'].FS;
+var check = Package.check.check;
+var Match = Package.check.Match;
+var EJSON = Package.ejson.EJSON;
+var HTTP = Package['cfs:http-methods'].HTTP;
+
+/* Package-scope variables */
+var rootUrlPathPrefix, baseUrl, getHeaders, getHeadersByCollection, _existingMountPoints, mountUrls;
+
+(function(){
+
+///////////////////////////////////////////////////////////////////////
+//                                                                   //
+// packages/cfs_access-point/packages/cfs_access-point.js            //
+//                                                                   //
+///////////////////////////////////////////////////////////////////////
+                                                                     //
+(function () {
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                                    //
+// packages/cfs:access-point/access-point-common.js                                                                   //
+//                                                                                                                    //
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+                                                                                                                      //
+rootUrlPathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || "";                                             // 1
+// Adjust the rootUrlPathPrefix if necessary                                                                          // 2
+if (rootUrlPathPrefix.length > 0) {                                                                                   // 3
+  if (rootUrlPathPrefix.slice(0, 1) !== '/') {                                                                        // 4
+    rootUrlPathPrefix = '/' + rootUrlPathPrefix;                                                                      // 5
+  }                                                                                                                   // 6
+  if (rootUrlPathPrefix.slice(-1) === '/') {                                                                          // 7
+    rootUrlPathPrefix = rootUrlPathPrefix.slice(0, -1);                                                               // 8
+  }                                                                                                                   // 9
+}                                                                                                                     // 10
+                                                                                                                      // 11
+// prepend ROOT_URL when isCordova                                                                                    // 12
+if (Meteor.isCordova) {                                                                                               // 13
+  rootUrlPathPrefix = Meteor.absoluteUrl(rootUrlPathPrefix.replace(/^\/+/, '')).replace(/\/+$/, '');                  // 14
+}                                                                                                                     // 15
+                                                                                                                      // 16
+baseUrl = '/cfs';                                                                                                     // 17
+FS.HTTP = FS.HTTP || {};                                                                                              // 18
+                                                                                                                      // 19
+// Note the upload URL so that client uploader packages know what it is                                               // 20
+FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files';                                                           // 21
+                                                                                                                      // 22
+/**                                                                                                                   // 23
+ * @method FS.HTTP.setBaseUrl                                                                                         // 24
+ * @public                                                                                                            // 25
+ * @param {String} newBaseUrl - Change the base URL for the HTTP GET and DELETE endpoints.                            // 26
+ * @returns {undefined}                                                                                               // 27
+ */                                                                                                                   // 28
+FS.HTTP.setBaseUrl = function setBaseUrl(newBaseUrl) {                                                                // 29
+                                                                                                                      // 30
+  // Adjust the baseUrl if necessary                                                                                  // 31
+  if (newBaseUrl.slice(0, 1) !== '/') {                                                                               // 32
+    newBaseUrl = '/' + newBaseUrl;                                                                                    // 33
+  }                                                                                                                   // 34
+  if (newBaseUrl.slice(-1) === '/') {                                                                                 // 35
+    newBaseUrl = newBaseUrl.slice(0, -1);                                                                             // 36
+  }                                                                                                                   // 37
+                                                                                                                      // 38
+  // Update the base URL                                                                                              // 39
+  baseUrl = newBaseUrl;                                                                                               // 40
+                                                                                                                      // 41
+  // Change the upload URL so that client uploader packages know what it is                                           // 42
+  FS.HTTP.uploadUrl = rootUrlPathPrefix + baseUrl + '/files';                                                         // 43
+                                                                                                                      // 44
+  // Remount URLs with the new baseUrl, unmounting the old, on the server only.                                       // 45
+  // If existingMountPoints is empty, then we haven't run the server startup                                          // 46
+  // code yet, so this new URL will be used at that point for the initial mount.                                      // 47
+  if (Meteor.isServer && !FS.Utility.isEmpty(_existingMountPoints)) {                                                 // 48
+    mountUrls();                                                                                                      // 49
+  }                                                                                                                   // 50
+};                                                                                                                    // 51
+                                                                                                                      // 52
+/*                                                                                                                    // 53
+ * FS.File extensions                                                                                                 // 54
+ */                                                                                                                   // 55
+                                                                                                                      // 56
+/**                                                                                                                   // 57
+ * @method FS.File.prototype.url Construct the file url                                                               // 58
+ * @public                                                                                                            // 59
+ * @param {Object} [options]                                                                                          // 60
+ * @param {String} [options.store] Name of the store to get from. If not defined, the first store defined in `options.stores` for the collection on the client is used.
+ * @param {Boolean} [options.auth=null] Add authentication token to the URL query string? By default, a token for the current logged in user is added on the client. Set this to `false` to omit the token. Set this to a string to provide your own token. Set this to a number to specify an expiration time for the token in seconds.
+ * @param {Boolean} [options.download=false] Should headers be set to force a download? Typically this means that clicking the link with this URL will download the file to the user's Downloads folder instead of displaying the file in the browser.
+ * @param {Boolean} [options.brokenIsFine=false] Return the URL even if we know it's currently a broken link because the file hasn't been saved in the requested store yet.
+ * @param {Boolean} [options.metadata=false] Return the URL for the file metadata access point rather than the file itself.
+ * @param {String} [options.uploading=null] A URL to return while the file is being uploaded.                         // 66
+ * @param {String} [options.storing=null] A URL to return while the file is being stored.                             // 67
+ * @param {String} [options.filename=null] Override the filename that should appear at the end of the URL. By default it is the name of the file in the requested store.
+ *                                                                                                                    // 69
+ * Returns the HTTP URL for getting the file or its metadata.                                                         // 70
+ */                                                                                                                   // 71
+FS.File.prototype.url = function(options) {                                                                           // 72
+  var self = this;                                                                                                    // 73
+  options = options || {};                                                                                            // 74
+  options = FS.Utility.extend({                                                                                       // 75
+    store: null,                                                                                                      // 76
+    auth: null,                                                                                                       // 77
+    download: false,                                                                                                  // 78
+    metadata: false,                                                                                                  // 79
+    brokenIsFine: false,                                                                                              // 80
+    uploading: null, // return this URL while uploading                                                               // 81
+    storing: null, // return this URL while storing                                                                   // 82
+    filename: null // override the filename that is shown to the user                                                 // 83
+  }, options.hash || options); // check for "hash" prop if called as helper                                           // 84
+                                                                                                                      // 85
+  // Primarily useful for displaying a temporary image while uploading an image                                       // 86
+  if (options.uploading && !self.isUploaded()) {                                                                      // 87
+    return options.uploading;                                                                                         // 88
+  }                                                                                                                   // 89
+                                                                                                                      // 90
+  if (self.isMounted()) {                                                                                             // 91
+    // See if we've stored in the requested store yet                                                                 // 92
+    var storeName = options.store || self.collection.primaryStore.name;                                               // 93
+    if (!self.hasStored(storeName)) {                                                                                 // 94
+      if (options.storing) {                                                                                          // 95
+        return options.storing;                                                                                       // 96
+      } else if (!options.brokenIsFine) {                                                                             // 97
+        // We want to return null if we know the URL will be a broken                                                 // 98
+        // link because then we can avoid rendering broken links, broken                                              // 99
+        // images, etc.                                                                                               // 100
+        return null;                                                                                                  // 101
+      }                                                                                                               // 102
+    }                                                                                                                 // 103
+                                                                                                                      // 104
+    // Add filename to end of URL if we can determine one                                                             // 105
+    var filename = options.filename || self.name({store: storeName});                                                 // 106
+    if (typeof filename === "string" && filename.length) {                                                            // 107
+      filename = '/' + filename;                                                                                      // 108
+    } else {                                                                                                          // 109
+      filename = '';                                                                                                  // 110
+    }                                                                                                                 // 111
+                                                                                                                      // 112
+    // TODO: Could we somehow figure out if the collection requires login?                                            // 113
+    var authToken = '';                                                                                               // 114
+    if (Meteor.isClient && typeof Accounts !== "undefined" && typeof Accounts._storedLoginToken === "function") {     // 115
+      if (options.auth !== false) {                                                                                   // 116
+        // Add reactive deps on the user                                                                              // 117
+        Meteor.userId();                                                                                              // 118
+                                                                                                                      // 119
+        var authObject = {                                                                                            // 120
+          authToken: Accounts._storedLoginToken() || ''                                                               // 121
+        };                                                                                                            // 122
+                                                                                                                      // 123
+        // If it's a number, we use that as the expiration time (in seconds)                                          // 124
+        if (options.auth === +options.auth) {                                                                         // 125
+          authObject.expiration = FS.HTTP.now() + options.auth * 1000;                                                // 126
+        }                                                                                                             // 127
+                                                                                                                      // 128
+        // Set the authToken                                                                                          // 129
+        var authString = JSON.stringify(authObject);                                                                  // 130
+        authToken = FS.Utility.btoa(authString);                                                                      // 131
+      }                                                                                                               // 132
+    } else if (typeof options.auth === "string") {                                                                    // 133
+      // If the user supplies auth token the user will be responsible for                                             // 134
+      // updating                                                                                                     // 135
+      authToken = options.auth;                                                                                       // 136
+    }                                                                                                                 // 137
+                                                                                                                      // 138
+    // Construct query string                                                                                         // 139
+    var params = {};                                                                                                  // 140
+    if (authToken !== '') {                                                                                           // 141
+      params.token = authToken;                                                                                       // 142
+    }                                                                                                                 // 143
+    if (options.download) {                                                                                           // 144
+      params.download = true;                                                                                         // 145
+    }                                                                                                                 // 146
+    if (options.store) {                                                                                              // 147
+      // We use options.store here instead of storeName because we want to omit the queryString                       // 148
+      // whenever possible, allowing users to have "clean" URLs if they want. The server will                         // 149
+      // assume the first store defined on the server, which means that we are assuming that                          // 150
+      // the first on the client is also the first on the server. If that's not the case, the                         // 151
+      // store option should be supplied.                                                                             // 152
+      params.store = options.store;                                                                                   // 153
+    }                                                                                                                 // 154
+    var queryString = FS.Utility.encodeParams(params);                                                                // 155
+    if (queryString.length) {                                                                                         // 156
+      queryString = '?' + queryString;                                                                                // 157
+    }                                                                                                                 // 158
+                                                                                                                      // 159
+    // Determine which URL to use                                                                                     // 160
+    var area;                                                                                                         // 161
+    if (options.metadata) {                                                                                           // 162
+      area = '/record';                                                                                               // 163
+    } else {                                                                                                          // 164
+      area = '/files';                                                                                                // 165
+    }                                                                                                                 // 166
+                                                                                                                      // 167
+    // Construct and return the http method url                                                                       // 168
+    return rootUrlPathPrefix + baseUrl + area + '/' + self.collection.name + '/' + self._id + filename + queryString; // 169
+  }                                                                                                                   // 170
+                                                                                                                      // 171
+};                                                                                                                    // 172
+                                                                                                                      // 173
+                                                                                                                      // 174
+                                                                                                                      // 175
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                                    //
+// packages/cfs:access-point/access-point-handlers.js                                                                 //
+//                                                                                                                    //
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+                                                                                                                      //
+getHeaders = [];                                                                                                      // 1
+getHeadersByCollection = {};                                                                                          // 2
+                                                                                                                      // 3
+FS.HTTP.Handlers = {};                                                                                                // 4
+                                                                                                                      // 5
+/**                                                                                                                   // 6
+ * @method FS.HTTP.Handlers.Del                                                                                       // 7
+ * @public                                                                                                            // 8
+ * @returns {any} response                                                                                            // 9
+ *                                                                                                                    // 10
+ * HTTP DEL request handler                                                                                           // 11
+ */                                                                                                                   // 12
+FS.HTTP.Handlers.Del = function httpDelHandler(ref) {                                                                 // 13
+  var self = this;                                                                                                    // 14
+  var opts = FS.Utility.extend({}, self.query || {}, self.params || {});                                              // 15
+                                                                                                                      // 16
+  // If DELETE request, validate with 'remove' allow/deny, delete the file, and return                                // 17
+  FS.Utility.validateAction(ref.collection.files._validators['remove'], ref.file, self.userId);                       // 18
+                                                                                                                      // 19
+  /*                                                                                                                  // 20
+   * From the DELETE spec:                                                                                            // 21
+   * A successful response SHOULD be 200 (OK) if the response includes an                                             // 22
+   * entity describing the status, 202 (Accepted) if the action has not                                               // 23
+   * yet been enacted, or 204 (No Content) if the action has been enacted                                             // 24
+   * but the response does not include an entity.                                                                     // 25
+   */                                                                                                                 // 26
+  self.setStatusCode(200);                                                                                            // 27
+                                                                                                                      // 28
+  return {                                                                                                            // 29
+    deleted: !!ref.file.remove()                                                                                      // 30
+  };                                                                                                                  // 31
+};                                                                                                                    // 32
+                                                                                                                      // 33
+/**                                                                                                                   // 34
+ * @method FS.HTTP.Handlers.GetList                                                                                   // 35
+ * @public                                                                                                            // 36
+ * @returns {Object} response                                                                                         // 37
+ *                                                                                                                    // 38
+ * HTTP GET file list request handler                                                                                 // 39
+ */                                                                                                                   // 40
+FS.HTTP.Handlers.GetList = function httpGetListHandler() {                                                            // 41
+  // Not Yet Implemented                                                                                              // 42
+  // Need to check publications and return file list based on                                                         // 43
+  // what user is allowed to see                                                                                      // 44
+};                                                                                                                    // 45
+                                                                                                                      // 46
+/*                                                                                                                    // 47
+  requestRange will parse the range set in request header - if not possible it                                        // 48
+  will throw fitting errors and autofill range for both partial and full ranges                                       // 49
+                                                                                                                      // 50
+  throws error or returns the object:                                                                                 // 51
+  {                                                                                                                   // 52
+    start                                                                                                             // 53
+    end                                                                                                               // 54
+    length                                                                                                            // 55
+    unit                                                                                                              // 56
+    partial                                                                                                           // 57
+  }                                                                                                                   // 58
+*/                                                                                                                    // 59
+var requestRange = function(req, fileSize) {                                                                          // 60
+  if (req) {                                                                                                          // 61
+    if (req.headers) {                                                                                                // 62
+      var rangeString = req.headers.range;                                                                            // 63
+                                                                                                                      // 64
+      // Make sure range is a string                                                                                  // 65
+      if (rangeString === ''+rangeString) {                                                                           // 66
+                                                                                                                      // 67
+        // range will be in the format "bytes=0-32767"                                                                // 68
+        var parts = rangeString.split('=');                                                                           // 69
+        var unit = parts[0];                                                                                          // 70
+                                                                                                                      // 71
+        // Make sure parts consists of two strings and range is of type "byte"                                        // 72
+        if (parts.length == 2 && unit == 'bytes') {                                                                   // 73
+          // Parse the range                                                                                          // 74
+          var range = parts[1].split('-');                                                                            // 75
+          var start = Number(range[0]);                                                                               // 76
+          var end = Number(range[1]);                                                                                 // 77
+                                                                                                                      // 78
+          // Fix invalid ranges?                                                                                      // 79
+          if (range[0] != start) start = 0;                                                                           // 80
+          if (range[1] != end || !end) end = fileSize - 1;                                                            // 81
+                                                                                                                      // 82
+          // Make sure range consists of a start and end point of numbers and start is less than end                  // 83
+          if (start < end) {                                                                                          // 84
+                                                                                                                      // 85
+            var partSize = 0 - start + end + 1;                                                                       // 86
+                                                                                                                      // 87
+            // Return the parsed range                                                                                // 88
+            return {                                                                                                  // 89
+              start: start,                                                                                           // 90
+              end: end,                                                                                               // 91
+              length: partSize,                                                                                       // 92
+              size: fileSize,                                                                                         // 93
+              unit: unit,                                                                                             // 94
+              partial: (partSize < fileSize)                                                                          // 95
+            };                                                                                                        // 96
+                                                                                                                      // 97
+          } else {                                                                                                    // 98
+            throw new Meteor.Error(416, "Requested Range Not Satisfiable");                                           // 99
+          }                                                                                                           // 100
+                                                                                                                      // 101
+        } else {                                                                                                      // 102
+          // The first part should be bytes                                                                           // 103
+          throw new Meteor.Error(416, "Requested Range Unit Not Satisfiable");                                        // 104
+        }                                                                                                             // 105
+                                                                                                                      // 106
+      } else {                                                                                                        // 107
+        // No range found                                                                                             // 108
+      }                                                                                                               // 109
+                                                                                                                      // 110
+    } else {                                                                                                          // 111
+      // throw new Error('No request headers set for _parseRange function');                                          // 112
+    }                                                                                                                 // 113
+  } else {                                                                                                            // 114
+    throw new Error('No request object passed to _parseRange function');                                              // 115
+  }                                                                                                                   // 116
+                                                                                                                      // 117
+  return {                                                                                                            // 118
+    start: 0,                                                                                                         // 119
+    end: fileSize - 1,                                                                                                // 120
+    length: fileSize,                                                                                                 // 121
+    size: fileSize,                                                                                                   // 122
+    unit: 'bytes',                                                                                                    // 123
+    partial: false                                                                                                    // 124
+  };                                                                                                                  // 125
+};                                                                                                                    // 126
+                                                                                                                      // 127
+/**                                                                                                                   // 128
+ * @method FS.HTTP.Handlers.Get                                                                                       // 129
+ * @public                                                                                                            // 130
+ * @returns {any} response                                                                                            // 131
+ *                                                                                                                    // 132
+ * HTTP GET request handler                                                                                           // 133
+ */                                                                                                                   // 134
+FS.HTTP.Handlers.Get = function httpGetHandler(ref) {                                                                 // 135
+  var self = this;                                                                                                    // 136
+  // Once we have the file, we can test allow/deny validators                                                         // 137
+  // XXX: pass on the "share" query eg. ?share=342hkjh23ggj for shared url access?                                    // 138
+  FS.Utility.validateAction(ref.collection._validators['download'], ref.file, self.userId /*, self.query.shareId*/);  // 139
+                                                                                                                      // 140
+  var storeName = ref.storeName;                                                                                      // 141
+                                                                                                                      // 142
+  // If no storeName was specified, use the first defined storeName                                                   // 143
+  if (typeof storeName !== "string") {                                                                                // 144
+    // No store handed, we default to primary store                                                                   // 145
+    storeName = ref.collection.primaryStore.name;                                                                     // 146
+  }                                                                                                                   // 147
+                                                                                                                      // 148
+  // Get the storage reference                                                                                        // 149
+  var storage = ref.collection.storesLookup[storeName];                                                               // 150
+                                                                                                                      // 151
+  if (!storage) {                                                                                                     // 152
+    throw new Meteor.Error(404, "Not Found", 'There is no store "' + storeName + '"');                                // 153
+  }                                                                                                                   // 154
+                                                                                                                      // 155
+  // Get the file                                                                                                     // 156
+  var copyInfo = ref.file.copies[storeName];                                                                          // 157
+                                                                                                                      // 158
+  if (!copyInfo) {                                                                                                    // 159
+    throw new Meteor.Error(404, "Not Found", 'This file was not stored in the ' + storeName + ' store');              // 160
+  }                                                                                                                   // 161
+                                                                                                                      // 162
+  // Set the content type for file                                                                                    // 163
+  if (typeof copyInfo.type === "string") {                                                                            // 164
+    self.setContentType(copyInfo.type);                                                                               // 165
+  } else {                                                                                                            // 166
+    self.setContentType('application/octet-stream');                                                                  // 167
+  }                                                                                                                   // 168
+                                                                                                                      // 169
+  // Add 'Content-Disposition' header if requested a download/attachment URL                                          // 170
+  if (typeof ref.download !== "undefined") {                                                                          // 171
+    var filename = ref.filename || copyInfo.name;                                                                     // 172
+    self.addHeader('Content-Disposition', 'attachment; filename="' + filename + '"');                                 // 173
+  } else {                                                                                                            // 174
+    self.addHeader('Content-Disposition', 'inline');                                                                  // 175
+  }                                                                                                                   // 176
+                                                                                                                      // 177
+  // Get the contents range from request                                                                              // 178
+  var range = requestRange(self.request, copyInfo.size);                                                              // 179
+                                                                                                                      // 180
+  // Some browsers cope better if the content-range header is                                                         // 181
+  // still included even for the full file being returned.                                                            // 182
+  self.addHeader('Content-Range', range.unit + ' ' + range.start + '-' + range.end + '/' + range.size);               // 183
+                                                                                                                      // 184
+  // If a chunk/range was requested instead of the whole file, serve that'                                            // 185
+  if (range.partial) {                                                                                                // 186
+    self.setStatusCode(206, 'Partial Content');                                                                       // 187
+  } else {                                                                                                            // 188
+    self.setStatusCode(200, 'OK');                                                                                    // 189
+  }                                                                                                                   // 190
+                                                                                                                      // 191
+  // Add any other global custom headers and collection-specific custom headers                                       // 192
+  FS.Utility.each(getHeaders.concat(getHeadersByCollection[ref.collection.name] || []), function(header) {            // 193
+    self.addHeader(header[0], header[1]);                                                                             // 194
+  });                                                                                                                 // 195
+                                                                                                                      // 196
+  // Inform clients about length (or chunk length in case of ranges)                                                  // 197
+  self.addHeader('Content-Length', range.length);                                                                     // 198
+                                                                                                                      // 199
+  // Last modified header (updatedAt from file info)                                                                  // 200
+  self.addHeader('Last-Modified', copyInfo.updatedAt.toUTCString());                                                  // 201
+                                                                                                                      // 202
+  // Inform clients that we accept ranges for resumable chunked downloads                                             // 203
+  self.addHeader('Accept-Ranges', range.unit);                                                                        // 204
+                                                                                                                      // 205
+  if (FS.debug) console.log('Read file "' + (ref.filename || copyInfo.name) + '" ' + range.unit + ' ' + range.start + '-' + range.end + '/' + range.size);
+                                                                                                                      // 207
+  var readStream = storage.adapter.createReadStream(ref.file, {start: range.start, end: range.end});                  // 208
+                                                                                                                      // 209
+  readStream.on('error', function(err) {                                                                              // 210
+    // Send proper error message on get error                                                                         // 211
+    if (err.message && err.statusCode) {                                                                              // 212
+      self.Error(new Meteor.Error(err.statusCode, err.message));                                                      // 213
+    } else {                                                                                                          // 214
+      self.Error(new Meteor.Error(503, 'Service unavailable'));                                                       // 215
+    }                                                                                                                 // 216
+  });                                                                                                                 // 217
+                                                                                                                      // 218
+  readStream.pipe(self.createWriteStream());                                                                          // 219
+};                                                                                                                    // 220
+
+const originalHandler = FS.HTTP.Handlers.Get;
+FS.HTTP.Handlers.Get = function (ref) {
+//console.log(ref.filename);
+  try {
+     var userAgent = (this.requestHeaders['user-agent']||'').toLowerCase();
+
+        if(userAgent.indexOf('msie') >= 0 || userAgent.indexOf('trident') >= 0 || userAgent.indexOf('chrome') >= 0) {
+            ref.filename =  encodeURIComponent(ref.filename);
+        } else if(userAgent.indexOf('firefox') >= 0) {
+            ref.filename = new Buffer(ref.filename).toString('binary');
+        } else {
+            /* safari*/
+            ref.filename = new Buffer(ref.filename).toString('binary');
+        }   
+   } catch (ex){
+        ref.filename = 'tempfix';
+   } 
+   return originalHandler.call(this, ref);
+};
+                                                                                                                      // 221
+/**                                                                                                                   // 222
+ * @method FS.HTTP.Handlers.PutInsert                                                                                 // 223
+ * @public                                                                                                            // 224
+ * @returns {Object} response object with _id property                                                                // 225
+ *                                                                                                                    // 226
+ * HTTP PUT file insert request handler                                                                               // 227
+ */                                                                                                                   // 228
+FS.HTTP.Handlers.PutInsert = function httpPutInsertHandler(ref) {                                                     // 229
+  var self = this;                                                                                                    // 230
+  var opts = FS.Utility.extend({}, self.query || {}, self.params || {});                                              // 231
+                                                                                                                      // 232
+  FS.debug && console.log("HTTP PUT (insert) handler");                                                               // 233
+                                                                                                                      // 234
+  // Create the nice FS.File                                                                                          // 235
+  var fileObj = new FS.File();                                                                                        // 236
+                                                                                                                      // 237
+  // Set its name                                                                                                     // 238
+  fileObj.name(opts.filename || null);                                                                                // 239
+                                                                                                                      // 240
+  // Attach the readstream as the file's data                                                                         // 241
+  fileObj.attachData(self.createReadStream(), {type: self.requestHeaders['content-type'] || 'application/octet-stream'});
+                                                                                                                      // 243
+  // Validate with insert allow/deny                                                                                  // 244
+  FS.Utility.validateAction(ref.collection.files._validators['insert'], fileObj, self.userId);                        // 245
+                                                                                                                      // 246
+  // Insert file into collection, triggering readStream storage                                                       // 247
+  ref.collection.insert(fileObj);                                                                                     // 248
+                                                                                                                      // 249
+  // Send response                                                                                                    // 250
+  self.setStatusCode(200);                                                                                            // 251
+                                                                                                                      // 252
+  // Return the new file id                                                                                           // 253
+  return {_id: fileObj._id};                                                                                          // 254
+};                                                                                                                    // 255
+                                                                                                                      // 256
+/**                                                                                                                   // 257
+ * @method FS.HTTP.Handlers.PutUpdate                                                                                 // 258
+ * @public                                                                                                            // 259
+ * @returns {Object} response object with _id and chunk properties                                                    // 260
+ *                                                                                                                    // 261
+ * HTTP PUT file update chunk request handler                                                                         // 262
+ */                                                                                                                   // 263
+FS.HTTP.Handlers.PutUpdate = function httpPutUpdateHandler(ref) {                                                     // 264
+  var self = this;                                                                                                    // 265
+  var opts = FS.Utility.extend({}, self.query || {}, self.params || {});                                              // 266
+                                                                                                                      // 267
+  var chunk = parseInt(opts.chunk, 10);                                                                               // 268
+  if (isNaN(chunk)) chunk = 0;                                                                                        // 269
+                                                                                                                      // 270
+  FS.debug && console.log("HTTP PUT (update) handler received chunk: ", chunk);                                       // 271
+                                                                                                                      // 272
+  // Validate with insert allow/deny; also mounts and retrieves the file                                              // 273
+  FS.Utility.validateAction(ref.collection.files._validators['insert'], ref.file, self.userId);                       // 274
+                                                                                                                      // 275
+  self.createReadStream().pipe( FS.TempStore.createWriteStream(ref.file, chunk) );                                    // 276
+                                                                                                                      // 277
+  // Send response                                                                                                    // 278
+  self.setStatusCode(200);                                                                                            // 279
+                                                                                                                      // 280
+  return { _id: ref.file._id, chunk: chunk };                                                                         // 281
+};                                                                                                                    // 282
+                                                                                                                      // 283
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                                    //
+// packages/cfs:access-point/access-point-server.js                                                                   //
+//                                                                                                                    //
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+                                                                                                                      //
+var path = Npm.require("path");                                                                                       // 1
+                                                                                                                      // 2
+HTTP.publishFormats({                                                                                                 // 3
+  fileRecordFormat: function (input) {                                                                                // 4
+    // Set the method scope content type to json                                                                      // 5
+    this.setContentType('application/json');                                                                          // 6
+    if (FS.Utility.isArray(input)) {                                                                                  // 7
+      return EJSON.stringify(FS.Utility.map(input, function (obj) {                                                   // 8
+        return FS.Utility.cloneFileRecord(obj);                                                                       // 9
+      }));                                                                                                            // 10
+    } else {                                                                                                          // 11
+      return EJSON.stringify(FS.Utility.cloneFileRecord(input));                                                      // 12
+    }                                                                                                                 // 13
+  }                                                                                                                   // 14
+});                                                                                                                   // 15
+                                                                                                                      // 16
+/**                                                                                                                   // 17
+ * @method FS.HTTP.setHeadersForGet                                                                                   // 18
+ * @public                                                                                                            // 19
+ * @param {Array} headers - List of headers, where each is a two-item array in which item 1 is the header name and item 2 is the header value.
+ * @param {Array|String} [collections] - Which collections the headers should be added for. Omit this argument to add the header for all collections.
+ * @returns {undefined}                                                                                               // 22
+ */                                                                                                                   // 23
+FS.HTTP.setHeadersForGet = function setHeadersForGet(headers, collections) {                                          // 24
+  if (typeof collections === "string") {                                                                              // 25
+    collections = [collections];                                                                                      // 26
+  }                                                                                                                   // 27
+  if (collections) {                                                                                                  // 28
+    FS.Utility.each(collections, function(collectionName) {                                                           // 29
+      getHeadersByCollection[collectionName] = headers || [];                                                         // 30
+    });                                                                                                               // 31
+  } else {                                                                                                            // 32
+    getHeaders = headers || [];                                                                                       // 33
+  }                                                                                                                   // 34
+};                                                                                                                    // 35
+                                                                                                                      // 36
+/**                                                                                                                   // 37
+ * @method FS.HTTP.publish                                                                                            // 38
+ * @public                                                                                                            // 39
+ * @param {FS.Collection} collection                                                                                  // 40
+ * @param {Function} func - Publish function that returns a cursor.                                                   // 41
+ * @returns {undefined}                                                                                               // 42
+ *                                                                                                                    // 43
+ * Publishes all documents returned by the cursor at a GET URL                                                        // 44
+ * with the format baseUrl/record/collectionName. The publish                                                         // 45
+ * function `this` is similar to normal `Meteor.publish`.                                                             // 46
+ */                                                                                                                   // 47
+FS.HTTP.publish = function fsHttpPublish(collection, func) {                                                          // 48
+  var name = baseUrl + '/record/' + collection.name;                                                                  // 49
+  // Mount collection listing URL using http-publish package                                                          // 50
+  HTTP.publish({                                                                                                      // 51
+    name: name,                                                                                                       // 52
+    defaultFormat: 'fileRecordFormat',                                                                                // 53
+    collection: collection,                                                                                           // 54
+    collectionGet: true,                                                                                              // 55
+    collectionPost: false,                                                                                            // 56
+    documentGet: true,                                                                                                // 57
+    documentPut: false,                                                                                               // 58
+    documentDelete: false                                                                                             // 59
+  }, func);                                                                                                           // 60
+                                                                                                                      // 61
+  FS.debug && console.log("Registered HTTP method GET URLs:\n\n" + name + '\n' + name + '/:id\n');                    // 62
+};                                                                                                                    // 63
+                                                                                                                      // 64
+/**                                                                                                                   // 65
+ * @method FS.HTTP.unpublish                                                                                          // 66
+ * @public                                                                                                            // 67
+ * @param {FS.Collection} collection                                                                                  // 68
+ * @returns {undefined}                                                                                               // 69
+ *                                                                                                                    // 70
+ * Unpublishes a restpoint created by a call to `FS.HTTP.publish`                                                     // 71
+ */                                                                                                                   // 72
+FS.HTTP.unpublish = function fsHttpUnpublish(collection) {                                                            // 73
+  // Mount collection listing URL using http-publish package                                                          // 74
+  HTTP.unpublish(baseUrl + '/record/' + collection.name);                                                             // 75
+};                                                                                                                    // 76
+                                                                                                                      // 77
+_existingMountPoints = {};                                                                                            // 78
+                                                                                                                      // 79
+/**                                                                                                                   // 80
+ * @method defaultSelectorFunction                                                                                    // 81
+ * @private                                                                                                           // 82
+ * @returns { collection, file }                                                                                      // 83
+ *                                                                                                                    // 84
+ * This is the default selector function                                                                              // 85
+ */                                                                                                                   // 86
+var defaultSelectorFunction = function() {                                                                            // 87
+  var self = this;                                                                                                    // 88
+  // Selector function                                                                                                // 89
+  //                                                                                                                  // 90
+  // This function will have to return the collection and the                                                         // 91
+  // file. If file not found undefined is returned - if null is returned the                                          // 92
+  // search was not possible                                                                                          // 93
+  var opts = FS.Utility.extend({}, self.query || {}, self.params || {});                                              // 94
+                                                                                                                      // 95
+  // Get the collection name from the url                                                                             // 96
+  var collectionName = opts.collectionName;                                                                           // 97
+                                                                                                                      // 98
+  // Get the id from the url                                                                                          // 99
+  var id = opts.id;                                                                                                   // 100
+                                                                                                                      // 101
+  // Get the collection                                                                                               // 102
+  var collection = FS._collections[collectionName];                                                                   // 103
+                                                                                                                      // 104
+  // Get the file if possible else return null                                                                        // 105
+  var file = (id && collection)? collection.findOne({ _id: id }): null;                                               // 106
+                                                                                                                      // 107
+  // Return the collection and the file                                                                               // 108
+  return {                                                                                                            // 109
+    collection: collection,                                                                                           // 110
+    file: file,                                                                                                       // 111
+    storeName: opts.store,                                                                                            // 112
+    download: opts.download,                                                                                          // 113
+    filename: opts.filename                                                                                           // 114
+  };                                                                                                                  // 115
+};                                                                                                                    // 116
+                                                                                                                      // 117
+/*                                                                                                                    // 118
+ * @method FS.HTTP.mount                                                                                              // 119
+ * @public                                                                                                            // 120
+ * @param {array of string} mountPoints mount points to map rest functinality on                                      // 121
+ * @param {function} selector_f [selector] function returns `{ collection, file }` for mount points to work with      // 122
+ *                                                                                                                    // 123
+*/                                                                                                                    // 124
+FS.HTTP.mount = function(mountPoints, selector_f) {                                                                   // 125
+  // We take mount points as an array and we get a selector function                                                  // 126
+  var selectorFunction = selector_f || defaultSelectorFunction;                                                       // 127
+                                                                                                                      // 128
+  var accessPoint = {                                                                                                 // 129
+    'stream': true,                                                                                                   // 130
+    'auth': expirationAuth,                                                                                           // 131
+    'post': function(data) {                                                                                          // 132
+      // Use the selector for finding the collection and file reference                                               // 133
+      var ref = selectorFunction.call(this);                                                                          // 134
+                                                                                                                      // 135
+      // We dont support post - this would be normal insert eg. of filerecord?                                        // 136
+      throw new Meteor.Error(501, "Not implemented", "Post is not supported");                                        // 137
+    },                                                                                                                // 138
+    'put': function(data) {                                                                                           // 139
+      // Use the selector for finding the collection and file reference                                               // 140
+      var ref = selectorFunction.call(this);                                                                          // 141
+                                                                                                                      // 142
+      // Make sure we have a collection reference                                                                     // 143
+      if (!ref.collection)                                                                                            // 144
+        throw new Meteor.Error(404, "Not Found", "No collection found");                                              // 145
+                                                                                                                      // 146
+      // Make sure we have a file reference                                                                           // 147
+      if (ref.file === null) {                                                                                        // 148
+        // No id supplied so we will create a new FS.File instance and                                                // 149
+        // insert the supplied data.                                                                                  // 150
+        return FS.HTTP.Handlers.PutInsert.apply(this, [ref]);                                                         // 151
+      } else {                                                                                                        // 152
+        if (ref.file) {                                                                                               // 153
+          return FS.HTTP.Handlers.PutUpdate.apply(this, [ref]);                                                       // 154
+        } else {                                                                                                      // 155
+          throw new Meteor.Error(404, "Not Found", 'No file found');                                                  // 156
+        }                                                                                                             // 157
+      }                                                                                                               // 158
+    },                                                                                                                // 159
+    'get': function(data) {                                                                                           // 160
+      // Use the selector for finding the collection and file reference                                               // 161
+      var ref = selectorFunction.call(this);                                                                          // 162
+                                                                                                                      // 163
+      // Make sure we have a collection reference                                                                     // 164
+      if (!ref.collection)                                                                                            // 165
+        throw new Meteor.Error(404, "Not Found", "No collection found");                                              // 166
+                                                                                                                      // 167
+      // Make sure we have a file reference                                                                           // 168
+      if (ref.file === null) {                                                                                        // 169
+        // No id supplied so we will return the published list of files ala                                           // 170
+        // http.publish in json format                                                                                // 171
+        return FS.HTTP.Handlers.GetList.apply(this, [ref]);                                                           // 172
+      } else {                                                                                                        // 173
+        if (ref.file) {                                                                                               // 174
+          return FS.HTTP.Handlers.Get.apply(this, [ref]);                                                             // 175
+        } else {                                                                                                      // 176
+          throw new Meteor.Error(404, "Not Found", 'No file found');                                                  // 177
+        }                                                                                                             // 178
+      }                                                                                                               // 179
+    },                                                                                                                // 180
+    'delete': function(data) {                                                                                        // 181
+      // Use the selector for finding the collection and file reference                                               // 182
+      var ref = selectorFunction.call(this);                                                                          // 183
+                                                                                                                      // 184
+      // Make sure we have a collection reference                                                                     // 185
+      if (!ref.collection)                                                                                            // 186
+        throw new Meteor.Error(404, "Not Found", "No collection found");                                              // 187
+                                                                                                                      // 188
+      // Make sure we have a file reference                                                                           // 189
+      if (ref.file) {                                                                                                 // 190
+        return FS.HTTP.Handlers.Del.apply(this, [ref]);                                                               // 191
+      } else {                                                                                                        // 192
+        throw new Meteor.Error(404, "Not Found", 'No file found');                                                    // 193
+      }                                                                                                               // 194
+    }                                                                                                                 // 195
+  };                                                                                                                  // 196
+                                                                                                                      // 197
+  var accessPoints = {};                                                                                              // 198
+                                                                                                                      // 199
+  // Add debug message                                                                                                // 200
+  FS.debug && console.log('Registered HTTP method URLs:');                                                            // 201
+                                                                                                                      // 202
+  FS.Utility.each(mountPoints, function(mountPoint) {                                                                 // 203
+    // Couple mountpoint and accesspoint                                                                              // 204
+    accessPoints[mountPoint] = accessPoint;                                                                           // 205
+    // Remember our mountpoints                                                                                       // 206
+    _existingMountPoints[mountPoint] = mountPoint;                                                                    // 207
+    // Add debug message                                                                                              // 208
+    FS.debug && console.log(mountPoint);                                                                              // 209
+  });                                                                                                                 // 210
+                                                                                                                      // 211
+  // XXX: HTTP:methods should unmount existing mounts in case of overwriting?                                         // 212
+  HTTP.methods(accessPoints);                                                                                         // 213
+                                                                                                                      // 214
+};                                                                                                                    // 215
+                                                                                                                      // 216
+/**                                                                                                                   // 217
+ * @method FS.HTTP.unmount                                                                                            // 218
+ * @public                                                                                                            // 219
+ * @param {string | array of string} [mountPoints] Optional, if not specified all mountpoints are unmounted           // 220
+ *                                                                                                                    // 221
+ */                                                                                                                   // 222
+FS.HTTP.unmount = function(mountPoints) {                                                                             // 223
+  // The mountPoints is optional, can be string or array if undefined then                                            // 224
+  // _existingMountPoints will be used                                                                                // 225
+  var unmountList;                                                                                                    // 226
+  // Container for the mount points to unmount                                                                        // 227
+  var unmountPoints = {};                                                                                             // 228
+                                                                                                                      // 229
+  if (typeof mountPoints === 'undefined') {                                                                           // 230
+    // Use existing mount points - unmount all                                                                        // 231
+    unmountList = _existingMountPoints;                                                                               // 232
+  } else if (mountPoints === ''+mountPoints) {                                                                        // 233
+    // Got a string                                                                                                   // 234
+    unmountList = [mountPoints];                                                                                      // 235
+  } else if (mountPoints.length) {                                                                                    // 236
+    // Got an array                                                                                                   // 237
+    unmountList = mountPoints;                                                                                        // 238
+  }                                                                                                                   // 239
+                                                                                                                      // 240
+  // If we have a list to unmount                                                                                     // 241
+  if (unmountList) {                                                                                                  // 242
+    // Iterate over each item                                                                                         // 243
+    FS.Utility.each(unmountList, function(mountPoint) {                                                               // 244
+      // Check _existingMountPoints to make sure the mount point exists in our                                        // 245
+      // context / was created by the FS.HTTP.mount                                                                   // 246
+      if (_existingMountPoints[mountPoint]) {                                                                         // 247
+        // Mark as unmount                                                                                            // 248
+        unmountPoints[mountPoint] = false;                                                                            // 249
+        // Release                                                                                                    // 250
+        delete _existingMountPoints[mountPoint];                                                                      // 251
+      }                                                                                                               // 252
+    });                                                                                                               // 253
+    FS.debug && console.log('FS.HTTP.unmount:');                                                                      // 254
+    FS.debug && console.log(unmountPoints);                                                                           // 255
+    // Complete unmount                                                                                               // 256
+    HTTP.methods(unmountPoints);                                                                                      // 257
+  }                                                                                                                   // 258
+};                                                                                                                    // 259
+                                                                                                                      // 260
+// ### FS.Collection maps on HTTP pr. default on the following restpoints:                                            // 261
+// *                                                                                                                  // 262
+//    baseUrl + '/files/:collectionName/:id/:filename',                                                               // 263
+//    baseUrl + '/files/:collectionName/:id',                                                                         // 264
+//    baseUrl + '/files/:collectionName'                                                                              // 265
+//                                                                                                                    // 266
+// Change/ replace the existing mount point by:                                                                       // 267
+// ```js                                                                                                              // 268
+//   // unmount all existing                                                                                          // 269
+//   FS.HTTP.unmount();                                                                                               // 270
+//   // Create new mount point                                                                                        // 271
+//   FS.HTTP.mount([                                                                                                  // 272
+//    '/cfs/files/:collectionName/:id/:filename',                                                                     // 273
+//    '/cfs/files/:collectionName/:id',                                                                               // 274
+//    '/cfs/files/:collectionName'                                                                                    // 275
+//  ]);                                                                                                               // 276
+//  ```                                                                                                               // 277
+//                                                                                                                    // 278
+mountUrls = function mountUrls() {                                                                                    // 279
+  // We unmount first in case we are calling this a second time                                                       // 280
+  FS.HTTP.unmount();                                                                                                  // 281
+                                                                                                                      // 282
+  FS.HTTP.mount([                                                                                                     // 283
+    baseUrl + '/files/:collectionName/:id/:filename',                                                                 // 284
+    baseUrl + '/files/:collectionName/:id',                                                                           // 285
+    baseUrl + '/files/:collectionName'                                                                                // 286
+  ]);                                                                                                                 // 287
+};                                                                                                                    // 288
+                                                                                                                      // 289
+// Returns the userId from URL token                                                                                  // 290
+var expirationAuth = function expirationAuth() {                                                                      // 291
+  var self = this;                                                                                                    // 292
+                                                                                                                      // 293
+  // Read the token from '/hello?token=base64'                                                                        // 294
+  var encodedToken = self.query.token;                                                                                // 295
+                                                                                                                      // 296
+  FS.debug && console.log("token: "+encodedToken);                                                                    // 297
+                                                                                                                      // 298
+  if (!encodedToken || !Meteor.users) return false;                                                                   // 299
+                                                                                                                      // 300
+  // Check the userToken before adding it to the db query                                                             // 301
+  // Set the this.userId                                                                                              // 302
+  var tokenString = FS.Utility.atob(encodedToken);                                                                    // 303
+                                                                                                                      // 304
+  var tokenObject;                                                                                                    // 305
+  try {                                                                                                               // 306
+    tokenObject = JSON.parse(tokenString);                                                                            // 307
+  } catch(err) {                                                                                                      // 308
+    throw new Meteor.Error(400, 'Bad Request');                                                                       // 309
+  }                                                                                                                   // 310
+                                                                                                                      // 311
+  // XXX: Do some check here of the object                                                                            // 312
+  var userToken = tokenObject.authToken;                                                                              // 313
+  if (userToken !== ''+userToken) {                                                                                   // 314
+    throw new Meteor.Error(400, 'Bad Request');                                                                       // 315
+  }                                                                                                                   // 316
+                                                                                                                      // 317
+  // If we have an expiration token we should check that it's still valid                                             // 318
+  if (tokenObject.expiration != null) {                                                                               // 319
+    // check if its too old                                                                                           // 320
+    var now = Date.now();                                                                                             // 321
+    if (tokenObject.expiration < now) {                                                                               // 322
+      FS.debug && console.log('Expired token: ' + tokenObject.expiration + ' is less than ' + now);                   // 323
+      throw new Meteor.Error(500, 'Expired token');                                                                   // 324
+    }                                                                                                                 // 325
+  }                                                                                                                   // 326
+                                                                                                                      // 327
+  // We are not on a secure line - so we have to look up the user...                                                  // 328
+  var user = Meteor.users.findOne({                                                                                   // 329
+    $or: [                                                                                                            // 330
+      {'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(userToken)},                               // 331
+      {'services.resume.loginTokens.token': userToken}                                                                // 332
+    ]                                                                                                                 // 333
+  });                                                                                                                 // 334
+                                                                                                                      // 335
+  // Set the userId in the scope                                                                                      // 336
+  return user && user._id;                                                                                            // 337
+};                                                                                                                    // 338
+                                                                                                                      // 339
+HTTP.methods(                                                                                                         // 340
+  {'/cfs/servertime': {                                                                                               // 341
+    get: function(data) {                                                                                             // 342
+      return Date.now().toString();                                                                                   // 343
+    }                                                                                                                 // 344
+  }                                                                                                                   // 345
+});                                                                                                                   // 346
+                                                                                                                      // 347
+// Unify client / server api                                                                                          // 348
+FS.HTTP.now = function() {                                                                                            // 349
+  return Date.now();                                                                                                  // 350
+};                                                                                                                    // 351
+                                                                                                                      // 352
+// Start up the basic mount points                                                                                    // 353
+Meteor.startup(function () {                                                                                          // 354
+  mountUrls();                                                                                                        // 355
+});                                                                                                                   // 356
+                                                                                                                      // 357
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+///////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package['cfs:access-point'] = {};
+
+})();

+ 238 - 0
.snap-meteor-1.8/export.js

@@ -0,0 +1,238 @@
+/* global JsonRoutes */
+if (Meteor.isServer) {
+  // todo XXX once we have a real API in place, move that route there
+  // todo XXX also  share the route definition between the client and the server
+  // so that we could use something like
+  // `ApiRoutes.path('boards/export', boardId)``
+  // on the client instead of copy/pasting the route path manually between the
+  // client and the server.
+  /**
+   * @operation export
+   * @tag Boards
+   *
+   * @summary This route is used to export the board.
+   *
+   * @description If user is already logged-in, pass loginToken as param
+   * "authToken": '/api/boards/:boardId/export?authToken=:token'
+   *
+   * See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
+   * for detailed explanations
+   *
+   * @param {string} boardId the ID of the board we are exporting
+   * @param {string} authToken the loginToken
+   */
+  JsonRoutes.add('get', '/api/boards/:boardId/export', function(req, res) {
+    const boardId = req.params.boardId;
+    let user = null;
+
+    const loginToken = req.query.authToken;
+    if (loginToken) {
+      const hashToken = Accounts._hashLoginToken(loginToken);
+      user = Meteor.users.findOne({
+        'services.resume.loginTokens.hashedToken': hashToken,
+      });
+    } else if (!Meteor.settings.public.sandstorm) {
+      Authentication.checkUserId(req.userId);
+      user = Users.findOne({ _id: req.userId, isAdmin: true });
+    }
+
+    const exporter = new Exporter(boardId);
+    if (exporter.canExport(user)) {
+      JsonRoutes.sendResult(res, {
+        code: 200,
+        data: exporter.build(),
+      });
+    } else {
+      // we could send an explicit error message, but on the other hand the only
+      // way to get there is by hacking the UI so let's keep it raw.
+      JsonRoutes.sendResult(res, 403);
+    }
+  });
+}
+
+// exporter maybe is broken since Gridfs introduced, add fs and path
+
+export class Exporter {
+  constructor(boardId) {
+    this._boardId = boardId;
+  }
+
+  build() {
+    const fs = Npm.require('fs');
+    const os = Npm.require('os');
+    const path = Npm.require('path');
+
+    const byBoard = { boardId: this._boardId };
+    const byBoardNoLinked = {
+      boardId: this._boardId,
+      linkedId: { $in: ['', null] },
+    };
+    // we do not want to retrieve boardId in related elements
+    const noBoardId = {
+      fields: {
+        boardId: 0,
+      },
+    };
+    const result = {
+      _format: 'wekan-board-1.0.0',
+    };
+    _.extend(
+      result,
+      Boards.findOne(this._boardId, {
+        fields: {
+          stars: 0,
+        },
+      }),
+    );
+    result.lists = Lists.find(byBoard, noBoardId).fetch();
+    result.cards = Cards.find(byBoardNoLinked, noBoardId).fetch();
+    result.swimlanes = Swimlanes.find(byBoard, noBoardId).fetch();
+    result.customFields = CustomFields.find(
+      { boardIds: { $in: [this.boardId] } },
+      { fields: { boardId: 0 } },
+    ).fetch();
+    result.comments = CardComments.find(byBoard, noBoardId).fetch();
+    result.activities = Activities.find(byBoard, noBoardId).fetch();
+    result.rules = Rules.find(byBoard, noBoardId).fetch();
+    result.checklists = [];
+    result.checklistItems = [];
+    result.subtaskItems = [];
+    result.triggers = [];
+    result.actions = [];
+    result.cards.forEach(card => {
+      result.checklists.push(
+        ...Checklists.find({
+          cardId: card._id,
+        }).fetch(),
+      );
+      result.checklistItems.push(
+        ...ChecklistItems.find({
+          cardId: card._id,
+        }).fetch(),
+      );
+      result.subtaskItems.push(
+        ...Cards.find({
+          parentId: card._id,
+        }).fetch(),
+      );
+    });
+    result.rules.forEach(rule => {
+      result.triggers.push(
+        ...Triggers.find(
+          {
+            _id: rule.triggerId,
+          },
+          noBoardId,
+        ).fetch(),
+      );
+      result.actions.push(
+        ...Actions.find(
+          {
+            _id: rule.actionId,
+          },
+          noBoardId,
+        ).fetch(),
+      );
+    });
+
+    // [Old] for attachments we only export IDs and absolute url to original doc
+    // [New] Encode attachment to base64
+    const getBase64Data = function(doc, callback) {
+      let buffer = new Buffer(0);
+      // callback has the form function (err, res) {}
+      const tmpFile = path.join(
+        os.tmpdir(),
+        `tmpexport${process.pid}${Math.random()}`,
+      );
+      const tmpWriteable = fs.createWriteStream(tmpFile);
+      const readStream = doc.createReadStream();
+      readStream.on('data', function(chunk) {
+        buffer = Buffer.concat([buffer, chunk]);
+      });
+      readStream.on('error', function(err) {
+        callback(err, null);
+      });
+      readStream.on('end', function() {
+        // done
+        fs.unlink(tmpFile, () => {
+          //ignored
+        });
+        callback(null, buffer.toString('base64'));
+      });
+      readStream.pipe(tmpWriteable);
+    };
+    const getBase64DataSync = Meteor.wrapAsync(getBase64Data);
+    result.attachments = Attachments.find(byBoard)
+      .fetch()
+      .map(attachment => {
+        return {
+          _id: attachment._id,
+          cardId: attachment.cardId,
+          // url: FlowRouter.url(attachment.url()),
+          file: getBase64DataSync(attachment),
+          name: attachment.original.name,
+          type: attachment.original.type,
+        };
+      });
+
+    // we also have to export some user data - as the other elements only
+    // include id but we have to be careful:
+    // 1- only exports users that are linked somehow to that board
+    // 2- do not export any sensitive information
+    const users = {};
+    result.members.forEach(member => {
+      users[member.userId] = true;
+    });
+    result.lists.forEach(list => {
+      users[list.userId] = true;
+    });
+    result.cards.forEach(card => {
+      users[card.userId] = true;
+      if (card.members) {
+        card.members.forEach(memberId => {
+          users[memberId] = true;
+        });
+      }
+    });
+    result.comments.forEach(comment => {
+      users[comment.userId] = true;
+    });
+    result.activities.forEach(activity => {
+      users[activity.userId] = true;
+    });
+    result.checklists.forEach(checklist => {
+      users[checklist.userId] = true;
+    });
+    const byUserIds = {
+      _id: {
+        $in: Object.getOwnPropertyNames(users),
+      },
+    };
+    // we use whitelist to be sure we do not expose inadvertently
+    // some secret fields that gets added to User later.
+    const userFields = {
+      fields: {
+        _id: 1,
+        username: 1,
+        'profile.fullname': 1,
+        'profile.initials': 1,
+        'profile.avatarUrl': 1,
+      },
+    };
+    result.users = Users.find(byUserIds, userFields)
+      .fetch()
+      .map(user => {
+        // user avatar is stored as a relative url, we export absolute
+        if ((user.profile || {}).avatarUrl) {
+          user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
+        }
+        return user;
+      });
+    return result;
+  }
+
+  canExport(user) {
+    const board = Boards.findOne(this._boardId);
+    return board && board.isVisibleBy(user);
+  }
+}

+ 155 - 0
.snap-meteor-1.8/future/snapcraft.yaml

@@ -0,0 +1,155 @@
+name: wekan
+version: git
+summary: The open-source kanban
+description: |
+   Wekan is an open-source and collaborative kanban board application.
+
+   Whether you’re maintaining a personal todo list, planning your holidays with some friends, or working in a team on your next revolutionary idea, Kanban boards are an unbeatable tool to keep your things organized. They give you a visual overview of the current state of your project, and make you productive by allowing you to focus on the few items that matter the most.
+   Depending on target environment, some configuration settings might need to be adjusted.
+   For full list of configuration options call:
+   $ wekan.help
+
+confinement: strict
+grade: stable
+base: core18
+
+architectures:
+  - amd64
+
+plugs:
+  mongodb-plug:
+    interface: content
+    target: $SNAP_DATA/shared
+
+hooks:
+  configure:
+    plugs:
+      - network
+      - network-bind
+
+slots:
+  mongodb-slot:
+    interface: content
+    write:
+      - $SNAP_DATA/share
+
+apps:
+    wekan:
+        command: wekan-control
+        daemon: simple
+        plugs: [network, network-bind]
+
+    mongodb:
+        command: mongodb-control
+        daemon: simple
+        plugs: [network, network-bind]
+
+    caddy:
+        command: caddy-control
+        daemon: simple
+        plugs: [network, network-bind]
+
+    help:
+        command: wekan-help
+
+    database-backup:
+        command: mongodb-backup
+        plugs: [network, network-bind]
+
+    database-list-backups:
+        command: ls -al $SNAP_COMMON/db-backups/
+
+    database-restore:
+        command: mongodb-restore
+        plugs: [network, network-bind]
+
+parts:
+    mongodb:
+        source: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.2/multiverse/binary-amd64/mongodb-org-server_4.2.2_amd64.deb
+        #https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.14.tgz
+        #https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
+        plugin: dump
+        stage-packages: [libssl1.0.0, libcurl3]
+        filesets:
+            mongo:
+                - usr
+                - bin
+                - lib
+        stage:
+            - $mongo
+        prime:
+            - $mongo
+
+    wekan:
+        source: .
+        plugin: nodejs
+        node-engine: 12.14.1
+        node-packages:
+            - node-gyp
+            - node-pre-gyp
+            - fibers
+        build-packages:
+            - ca-certificates
+            - apt-utils
+            - build-essential
+            - python
+            - python3
+            - g++
+            - capnproto
+            - curl
+            - libcurl3
+            - execstack
+            - nodejs
+            - npm
+        stage-packages:
+            - libfontconfig1
+        override-build: |
+            echo "Cleaning environment first"
+            rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
+            rm -rf .build
+            echo "Installing meteor"
+            curl https://install.meteor.com/ -o install_meteor.sh
+            chmod +x install_meteor.sh
+            sh install_meteor.sh
+            rm install_meteor.sh
+            rm -rf .build
+            meteor add standard-minifier-js --allow-superuser
+            meteor npm install --allow-superuser
+            meteor npm install --allow-superuser --save babel-runtime
+            meteor build .build --directory --allow-superuser
+            cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
+            cd .build/bundle/programs/server
+            npm install
+            npm install --allow-superuser --save babel-runtime
+            # Change back to Wekan source directory
+            cd ../../../..
+            cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
+            cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
+            rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
+            rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
+            rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
+            rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
+            rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
+            rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
+
+        organize:
+            README: README.wekan
+        prime:
+            - -lib/node_modules/node-pre-gyp/node_modules/tar/lib/.unpack.js.swp
+
+    helpers:
+        source: snap-src
+        plugin: dump
+
+    caddy:
+        plugin: dump
+        source: https://caddyserver.com/download/linux/amd64?license=personal&telemetry=off
+        source-type: tar
+        organize:
+          caddy: bin/caddy
+          CHANGES.txt: CADDY_CHANGES.txt
+          EULA.txt: CADDY_EULA.txt
+          LICENSES.txt: CADDY_LICENSES.txt
+          README.txt: CADDY_README.txt
+        stage:
+          - -init

+ 584 - 0
.snap-meteor-1.8/ldap.js

@@ -0,0 +1,584 @@
+import ldapjs from 'ldapjs';
+import util from 'util';
+import Bunyan from 'bunyan';
+import {log_debug, log_info, log_warn, log_error} from './logger';
+
+
+export default class LDAP {
+  constructor() {
+    this.ldapjs = ldapjs;
+
+    this.connected = false;
+
+    this.options = {
+      host                               : this.constructor.settings_get('LDAP_HOST'),
+      port                               : this.constructor.settings_get('LDAP_PORT'),
+      Reconnect                          : this.constructor.settings_get('LDAP_RECONNECT'),
+      timeout                            : this.constructor.settings_get('LDAP_TIMEOUT'),
+      connect_timeout                    : this.constructor.settings_get('LDAP_CONNECT_TIMEOUT'),
+      idle_timeout                       : this.constructor.settings_get('LDAP_IDLE_TIMEOUT'),
+      encryption                         : this.constructor.settings_get('LDAP_ENCRYPTION'),
+      ca_cert                            : this.constructor.settings_get('LDAP_CA_CERT'),
+      reject_unauthorized                : this.constructor.settings_get('LDAP_REJECT_UNAUTHORIZED') || false,
+      Authentication                     : this.constructor.settings_get('LDAP_AUTHENTIFICATION'),
+      Authentication_UserDN              : this.constructor.settings_get('LDAP_AUTHENTIFICATION_USERDN'),
+      Authentication_Password            : this.constructor.settings_get('LDAP_AUTHENTIFICATION_PASSWORD'),
+      Authentication_Fallback            : this.constructor.settings_get('LDAP_LOGIN_FALLBACK'),
+      BaseDN                             : this.constructor.settings_get('LDAP_BASEDN'),
+      Internal_Log_Level                 : this.constructor.settings_get('INTERNAL_LOG_LEVEL'),
+      User_Authentication                : this.constructor.settings_get('LDAP_USER_AUTHENTICATION'),
+      User_Authentication_Field          : this.constructor.settings_get('LDAP_USER_AUTHENTICATION_FIELD'),
+      User_Attributes                    : this.constructor.settings_get('LDAP_USER_ATTRIBUTES'),
+      User_Search_Filter                 : this.constructor.settings_get('LDAP_USER_SEARCH_FILTER'),
+      User_Search_Scope                  : this.constructor.settings_get('LDAP_USER_SEARCH_SCOPE'),
+      User_Search_Field                  : this.constructor.settings_get('LDAP_USER_SEARCH_FIELD'),
+      Search_Page_Size                   : this.constructor.settings_get('LDAP_SEARCH_PAGE_SIZE'),
+      Search_Size_Limit                  : this.constructor.settings_get('LDAP_SEARCH_SIZE_LIMIT'),
+      group_filter_enabled               : this.constructor.settings_get('LDAP_GROUP_FILTER_ENABLE'),
+      group_filter_object_class          : this.constructor.settings_get('LDAP_GROUP_FILTER_OBJECTCLASS'),
+      group_filter_group_id_attribute    : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE'),
+      group_filter_group_member_attribute: this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE'),
+      group_filter_group_member_format   : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT'),
+      group_filter_group_name            : this.constructor.settings_get('LDAP_GROUP_FILTER_GROUP_NAME'),
+    };
+  }
+
+  static settings_get(name, ...args) {
+    let value = process.env[name];
+    if (value !== undefined) {
+      if (value === 'true' || value === 'false') {
+        value = JSON.parse(value);
+      } else if (value !== '' && !isNaN(value)) {
+        value = Number(value);
+      }
+      return value;
+    } else {
+      log_warn(`Lookup for unset variable: ${name}`);
+    }
+  }
+
+  connectSync(...args) {
+     if (!this._connectSync) {
+      this._connectSync = Meteor.wrapAsync(this.connectAsync, this);
+    }
+    return this._connectSync(...args);
+  }
+
+  searchAllSync(...args) {
+
+    if (!this._searchAllSync) {
+      this._searchAllSync = Meteor.wrapAsync(this.searchAllAsync, this);
+    }
+    return this._searchAllSync(...args);
+  }
+
+  connectAsync(callback) {
+    log_info('Init setup');
+
+    let replied = false;
+
+    const connectionOptions = {
+      url           : `${this.options.host}:${this.options.port}`,
+      timeout       : this.options.timeout,
+      connectTimeout: this.options.connect_timeout,
+      idleTimeout   : this.options.idle_timeout,
+      reconnect     : this.options.Reconnect,
+    };
+
+    if (this.options.Internal_Log_Level !== 'disabled') {
+      connectionOptions.log = new Bunyan({
+        name     : 'ldapjs',
+        component: 'client',
+        stream   : process.stderr,
+        level    : this.options.Internal_Log_Level,
+      });
+    }
+
+    const tlsOptions = {
+      rejectUnauthorized: this.options.reject_unauthorized,
+    };
+
+    if (this.options.ca_cert && this.options.ca_cert !== '') {
+      // Split CA cert into array of strings
+      const chainLines = this.constructor.settings_get('LDAP_CA_CERT').split('\n');
+      let cert         = [];
+      const ca         = [];
+      chainLines.forEach((line) => {
+        cert.push(line);
+        if (line.match(/-END CERTIFICATE-/)) {
+          ca.push(cert.join('\n'));
+          cert = [];
+        }
+      });
+      tlsOptions.ca = ca;
+    }
+
+    if (this.options.encryption === 'ssl') {
+      connectionOptions.url        = `ldaps://${connectionOptions.url}`;
+      connectionOptions.tlsOptions = tlsOptions;
+    } else {
+      connectionOptions.url = `ldap://${connectionOptions.url}`;
+    }
+
+    log_info('Connecting', connectionOptions.url);
+    log_debug(`connectionOptions${util.inspect(connectionOptions)}`);
+
+    this.client = ldapjs.createClient(connectionOptions);
+
+    this.bindSync = Meteor.wrapAsync(this.client.bind, this.client);
+
+    this.client.on('error', (error) => {
+      log_error('connection', error);
+      if (replied === false) {
+        replied = true;
+        callback(error, null);
+      }
+    });
+
+    this.client.on('idle', () => {
+      log_info('Idle');
+      this.disconnect();
+    });
+
+    this.client.on('close', () => {
+      log_info('Closed');
+    });
+
+    if (this.options.encryption === 'tls') {
+      // Set host parameter for tls.connect which is used by ldapjs starttls. This shouldn't be needed in newer nodejs versions (e.g v5.6.0).
+      // https://github.com/RocketChat/Rocket.Chat/issues/2035
+      // https://github.com/mcavage/node-ldapjs/issues/349
+      tlsOptions.host = this.options.host;
+
+      log_info('Starting TLS');
+      log_debug('tlsOptions', tlsOptions);
+
+      this.client.starttls(tlsOptions, null, (error, response) => {
+        if (error) {
+          log_error('TLS connection', error);
+          if (replied === false) {
+            replied = true;
+            callback(error, null);
+          }
+          return;
+        }
+
+        log_info('TLS connected');
+        this.connected = true;
+        if (replied === false) {
+          replied = true;
+          callback(null, response);
+        }
+      });
+    } else {
+      this.client.on('connect', (response) => {
+        log_info('LDAP connected');
+        this.connected = true;
+        if (replied === false) {
+          replied = true;
+          callback(null, response);
+        }
+      });
+    }
+
+    setTimeout(() => {
+      if (replied === false) {
+        log_error('connection time out', connectionOptions.connectTimeout);
+        replied = true;
+        callback(new Error('Timeout'));
+      }
+    }, connectionOptions.connectTimeout);
+  }
+
+  getUserFilter(username) {
+    const filter = [];
+
+    if (this.options.User_Search_Filter !== '') {
+      if (this.options.User_Search_Filter[0] === '(') {
+        filter.push(`${this.options.User_Search_Filter}`);
+      } else {
+        filter.push(`(${this.options.User_Search_Filter})`);
+      }
+    }
+
+    const usernameFilter = this.options.User_Search_Field.split(',').map((item) => `(${item}=${username})`);
+
+    if (usernameFilter.length === 0) {
+      log_error('LDAP_LDAP_User_Search_Field not defined');
+    } else if (usernameFilter.length === 1) {
+      filter.push(`${usernameFilter[0]}`);
+    } else {
+      filter.push(`(|${usernameFilter.join('')})`);
+    }
+
+    return `(&${filter.join('')})`;
+  }
+
+  bindUserIfNecessary(username, password) {
+
+    if (this.domainBinded === true) {
+      return;
+    }
+
+    if (!this.options.User_Authentication) {
+      return;
+    }
+
+
+    if (!this.options.BaseDN) throw new Error('BaseDN is not provided');
+
+    const userDn = `${this.options.User_Authentication_Field}=${username},${this.options.BaseDN}`;
+
+    this.bindSync(userDn, password);
+    this.domainBinded = true;
+  }
+
+  bindIfNecessary() {
+    if (this.domainBinded === true) {
+      return;
+    }
+
+    if (this.options.Authentication !== true) {
+      return;
+    }
+
+    log_info('Binding UserDN', this.options.Authentication_UserDN);
+
+    this.bindSync(this.options.Authentication_UserDN, this.options.Authentication_Password);
+    this.domainBinded = true;
+  }
+
+  searchUsersSync(username, page) {
+    this.bindIfNecessary();
+    const searchOptions = {
+      filter   : this.getUserFilter(username),
+      scope    : this.options.User_Search_Scope || 'sub',
+      sizeLimit: this.options.Search_Size_Limit,
+    };
+
+    if (!!this.options.User_Attributes) searchOptions.attributes = this.options.User_Attributes.split(',');
+
+    if (this.options.Search_Page_Size > 0) {
+      searchOptions.paged = {
+        pageSize : this.options.Search_Page_Size,
+        pagePause: !!page,
+      };
+    }
+
+    log_info('Searching user', username);
+    log_debug('searchOptions', searchOptions);
+    log_debug('BaseDN', this.options.BaseDN);
+
+    if (page) {
+      return this.searchAllPaged(this.options.BaseDN, searchOptions, page);
+    }
+
+    return this.searchAllSync(this.options.BaseDN, searchOptions);
+  }
+
+  getUserByIdSync(id, attribute) {
+    this.bindIfNecessary();
+
+    const Unique_Identifier_Field = this.constructor.settings_get('LDAP_UNIQUE_IDENTIFIER_FIELD').split(',');
+
+    let filter;
+
+    if (attribute) {
+      filter = new this.ldapjs.filters.EqualityFilter({
+        attribute,
+        value: new Buffer(id, 'hex'),
+      });
+    } else {
+      const filters = [];
+      Unique_Identifier_Field.forEach((item) => {
+        filters.push(new this.ldapjs.filters.EqualityFilter({
+          attribute: item,
+          value    : new Buffer(id, 'hex'),
+        }));
+      });
+
+      filter = new this.ldapjs.filters.OrFilter({ filters });
+    }
+
+    const searchOptions = {
+      filter,
+      scope: 'sub',
+    };
+
+    log_info('Searching by id', id);
+    log_debug('search filter', searchOptions.filter.toString());
+    log_debug('BaseDN', this.options.BaseDN);
+
+    const result = this.searchAllSync(this.options.BaseDN, searchOptions);
+
+    if (!Array.isArray(result) || result.length === 0) {
+      return;
+    }
+
+    if (result.length > 1) {
+      log_error('Search by id', id, 'returned', result.length, 'records');
+    }
+
+    return result[0];
+  }
+
+  getUserByUsernameSync(username) {
+    this.bindIfNecessary();
+
+    const searchOptions = {
+      filter: this.getUserFilter(username),
+      scope : this.options.User_Search_Scope || 'sub',
+    };
+
+    log_info('Searching user', username);
+    log_debug('searchOptions', searchOptions);
+    log_debug('BaseDN', this.options.BaseDN);
+
+    const result = this.searchAllSync(this.options.BaseDN, searchOptions);
+
+    if (!Array.isArray(result) || result.length === 0) {
+      return;
+    }
+
+    if (result.length > 1) {
+      log_error('Search by username', username, 'returned', result.length, 'records');
+    }
+
+    return result[0];
+  }
+
+  getUserGroups(username, ldapUser) {
+    if (!this.options.group_filter_enabled) {
+      return true;
+    }
+
+    const filter = ['(&'];
+
+    if (this.options.group_filter_object_class !== '') {
+      filter.push(`(objectclass=${this.options.group_filter_object_class})`);
+    }
+
+    if (this.options.group_filter_group_member_attribute !== '') {
+      const format_value = ldapUser[this.options.group_filter_group_member_format];
+      if (format_value) {
+        filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
+      }
+    }
+
+    filter.push(')');
+
+    const searchOptions = {
+      filter: filter.join('').replace(/#{username}/g, username),
+      scope : 'sub',
+    };
+
+    log_debug('Group list filter LDAP:', searchOptions.filter);
+
+    const result = this.searchAllSync(this.options.BaseDN, searchOptions);
+
+    if (!Array.isArray(result) || result.length === 0) {
+      return [];
+    }
+
+    const grp_identifier = this.options.group_filter_group_id_attribute || 'cn';
+    const groups         = [];
+    result.map((item) => {
+      groups.push(item[grp_identifier]);
+    });
+    log_debug(`Groups: ${groups.join(', ')}`);
+    return groups;
+
+  }
+
+  isUserInGroup(username, ldapUser) {
+    if (!this.options.group_filter_enabled) {
+      return true;
+    }
+
+    const grps = this.getUserGroups(username, ldapUser);
+
+    const filter = ['(&'];
+
+    if (this.options.group_filter_object_class !== '') {
+      filter.push(`(objectclass=${this.options.group_filter_object_class})`);
+    }
+
+    if (this.options.group_filter_group_member_attribute !== '') {
+      const format_value = ldapUser[this.options.group_filter_group_member_format];
+      if (format_value) {
+        filter.push(`(${this.options.group_filter_group_member_attribute}=${format_value})`);
+      }
+    }
+
+    if (this.options.group_filter_group_id_attribute !== '') {
+      filter.push(`(${this.options.group_filter_group_id_attribute}=${this.options.group_filter_group_name})`);
+    }
+    filter.push(')');
+
+    const searchOptions = {
+      filter: filter.join('').replace(/#{username}/g, username),
+      scope : 'sub',
+    };
+
+    log_debug('Group filter LDAP:', searchOptions.filter);
+
+    const result = this.searchAllSync(this.options.BaseDN, searchOptions);
+
+    if (!Array.isArray(result) || result.length === 0) {
+      return false;
+    }
+    return true;
+  }
+
+  extractLdapEntryData(entry) {
+    const values = {
+      _raw: entry.raw,
+    };
+
+    Object.keys(values._raw).forEach((key) => {
+      const value = values._raw[key];
+
+      if (!['thumbnailPhoto', 'jpegPhoto'].includes(key)) {
+        if (value instanceof Buffer) {
+          values[key] = value.toString();
+        } else {
+          values[key] = value;
+        }
+      }
+    });
+
+    return values;
+  }
+
+  searchAllPaged(BaseDN, options, page) {
+    this.bindIfNecessary();
+
+    const processPage = ({ entries, title, end, next }) => {
+      log_info(title);
+      // Force LDAP idle to wait the record processing
+      this.client._updateIdle(true);
+      page(null, entries, {
+        end, next: () => {
+          // Reset idle timer
+          this.client._updateIdle();
+          next && next();
+        }
+      });
+    };
+
+    this.client.search(BaseDN, options, (error, res) => {
+      if (error) {
+        log_error(error);
+        page(error);
+        return;
+      }
+
+      res.on('error', (error) => {
+        log_error(error);
+        page(error);
+        return;
+      });
+
+      let entries = [];
+
+      const internalPageSize = options.paged && options.paged.pageSize > 0 ? options.paged.pageSize * 2 : 500;
+
+      res.on('searchEntry', (entry) => {
+        entries.push(this.extractLdapEntryData(entry));
+
+        if (entries.length >= internalPageSize) {
+          processPage({
+            entries,
+            title: 'Internal Page',
+            end  : false,
+          });
+          entries = [];
+        }
+      });
+
+      res.on('page', (result, next) => {
+        if (!next) {
+          this.client._updateIdle(true);
+          processPage({
+            entries,
+            title: 'Final Page',
+            end  : true,
+          });
+        } else if (entries.length) {
+          log_info('Page');
+          processPage({
+            entries,
+            title: 'Page',
+            end  : false,
+            next,
+          });
+          entries = [];
+        }
+      });
+
+      res.on('end', () => {
+        if (entries.length) {
+          processPage({
+            entries,
+            title: 'Final Page',
+            end  : true,
+          });
+          entries = [];
+        }
+      });
+    });
+  }
+
+  searchAllAsync(BaseDN, options, callback) {
+    this.bindIfNecessary();
+
+    this.client.search(BaseDN, options, (error, res) => {
+      if (error) {
+        log_error(error);
+        callback(error);
+        return;
+      }
+
+      res.on('error', (error) => {
+        log_error(error);
+        callback(error);
+        return;
+      });
+
+      const entries = [];
+
+      res.on('searchEntry', (entry) => {
+        entries.push(this.extractLdapEntryData(entry));
+      });
+
+      res.on('end', () => {
+        log_info('Search result count', entries.length);
+        callback(null, entries);
+      });
+    });
+  }
+
+  authSync(dn, password) {
+    log_info('Authenticating', dn);
+
+    try {
+      if (password === '') {
+        throw new Error('Password is not provided');
+      }
+      this.bindSync(dn, password);
+      log_info('Authenticated', dn);
+      return true;
+    } catch (error) {
+      log_info('Not authenticated', dn);
+      log_debug('error', error);
+      return false;
+    }
+  }
+
+  disconnect() {
+    this.connected    = false;
+    this.domainBinded = false;
+    log_info('Disconecting');
+    this.client.unbind();
+  }
+}

+ 149 - 0
.snap-meteor-1.8/oidc_server.js

@@ -0,0 +1,149 @@
+Oidc = {};
+
+OAuth.registerService('oidc', 2, null, function (query) {
+
+  var debug = process.env.DEBUG || false;
+  var token = getToken(query);
+  if (debug) console.log('XXX: register token:', token);
+
+  var accessToken = token.access_token || token.id_token;
+  var expiresAt = (+new Date) + (1000 * parseInt(token.expires_in, 10));
+
+  var userinfo = getUserInfo(accessToken);
+  if (debug) console.log('XXX: userinfo:', userinfo);
+
+  var serviceData = {};
+  serviceData.id = userinfo[process.env.OAUTH2_ID_MAP]; // || userinfo["id"];
+  serviceData.username = userinfo[process.env.OAUTH2_USERNAME_MAP]; // || userinfo["uid"];
+  serviceData.fullname = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
+  serviceData.accessToken = accessToken;
+  serviceData.expiresAt = expiresAt;
+  serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
+
+  if (accessToken) {
+    var tokenContent = getTokenContent(accessToken);
+    var fields = _.pick(tokenContent, getConfiguration().idTokenWhitelistFields);
+    _.extend(serviceData, fields);
+  }
+
+  if (token.refresh_token)
+    serviceData.refreshToken = token.refresh_token;
+  if (debug) console.log('XXX: serviceData:', serviceData);
+
+  var profile = {};
+  profile.name = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
+  profile.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
+  if (debug) console.log('XXX: profile:', profile);
+
+  return {
+    serviceData: serviceData,
+    options: { profile: profile }
+  };
+});
+
+var userAgent = "Meteor";
+if (Meteor.release) {
+  userAgent += "/" + Meteor.release;
+}
+
+var getToken = function (query) {
+  var debug = process.env.DEBUG || false;
+  var config = getConfiguration();
+  if(config.tokenEndpoint.includes('https://')){
+    var serverTokenEndpoint = config.tokenEndpoint;
+  }else{
+    var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
+  }
+  var requestPermissions = config.requestPermissions;
+  var response;
+
+  try {
+    response = HTTP.post(
+      serverTokenEndpoint,
+      {
+        headers: {
+          Accept: 'application/json',
+          "User-Agent": userAgent
+        },
+        params: {
+          code: query.code,
+          client_id: config.clientId,
+          client_secret: OAuth.openSecret(config.secret),
+          redirect_uri: OAuth._redirectUri('oidc', config),
+          grant_type: 'authorization_code',
+          scope: requestPermissions,
+          state: query.state
+        }
+      }
+    );
+  } catch (err) {
+    throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
+      { response: err.response });
+  }
+  if (response.data.error) {
+    // if the http response was a json object with an error attribute
+    throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
+  } else {
+    if (debug) console.log('XXX: getToken response: ', response.data);
+    return response.data;
+  }
+};
+
+var getUserInfo = function (accessToken) {
+  var debug = process.env.DEBUG || false;
+  var config = getConfiguration();
+  // Some userinfo endpoints use a different base URL than the authorization or token endpoints.
+  // This logic allows the end user to override the setting by providing the full URL to userinfo in their config.
+  if (config.userinfoEndpoint.includes("https://")) {
+    var serverUserinfoEndpoint = config.userinfoEndpoint;
+  } else {
+    var serverUserinfoEndpoint = config.serverUrl + config.userinfoEndpoint;
+  }
+  var response;
+  try {
+    response = HTTP.get(
+      serverUserinfoEndpoint,
+      {
+        headers: {
+          "User-Agent": userAgent,
+          "Authorization": "Bearer " + accessToken
+        }
+      }
+    );
+  } catch (err) {
+    throw _.extend(new Error("Failed to fetch userinfo from OIDC " + serverUserinfoEndpoint + ": " + err.message),
+                   {response: err.response});
+  }
+  if (debug) console.log('XXX: getUserInfo response: ', response.data);
+  return response.data;
+};
+
+var getConfiguration = function () {
+  var config = ServiceConfiguration.configurations.findOne({ service: 'oidc' });
+  if (!config) {
+    throw new ServiceConfiguration.ConfigError('Service oidc not configured.');
+  }
+  return config;
+};
+
+var getTokenContent = function (token) {
+  var content = null;
+  if (token) {
+    try {
+      var parts = token.split('.');
+      var header = JSON.parse(new Buffer(parts[0], 'base64').toString());
+      content = JSON.parse(new Buffer(parts[1], 'base64').toString());
+      var signature = new Buffer(parts[2], 'base64');
+      var signed = parts[0] + '.' + parts[1];
+    } catch (err) {
+      this.content = {
+        exp: 0
+      };
+    }
+  }
+  return content;
+}
+
+Oidc.retrieveCredential = function (credentialToken, credentialSecret) {
+  return OAuth.retrieveCredential(credentialToken, credentialSecret);
+};

+ 5184 - 0
.snap-meteor-1.8/package-lock.json

@@ -0,0 +1,5184 @@
+{
+  "name": "wekan",
+  "version": "v3.78.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.5.5",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
+      "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.0.0"
+      }
+    },
+    "@babel/highlight": {
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
+      "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.0.0",
+        "esutils": "^2.0.2",
+        "js-tokens": "^4.0.0"
+      }
+    },
+    "@babel/runtime": {
+      "version": "7.6.2",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.2.tgz",
+      "integrity": "sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==",
+      "requires": {
+        "regenerator-runtime": "^0.13.2"
+      }
+    },
+    "@samverschueren/stream-to-observable": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz",
+      "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==",
+      "dev": true,
+      "requires": {
+        "any-observable": "^0.3.0"
+      }
+    },
+    "abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+    },
+    "acorn": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
+      "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
+      "dev": true
+    },
+    "acorn-jsx": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
+      "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
+      "dev": true
+    },
+    "ajv": {
+      "version": "5.5.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+      "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+      "requires": {
+        "co": "^4.6.0",
+        "fast-deep-equal": "^1.0.0",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.3.0"
+      }
+    },
+    "ajv-keywords": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
+      "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
+      "dev": true
+    },
+    "ansi-escapes": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz",
+      "integrity": "sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==",
+      "dev": true,
+      "requires": {
+        "type-fest": "^0.5.2"
+      }
+    },
+    "ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "any-observable": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz",
+      "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==",
+      "dev": true
+    },
+    "aproba": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+    },
+    "are-we-there-yet": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+      "requires": {
+        "delegates": "^1.0.0",
+        "readable-stream": "^2.0.6"
+      }
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "arr-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+      "dev": true
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+      "dev": true
+    },
+    "arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+      "dev": true
+    },
+    "array-includes": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
+      "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.2",
+        "es-abstract": "^1.7.0"
+      }
+    },
+    "array-unique": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+      "dev": true
+    },
+    "asn1": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
+    },
+    "assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+    },
+    "assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+      "dev": true
+    },
+    "astral-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+      "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+      "dev": true
+    },
+    "atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+      "dev": true
+    },
+    "babel-code-frame": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+      "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+      "dev": true,
+      "requires": {
+        "chalk": "^1.1.3",
+        "esutils": "^2.0.2",
+        "js-tokens": "^3.0.2"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^2.2.1",
+            "escape-string-regexp": "^1.0.2",
+            "has-ansi": "^2.0.0",
+            "strip-ansi": "^3.0.0",
+            "supports-color": "^2.0.0"
+          }
+        },
+        "js-tokens": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+          "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "dev": true
+        }
+      }
+    },
+    "babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+      "requires": {
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.11.0"
+      },
+      "dependencies": {
+        "regenerator-runtime": {
+          "version": "0.11.1",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+          "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+        }
+      }
+    },
+    "backoff": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
+      "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=",
+      "requires": {
+        "precond": "0.2"
+      }
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+    },
+    "base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "dev": true,
+      "requires": {
+        "cache-base": "^1.0.1",
+        "class-utils": "^0.3.5",
+        "component-emitter": "^1.2.1",
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.1",
+        "mixin-deep": "^1.2.0",
+        "pascalcase": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "base64-js": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
+    },
+    "bcrypt": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.6.tgz",
+      "integrity": "sha512-taA5bCTfXe7FUjKroKky9EXpdhkVvhE5owfxfLYodbrAR1Ul3juLmIQmIQBK4L9a5BuUcE6cqmwT+Da20lF9tg==",
+      "requires": {
+        "nan": "2.13.2",
+        "node-pre-gyp": "0.12.0"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+      "dev": true,
+      "requires": {
+        "arr-flatten": "^1.1.0",
+        "array-unique": "^0.3.2",
+        "extend-shallow": "^2.0.1",
+        "fill-range": "^4.0.0",
+        "isobject": "^3.0.1",
+        "repeat-element": "^1.1.2",
+        "snapdragon": "^0.8.1",
+        "snapdragon-node": "^2.0.1",
+        "split-string": "^3.0.2",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "bson": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/bson/-/bson-4.0.2.tgz",
+      "integrity": "sha512-rBdCxMBCg2aR420e1oKUejjcuPZLTibA7zEhWAlliFWEwzuBCC9Dkp5r7VFFIQB2t1WVsvTbohry575mc7Xw5A==",
+      "requires": {
+        "buffer": "^5.1.0",
+        "long": "^4.0.0"
+      }
+    },
+    "buffer": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.3.0.tgz",
+      "integrity": "sha512-XykNc84nIOC32vZ9euOKbmGAP69JUkXDtBQfLq88c8/6J/gZi/t14A+l/p/9EM2TcT5xNC1MKPCrvO3LVUpVPw==",
+      "requires": {
+        "base64-js": "^1.0.2",
+        "ieee754": "^1.1.4"
+      }
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+    },
+    "bunyan": {
+      "version": "1.8.12",
+      "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz",
+      "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=",
+      "requires": {
+        "dtrace-provider": "~0.8",
+        "moment": "^2.10.6",
+        "mv": "~2",
+        "safe-json-stringify": "~1"
+      }
+    },
+    "cache-base": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+      "dev": true,
+      "requires": {
+        "collection-visit": "^1.0.0",
+        "component-emitter": "^1.2.1",
+        "get-value": "^2.0.6",
+        "has-value": "^1.0.0",
+        "isobject": "^3.0.1",
+        "set-value": "^2.0.0",
+        "to-object-path": "^0.3.0",
+        "union-value": "^1.0.0",
+        "unset-value": "^1.0.0"
+      }
+    },
+    "caller-callsite": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+      "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+      "dev": true,
+      "requires": {
+        "callsites": "^2.0.0"
+      },
+      "dependencies": {
+        "callsites": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+          "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
+          "dev": true
+        }
+      }
+    },
+    "caller-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+      "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+      "dev": true,
+      "requires": {
+        "caller-callsite": "^2.0.0"
+      }
+    },
+    "callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      }
+    },
+    "chardet": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+      "dev": true
+    },
+    "chownr": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz",
+      "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A=="
+    },
+    "circular-json": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
+      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
+      "dev": true
+    },
+    "class-utils": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "define-property": "^0.2.5",
+        "isobject": "^3.0.0",
+        "static-extend": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+      "dev": true,
+      "requires": {
+        "restore-cursor": "^3.1.0"
+      }
+    },
+    "cli-truncate": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz",
+      "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=",
+      "dev": true,
+      "requires": {
+        "slice-ansi": "0.0.4",
+        "string-width": "^1.0.1"
+      },
+      "dependencies": {
+        "slice-ansi": {
+          "version": "0.0.4",
+          "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
+          "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
+          "dev": true
+        }
+      }
+    },
+    "cli-width": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+      "dev": true
+    },
+    "co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+    },
+    "code-point-at": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+    },
+    "collection-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "dev": true,
+      "requires": {
+        "map-visit": "^1.0.0",
+        "object-visit": "^1.0.0"
+      }
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "commander": {
+      "version": "2.20.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+      "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
+    },
+    "common-tags": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
+      "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==",
+      "dev": true
+    },
+    "component-emitter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+      "dev": true
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+    },
+    "concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "console-control-strings": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+    },
+    "contains-path": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+      "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+      "dev": true
+    },
+    "copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+      "dev": true
+    },
+    "core-js": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz",
+      "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A=="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "cosmiconfig": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+      "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+      "dev": true,
+      "requires": {
+        "import-fresh": "^2.0.0",
+        "is-directory": "^0.3.1",
+        "js-yaml": "^3.13.1",
+        "parse-json": "^4.0.0"
+      },
+      "dependencies": {
+        "import-fresh": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+          "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+          "dev": true,
+          "requires": {
+            "caller-path": "^2.0.0",
+            "resolve-from": "^3.0.0"
+          }
+        },
+        "parse-json": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "dev": true,
+          "requires": {
+            "error-ex": "^1.3.1",
+            "json-parse-better-errors": "^1.0.1"
+          }
+        },
+        "resolve-from": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+          "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+          "dev": true
+        }
+      }
+    },
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+      "dev": true,
+      "requires": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      }
+    },
+    "cssfilter": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
+      "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4="
+    },
+    "dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "date-fns": {
+      "version": "1.30.1",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
+      "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==",
+      "dev": true
+    },
+    "debug": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+      "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+      "requires": {
+        "ms": "^2.1.1"
+      }
+    },
+    "decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "dev": true
+    },
+    "dedent": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
+      "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
+      "dev": true
+    },
+    "deep-extend": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+    },
+    "deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+      "dev": true
+    },
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "dev": true,
+      "requires": {
+        "object-keys": "^1.0.12"
+      }
+    },
+    "define-property": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+      "dev": true,
+      "requires": {
+        "is-descriptor": "^1.0.2",
+        "isobject": "^3.0.1"
+      },
+      "dependencies": {
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "delegates": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+    },
+    "detect-libc": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
+    },
+    "dlv": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+      "dev": true
+    },
+    "doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
+    "dtrace-provider": {
+      "version": "0.8.7",
+      "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz",
+      "integrity": "sha1-3JObTT4GIM/gwc2APQ0tftBP/QQ=",
+      "optional": true,
+      "requires": {
+        "nan": "^2.10.0"
+      }
+    },
+    "elegant-spinner": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz",
+      "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=",
+      "dev": true
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "es-abstract": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
+      "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
+      "dev": true,
+      "requires": {
+        "es-to-primitive": "^1.2.0",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "is-callable": "^1.1.4",
+        "is-regex": "^1.0.4",
+        "object-keys": "^1.0.12"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
+      "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+      "dev": true,
+      "requires": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "es6-promise": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+      "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "eslint": {
+      "version": "5.16.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz",
+      "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "ajv": "^6.9.1",
+        "chalk": "^2.1.0",
+        "cross-spawn": "^6.0.5",
+        "debug": "^4.0.1",
+        "doctrine": "^3.0.0",
+        "eslint-scope": "^4.0.3",
+        "eslint-utils": "^1.3.1",
+        "eslint-visitor-keys": "^1.0.0",
+        "espree": "^5.0.1",
+        "esquery": "^1.0.1",
+        "esutils": "^2.0.2",
+        "file-entry-cache": "^5.0.1",
+        "functional-red-black-tree": "^1.0.1",
+        "glob": "^7.1.2",
+        "globals": "^11.7.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "inquirer": "^6.2.2",
+        "js-yaml": "^3.13.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.3.0",
+        "lodash": "^4.17.11",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.1",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.8.2",
+        "path-is-inside": "^1.0.2",
+        "progress": "^2.0.0",
+        "regexpp": "^2.0.1",
+        "semver": "^5.5.1",
+        "strip-ansi": "^4.0.0",
+        "strip-json-comments": "^2.0.1",
+        "table": "^5.2.3",
+        "text-table": "^0.2.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.10.2",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
+          "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+          "dev": true
+        },
+        "json-schema-traverse": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^3.0.0"
+          }
+        }
+      }
+    },
+    "eslint-config-meteor": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/eslint-config-meteor/-/eslint-config-meteor-0.0.9.tgz",
+      "integrity": "sha1-a+IZQguko+oCPbMKhm5g70h2Uvo=",
+      "dev": true
+    },
+    "eslint-config-prettier": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-3.6.0.tgz",
+      "integrity": "sha512-ixJ4U3uTLXwJts4rmSVW/lMXjlGwCijhBJHk8iVqKKSifeI0qgFEfWl8L63isfc8Od7EiBALF6BX3jKLluf/jQ==",
+      "dev": true,
+      "requires": {
+        "get-stdin": "^6.0.0"
+      }
+    },
+    "eslint-import-resolver-meteor": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-meteor/-/eslint-import-resolver-meteor-0.4.0.tgz",
+      "integrity": "sha1-yGhjhAghIIz4EzxczlGQnCamFWk=",
+      "dev": true,
+      "requires": {
+        "object-assign": "^4.0.1",
+        "resolve": "^1.1.6"
+      }
+    },
+    "eslint-import-resolver-node": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
+      "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
+      "dev": true,
+      "requires": {
+        "debug": "^2.6.9",
+        "resolve": "^1.5.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "eslint-module-utils": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz",
+      "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==",
+      "dev": true,
+      "requires": {
+        "debug": "^2.6.8",
+        "pkg-dir": "^2.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "eslint-plugin-import": {
+      "version": "2.18.2",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz",
+      "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==",
+      "dev": true,
+      "requires": {
+        "array-includes": "^3.0.3",
+        "contains-path": "^0.1.0",
+        "debug": "^2.6.9",
+        "doctrine": "1.5.0",
+        "eslint-import-resolver-node": "^0.3.2",
+        "eslint-module-utils": "^2.4.0",
+        "has": "^1.0.3",
+        "minimatch": "^3.0.4",
+        "object.values": "^1.1.0",
+        "read-pkg-up": "^2.0.0",
+        "resolve": "^1.11.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "doctrine": {
+          "version": "1.5.0",
+          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+          "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+          "dev": true,
+          "requires": {
+            "esutils": "^2.0.2",
+            "isarray": "^1.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "eslint-plugin-meteor": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-meteor/-/eslint-plugin-meteor-5.2.0.tgz",
+      "integrity": "sha512-bHzs/0BwHdKcBbX7tYrSnBaMG+1i2f1wy8k6H/sBBsERD/yifmBUrNLiPyZkIvyVUeI8OaZw8U9fsMvLP5GhIg==",
+      "dev": true,
+      "requires": {
+        "invariant": "2.2.4"
+      }
+    },
+    "eslint-plugin-prettier": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz",
+      "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==",
+      "dev": true,
+      "requires": {
+        "prettier-linter-helpers": "^1.0.0"
+      }
+    },
+    "eslint-scope": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+      "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.1.0",
+        "estraverse": "^4.1.1"
+      }
+    },
+    "eslint-utils": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
+      "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^1.0.0"
+      }
+    },
+    "eslint-visitor-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+      "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+      "dev": true
+    },
+    "espree": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz",
+      "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==",
+      "dev": true,
+      "requires": {
+        "acorn": "^6.0.7",
+        "acorn-jsx": "^5.0.0",
+        "eslint-visitor-keys": "^1.0.0"
+      }
+    },
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true
+    },
+    "esquery": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+      "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^4.0.0"
+      }
+    },
+    "esrecurse": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^4.1.0"
+      }
+    },
+    "estraverse": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+      "dev": true
+    },
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true
+    },
+    "execa": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-0.9.0.tgz",
+      "integrity": "sha512-BbUMBiX4hqiHZUA5+JujIjNb6TyAlp2D5KLheMjMluwOuzcnylDL4AxZYLLn1n2AGB49eSWwyKvvEQoRpnAtmA==",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^5.0.1",
+        "get-stream": "^3.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
+      },
+      "dependencies": {
+        "cross-spawn": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^4.0.1",
+            "shebang-command": "^1.2.0",
+            "which": "^1.2.9"
+          }
+        }
+      }
+    },
+    "expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+      "dev": true,
+      "requires": {
+        "debug": "^2.3.3",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "posix-character-classes": "^0.1.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "dev": true,
+      "requires": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "external-editor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+      "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+      "dev": true,
+      "requires": {
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
+      }
+    },
+    "extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "dev": true,
+      "requires": {
+        "array-unique": "^0.3.2",
+        "define-property": "^1.0.0",
+        "expand-brackets": "^2.1.4",
+        "extend-shallow": "^2.0.1",
+        "fragment-cache": "^0.2.1",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "extsprintf": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz",
+      "integrity": "sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk="
+    },
+    "fast-deep-equal": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+      "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
+    },
+    "fast-diff": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
+    "figures": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-3.0.0.tgz",
+      "integrity": "sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==",
+      "dev": true,
+      "requires": {
+        "escape-string-regexp": "^1.0.5"
+      }
+    },
+    "file-entry-cache": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+      "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^2.0.1"
+      }
+    },
+    "fill-range": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1",
+        "to-regex-range": "^2.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "find-parent-dir": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz",
+      "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=",
+      "dev": true
+    },
+    "find-up": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+      "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+      "dev": true,
+      "requires": {
+        "locate-path": "^2.0.0"
+      }
+    },
+    "flat-cache": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+      "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+      "dev": true,
+      "requires": {
+        "flatted": "^2.0.0",
+        "rimraf": "2.6.3",
+        "write": "1.0.3"
+      }
+    },
+    "flatted": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
+      "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
+      "dev": true
+    },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+      "dev": true
+    },
+    "fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "dev": true,
+      "requires": {
+        "map-cache": "^0.2.2"
+      }
+    },
+    "fs-minipass": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz",
+      "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==",
+      "requires": {
+        "minipass": "^2.2.1"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "dev": true
+    },
+    "gauge": {
+      "version": "2.7.4",
+      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+      "requires": {
+        "aproba": "^1.0.3",
+        "console-control-strings": "^1.0.0",
+        "has-unicode": "^2.0.0",
+        "object-assign": "^4.1.0",
+        "signal-exit": "^3.0.0",
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wide-align": "^1.1.0"
+      }
+    },
+    "get-own-enumerable-property-symbols": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz",
+      "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==",
+      "dev": true
+    },
+    "get-stdin": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
+      "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
+      "dev": true
+    },
+    "get-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+      "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+      "dev": true
+    },
+    "get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+      "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+      "dev": true
+    },
+    "graceful-fs": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
+      "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==",
+      "dev": true
+    },
+    "gridfs-stream": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/gridfs-stream/-/gridfs-stream-0.5.3.tgz",
+      "integrity": "sha1-wIlnKPo+qD9fo8nO1GGvt6A20Uk="
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-ansi": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^2.0.0"
+      }
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "has-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+      "dev": true
+    },
+    "has-unicode": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+    },
+    "has-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+      "dev": true,
+      "requires": {
+        "get-value": "^2.0.6",
+        "has-values": "^1.0.0",
+        "isobject": "^3.0.0"
+      }
+    },
+    "has-values": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+      "dev": true,
+      "requires": {
+        "is-number": "^3.0.0",
+        "kind-of": "^4.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "hosted-git-info": {
+      "version": "2.8.4",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz",
+      "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==",
+      "dev": true
+    },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "ieee754": {
+      "version": "1.1.13",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+    },
+    "ignore": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+      "dev": true
+    },
+    "ignore-walk": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
+      "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
+      "requires": {
+        "minimatch": "^3.0.4"
+      }
+    },
+    "import-fresh": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
+      "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "dependencies": {
+        "resolve-from": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+          "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+          "dev": true
+        }
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+      "dev": true
+    },
+    "indent-string": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
+      "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "ini": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
+    },
+    "inquirer": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.1.tgz",
+      "integrity": "sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw==",
+      "dev": true,
+      "requires": {
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^2.4.2",
+        "cli-cursor": "^3.1.0",
+        "cli-width": "^2.0.0",
+        "external-editor": "^3.0.3",
+        "figures": "^3.0.0",
+        "lodash": "^4.17.15",
+        "mute-stream": "0.0.8",
+        "run-async": "^2.2.0",
+        "rxjs": "^6.4.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^5.1.0",
+        "through": "^2.3.6"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz",
+          "integrity": "sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^5.2.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        }
+      }
+    },
+    "invariant": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+      "dev": true,
+      "requires": {
+        "loose-envify": "^1.0.0"
+      }
+    },
+    "is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+      "dev": true
+    },
+    "is-callable": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+      "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
+      "dev": true
+    },
+    "is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-date-object": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
+      "dev": true
+    },
+    "is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "requires": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+          "dev": true
+        }
+      }
+    },
+    "is-directory": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+      "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+      "dev": true
+    },
+    "is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+      "dev": true
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+      "requires": {
+        "number-is-nan": "^1.0.0"
+      }
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-obj": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+      "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
+      "dev": true
+    },
+    "is-observable": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz",
+      "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==",
+      "dev": true,
+      "requires": {
+        "symbol-observable": "^1.1.0"
+      }
+    },
+    "is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
+    "is-promise": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+      "dev": true
+    },
+    "is-regex": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.1"
+      }
+    },
+    "is-regexp": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
+      "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=",
+      "dev": true
+    },
+    "is-resolvable": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
+      "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
+      "dev": true
+    },
+    "is-stream": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+      "dev": true
+    },
+    "is-symbol": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+      "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+      "dev": true,
+      "requires": {
+        "has-symbols": "^1.0.0"
+      }
+    },
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+      "dev": true
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+      "dev": true
+    },
+    "jest-get-type": {
+      "version": "22.4.3",
+      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz",
+      "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==",
+      "dev": true
+    },
+    "jest-validate": {
+      "version": "23.6.0",
+      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-23.6.0.tgz",
+      "integrity": "sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.0.1",
+        "jest-get-type": "^22.1.0",
+        "leven": "^2.1.0",
+        "pretty-format": "^23.6.0"
+      }
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "3.13.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+      "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+      "dev": true,
+      "requires": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      }
+    },
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "dev": true
+    },
+    "kind-of": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+      "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+      "dev": true
+    },
+    "ldap-filter": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz",
+      "integrity": "sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=",
+      "requires": {
+        "assert-plus": "0.1.5"
+      },
+      "dependencies": {
+        "assert-plus": {
+          "version": "0.1.5",
+          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz",
+          "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA="
+        }
+      }
+    },
+    "ldapjs": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz",
+      "integrity": "sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk=",
+      "requires": {
+        "asn1": "0.2.3",
+        "assert-plus": "^1.0.0",
+        "backoff": "^2.5.0",
+        "bunyan": "^1.8.3",
+        "dashdash": "^1.14.0",
+        "dtrace-provider": "~0.8",
+        "ldap-filter": "0.2.2",
+        "once": "^1.4.0",
+        "vasync": "^1.6.4",
+        "verror": "^1.8.1"
+      }
+    },
+    "leven": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
+      "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
+      "dev": true
+    },
+    "levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      }
+    },
+    "lint-staged": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-7.3.0.tgz",
+      "integrity": "sha512-AXk40M9DAiPi7f4tdJggwuKIViUplYtVj1os1MVEteW7qOkU50EOehayCfO9TsoGK24o/EsWb41yrEgfJDDjCw==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.3.1",
+        "commander": "^2.14.1",
+        "cosmiconfig": "^5.0.2",
+        "debug": "^3.1.0",
+        "dedent": "^0.7.0",
+        "execa": "^0.9.0",
+        "find-parent-dir": "^0.3.0",
+        "is-glob": "^4.0.0",
+        "is-windows": "^1.0.2",
+        "jest-validate": "^23.5.0",
+        "listr": "^0.14.1",
+        "lodash": "^4.17.5",
+        "log-symbols": "^2.2.0",
+        "micromatch": "^3.1.8",
+        "npm-which": "^3.0.1",
+        "p-map": "^1.1.1",
+        "path-is-inside": "^1.0.2",
+        "pify": "^3.0.0",
+        "please-upgrade-node": "^3.0.2",
+        "staged-git-files": "1.1.1",
+        "string-argv": "^0.0.2",
+        "stringify-object": "^3.2.2"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+          "dev": true
+        }
+      }
+    },
+    "listr": {
+      "version": "0.14.3",
+      "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz",
+      "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==",
+      "dev": true,
+      "requires": {
+        "@samverschueren/stream-to-observable": "^0.3.0",
+        "is-observable": "^1.1.0",
+        "is-promise": "^2.1.0",
+        "is-stream": "^1.1.0",
+        "listr-silent-renderer": "^1.1.1",
+        "listr-update-renderer": "^0.5.0",
+        "listr-verbose-renderer": "^0.5.0",
+        "p-map": "^2.0.0",
+        "rxjs": "^6.3.3"
+      },
+      "dependencies": {
+        "p-map": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+          "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+          "dev": true
+        }
+      }
+    },
+    "listr-silent-renderer": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz",
+      "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=",
+      "dev": true
+    },
+    "listr-update-renderer": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz",
+      "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==",
+      "dev": true,
+      "requires": {
+        "chalk": "^1.1.3",
+        "cli-truncate": "^0.2.1",
+        "elegant-spinner": "^1.0.1",
+        "figures": "^1.7.0",
+        "indent-string": "^3.0.0",
+        "log-symbols": "^1.0.2",
+        "log-update": "^2.3.0",
+        "strip-ansi": "^3.0.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^2.2.1",
+            "escape-string-regexp": "^1.0.2",
+            "has-ansi": "^2.0.0",
+            "strip-ansi": "^3.0.0",
+            "supports-color": "^2.0.0"
+          }
+        },
+        "figures": {
+          "version": "1.7.0",
+          "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+          "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+          "dev": true,
+          "requires": {
+            "escape-string-regexp": "^1.0.5",
+            "object-assign": "^4.1.0"
+          }
+        },
+        "log-symbols": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
+          "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
+          "dev": true,
+          "requires": {
+            "chalk": "^1.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "dev": true
+        }
+      }
+    },
+    "listr-verbose-renderer": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz",
+      "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.4.1",
+        "cli-cursor": "^2.1.0",
+        "date-fns": "^1.27.2",
+        "figures": "^2.0.0"
+      },
+      "dependencies": {
+        "cli-cursor": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+          "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+          "dev": true,
+          "requires": {
+            "restore-cursor": "^2.0.0"
+          }
+        },
+        "figures": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+          "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+          "dev": true,
+          "requires": {
+            "escape-string-regexp": "^1.0.5"
+          }
+        },
+        "mimic-fn": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+          "dev": true
+        },
+        "onetime": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+          "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+          "dev": true,
+          "requires": {
+            "mimic-fn": "^1.0.0"
+          }
+        },
+        "restore-cursor": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+          "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+          "dev": true,
+          "requires": {
+            "onetime": "^2.0.0",
+            "signal-exit": "^3.0.2"
+          }
+        }
+      }
+    },
+    "load-json-file": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+      "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^2.2.0",
+        "pify": "^2.0.0",
+        "strip-bom": "^3.0.0"
+      }
+    },
+    "locate-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+      "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+      "dev": true,
+      "requires": {
+        "p-locate": "^2.0.0",
+        "path-exists": "^3.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.15",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+      "dev": true
+    },
+    "lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
+    "lodash.unescape": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
+      "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=",
+      "dev": true
+    },
+    "log-symbols": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
+      "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.0.1"
+      }
+    },
+    "log-update": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz",
+      "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=",
+      "dev": true,
+      "requires": {
+        "ansi-escapes": "^3.0.0",
+        "cli-cursor": "^2.0.0",
+        "wrap-ansi": "^3.0.1"
+      },
+      "dependencies": {
+        "ansi-escapes": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+          "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+          "dev": true
+        },
+        "cli-cursor": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+          "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+          "dev": true,
+          "requires": {
+            "restore-cursor": "^2.0.0"
+          }
+        },
+        "mimic-fn": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+          "dev": true
+        },
+        "onetime": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+          "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+          "dev": true,
+          "requires": {
+            "mimic-fn": "^1.0.0"
+          }
+        },
+        "restore-cursor": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+          "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+          "dev": true,
+          "requires": {
+            "onetime": "^2.0.0",
+            "signal-exit": "^3.0.2"
+          }
+        }
+      }
+    },
+    "loglevel": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.3.tgz",
+      "integrity": "sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==",
+      "dev": true
+    },
+    "loglevel-colored-level-prefix": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz",
+      "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=",
+      "dev": true,
+      "requires": {
+        "chalk": "^1.1.3",
+        "loglevel": "^1.4.1"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+          "dev": true
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^2.2.1",
+            "escape-string-regexp": "^1.0.2",
+            "has-ansi": "^2.0.0",
+            "strip-ansi": "^3.0.0",
+            "supports-color": "^2.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "dev": true
+        }
+      }
+    },
+    "long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+    },
+    "loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dev": true,
+      "requires": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      }
+    },
+    "lru-cache": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+      "dev": true,
+      "requires": {
+        "pseudomap": "^1.0.2",
+        "yallist": "^2.1.2"
+      },
+      "dependencies": {
+        "yallist": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+          "dev": true
+        }
+      }
+    },
+    "map-cache": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+      "dev": true
+    },
+    "map-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "dev": true,
+      "requires": {
+        "object-visit": "^1.0.0"
+      }
+    },
+    "memory-pager": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+      "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+      "optional": true
+    },
+    "meteor-node-stubs": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-0.4.1.tgz",
+      "integrity": "sha512-UO2OStvLOKoApmOdIP5eCqoLaa/ritMXRg4ffJVdkNLEsczzPvTjgC0Mxk4cM4R8MZkwll90FYgjDf5qUTJdMA==",
+      "requires": {
+        "assert": "^1.4.1",
+        "browserify-zlib": "^0.1.4",
+        "buffer": "^4.9.1",
+        "console-browserify": "^1.1.0",
+        "constants-browserify": "^1.0.0",
+        "crypto-browserify": "^3.11.0",
+        "domain-browser": "^1.1.7",
+        "events": "^1.1.1",
+        "https-browserify": "0.0.1",
+        "os-browserify": "^0.2.1",
+        "path-browserify": "0.0.0",
+        "process": "^0.11.9",
+        "punycode": "^1.4.1",
+        "querystring-es3": "^0.2.1",
+        "readable-stream": "^2.3.6",
+        "stream-browserify": "^2.0.1",
+        "stream-http": "^2.8.0",
+        "string_decoder": "^1.1.0",
+        "timers-browserify": "^1.4.2",
+        "tty-browserify": "0.0.0",
+        "url": "^0.11.0",
+        "util": "^0.10.3",
+        "vm-browserify": "0.0.4"
+      },
+      "dependencies": {
+        "asn1.js": {
+          "version": "4.10.1",
+          "bundled": true,
+          "requires": {
+            "bn.js": "^4.0.0",
+            "inherits": "^2.0.1",
+            "minimalistic-assert": "^1.0.0"
+          }
+        },
+        "assert": {
+          "version": "1.4.1",
+          "bundled": true,
+          "requires": {
+            "util": "0.10.3"
+          }
+        },
+        "base64-js": {
+          "version": "1.3.0",
+          "bundled": true
+        },
+        "bn.js": {
+          "version": "4.11.8",
+          "bundled": true
+        },
+        "brorand": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "browserify-aes": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "buffer-xor": "^1.0.3",
+            "cipher-base": "^1.0.0",
+            "create-hash": "^1.1.0",
+            "evp_bytestokey": "^1.0.3",
+            "inherits": "^2.0.1",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "browserify-cipher": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "browserify-aes": "^1.0.4",
+            "browserify-des": "^1.0.0",
+            "evp_bytestokey": "^1.0.0"
+          }
+        },
+        "browserify-des": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "cipher-base": "^1.0.1",
+            "des.js": "^1.0.0",
+            "inherits": "^2.0.1"
+          }
+        },
+        "browserify-rsa": {
+          "version": "4.0.1",
+          "bundled": true,
+          "requires": {
+            "bn.js": "^4.1.0",
+            "randombytes": "^2.0.1"
+          }
+        },
+        "browserify-sign": {
+          "version": "4.0.4",
+          "bundled": true,
+          "requires": {
+            "bn.js": "^4.1.1",
+            "browserify-rsa": "^4.0.0",
+            "create-hash": "^1.1.0",
+            "create-hmac": "^1.1.2",
+            "elliptic": "^6.0.0",
+            "inherits": "^2.0.1",
+            "parse-asn1": "^5.0.0"
+          }
+        },
+        "browserify-zlib": {
+          "version": "0.1.4",
+          "bundled": true,
+          "requires": {
+            "pako": "~0.2.0"
+          }
+        },
+        "buffer": {
+          "version": "4.9.1",
+          "bundled": true,
+          "requires": {
+            "base64-js": "^1.0.2",
+            "ieee754": "^1.1.4",
+            "isarray": "^1.0.0"
+          }
+        },
+        "buffer-xor": {
+          "version": "1.0.3",
+          "bundled": true
+        },
+        "builtin-status-codes": {
+          "version": "3.0.0",
+          "bundled": true
+        },
+        "cipher-base": {
+          "version": "1.0.4",
+          "bundled": true,
+          "requires": {
+            "inherits": "^2.0.1",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "console-browserify": {
+          "version": "1.1.0",
+          "bundled": true,
+          "requires": {
+            "date-now": "^0.1.4"
+          }
+        },
+        "constants-browserify": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "core-util-is": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "create-ecdh": {
+          "version": "4.0.3",
+          "bundled": true,
+          "requires": {
+            "bn.js": "^4.1.0",
+            "elliptic": "^6.0.0"
+          }
+        },
+        "create-hash": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "cipher-base": "^1.0.1",
+            "inherits": "^2.0.1",
+            "md5.js": "^1.3.4",
+            "ripemd160": "^2.0.1",
+            "sha.js": "^2.4.0"
+          }
+        },
+        "create-hmac": {
+          "version": "1.1.7",
+          "bundled": true,
+          "requires": {
+            "cipher-base": "^1.0.3",
+            "create-hash": "^1.1.0",
+            "inherits": "^2.0.1",
+            "ripemd160": "^2.0.0",
+            "safe-buffer": "^5.0.1",
+            "sha.js": "^2.4.8"
+          }
+        },
+        "crypto-browserify": {
+          "version": "3.12.0",
+          "bundled": true,
+          "requires": {
+            "browserify-cipher": "^1.0.0",
+            "browserify-sign": "^4.0.0",
+            "create-ecdh": "^4.0.0",
+            "create-hash": "^1.1.0",
+            "create-hmac": "^1.1.0",
+            "diffie-hellman": "^5.0.0",
+            "inherits": "^2.0.1",
+            "pbkdf2": "^3.0.3",
+            "public-encrypt": "^4.0.0",
+            "randombytes": "^2.0.0",
+            "randomfill": "^1.0.3"
+          }
+        },
+        "date-now": {
+          "version": "0.1.4",
+          "bundled": true
+        },
+        "des.js": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "inherits": "^2.0.1",
+            "minimalistic-assert": "^1.0.0"
+          }
+        },
+        "diffie-hellman": {
+          "version": "5.0.3",
+          "bundled": true,
+          "requires": {
+            "bn.js": "^4.1.0",
+            "miller-rabin": "^4.0.0",
+            "randombytes": "^2.0.0"
+          }
+        },
+        "domain-browser": {
+          "version": "1.2.0",
+          "bundled": true
+        },
+        "elliptic": {
+          "version": "6.4.0",
+          "bundled": true,
+          "requires": {
+            "bn.js": "^4.4.0",
+            "brorand": "^1.0.1",
+            "hash.js": "^1.0.0",
+            "hmac-drbg": "^1.0.0",
+            "inherits": "^2.0.1",
+            "minimalistic-assert": "^1.0.0",
+            "minimalistic-crypto-utils": "^1.0.0"
+          }
+        },
+        "events": {
+          "version": "1.1.1",
+          "bundled": true
+        },
+        "evp_bytestokey": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "md5.js": "^1.3.4",
+            "safe-buffer": "^5.1.1"
+          }
+        },
+        "hash-base": {
+          "version": "3.0.4",
+          "bundled": true,
+          "requires": {
+            "inherits": "^2.0.1",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "hash.js": {
+          "version": "1.1.3",
+          "bundled": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "minimalistic-assert": "^1.0.0"
+          },
+          "dependencies": {
+            "inherits": {
+              "version": "2.0.3",
+              "bundled": true
+            }
+          }
+        },
+        "hmac-drbg": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "hash.js": "^1.0.3",
+            "minimalistic-assert": "^1.0.0",
+            "minimalistic-crypto-utils": "^1.0.1"
+          }
+        },
+        "https-browserify": {
+          "version": "0.0.1",
+          "bundled": true
+        },
+        "ieee754": {
+          "version": "1.1.11",
+          "bundled": true
+        },
+        "indexof": {
+          "version": "0.0.1",
+          "bundled": true
+        },
+        "inherits": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "isarray": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "md5.js": {
+          "version": "1.3.4",
+          "bundled": true,
+          "requires": {
+            "hash-base": "^3.0.0",
+            "inherits": "^2.0.1"
+          }
+        },
+        "miller-rabin": {
+          "version": "4.0.1",
+          "bundled": true,
+          "requires": {
+            "bn.js": "^4.0.0",
+            "brorand": "^1.0.1"
+          }
+        },
+        "minimalistic-assert": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "minimalistic-crypto-utils": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "os-browserify": {
+          "version": "0.2.1",
+          "bundled": true
+        },
+        "pako": {
+          "version": "0.2.9",
+          "bundled": true
+        },
+        "parse-asn1": {
+          "version": "5.1.1",
+          "bundled": true,
+          "requires": {
+            "asn1.js": "^4.0.0",
+            "browserify-aes": "^1.0.0",
+            "create-hash": "^1.1.0",
+            "evp_bytestokey": "^1.0.0",
+            "pbkdf2": "^3.0.3"
+          }
+        },
+        "path-browserify": {
+          "version": "0.0.0",
+          "bundled": true
+        },
+        "pbkdf2": {
+          "version": "3.0.16",
+          "bundled": true,
+          "requires": {
+            "create-hash": "^1.1.2",
+            "create-hmac": "^1.1.4",
+            "ripemd160": "^2.0.1",
+            "safe-buffer": "^5.0.1",
+            "sha.js": "^2.4.8"
+          }
+        },
+        "process": {
+          "version": "0.11.10",
+          "bundled": true
+        },
+        "process-nextick-args": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "public-encrypt": {
+          "version": "4.0.2",
+          "bundled": true,
+          "requires": {
+            "bn.js": "^4.1.0",
+            "browserify-rsa": "^4.0.0",
+            "create-hash": "^1.1.0",
+            "parse-asn1": "^5.0.0",
+            "randombytes": "^2.0.1"
+          }
+        },
+        "punycode": {
+          "version": "1.4.1",
+          "bundled": true
+        },
+        "querystring": {
+          "version": "0.2.0",
+          "bundled": true
+        },
+        "querystring-es3": {
+          "version": "0.2.1",
+          "bundled": true
+        },
+        "randombytes": {
+          "version": "2.0.6",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "^5.1.0"
+          }
+        },
+        "randomfill": {
+          "version": "1.0.4",
+          "bundled": true,
+          "requires": {
+            "randombytes": "^2.0.5",
+            "safe-buffer": "^5.1.0"
+          }
+        },
+        "readable-stream": {
+          "version": "2.3.6",
+          "bundled": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          },
+          "dependencies": {
+            "inherits": {
+              "version": "2.0.3",
+              "bundled": true
+            }
+          }
+        },
+        "ripemd160": {
+          "version": "2.0.2",
+          "bundled": true,
+          "requires": {
+            "hash-base": "^3.0.0",
+            "inherits": "^2.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "bundled": true
+        },
+        "sha.js": {
+          "version": "2.4.11",
+          "bundled": true,
+          "requires": {
+            "inherits": "^2.0.1",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "stream-browserify": {
+          "version": "2.0.1",
+          "bundled": true,
+          "requires": {
+            "inherits": "~2.0.1",
+            "readable-stream": "^2.0.2"
+          }
+        },
+        "stream-http": {
+          "version": "2.8.1",
+          "bundled": true,
+          "requires": {
+            "builtin-status-codes": "^3.0.0",
+            "inherits": "^2.0.1",
+            "readable-stream": "^2.3.3",
+            "to-arraybuffer": "^1.0.0",
+            "xtend": "^4.0.0"
+          }
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        },
+        "timers-browserify": {
+          "version": "1.4.2",
+          "bundled": true,
+          "requires": {
+            "process": "~0.11.0"
+          }
+        },
+        "to-arraybuffer": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "tty-browserify": {
+          "version": "0.0.0",
+          "bundled": true
+        },
+        "url": {
+          "version": "0.11.0",
+          "bundled": true,
+          "requires": {
+            "punycode": "1.3.2",
+            "querystring": "0.2.0"
+          },
+          "dependencies": {
+            "punycode": {
+              "version": "1.3.2",
+              "bundled": true
+            }
+          }
+        },
+        "util": {
+          "version": "0.10.3",
+          "bundled": true,
+          "requires": {
+            "inherits": "2.0.1"
+          }
+        },
+        "util-deprecate": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "vm-browserify": {
+          "version": "0.0.4",
+          "bundled": true,
+          "requires": {
+            "indexof": "0.0.1"
+          }
+        },
+        "xtend": {
+          "version": "4.0.1",
+          "bundled": true
+        }
+      }
+    },
+    "micromatch": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "braces": "^2.3.1",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "extglob": "^2.0.4",
+        "fragment-cache": "^0.2.1",
+        "kind-of": "^6.0.2",
+        "nanomatch": "^1.2.9",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.2"
+      }
+    },
+    "mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "minimist": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+    },
+    "minipass": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
+      "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
+      "requires": {
+        "safe-buffer": "^5.1.2",
+        "yallist": "^3.0.0"
+      }
+    },
+    "minizlib": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
+      "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
+      "requires": {
+        "minipass": "^2.2.1"
+      }
+    },
+    "mixin-deep": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+      "dev": true,
+      "requires": {
+        "for-in": "^1.0.2",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        }
+      }
+    },
+    "mkdirp": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+      "requires": {
+        "minimist": "0.0.8"
+      }
+    },
+    "moment": {
+      "version": "2.24.0",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+      "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
+      "optional": true
+    },
+    "mongodb": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.3.tgz",
+      "integrity": "sha512-MdRnoOjstmnrKJsK8PY0PjP6fyF/SBS4R8coxmhsfEU7tQ46/J6j+aSHF2n4c2/H8B+Hc/Klbfp8vggZfI0mmA==",
+      "requires": {
+        "bson": "^1.1.1",
+        "require_optional": "^1.0.1",
+        "safe-buffer": "^5.1.2",
+        "saslprep": "^1.0.0"
+      },
+      "dependencies": {
+        "bson": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz",
+          "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg=="
+        }
+      }
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+    },
+    "mute-stream": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+      "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+      "dev": true
+    },
+    "mv": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
+      "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=",
+      "optional": true,
+      "requires": {
+        "mkdirp": "~0.5.1",
+        "ncp": "~2.0.0",
+        "rimraf": "~2.4.0"
+      },
+      "dependencies": {
+        "glob": {
+          "version": "6.0.4",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
+          "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
+          "optional": true,
+          "requires": {
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "2 || 3",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "rimraf": {
+          "version": "2.4.5",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
+          "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=",
+          "optional": true,
+          "requires": {
+            "glob": "^6.0.1"
+          }
+        }
+      }
+    },
+    "nan": {
+      "version": "2.13.2",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
+      "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw=="
+    },
+    "nanomatch": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "fragment-cache": "^0.2.1",
+        "is-windows": "^1.0.2",
+        "kind-of": "^6.0.2",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      }
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+      "dev": true
+    },
+    "ncp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
+      "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=",
+      "optional": true
+    },
+    "needle": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz",
+      "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==",
+      "requires": {
+        "debug": "^3.2.6",
+        "iconv-lite": "^0.4.4",
+        "sax": "^1.2.4"
+      }
+    },
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
+    },
+    "node-pre-gyp": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz",
+      "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
+      "requires": {
+        "detect-libc": "^1.0.2",
+        "mkdirp": "^0.5.1",
+        "needle": "^2.2.1",
+        "nopt": "^4.0.1",
+        "npm-packlist": "^1.1.6",
+        "npmlog": "^4.0.2",
+        "rc": "^1.2.7",
+        "rimraf": "^2.6.1",
+        "semver": "^5.3.0",
+        "tar": "^4"
+      }
+    },
+    "nopt": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
+      "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
+      "requires": {
+        "abbrev": "1",
+        "osenv": "^0.1.4"
+      }
+    },
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "npm-bundled": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
+      "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g=="
+    },
+    "npm-packlist": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz",
+      "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==",
+      "requires": {
+        "ignore-walk": "^3.0.1",
+        "npm-bundled": "^1.0.1"
+      }
+    },
+    "npm-path": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz",
+      "integrity": "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==",
+      "dev": true,
+      "requires": {
+        "which": "^1.2.10"
+      }
+    },
+    "npm-run-path": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+      "dev": true,
+      "requires": {
+        "path-key": "^2.0.0"
+      }
+    },
+    "npm-which": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz",
+      "integrity": "sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=",
+      "dev": true,
+      "requires": {
+        "commander": "^2.9.0",
+        "npm-path": "^2.0.2",
+        "which": "^1.2.10"
+      }
+    },
+    "npmlog": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+      "requires": {
+        "are-we-there-yet": "~1.1.2",
+        "console-control-strings": "~1.1.0",
+        "gauge": "~2.7.3",
+        "set-blocking": "~2.0.0"
+      }
+    },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+    },
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+    },
+    "object-copy": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+      "dev": true,
+      "requires": {
+        "copy-descriptor": "^0.1.0",
+        "define-property": "^0.2.5",
+        "kind-of": "^3.0.3"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true
+    },
+    "object-visit": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.0"
+      }
+    },
+    "object.pick": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.1"
+      }
+    },
+    "object.values": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
+      "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.12.0",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3"
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "onetime": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+      "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "^2.1.0"
+      }
+    },
+    "optionator": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+      "dev": true,
+      "requires": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.4",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "wordwrap": "~1.0.0"
+      }
+    },
+    "os": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz",
+      "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M="
+    },
+    "os-homedir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+    },
+    "os-shim": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
+      "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=",
+      "dev": true
+    },
+    "os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+    },
+    "osenv": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+      "requires": {
+        "os-homedir": "^1.0.0",
+        "os-tmpdir": "^1.0.0"
+      }
+    },
+    "p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+      "dev": true
+    },
+    "p-limit": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+      "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+      "dev": true,
+      "requires": {
+        "p-try": "^1.0.0"
+      }
+    },
+    "p-locate": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+      "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+      "dev": true,
+      "requires": {
+        "p-limit": "^1.1.0"
+      }
+    },
+    "p-map": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
+      "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
+      "dev": true
+    },
+    "p-try": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+      "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+      "dev": true
+    },
+    "page": {
+      "version": "1.11.4",
+      "resolved": "https://registry.npmjs.org/page/-/page-1.11.4.tgz",
+      "integrity": "sha512-8JMZzcE5W4qk+/DtmogN57cI+Yscy7xTYCpfSO7s3Tx6LjZuAfHFQY1+cKIAy60NaXdzVD6nOc3objaVbE0HJg==",
+      "requires": {
+        "path-to-regexp": "~1.2.1"
+      }
+    },
+    "parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
+    "parse-json": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+      "dev": true,
+      "requires": {
+        "error-ex": "^1.2.0"
+      }
+    },
+    "pascalcase": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+      "dev": true
+    },
+    "path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+      "dev": true
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+    },
+    "path-is-inside": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+      "dev": true
+    },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+      "dev": true
+    },
+    "path-to-regexp": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.2.1.tgz",
+      "integrity": "sha1-szcFwUAjTYc8hyHHuf2LVB7Tr/k=",
+      "requires": {
+        "isarray": "0.0.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        }
+      }
+    },
+    "path-type": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+      "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+      "dev": true,
+      "requires": {
+        "pify": "^2.0.0"
+      }
+    },
+    "pify": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+      "dev": true
+    },
+    "pkg-dir": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+      "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+      "dev": true,
+      "requires": {
+        "find-up": "^2.1.0"
+      }
+    },
+    "please-upgrade-node": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
+      "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==",
+      "dev": true,
+      "requires": {
+        "semver-compare": "^1.0.0"
+      }
+    },
+    "pluralize": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
+      "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
+      "dev": true
+    },
+    "posix-character-classes": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+      "dev": true
+    },
+    "pre-commit": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz",
+      "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^5.0.1",
+        "spawn-sync": "^1.0.15",
+        "which": "1.2.x"
+      },
+      "dependencies": {
+        "cross-spawn": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^4.0.1",
+            "shebang-command": "^1.2.0",
+            "which": "^1.2.9"
+          }
+        },
+        "which": {
+          "version": "1.2.14",
+          "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
+          "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
+          "dev": true,
+          "requires": {
+            "isexe": "^2.0.0"
+          }
+        }
+      }
+    },
+    "precond": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
+      "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw="
+    },
+    "prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+      "dev": true
+    },
+    "prettier": {
+      "version": "1.18.2",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz",
+      "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==",
+      "dev": true
+    },
+    "prettier-eslint": {
+      "version": "8.8.2",
+      "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-8.8.2.tgz",
+      "integrity": "sha512-2UzApPuxi2yRoyMlXMazgR6UcH9DKJhNgCviIwY3ixZ9THWSSrUww5vkiZ3C48WvpFl1M1y/oU63deSy1puWEA==",
+      "dev": true,
+      "requires": {
+        "babel-runtime": "^6.26.0",
+        "common-tags": "^1.4.0",
+        "dlv": "^1.1.0",
+        "eslint": "^4.0.0",
+        "indent-string": "^3.2.0",
+        "lodash.merge": "^4.6.0",
+        "loglevel-colored-level-prefix": "^1.0.0",
+        "prettier": "^1.7.0",
+        "pretty-format": "^23.0.1",
+        "require-relative": "^0.8.7",
+        "typescript": "^2.5.1",
+        "typescript-eslint-parser": "^16.0.0",
+        "vue-eslint-parser": "^2.0.2"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "5.7.3",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
+          "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
+          "dev": true
+        },
+        "acorn-jsx": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+          "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
+          "dev": true,
+          "requires": {
+            "acorn": "^3.0.4"
+          },
+          "dependencies": {
+            "acorn": {
+              "version": "3.3.0",
+              "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+              "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
+              "dev": true
+            }
+          }
+        },
+        "ansi-escapes": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+          "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+          "dev": true
+        },
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "chardet": {
+          "version": "0.4.2",
+          "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
+          "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
+          "dev": true
+        },
+        "cli-cursor": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+          "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+          "dev": true,
+          "requires": {
+            "restore-cursor": "^2.0.0"
+          }
+        },
+        "cross-spawn": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^4.0.1",
+            "shebang-command": "^1.2.0",
+            "which": "^1.2.9"
+          }
+        },
+        "doctrine": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+          "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+          "dev": true,
+          "requires": {
+            "esutils": "^2.0.2"
+          }
+        },
+        "eslint": {
+          "version": "4.19.1",
+          "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
+          "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
+          "dev": true,
+          "requires": {
+            "ajv": "^5.3.0",
+            "babel-code-frame": "^6.22.0",
+            "chalk": "^2.1.0",
+            "concat-stream": "^1.6.0",
+            "cross-spawn": "^5.1.0",
+            "debug": "^3.1.0",
+            "doctrine": "^2.1.0",
+            "eslint-scope": "^3.7.1",
+            "eslint-visitor-keys": "^1.0.0",
+            "espree": "^3.5.4",
+            "esquery": "^1.0.0",
+            "esutils": "^2.0.2",
+            "file-entry-cache": "^2.0.0",
+            "functional-red-black-tree": "^1.0.1",
+            "glob": "^7.1.2",
+            "globals": "^11.0.1",
+            "ignore": "^3.3.3",
+            "imurmurhash": "^0.1.4",
+            "inquirer": "^3.0.6",
+            "is-resolvable": "^1.0.0",
+            "js-yaml": "^3.9.1",
+            "json-stable-stringify-without-jsonify": "^1.0.1",
+            "levn": "^0.3.0",
+            "lodash": "^4.17.4",
+            "minimatch": "^3.0.2",
+            "mkdirp": "^0.5.1",
+            "natural-compare": "^1.4.0",
+            "optionator": "^0.8.2",
+            "path-is-inside": "^1.0.2",
+            "pluralize": "^7.0.0",
+            "progress": "^2.0.0",
+            "regexpp": "^1.0.1",
+            "require-uncached": "^1.0.3",
+            "semver": "^5.3.0",
+            "strip-ansi": "^4.0.0",
+            "strip-json-comments": "~2.0.1",
+            "table": "4.0.2",
+            "text-table": "~0.2.0"
+          }
+        },
+        "eslint-scope": {
+          "version": "3.7.3",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz",
+          "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
+          }
+        },
+        "espree": {
+          "version": "3.5.4",
+          "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
+          "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
+          "dev": true,
+          "requires": {
+            "acorn": "^5.5.0",
+            "acorn-jsx": "^3.0.0"
+          }
+        },
+        "external-editor": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
+          "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
+          "dev": true,
+          "requires": {
+            "chardet": "^0.4.0",
+            "iconv-lite": "^0.4.17",
+            "tmp": "^0.0.33"
+          }
+        },
+        "figures": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+          "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+          "dev": true,
+          "requires": {
+            "escape-string-regexp": "^1.0.5"
+          }
+        },
+        "file-entry-cache": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+          "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+          "dev": true,
+          "requires": {
+            "flat-cache": "^1.2.1",
+            "object-assign": "^4.0.1"
+          }
+        },
+        "flat-cache": {
+          "version": "1.3.4",
+          "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
+          "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
+          "dev": true,
+          "requires": {
+            "circular-json": "^0.3.1",
+            "graceful-fs": "^4.1.2",
+            "rimraf": "~2.6.2",
+            "write": "^0.2.1"
+          }
+        },
+        "ignore": {
+          "version": "3.3.10",
+          "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
+          "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
+          "dev": true
+        },
+        "inquirer": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
+          "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
+          "dev": true,
+          "requires": {
+            "ansi-escapes": "^3.0.0",
+            "chalk": "^2.0.0",
+            "cli-cursor": "^2.1.0",
+            "cli-width": "^2.0.0",
+            "external-editor": "^2.0.4",
+            "figures": "^2.0.0",
+            "lodash": "^4.3.0",
+            "mute-stream": "0.0.7",
+            "run-async": "^2.2.0",
+            "rx-lite": "^4.0.8",
+            "rx-lite-aggregates": "^4.0.8",
+            "string-width": "^2.1.0",
+            "strip-ansi": "^4.0.0",
+            "through": "^2.3.6"
+          }
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
+        "mimic-fn": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+          "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+          "dev": true
+        },
+        "mute-stream": {
+          "version": "0.0.7",
+          "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+          "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+          "dev": true
+        },
+        "onetime": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+          "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+          "dev": true,
+          "requires": {
+            "mimic-fn": "^1.0.0"
+          }
+        },
+        "regexpp": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
+          "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
+          "dev": true
+        },
+        "restore-cursor": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+          "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+          "dev": true,
+          "requires": {
+            "onetime": "^2.0.0",
+            "signal-exit": "^3.0.2"
+          }
+        },
+        "slice-ansi": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
+          "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
+          "dev": true,
+          "requires": {
+            "is-fullwidth-code-point": "^2.0.0"
+          }
+        },
+        "string-width": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+          "dev": true,
+          "requires": {
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^4.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^3.0.0"
+          }
+        },
+        "table": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
+          "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
+          "dev": true,
+          "requires": {
+            "ajv": "^5.2.3",
+            "ajv-keywords": "^2.1.0",
+            "chalk": "^2.1.0",
+            "lodash": "^4.17.4",
+            "slice-ansi": "1.0.0",
+            "string-width": "^2.1.1"
+          }
+        },
+        "write": {
+          "version": "0.2.1",
+          "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+          "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+          "dev": true,
+          "requires": {
+            "mkdirp": "^0.5.1"
+          }
+        }
+      }
+    },
+    "prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "requires": {
+        "fast-diff": "^1.1.2"
+      }
+    },
+    "pretty-format": {
+      "version": "23.6.0",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz",
+      "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^3.0.0",
+        "ansi-styles": "^3.2.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        }
+      }
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
+    "progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true
+    },
+    "pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+      "dev": true
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true
+    },
+    "qs": {
+      "version": "6.8.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.8.0.tgz",
+      "integrity": "sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w=="
+    },
+    "rc": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+      "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+      "requires": {
+        "deep-extend": "^0.6.0",
+        "ini": "~1.3.0",
+        "minimist": "^1.2.0",
+        "strip-json-comments": "~2.0.1"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+        }
+      }
+    },
+    "read-pkg": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+      "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+      "dev": true,
+      "requires": {
+        "load-json-file": "^2.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^2.0.0"
+      }
+    },
+    "read-pkg-up": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+      "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+      "dev": true,
+      "requires": {
+        "find-up": "^2.0.0",
+        "read-pkg": "^2.0.0"
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "regenerator-runtime": {
+      "version": "0.13.3",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+      "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
+    },
+    "regex-not": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.2",
+        "safe-regex": "^1.1.0"
+      }
+    },
+    "regexpp": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+      "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+      "dev": true
+    },
+    "repeat-element": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+      "dev": true
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+      "dev": true
+    },
+    "require-relative": {
+      "version": "0.8.7",
+      "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
+      "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
+      "dev": true
+    },
+    "require-uncached": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
+      "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
+      "dev": true,
+      "requires": {
+        "caller-path": "^0.1.0",
+        "resolve-from": "^1.0.0"
+      },
+      "dependencies": {
+        "caller-path": {
+          "version": "0.1.0",
+          "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
+          "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+          "dev": true,
+          "requires": {
+            "callsites": "^0.2.0"
+          }
+        },
+        "callsites": {
+          "version": "0.2.0",
+          "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
+          "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
+          "dev": true
+        },
+        "resolve-from": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
+          "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
+          "dev": true
+        }
+      }
+    },
+    "require_optional": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
+      "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
+      "requires": {
+        "resolve-from": "^2.0.0",
+        "semver": "^5.1.0"
+      }
+    },
+    "resolve": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
+      "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
+      "dev": true,
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "resolve-from": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
+      "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
+    },
+    "resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+      "dev": true
+    },
+    "restore-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+      "dev": true,
+      "requires": {
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
+      }
+    },
+    "ret": {
+      "version": "0.1.15",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "2.6.3",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "run-async": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+      "dev": true,
+      "requires": {
+        "is-promise": "^2.1.0"
+      }
+    },
+    "rx-lite": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
+      "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
+      "dev": true
+    },
+    "rx-lite-aggregates": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
+      "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
+      "dev": true,
+      "requires": {
+        "rx-lite": "*"
+      }
+    },
+    "rxjs": {
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz",
+      "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "safe-json-stringify": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz",
+      "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==",
+      "optional": true
+    },
+    "safe-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "dev": true,
+      "requires": {
+        "ret": "~0.1.10"
+      }
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "saslprep": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
+      "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
+      "optional": true,
+      "requires": {
+        "sparse-bitfield": "^3.0.3"
+      }
+    },
+    "sax": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+    },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+    },
+    "semver-compare": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
+      "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
+      "dev": true
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+    },
+    "set-value": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-extendable": "^0.1.1",
+        "is-plain-object": "^2.0.3",
+        "split-string": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        }
+      }
+    },
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^1.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true
+    },
+    "signal-exit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+    },
+    "slice-ansi": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+      "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.0",
+        "astral-regex": "^1.0.0",
+        "is-fullwidth-code-point": "^2.0.0"
+      },
+      "dependencies": {
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        }
+      }
+    },
+    "snapdragon": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+      "dev": true,
+      "requires": {
+        "base": "^0.11.1",
+        "debug": "^2.2.0",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "map-cache": "^0.2.2",
+        "source-map": "^0.5.6",
+        "source-map-resolve": "^0.5.0",
+        "use": "^3.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "dev": true,
+          "requires": {
+            "is-extendable": "^0.1.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "snapdragon-node": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.0",
+        "snapdragon-util": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "dev": true,
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
+        },
+        "is-descriptor": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
+          }
+        }
+      }
+    },
+    "snapdragon-util": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.2.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+    },
+    "source-map-resolve": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
+      "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+      "dev": true,
+      "requires": {
+        "atob": "^2.1.1",
+        "decode-uri-component": "^0.2.0",
+        "resolve-url": "^0.2.1",
+        "source-map-url": "^0.4.0",
+        "urix": "^0.1.0"
+      }
+    },
+    "source-map-support": {
+      "version": "0.5.13",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+      "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "source-map-url": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+      "dev": true
+    },
+    "sparse-bitfield": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+      "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
+      "optional": true,
+      "requires": {
+        "memory-pager": "^1.0.2"
+      }
+    },
+    "spawn-sync": {
+      "version": "1.0.15",
+      "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz",
+      "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=",
+      "dev": true,
+      "requires": {
+        "concat-stream": "^1.4.7",
+        "os-shim": "^0.1.2"
+      }
+    },
+    "spdx-correct": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+      "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+      "dev": true,
+      "requires": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+      "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+      "dev": true
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+      "dev": true,
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
+      "dev": true
+    },
+    "split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.0"
+      }
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "staged-git-files": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-1.1.1.tgz",
+      "integrity": "sha512-H89UNKr1rQJvI1c/PIR3kiAMBV23yvR7LItZiV74HWZwzt7f3YHuujJ9nJZlt58WlFox7XQsOahexwk7nTe69A==",
+      "dev": true
+    },
+    "static-extend": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+      "dev": true,
+      "requires": {
+        "define-property": "^0.2.5",
+        "object-copy": "^0.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        }
+      }
+    },
+    "string-argv": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.0.2.tgz",
+      "integrity": "sha1-2sMECGkMIfPDYwo/86BYd73L1zY=",
+      "dev": true
+    },
+    "string-width": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+      "requires": {
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      }
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "stringify-object": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
+      "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
+      "dev": true,
+      "requires": {
+        "get-own-enumerable-property-symbols": "^3.0.0",
+        "is-obj": "^1.0.1",
+        "is-regexp": "^1.0.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "requires": {
+        "ansi-regex": "^2.0.0"
+      }
+    },
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "dev": true
+    },
+    "strip-eof": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+      "dev": true
+    },
+    "strip-json-comments": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "symbol-observable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+      "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+      "dev": true
+    },
+    "table": {
+      "version": "5.4.5",
+      "resolved": "https://registry.npmjs.org/table/-/table-5.4.5.tgz",
+      "integrity": "sha512-oGa2Hl7CQjfoaogtrOHEJroOcYILTx7BZWLGsJIlzoWmB2zmguhNfPJZsWPKYek/MgCxfco54gEi31d1uN2hFA==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.10.2",
+        "lodash": "^4.17.14",
+        "slice-ansi": "^2.1.0",
+        "string-width": "^3.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.10.2",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
+          "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "emoji-regex": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+          "dev": true
+        },
+        "fast-deep-equal": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
+        "json-schema-traverse": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        }
+      }
+    },
+    "tar": {
+      "version": "4.4.10",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz",
+      "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==",
+      "requires": {
+        "chownr": "^1.1.1",
+        "fs-minipass": "^1.2.5",
+        "minipass": "^2.3.5",
+        "minizlib": "^1.2.1",
+        "mkdirp": "^0.5.0",
+        "safe-buffer": "^5.1.2",
+        "yallist": "^3.0.3"
+      }
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+      "dev": true
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+      "dev": true
+    },
+    "tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "dev": true,
+      "requires": {
+        "os-tmpdir": "~1.0.2"
+      }
+    },
+    "to-object-path": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "to-regex": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "regex-not": "^1.0.2",
+        "safe-regex": "^1.1.0"
+      }
+    },
+    "to-regex-range": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+      "dev": true,
+      "requires": {
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1"
+      }
+    },
+    "tslib": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
+      "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
+      "dev": true
+    },
+    "type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2"
+      }
+    },
+    "type-fest": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz",
+      "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==",
+      "dev": true
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+      "dev": true
+    },
+    "typescript": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
+      "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
+      "dev": true
+    },
+    "typescript-eslint-parser": {
+      "version": "16.0.1",
+      "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-16.0.1.tgz",
+      "integrity": "sha512-IKawLTu4A2xN3aN/cPLxvZ0bhxZHILGDKTZWvWNJ3sLNhJ3PjfMEDQmR2VMpdRPrmWOadgWXRwjLBzSA8AGsaQ==",
+      "dev": true,
+      "requires": {
+        "lodash.unescape": "4.0.1",
+        "semver": "5.5.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+          "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+          "dev": true
+        }
+      }
+    },
+    "union-value": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "get-value": "^2.0.6",
+        "is-extendable": "^0.1.1",
+        "set-value": "^2.0.1"
+      }
+    },
+    "unset-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+      "dev": true,
+      "requires": {
+        "has-value": "^0.3.1",
+        "isobject": "^3.0.0"
+      },
+      "dependencies": {
+        "has-value": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+          "dev": true,
+          "requires": {
+            "get-value": "^2.0.3",
+            "has-values": "^0.1.4",
+            "isobject": "^2.0.0"
+          },
+          "dependencies": {
+            "isobject": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+              "dev": true,
+              "requires": {
+                "isarray": "1.0.0"
+              }
+            }
+          }
+        },
+        "has-values": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+          "dev": true
+        }
+      }
+    },
+    "uri-js": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "urix": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+      "dev": true
+    },
+    "use": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+      "dev": true
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "vasync": {
+      "version": "1.6.4",
+      "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz",
+      "integrity": "sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=",
+      "requires": {
+        "verror": "1.6.0"
+      },
+      "dependencies": {
+        "verror": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz",
+          "integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=",
+          "requires": {
+            "extsprintf": "1.2.0"
+          }
+        }
+      }
+    },
+    "verror": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "requires": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      }
+    },
+    "vue-eslint-parser": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz",
+      "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==",
+      "dev": true,
+      "requires": {
+        "debug": "^3.1.0",
+        "eslint-scope": "^3.7.1",
+        "eslint-visitor-keys": "^1.0.0",
+        "espree": "^3.5.2",
+        "esquery": "^1.0.0",
+        "lodash": "^4.17.4"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "5.7.3",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
+          "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
+          "dev": true
+        },
+        "acorn-jsx": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+          "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
+          "dev": true,
+          "requires": {
+            "acorn": "^3.0.4"
+          },
+          "dependencies": {
+            "acorn": {
+              "version": "3.3.0",
+              "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+              "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
+              "dev": true
+            }
+          }
+        },
+        "eslint-scope": {
+          "version": "3.7.3",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz",
+          "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
+          }
+        },
+        "espree": {
+          "version": "3.5.4",
+          "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
+          "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
+          "dev": true,
+          "requires": {
+            "acorn": "^5.5.0",
+            "acorn-jsx": "^3.0.0"
+          }
+        }
+      }
+    },
+    "which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "wide-align": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+      "requires": {
+        "string-width": "^1.0.2 || 2"
+      }
+    },
+    "wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+      "dev": true
+    },
+    "wrap-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz",
+      "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=",
+      "dev": true,
+      "requires": {
+        "string-width": "^2.1.1",
+        "strip-ansi": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
+        "string-width": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+          "dev": true,
+          "requires": {
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^4.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^3.0.0"
+          }
+        }
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "write": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+      "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+      "dev": true,
+      "requires": {
+        "mkdirp": "^0.5.1"
+      }
+    },
+    "xss": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.6.tgz",
+      "integrity": "sha512-6Q9TPBeNyoTRxgZFk5Ggaepk/4vUOYdOsIUYvLehcsIZTFjaavbVnsuAkLA5lIFuug5hw8zxcB9tm01gsjph2A==",
+      "requires": {
+        "commander": "^2.9.0",
+        "cssfilter": "0.0.10"
+      }
+    },
+    "yallist": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+      "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
+    }
+  }
+}

+ 73 - 0
.snap-meteor-1.8/package.json

@@ -0,0 +1,73 @@
+{
+  "name": "wekan",
+  "version": "v3.78.0",
+  "description": "Open-Source kanban",
+  "private": true,
+  "scripts": {
+    "lint": "eslint --cache --ext .js --ignore-path .eslintignore .",
+    "lint:eslint:fix": "eslint --ext .js --ignore-path .eslintignore --fix .",
+    "lint:staged": "lint-staged",
+    "prettify": "prettier --write '**/*.js' '**/*.jsx'",
+    "test": "npm run lint"
+  },
+  "lint-staged": {
+    "*.js": [
+      "meteor npm run prettify",
+      "meteor npm run lint:eslint:fix",
+      "git add --force"
+    ],
+    "*.jsx": [
+      "meteor npm run prettify",
+      "meteor npm run lint:eslint:fix",
+      "git add --force"
+    ],
+    "*.json": [
+      "prettier --write",
+      "git add --force"
+    ]
+  },
+  "pre-commit": "lint:staged",
+  "eslintConfig": {
+    "extends": "@meteorjs/eslint-config-meteor"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/wekan/wekan.git"
+  },
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/wekan/wekan/issues"
+  },
+  "homepage": "https://wekan.github.io",
+  "devDependencies": {
+    "eslint": "^5.16.0",
+    "eslint-config-meteor": "0.0.9",
+    "eslint-config-prettier": "^3.6.0",
+    "eslint-import-resolver-meteor": "^0.4.0",
+    "eslint-plugin-import": "^2.18.0",
+    "eslint-plugin-meteor": "^5.1.0",
+    "eslint-plugin-prettier": "^3.1.0",
+    "lint-staged": "^7.3.0",
+    "pre-commit": "^1.2.2",
+    "prettier": "^1.18.2",
+    "prettier-eslint": "^8.8.2"
+  },
+  "dependencies": {
+    "@babel/runtime": "^7.6.2",
+    "ajv": "^5.0.0",
+    "babel-runtime": "^6.26.0",
+    "bcrypt": "^3.0.2",
+    "bson": "^4.0.0",
+    "bunyan": "^1.8.12",
+    "es6-promise": "^4.2.4",
+    "gridfs-stream": "^0.5.3",
+    "ldapjs": "^1.0.2",
+    "meteor-node-stubs": "^0.4.1",
+    "mongodb": "^3.3.3",
+    "os": "^0.1.1",
+    "page": "^1.8.6",
+    "qs": "^6.8.0",
+    "source-map-support": "^0.5.12",
+    "xss": "^1.0.6"
+  }
+}

+ 47 - 65
releases/not-working-meteor-1.8-snapcraft.yaml → .snap-meteor-1.8/snapcraft.yaml

@@ -1,6 +1,6 @@
 name: wekan
-version: '0'
-version-script: git describe --dirty --tags | cut -c 2-
+version: 0
+version-script: git describe --tags |  cut -c 2-
 summary: The open-source kanban
 description: |
    Wekan is an open-source and collaborative kanban board application.
@@ -65,75 +65,59 @@ apps:
 
 parts:
     mongodb:
-        source: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.0/multiverse/binary-amd64/mongodb-org-server_4.0.11_amd64.deb
+        source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.22.tgz
         plugin: dump
-        stage-packages: [libssl-dev]
+        stage-packages: [libssl1.0.0]
         filesets:
             mongo:
                 - usr
                 - bin
                 - lib
-#        stage:
-#            - $mongo
-#        prime:
-#            - $mongo
+        stage:
+            - $mongo
+        prime:
+            - $mongo
+
     wekan:
         source: .
         plugin: nodejs
-        node-engine: 8.16.0
-#        node-packages:
-#            - node-gyp
-#            - node-pre-gyp
-#            - fibers@4.0.1
+        node-engine: 8.17.0
+        node-packages:
+            - node-gyp
+            - node-pre-gyp
+            - fibers@2.0.0
         build-packages:
             - ca-certificates
             - apt-utils
-            - bsdtar
-            - gnupg
-            - bzip2
-        ##   - python3
+            - python
+#            - python3
             - g++
-            - build-essential
-            - git
-        #    - capnproto
+            - capnproto
             - curl
-            - libcurl3
-            - php-curl
-        #    - execstack
-        #    - nodejs
-        #    - npm
-        ##   - python3-pip
-        ##   - python3-venv
+            - execstack
+            - nodejs
+            - npm
         stage-packages:
             - libfontconfig1
         override-build: |
-            set -o xtrace
             echo "Cleaning environment first"
             rm -rf ~/.meteor ~/.npm /usr/local/lib/node_modules
-            # Meteor installer doesn't work with the default tar binary, so using bsdtar while installing.
-            # https://github.com/coreos/bugs/issues/1095#issuecomment-350574389
-            cp $(which tar) $(which tar)~
-            ln -sf $(which bsdtar) $(which tar)
             # Create the OpenAPI specification
             rm -rf .build
-            mkdir .build
-            ##mkdir -p .build/python
-            ##cd .build/python
-            ##python3 -m venv env
-            ##. ./env/bin/activate
-            ##pip3 install -U setuptools wheel
-            ##git clone --depth 1 -b master https://github.com/Kronuz/esprima-python
-            ##cd esprima-python
-            ##python3 setup.py install
-            ##cd ../../..
-            ##mkdir -p ./public/api
-            ##python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml
+            #mkdir -p .build/python
+            #cd .build/python
+            #git clone --depth 1 -b master https://github.com/Kronuz/esprima-python
+            #cd esprima-python
+            #python3 setup.py install
+            #cd ../../..
+            #mkdir -p ./public/api
+            #python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml
             # we temporary need api2html and mkdirp
-            ##npm install -g --unsafe-perm api2html@0.3.0
-            ##npm install -g  --unsafe-perm mkdirp
-            ##api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml
-            ##npm uninstall -g --unsafe-perm mkdirp
-            ##npm uninstall -g --unsafe-perm api2html
+            #npm install -g api2html@0.3.0
+            #npm install -g mkdirp
+            #api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml
+            #npm uninstall -g mkdirp
+            #npm uninstall -g api2html
             # Node Fibers 100% CPU usage issue:
             # https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161
             # https://github.com/meteor/meteor/issues/9796#issuecomment-381676326
@@ -206,17 +190,12 @@ parts:
             #  git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git
             #  cd ..
             #fi
-            rm -rf package-lock.json .build
-            #meteor add standard-minifier-js --allow-superuser
-            meteor npm install --allow-superuser --unsafe-perm
+            rm -rf .build
+            meteor add standard-minifier-js --allow-superuser
+            meteor npm install --allow-superuser
+            meteor npm install --allow-superuser --save babel-runtime
             meteor build .build --directory --allow-superuser
             cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js
-            # Disable code coverage on Snap, because it crashes Wekan starting.
-            # https://github.com/wekan/wekan/issues/2533#issuecomment-513506939
-            #sed -i 's|Profile(fileInfo.path, func).apply(global, args);|//Profile(fileInfo.path, func).apply(global, args);|g' .build/bundle/programs/server/boot.js
-            #   although it did not help, it said unable to find main function.
-            # Other option to fix would be to drop indexes at start of snap:
-            # https://stackoverflow.com/questions/24241742/drop-all-indexes-from-all-collections-in-a-mongodb-database-using-the-command-li
             #Removed binary version of bcrypt because of security vulnerability that is not fixed yet.
             #https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac
             #https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c
@@ -227,18 +206,21 @@ parts:
             #cd ../../../../
             # Change to directory .build/bundle/programs/server
             cd .build/bundle/programs/server
-            npm install --unsafe-perm
+            npm install
+            npm install --allow-superuser --save babel-runtime
             #meteor npm install --save bcrypt
             # Change back to Wekan source directory
             cd ../../../..
             cp -r .build/bundle/* $SNAPCRAFT_PART_INSTALL/
             cp .build/bundle/.node_version.txt $SNAPCRAFT_PART_INSTALL/
-            # rm $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
-            #rm $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
-            # rm $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
-            # rm $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
-            # Put back the original tar
-            mv $(which tar)~ $(which tar)
+            rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/wekan
+            rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
+            rm -f $SNAPCRAFT_PART_INSTALL/programs/server/npm/node_modules/tar/lib/.mkdir.js.swp
+            rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
+            rm -f $SNAPCRAFT_PART_INSTALL/lib/node_modules/node-gyp/node_modules/tar/lib/.mkdir.js.swp
+            # Meteor 1.8.x additional .swp remove
+            rm -f $SNAPCRAFT_PART_INSTALL/programs/server/node_modules/node-pre-gyp/node_modules/tar/lib/.mkdir.js.swp
+
         organize:
             README: README.wekan
         prime:

+ 853 - 0
.snap-meteor-1.8/wekanCreator.js

@@ -0,0 +1,853 @@
+const DateString = Match.Where(function(dateAsString) {
+  check(dateAsString, String);
+  return moment(dateAsString, moment.ISO_8601).isValid();
+});
+
+export class WekanCreator {
+  constructor(data) {
+    // we log current date, to use the same timestamp for all our actions.
+    // this helps to retrieve all elements performed by the same import.
+    this._nowDate = new Date();
+    // The object creation dates, indexed by Wekan id
+    // (so we only parse actions once!)
+    this.createdAt = {
+      board: null,
+      cards: {},
+      lists: {},
+      swimlanes: {},
+    };
+    // The object creator Wekan Id, indexed by the object Wekan id
+    // (so we only parse actions once!)
+    this.createdBy = {
+      cards: {}, // only cards have a field for that
+    };
+
+    // Map of labels Wekan ID => Wekan ID
+    this.labels = {};
+    // Map of swimlanes Wekan ID => Wekan ID
+    this.swimlanes = {};
+    // Map of lists Wekan ID => Wekan ID
+    this.lists = {};
+    // Map of cards Wekan ID => Wekan ID
+    this.cards = {};
+    // Map of comments Wekan ID => Wekan ID
+    this.commentIds = {};
+    // Map of attachments Wekan ID => Wekan ID
+    this.attachmentIds = {};
+    // Map of checklists Wekan ID => Wekan ID
+    this.checklists = {};
+    // Map of checklistItems Wekan ID => Wekan ID
+    this.checklistItems = {};
+    // The comments, indexed by Wekan card id (to map when importing cards)
+    this.comments = {};
+    // Map of rules Wekan ID => Wekan ID
+    this.rules = {};
+    // the members, indexed by Wekan member id => Wekan user ID
+    this.members = data.membersMapping ? data.membersMapping : {};
+    // Map of triggers Wekan ID => Wekan ID
+    this.triggers = {};
+    // Map of actions Wekan ID => Wekan ID
+    this.actions = {};
+
+    // maps a wekanCardId to an array of wekanAttachments
+    this.attachments = {};
+  }
+
+  /**
+   * If dateString is provided,
+   * return the Date it represents.
+   * If not, will return the date when it was first called.
+   * This is useful for us, as we want all import operations to
+   * have the exact same date for easier later retrieval.
+   *
+   * @param {String} dateString a properly formatted Date
+   */
+  _now(dateString) {
+    if (dateString) {
+      return new Date(dateString);
+    }
+    if (!this._nowDate) {
+      this._nowDate = new Date();
+    }
+    return this._nowDate;
+  }
+
+  /**
+   * if wekanUserId is provided and we have a mapping,
+   * return it.
+   * Otherwise return current logged user.
+   * @param wekanUserId
+   * @private
+   */
+  _user(wekanUserId) {
+    if (wekanUserId && this.members[wekanUserId]) {
+      return this.members[wekanUserId];
+    }
+    return Meteor.userId();
+  }
+
+  checkActivities(wekanActivities) {
+    check(wekanActivities, [
+      Match.ObjectIncluding({
+        activityType: String,
+        createdAt: DateString,
+      }),
+    ]);
+    // XXX we could perform more thorough checks based on action type
+  }
+
+  checkBoard(wekanBoard) {
+    check(
+      wekanBoard,
+      Match.ObjectIncluding({
+        archived: Boolean,
+        title: String,
+        // XXX refine control by validating 'color' against a list of
+        // allowed values (is it worth the maintenance?)
+        color: String,
+        permission: Match.Where(value => {
+          return ['private', 'public'].indexOf(value) >= 0;
+        }),
+      }),
+    );
+  }
+
+  checkCards(wekanCards) {
+    check(wekanCards, [
+      Match.ObjectIncluding({
+        archived: Boolean,
+        dateLastActivity: DateString,
+        labelIds: [String],
+        title: String,
+        sort: Number,
+      }),
+    ]);
+  }
+
+  checkLabels(wekanLabels) {
+    check(wekanLabels, [
+      Match.ObjectIncluding({
+        // XXX refine control by validating 'color' against a list of allowed
+        // values (is it worth the maintenance?)
+        color: String,
+      }),
+    ]);
+  }
+
+  checkLists(wekanLists) {
+    check(wekanLists, [
+      Match.ObjectIncluding({
+        archived: Boolean,
+        title: String,
+      }),
+    ]);
+  }
+
+  checkSwimlanes(wekanSwimlanes) {
+    check(wekanSwimlanes, [
+      Match.ObjectIncluding({
+        archived: Boolean,
+        title: String,
+      }),
+    ]);
+  }
+
+  checkChecklists(wekanChecklists) {
+    check(wekanChecklists, [
+      Match.ObjectIncluding({
+        cardId: String,
+        title: String,
+      }),
+    ]);
+  }
+
+  checkChecklistItems(wekanChecklistItems) {
+    check(wekanChecklistItems, [
+      Match.ObjectIncluding({
+        cardId: String,
+        title: String,
+      }),
+    ]);
+  }
+
+  checkRules(wekanRules) {
+    check(wekanRules, [
+      Match.ObjectIncluding({
+        triggerId: String,
+        actionId: String,
+        title: String,
+      }),
+    ]);
+  }
+
+  checkTriggers(wekanTriggers) {
+    // XXX More check based on trigger type
+    check(wekanTriggers, [
+      Match.ObjectIncluding({
+        activityType: String,
+        desc: String,
+      }),
+    ]);
+  }
+
+  getMembersToMap(data) {
+    // we will work on the list itself (an ordered array of objects) when a
+    // mapping is done, we add a 'wekan' field to the object representing the
+    // imported member
+    const membersToMap = data.members;
+    const users = data.users;
+    // auto-map based on username
+    membersToMap.forEach(importedMember => {
+      importedMember.id = importedMember.userId;
+      delete importedMember.userId;
+      const user = users.filter(user => {
+        return user._id === importedMember.id;
+      })[0];
+      if (user.profile && user.profile.fullname) {
+        importedMember.fullName = user.profile.fullname;
+      }
+      importedMember.username = user.username;
+      const wekanUser = Users.findOne({ username: importedMember.username });
+      if (wekanUser) {
+        importedMember.wekanId = wekanUser._id;
+      }
+    });
+    return membersToMap;
+  }
+
+  checkActions(wekanActions) {
+    // XXX More check based on action type
+    check(wekanActions, [
+      Match.ObjectIncluding({
+        actionType: String,
+        desc: String,
+      }),
+    ]);
+  }
+
+  // You must call parseActions before calling this one.
+  createBoardAndLabels(boardToImport) {
+    const boardToCreate = {
+      archived: boardToImport.archived,
+      color: boardToImport.color,
+      // very old boards won't have a creation activity so no creation date
+      createdAt: this._now(boardToImport.createdAt),
+      labels: [],
+      members: [
+        {
+          userId: Meteor.userId(),
+          wekanId: Meteor.userId(),
+          isActive: true,
+          isAdmin: true,
+          isNoComments: false,
+          isCommentOnly: false,
+          swimlaneId: false,
+        },
+      ],
+      // Standalone Export has modifiedAt missing, adding modifiedAt to fix it
+      modifiedAt: this._now(boardToImport.modifiedAt),
+      permission: boardToImport.permission,
+      slug: getSlug(boardToImport.title) || 'board',
+      stars: 0,
+      title: boardToImport.title,
+    };
+    // now add other members
+    if (boardToImport.members) {
+      boardToImport.members.forEach(wekanMember => {
+        // do we already have it in our list?
+        if (
+          !boardToCreate.members.some(
+            member => member.wekanId === wekanMember.wekanId,
+          )
+        )
+          boardToCreate.members.push({
+            ...wekanMember,
+            userId: wekanMember.wekanId,
+          });
+      });
+    }
+    boardToImport.labels.forEach(label => {
+      const labelToCreate = {
+        _id: Random.id(6),
+        color: label.color,
+        name: label.name,
+      };
+      // We need to remember them by Wekan ID, as this is the only ref we have
+      // when importing cards.
+      this.labels[label._id] = labelToCreate._id;
+      boardToCreate.labels.push(labelToCreate);
+    });
+    const boardId = Boards.direct.insert(boardToCreate);
+    Boards.direct.update(boardId, {
+      $set: {
+        modifiedAt: this._now(),
+      },
+    });
+    // log activity
+    Activities.direct.insert({
+      activityType: 'importBoard',
+      boardId,
+      createdAt: this._now(),
+      source: {
+        id: boardToImport.id,
+        system: 'Wekan',
+      },
+      // We attribute the import to current user,
+      // not the author from the original object.
+      userId: this._user(),
+    });
+    return boardId;
+  }
+
+  /**
+   * Create the Wekan cards corresponding to the supplied Wekan cards,
+   * as well as all linked data: activities, comments, and attachments
+   * @param wekanCards
+   * @param boardId
+   * @returns {Array}
+   */
+  createCards(wekanCards, boardId) {
+    const result = [];
+    wekanCards.forEach(card => {
+      const cardToCreate = {
+        archived: card.archived,
+        boardId,
+        // very old boards won't have a creation activity so no creation date
+        createdAt: this._now(this.createdAt.cards[card._id]),
+        dateLastActivity: this._now(),
+        description: card.description,
+        listId: this.lists[card.listId],
+        swimlaneId: this.swimlanes[card.swimlaneId],
+        sort: card.sort,
+        title: card.title,
+        // we attribute the card to its creator if available
+        userId: this._user(this.createdBy.cards[card._id]),
+        isOvertime: card.isOvertime || false,
+        startAt: card.startAt ? this._now(card.startAt) : null,
+        dueAt: card.dueAt ? this._now(card.dueAt) : null,
+        spentTime: card.spentTime || null,
+      };
+      // add labels
+      if (card.labelIds) {
+        cardToCreate.labelIds = card.labelIds.map(wekanId => {
+          return this.labels[wekanId];
+        });
+      }
+      // add members {
+      if (card.members) {
+        const wekanMembers = [];
+        // we can't just map, as some members may not have been mapped
+        card.members.forEach(sourceMemberId => {
+          if (this.members[sourceMemberId]) {
+            const wekanId = this.members[sourceMemberId];
+            // we may map multiple Wekan members to the same wekan user
+            // in which case we risk adding the same user multiple times
+            if (!wekanMembers.find(wId => wId === wekanId)) {
+              wekanMembers.push(wekanId);
+            }
+          }
+          return true;
+        });
+        if (wekanMembers.length > 0) {
+          cardToCreate.members = wekanMembers;
+        }
+      }
+      // set color
+      if (card.color) {
+        cardToCreate.color = card.color;
+      }
+      // insert card
+      const cardId = Cards.direct.insert(cardToCreate);
+      // keep track of Wekan id => Wekan id
+      this.cards[card._id] = cardId;
+      // // log activity
+      // Activities.direct.insert({
+      //   activityType: 'importCard',
+      //   boardId,
+      //   cardId,
+      //   createdAt: this._now(),
+      //   listId: cardToCreate.listId,
+      //   source: {
+      //     id: card._id,
+      //     system: 'Wekan',
+      //   },
+      //   // we attribute the import to current user,
+      //   // not the author of the original card
+      //   userId: this._user(),
+      // });
+      // add comments
+      const comments = this.comments[card._id];
+      if (comments) {
+        comments.forEach(comment => {
+          const commentToCreate = {
+            boardId,
+            cardId,
+            createdAt: this._now(comment.createdAt),
+            text: comment.text,
+            // we attribute the comment to the original author, default to current user
+            userId: this._user(comment.userId),
+          };
+          // dateLastActivity will be set from activity insert, no need to
+          // update it ourselves
+          const commentId = CardComments.direct.insert(commentToCreate);
+          this.commentIds[comment._id] = commentId;
+          // Activities.direct.insert({
+          //   activityType: 'addComment',
+          //   boardId: commentToCreate.boardId,
+          //   cardId: commentToCreate.cardId,
+          //   commentId,
+          //   createdAt: this._now(commentToCreate.createdAt),
+          //   // we attribute the addComment (not the import)
+          //   // to the original author - it is needed by some UI elements.
+          //   userId: commentToCreate.userId,
+          // });
+        });
+      }
+      const attachments = this.attachments[card._id];
+      const wekanCoverId = card.coverId;
+      if (attachments) {
+        attachments.forEach(att => {
+          const file = new FS.File();
+          // Simulating file.attachData on the client generates multiple errors
+          // - HEAD returns null, which causes exception down the line
+          // - the template then tries to display the url to the attachment which causes other errors
+          // so we make it server only, and let UI catch up once it is done, forget about latency comp.
+          const self = this;
+          if (Meteor.isServer) {
+            if (att.url) {
+              file.attachData(att.url, function(error) {
+                file.boardId = boardId;
+                file.cardId = cardId;
+                file.userId = self._user(att.userId);
+                // The field source will only be used to prevent adding
+                // attachments' related activities automatically
+                file.source = 'import';
+                if (error) {
+                  throw error;
+                } else {
+                  const wekanAtt = Attachments.insert(file, () => {
+                    // we do nothing
+                  });
+                  self.attachmentIds[att._id] = wekanAtt._id;
+                  //
+                  if (wekanCoverId === att._id) {
+                    Cards.direct.update(cardId, {
+                      $set: {
+                        coverId: wekanAtt._id,
+                      },
+                    });
+                  }
+                }
+              });
+            } else if (att.file) {
+              file.attachData(
+                new Buffer(att.file, 'base64'),
+                {
+                  type: att.type,
+                },
+                error => {
+                  file.name(att.name);
+                  file.boardId = boardId;
+                  file.cardId = cardId;
+                  file.userId = self._user(att.userId);
+                  // The field source will only be used to prevent adding
+                  // attachments' related activities automatically
+                  file.source = 'import';
+                  if (error) {
+                    throw error;
+                  } else {
+                    const wekanAtt = Attachments.insert(file, () => {
+                      // we do nothing
+                    });
+                    this.attachmentIds[att._id] = wekanAtt._id;
+                    //
+                    if (wekanCoverId === att._id) {
+                      Cards.direct.update(cardId, {
+                        $set: {
+                          coverId: wekanAtt._id,
+                        },
+                      });
+                    }
+                  }
+                },
+              );
+            }
+          }
+          // todo XXX set cover - if need be
+        });
+      }
+      result.push(cardId);
+    });
+    return result;
+  }
+
+  // Create labels if they do not exist and load this.labels.
+  createLabels(wekanLabels, board) {
+    wekanLabels.forEach(label => {
+      const color = label.color;
+      const name = label.name;
+      const existingLabel = board.getLabel(name, color);
+      if (existingLabel) {
+        this.labels[label.id] = existingLabel._id;
+      } else {
+        const idLabelCreated = board.pushLabel(name, color);
+        this.labels[label.id] = idLabelCreated;
+      }
+    });
+  }
+
+  createLists(wekanLists, boardId) {
+    wekanLists.forEach((list, listIndex) => {
+      const listToCreate = {
+        archived: list.archived,
+        boardId,
+        // We are being defensing here by providing a default date (now) if the
+        // creation date wasn't found on the action log. This happen on old
+        // Wekan boards (eg from 2013) that didn't log the 'createList' action
+        // we require.
+        createdAt: this._now(this.createdAt.lists[list.id]),
+        title: list.title,
+        sort: list.sort ? list.sort : listIndex,
+      };
+      const listId = Lists.direct.insert(listToCreate);
+      Lists.direct.update(listId, {
+        $set: {
+          updatedAt: this._now(),
+        },
+      });
+      this.lists[list._id] = listId;
+      // // log activity
+      // Activities.direct.insert({
+      //   activityType: 'importList',
+      //   boardId,
+      //   createdAt: this._now(),
+      //   listId,
+      //   source: {
+      //     id: list._id,
+      //     system: 'Wekan',
+      //   },
+      //   // We attribute the import to current user,
+      //   // not the creator of the original object
+      //   userId: this._user(),
+      // });
+    });
+  }
+
+  createSwimlanes(wekanSwimlanes, boardId) {
+    wekanSwimlanes.forEach((swimlane, swimlaneIndex) => {
+      const swimlaneToCreate = {
+        archived: swimlane.archived,
+        boardId,
+        // We are being defensing here by providing a default date (now) if the
+        // creation date wasn't found on the action log. This happen on old
+        // Wekan boards (eg from 2013) that didn't log the 'createList' action
+        // we require.
+        createdAt: this._now(this.createdAt.swimlanes[swimlane._id]),
+        title: swimlane.title,
+        sort: swimlane.sort ? swimlane.sort : swimlaneIndex,
+      };
+      // set color
+      if (swimlane.color) {
+        swimlaneToCreate.color = swimlane.color;
+      }
+      const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
+      Swimlanes.direct.update(swimlaneId, {
+        $set: {
+          updatedAt: this._now(),
+        },
+      });
+      this.swimlanes[swimlane._id] = swimlaneId;
+    });
+  }
+
+  createChecklists(wekanChecklists) {
+    const result = [];
+    wekanChecklists.forEach((checklist, checklistIndex) => {
+      // Create the checklist
+      const checklistToCreate = {
+        cardId: this.cards[checklist.cardId],
+        title: checklist.title,
+        createdAt: checklist.createdAt,
+        sort: checklist.sort ? checklist.sort : checklistIndex,
+      };
+      const checklistId = Checklists.direct.insert(checklistToCreate);
+      this.checklists[checklist._id] = checklistId;
+      result.push(checklistId);
+    });
+    return result;
+  }
+
+  createTriggers(wekanTriggers, boardId) {
+    wekanTriggers.forEach(trigger => {
+      if (trigger.hasOwnProperty('labelId')) {
+        trigger.labelId = this.labels[trigger.labelId];
+      }
+      if (trigger.hasOwnProperty('memberId')) {
+        trigger.memberId = this.members[trigger.memberId];
+      }
+      trigger.boardId = boardId;
+      const oldId = trigger._id;
+      delete trigger._id;
+      this.triggers[oldId] = Triggers.direct.insert(trigger);
+    });
+  }
+
+  createActions(wekanActions, boardId) {
+    wekanActions.forEach(action => {
+      if (action.hasOwnProperty('labelId')) {
+        action.labelId = this.labels[action.labelId];
+      }
+      if (action.hasOwnProperty('memberId')) {
+        action.memberId = this.members[action.memberId];
+      }
+      action.boardId = boardId;
+      const oldId = action._id;
+      delete action._id;
+      this.actions[oldId] = Actions.direct.insert(action);
+    });
+  }
+
+  createRules(wekanRules, boardId) {
+    wekanRules.forEach(rule => {
+      // Create the rule
+      rule.boardId = boardId;
+      rule.triggerId = this.triggers[rule.triggerId];
+      rule.actionId = this.actions[rule.actionId];
+      delete rule._id;
+      Rules.direct.insert(rule);
+    });
+  }
+
+  createChecklistItems(wekanChecklistItems) {
+    wekanChecklistItems.forEach((checklistitem, checklistitemIndex) => {
+      // Create the checklistItem
+      const checklistItemTocreate = {
+        title: checklistitem.title,
+        checklistId: this.checklists[checklistitem.checklistId],
+        cardId: this.cards[checklistitem.cardId],
+        sort: checklistitem.sort ? checklistitem.sort : checklistitemIndex,
+        isFinished: checklistitem.isFinished,
+      };
+      const checklistItemId = ChecklistItems.direct.insert(
+        checklistItemTocreate,
+      );
+      this.checklistItems[checklistitem._id] = checklistItemId;
+    });
+  }
+
+  parseActivities(wekanBoard) {
+    wekanBoard.activities.forEach(activity => {
+      switch (activity.activityType) {
+        case 'addAttachment': {
+          // We have to be cautious, because the attachment could have been removed later.
+          // In that case Wekan still reports its addition, but removes its 'url' field.
+          // So we test for that
+          const wekanAttachment = wekanBoard.attachments.filter(attachment => {
+            return attachment._id === activity.attachmentId;
+          })[0];
+
+          if (typeof wekanAttachment !== 'undefined' && wekanAttachment) {
+            if (wekanAttachment.url || wekanAttachment.file) {
+              // we cannot actually create the Wekan attachment, because we don't yet
+              // have the cards to attach it to, so we store it in the instance variable.
+              const wekanCardId = activity.cardId;
+              if (!this.attachments[wekanCardId]) {
+                this.attachments[wekanCardId] = [];
+              }
+              this.attachments[wekanCardId].push(wekanAttachment);
+            }
+          }
+          break;
+        }
+        case 'addComment': {
+          const wekanComment = wekanBoard.comments.filter(comment => {
+            return comment._id === activity.commentId;
+          })[0];
+          const id = activity.cardId;
+          if (!this.comments[id]) {
+            this.comments[id] = [];
+          }
+          this.comments[id].push(wekanComment);
+          break;
+        }
+        case 'createBoard': {
+          this.createdAt.board = activity.createdAt;
+          break;
+        }
+        case 'createCard': {
+          const cardId = activity.cardId;
+          this.createdAt.cards[cardId] = activity.createdAt;
+          this.createdBy.cards[cardId] = activity.userId;
+          break;
+        }
+        case 'createList': {
+          const listId = activity.listId;
+          this.createdAt.lists[listId] = activity.createdAt;
+          break;
+        }
+        case 'createSwimlane': {
+          const swimlaneId = activity.swimlaneId;
+          this.createdAt.swimlanes[swimlaneId] = activity.createdAt;
+          break;
+        }
+      }
+    });
+  }
+
+  importActivities(activities, boardId) {
+    activities.forEach(activity => {
+      switch (activity.activityType) {
+        // Board related activities
+        // TODO: addBoardMember, removeBoardMember
+        case 'createBoard': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            type: 'board',
+            activityTypeId: boardId,
+            activityType: activity.activityType,
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // List related activities
+        // TODO: removeList, archivedList
+        case 'createList': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            type: 'list',
+            activityType: activity.activityType,
+            listId: this.lists[activity.listId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // Card related activities
+        // TODO: archivedCard, restoredCard, joinMember, unjoinMember
+        case 'createCard': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            activityType: activity.activityType,
+            listId: this.lists[activity.listId],
+            cardId: this.cards[activity.cardId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        case 'moveCard': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            oldListId: this.lists[activity.oldListId],
+            activityType: activity.activityType,
+            listId: this.lists[activity.listId],
+            cardId: this.cards[activity.cardId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // Comment related activities
+        case 'addComment': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            activityType: activity.activityType,
+            cardId: this.cards[activity.cardId],
+            commentId: this.commentIds[activity.commentId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // Attachment related activities
+        case 'addAttachment': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            type: 'card',
+            activityType: activity.activityType,
+            attachmentId: this.attachmentIds[activity.attachmentId],
+            cardId: this.cards[activity.cardId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        // Checklist related activities
+        case 'addChecklist': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            activityType: activity.activityType,
+            cardId: this.cards[activity.cardId],
+            checklistId: this.checklists[activity.checklistId],
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+        case 'addChecklistItem': {
+          Activities.direct.insert({
+            userId: this._user(activity.userId),
+            activityType: activity.activityType,
+            cardId: this.cards[activity.cardId],
+            checklistId: this.checklists[activity.checklistId],
+            checklistItemId: activity.checklistItemId.replace(
+              activity.checklistId,
+              this.checklists[activity.checklistId],
+            ),
+            boardId,
+            createdAt: this._now(activity.createdAt),
+          });
+          break;
+        }
+      }
+    });
+  }
+
+  //check(board) {
+  check() {
+    //try {
+    // check(data, {
+    //   membersMapping: Match.Optional(Object),
+    // });
+    // this.checkActivities(board.activities);
+    // this.checkBoard(board);
+    // this.checkLabels(board.labels);
+    // this.checkLists(board.lists);
+    // this.checkSwimlanes(board.swimlanes);
+    // this.checkCards(board.cards);
+    //this.checkChecklists(board.checklists);
+    // this.checkRules(board.rules);
+    // this.checkActions(board.actions);
+    //this.checkTriggers(board.triggers);
+    //this.checkChecklistItems(board.checklistItems);
+    //} catch (e) {
+    //  throw new Meteor.Error('error-json-schema');
+    // }
+  }
+
+  create(board, currentBoardId) {
+    // TODO : Make isSandstorm variable global
+    const isSandstorm =
+      Meteor.settings &&
+      Meteor.settings.public &&
+      Meteor.settings.public.sandstorm;
+    if (isSandstorm && currentBoardId) {
+      const currentBoard = Boards.findOne(currentBoardId);
+      currentBoard.archive();
+    }
+    this.parseActivities(board);
+    const boardId = this.createBoardAndLabels(board);
+    this.createLists(board.lists, boardId);
+    this.createSwimlanes(board.swimlanes, boardId);
+    this.createCards(board.cards, boardId);
+    this.createChecklists(board.checklists);
+    this.createChecklistItems(board.checklistItems);
+    this.importActivities(board.activities, boardId);
+    this.createTriggers(board.triggers, boardId);
+    this.createActions(board.actions, boardId);
+    this.createRules(board.rules, boardId);
+    // XXX add members
+    return boardId;
+  }
+}

+ 3 - 3
.travis.yml

@@ -1,10 +1,10 @@
-dist: disco
+dist: eoan
 sudo: required
 
 env:
   TRAVIS_DOCKER_COMPOSE_VERSION: 1.24.0
-  TRAVIS_NODE_VERSION: 8.16.1
-  TRAVIS_NPM_VERSION: 6.4.1
+  TRAVIS_NODE_VERSION: 12.15.0
+  TRAVIS_NPM_VERSION: latest
 
 before_install:
   - sudo apt-get update -y

+ 444 - 5
CHANGELOG.md

@@ -1,4 +1,416 @@
-# Upcoming Wekan release
+# v3.78 2020-02-12 Wekan release
+
+This release adds the following features:
+
+- [Card Settings / Show on Card: Description Title and Description Text](https://github.com/wekan/wekan/commit/e89965f6422fd95b4ad2112ae407b1dde4853510).
+  Thanks to e-stoniauk, 2020product and xet7.
+
+and fixes the following bugs:
+
+- [Remove card element grouping to create compact card layout](https://github.com/wekan/wekan/commit/e89965f6422fd95b4ad2112ae407b1dde4853510).
+  Thanks to e-stoniauk, 2020product and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.77 2020-02-10 Wekan release
+
+This release removes the following features:
+
+- [Remove hiding comments and activities](https://github.com/wekan/wekan/commit/2a54218f3f68547032bd53a04a968b233be21e15).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- Fix Copy Card Link to Clipboard button at card title did not
+  work [Part 1](https://github.com/wekan/wekan/commit/9a21b0a1c933e7f778e4e57a8258e150ccea1620)
+  and [Part2](https://github.com/wekan/wekan/commit/4467a68b97a3fbf0fbae7f05177d978f2aa80287).
+  Thanks to 2020product and xet7.
+    
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.76 2020-02-07 Wekan release
+
+This release adds the following updates:
+
+- [Use Meteor 1.9 and Node.js 12.15.0 on Snap and Docker](https://github.com/wekan/wekan/commit/8384d68a060ef8f2c202ce2fa6064c5c823d28dc).
+  This also fixes bug that exporting some boards was not possible, downloading export file failed.
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Fix Bug enable/disable Comments in Card Settings](https://github.com/wekan/wekan/issues/2923).
+  Thanks to warnt, mdurokov and xet7.
+- [Try to disable dragging Swimlanes/Lists/Cards/Checklists/Subtasks on small mobile smartphones webbrowsers,
+  and hide drag handles on mobile web](https://github.com/wekan/wekan/commit/bf78b093bad7d463ee325ad96e8b485264d4a3be).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.75 2020-02-05 Wekan release
+
+This release adds the following new features:
+
+- [Fix](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16)
+  [adding comments](https://github.com/wekan/wekan/issues/2918).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Added some working layout changes like activities using less space from https://github.com/wekan/wekan/pull/2920](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16).
+  Thanks to 2020product.
+- [Fixed Card Settings not working at Sandstorm](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16).
+  Thanks to xet7.
+- Add [Card Description title](https://github.com/wekan/wekan/issues/2918#issuecomment-582346577)
+  [back](https://github.com/wekan/wekan/commit/f22785dbcde42e425c9ead209ec224aef6e11c16).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.74 2020-02-05 Wekan release
+
+This release adds the following new features:
+
+- [For BoardAdmin, add way to hide parts of a card, at Board Settings/Card Settings/Show on Card: Received, Start, ... etc.
+  Add to card title bar Copy card to Clipboard button](https://github.com/wekan/wekan/pull/2915).
+  Thanks to 2020product and xet7.
+- [Set default to RICHER_CARD_COMMENT_EDITOR=false](https://github.com/wekan/wekan/commit/65fa2f626f503b8089e0d982901cffb3990426cb).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.73 2020-01-29 Wekan release
+
+This release adds the following new features:
+
+- [Login to Wekan with Nextcloud](https://github.com/wekan/wekan/pull/2897).
+  Thanks to bogie.
+- [Add rule action to move cards to other boards](https://github.com/wekan/wekan/pull/2899).
+  Thanks to peterverraedt.
+
+and fixes the following bugs:
+
+- [Show System Wide Announcement in one line](https://github.com/wekan/wekan/pull/2891).
+  Thanks to tsia.
+- [Fixed board export with attachment in Wekan Meteor 1.9.x version](https://github.com/wekan/wekan/pull/2898).
+  Thanks to izadpoor.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.72 2020-01-19 Sandstorm-only Wekan release
+
+This release fixes the following bugs:
+
+- Try to fix Wekan at Sandstorm.
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.71 2020-01-18 Sandstorm-only Wekan release
+
+This release fixes the following bugs:
+
+- [Try to fix Wekan at Sandstorm by using Meteor 1.8.x and Node 8.17.0 at Sandstorm](https://github.com/wekan/wekan/commit/5e5ab95410c715a4379631456fc5547c497898b0).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.70 2020-01-18 Wekan release
+
+This release fixes the following bugs:
+
+- [Add missing LD_LIBRARY_PATH to use libssl and libcurl](https://github.com/wekan/wekan/10f142a1a05acb98a175ccb0326fb0c1d3e3713f).
+  Thanks to xet7.
+- [Use Meteor 1.8.x](https://github.com/wekan/wekan/commit/55a2aa90cbbf44200e9b0b9f4bd08b6177f1bb95)
+  [on Snap](https://github.com/wekan/wekan/commit/6a01170d8696322462c4065ce0cf4a637a058975), because
+  Snap builds do not work yet for Meteor 1.9, Node 12.14.1 and MongoDB 4.2.2.
+  Docker version works with Meteor 1.9.
+  Thanks to xet7.
+- [Try to fix Node 12 Buffer() deprecation errors](https://github.com/wekan/wekan/commit/9b905c2833d54cf34d1875148075b2bf756d943a).
+  Thanks to xet7.
+- [Add Snap Meteor 1.8.x files to lint ignore files](https://github.com/wekan/wekan/commit/48f8050c25e40f737dfdd3a98923cb87cd4e77e2).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.69 2020-01-10 Wekan release
+
+This release fixes the following bugs:
+
+- [Fix docker-compose.yml to not use --smallfiles that is not supported in
+  MongoDB 4.x](https://github.com/wekan/wekan/commit/ecb76842fcbd81701afcab8db0ed106e6be0fdec).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.68 2020-01-10 Wekan release
+
+This release tries to fix the following bugs:
+
+- [Try to fix Snap by removing MongoDB option --smallfiles that is not supported
+  in MongoDB 4.x](https://github.com/wekan/wekan/commit/031df54a2e0a03dcb7a2586667e60e5bd4eef706)
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.67 2020-01-10 Wekan release
+
+This release tries to fix the following bugs:
+
+- [Try to fix Snap](https://github.com/wekan/wekan/commit/2b382b940be9af575fab4c2e955702d8cde55ae9).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.66 2020-01-10 Wekan release
+
+This release tries to fix the following bugs:
+
+- [Try to fix Snap](https://github.com/wekan/wekan/commit/39bf1e375e2962f824e6f8cfa425ea51aa4efa24).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.65 2020-01-10 Wekan release
+
+This release adds the following features:
+
+- [More keyboard shortcuts: c for archive card](https://github.com/wekan/wekan/commit/d16a601c04aeb1d3550c5c541be02a67276a34cf).
+  Thanks to xet7.
+
+and adds the following updates:
+
+- [Upgrade to Meteor 1.9, Node 12.14.1 and MongoDB 4.2.2](https://github.com/wekan/wekan/commit/785f3cf88b61f687ef5ad4a529768221d1a54c86).
+  Thanks to xet7.
+- [Add more issue repo links to GitHub issue template](https://github.com/wekan/wekan/commit/5724674e73246f4e52843a6d6906c0ecdd85cccc).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.64 2020-01-06 Wekan release
+
+This release adds the following warning for CentOS 7 users:
+
+- [WARNING: DO NOT USE SNAP ON CENTOS 7, THERE IS UPDATE BUG](https://github.com/wekan/wekan-snap/wiki/CentOS-7).
+  Thanks to andy-twosticks and xet7.
+
+and adds the following features:
+
+- [Wider sidebar](https://github.com/wekan/wekan/commit/5058233509e44916296e38fb8a6c5dd591c46d8b).
+  Thanks to vjrj.
+
+and removes the following features:
+
+- [Removed Custom HTML feature that does not work](https://github.com/wekan/wekan/commit/ddce0ada094e6450be260b4cda21fdfa09ae0133).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.63 2020-01-06 Wekan release
+
+This release fixes the following bugs:
+
+- [Fix: Unable to find Archive Card/List/Swimlane in board
+  settings](https://github.com/wekan/wekan/commit/8ce993921718f3e10c2daa5fabb145b939d789dd).
+  Thanks to neobradley and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.62 2020-01-05 Wekan release
+
+This release adds the following features:
+
+- [Add Worker role](https://github.com/wekan/wekan/issues/2788).
+  This was originally added at Wekan v3.58, reverted at Wekan v3.60 because of bugs,
+  and now after fixes added back.
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.61 2020-01-03 Wekan release
+
+This release adds the following features:
+
+- [Add more Font Awesome icons. This was originally added
+  at Wekan v3.58, removed at Wekan v3.60, and now
+  added back at Wekan v3.61](https://github.com/wekan/wekan/commit/cd253522a305523e3e36bb73313e8c4db500a717).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Fix browser javascript console errors when editing profile. This was originally added
+  at Wekan v3.58, removed at Wekan v3.60, and now added back at
+  Wekan v3.61](https://github.com/wekan/wekan/commit/cd253522a305523e3e36bb73313e8c4db500a717).
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.60 2020-01-03 Wekan release
+
+This release fixes the following bugs:
+
+- [Revert to Wekan v3.57 version of client and models directories,
+  removing Worker role temporarily, because Worker role changes
+  broke saving card](https://github.com/wekan/wekan/commit/27943796ade78ca3c503637a1340918bf06a1267).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.59 2020-01-03 Wekan release
+
+This release fixes the following bugs:
+
+- [Fix not being able to edit received date](https://github.com/wekan/wekan/commit/5376bc7b7905c0dd99fae1aeae3f63b4583a3e3f).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.58 2020-01-03 Wekan release
+
+This release adds the following features:
+
+- [Add Worker role](https://github.com/wekan/wekan/issues/2788). Thanks to xet7.
+- [Add more Font Awesome icons](https://github.com/wekan/wekan/commit/2bf004120d5a43cd3c3c060fc7c0c30d1b01f220).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Fix: k8s templates update for helm](https://github.com/wekan/wekan/pull/2867).
+  1. Upgrade mongo replica version.
+  2. Access mongo via service url.
+  3. Change the expose servicePort to numeric.
+  Thanks to jiangytcn.
+- [Fix browser console errors when editing user profile name](https://github.com/wekan/wekan/commit/2bf004120d5a43cd3c3c060fc7c0c30d1b01f220).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.57 2019-12-22 Wekan release
+
+This release adds the following features:
+
+- [Allow card and checklist API creation for authorized board members](https://github.com/wekan/wekan/pull/2854).
+  Thanks to Robert-Lebedeu.
+- [Visual difference for inactive user in Administration: strikethrough](https://github.com/wekan/wekan/commit/1f1aea87a421ca5e7931d220d10c838574208e2c).
+  Thanks to hever and xet7.
+
+and adds the following updates:
+
+- [Upgrade to Meteor 1.8.3 and Node 8.17.0. Update release scripts. Fix ldap background sync documentation part 2](https://github.com/wekan/wekan/commit/782d0b620988628f40f50f9cd824f6652cfb0dd9).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Fix: Don't add a blank space for empty custom fields on minicards](https://github.com/wekan/wekan/commit/e2a374f0aad8489a84d6de9966c281a812b5eca3).
+  Thanks to roobre and xet7.
+- [Fix: Allow to set empty card title, AssignedBy and RequestedBy](https://github.com/wekan/wekan/commit/25561946edf37351f67cf7500902dde7d9114d2f).
+  Thanks to justinr1234 and xet7.
+- [Fix comment text disappearing when clicking outside of comment text area.
+  Fix lint error](https://github.com/wekan/wekan/commit/3b3950369ce07aa9e6fc4ab1bef9fb8a4993e398).
+  Thanks to xet7.
+- [Fix ldap background sync documentation](https://github.com/wekan/wekan/pull/2855).
+  Thanks to koelle25.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.56 2019-11-21 Wekan release
+
+This release adds the following updates:
+
+- [Update to Meteor 1.8.2. Update dependencies](https://github.com/wekan/wekan/commit/38dfe0b9a71a083adc2de1a81170fea0e4a8e53f).
+  Thanks to xet7.
+- [Fix lint errors and update travis NPM version](https://github.com/wekan/wekan/commit/b0f345ba21830b033c9edcc8ee5252b280111ae7).
+  Thanks to xet7.
+- [Change base image to rolling, that is currently Ubuntu eoan
+  version](https://github.com/wekan/wekan/commit/c66cc3d4dadb15b669256530cfda89359cdb9340).
+  Thanks to xet7.
+- [It seems Ubuntu eoan package bsdtar has been renamed to
+  libarchive-tools](https://github.com/wekan/wekan/commit/c60967e935bdc0e7e9aea0a1c23178aee8a73c29).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Fix slow scroll on card detail by setting scrollInertia to 0](https://github.com/wekan/wekan/commit/599ace1db7918df41d9708d14b0351acb0f8688e).
+  Thanks to cafeoh.
+- [Fix lint errors](https://github.com/wekan/wekan/commit/788dd0a81a06efee165007a92780f9e8c2c754ac).
+  Thanks to xet7.
+- [Remove eslint option that does not work](https://github.com/wekan/wekan/commit/a06daff92e5f7cca55d1698252e3aa6526877c8b).
+  Thanks to xet7.
+- [Try to fix lint errors](https://github.com/wekan/wekan/commit/58e505f79a0617011576bdded9427b0d448d6107).
+  Thanks to xet7.
+- [Add to Snap MongoDB logging option --quiet](https://github.com/wekan/wekan/commit/c7ded515022fff2c1167ce8938405a846185a710).
+  Thanks to fmeehan and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.55 2019-11-19 Wekan release
+
+This release fixes the following bugs:
+
+- [When logged in, use database for setting, so that changes are immediate. Only on public board use cookies.
+  Comment out Collapse CSS that is not in use](https://github.com/wekan/wekan/commit/351d4767d7e93c90ac798769d6071da8730d834f).
+  Thanks to xet7.
+- [Use database when logged in. Part 2](https://github.com/wekan/wekan/commit/4786b0c18ddeb8f48525216eabebdced7159467d).
+  Thanks to xet7.
+- [Use database when logged in. Part 3](https://github.com/wekan/wekan/commit/115d23f9293cad8a93f18f75a47a8a65756f71ce).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.54 2019-11-18 Wekan release
+
+This release adds the following new features:
+
+- [New feature: Now there is popup selection of Lists/Swimlanes/Calendar/Roles](https://github.com/wekan/wekan/commit/96abe3c6914ce37d9fb44da8fda375e40ad65c9e).
+  Thanks to xet7.
+- [New feature, not set visible yet, because switching to it does not
+  work properly yet: Collapsible Swimlanes](https://github.com/wekan/wekan/issues/2804).
+  Thanks to xet7.
+
+and fixes the following bugs:
+
+- [Fix: Public board now loads correctly. When you select one of Lists/Swimlanes/Calendar view and
+  reload webbrowser page, it can change view](https://github.com/wekan/wekan/issues/2311).
+  Thanks to xet7.
+- [Fix: List sorting commented out](https://github.com/wekan/wekan/issues/2800).
+  Thanks to xet7.
+- [Fix: Errors hasHiddenMinicardText, hasShowDragHandles, showSort, hasSortBy, profile,
+  FirefoxAndroid/IE11/Vivaldi/Chromium browsers not working by using cookies instead of
+  database](https://github.com/wekan/wekan/issues/2643#issuecomment-554907955).
+  Note: Cookie changes are not always immediate, if there is no effect, you may need to
+  reload webbrowser page. This could be improved later.
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.53 2019-11-14 Wekan release
+
+This release fixes the following bugs:
+
+- [Revert list sorting change of Wekan v3.51 because it reversed alphabetical sorting of
+  lists](https://github.com/wekan/wekan/commit/ab2a721a1443b903cdbbbe275f41ffd3269012c6).
+  Thanks to Dalisay and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.52 2019-11-14 Wekan release
+
+This release fixes the following bugs:
+
+- [Add database migration for assignee](https://github.com/wekan/wekan/commit/5b41d72e8de93833e1788962427422cff62c09a2).
+  Thanks to ocdtrekkie and xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.51 2019-11-14 Wekan release
+
+This release fixes the following bugs:
+
+- [Change sorting lists to work on desktop drag handle page instead,
+  where it seems to work better](https://github.com/wekan/wekan/commit/bbc3ab3f994c5a61a4414bc64b05f5a03d259e46).
+  Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v3.50 2019-11-13 Wekan release
 
 This release adds the following new features:
 
@@ -6,7 +418,7 @@ This release adds the following new features:
   mode](https://github.com/wekan/wekan/commits/77f8b76d4e13c35ea3451622176bbb69a4d39a32).
   Thanks to whowillcare.
 - Allow user to sort Lists in Board by his own preference boardadmin can star
-  list [1](https://github.com/wekan/wekan/commit/bc2a20f04e32607f8488a9cecd815647fb43e40e), 
+  list [1](https://github.com/wekan/wekan/commit/bc2a20f04e32607f8488a9cecd815647fb43e40e),
   [2](https://github.com/wekan/wekan/commit/bc2a20f04e32607f8488a9cecd815647fb43e40e).
   Thanks to whowillcare.
 - [Allowing user to filter list in Filter function not just cards
@@ -17,6 +429,31 @@ This release adds the following new features:
 - Enhancement: [Set card times more sensible using the 'Today' button in
   datepicker](https://github.com/wekan/wekan/pull/2747).
   Thanks to liske.
+- [At card, added Assignee field like Jira, and REST API for it](https://github.com/wekan/wekan/issues/2452).
+  Parts:
+  [Add assignee](https://github.com/wekan/wekan/commit/9e1aaf163f3bd0b3c2d2aee8225d111f83b3d421),
+  [Remove Assignee. Avatar icon is at card and assignee details](https://github.com/wekan/wekan/commit/3e8f9ef1a5275a5e9b691c7e74dc73b97a43689a),
+  [When selecting new assignee (+) icon, list shows names who to add](https://github.com/wekan/wekan/commit/32ce2b51d8bff5e8851732394a8bae3c56f8b0b6),
+  [More progress](https://github.com/wekan/wekan/commit/ea823ab68fd5243c8485177e44a074be836836b8),
+  [In add assignee popup, avatars are now visible](https://github.com/wekan/wekan/commit/56efb5c41075151eeb259d99990a7e86695b2b69),
+  [Add assignee popup title](https://github.com/wekan/wekan/commit/31dbdc835d5a092b8360a4dbe93e9fbcce068855),
+  [Prevent more than one assignee](https://github.com/wekan/wekan/commit/1728298659521ee8e6fc94fedad3160030b9a2c3),
+  [When there is one selected assignee on card, don't show + button for adding more assignees, because there can only be one
+  assignee](https://github.com/wekan/wekan/commit/3cf09efb13438d66db6cf739591c679ea538d812),
+  [Now assignee is visible also at minicard](https://github.com/wekan/wekan/commit/9fd14f7ecb593d3debf5adff8f6c61adb0c3feca),
+  [Update REST API docs, there can only be one assignee in array](https://github.com/wekan/wekan/commit/de7509dc60257667192054e320b381f9dd0f0a31).
+  Thanks to xet7.
+- [More mobile drag handles, and optional desktop drag handles](https://github.com/wekan/wekan/issues/2081): In Progress.
+  Parts:
+  [Some drag handle fixes](https://github.com/wekan/wekan/commit/6a8960547729148bd3085cb469f9e93d510ed66c),
+  [Fix desktop swimlane drag handle position](https://github.com/wekan/wekan/commit/2ec15602d284122fce1a45bed352d0d4050162e2),
+  [Fix card, list and swimlane move. Allow moving cards in multiselect mode](https://github.com/wekan/wekan/commit/537a48bede250155b30ec264904ba320625bab73).
+  Thanks to xet7.
+
+and adds the following updates:
+
+- [Update Node.js to v8.16.2](https://github.com/wekan/wekan/commit/1eb3d25b40797fdab41d7dd59405cfcea81dcc61).
+  Thanks to xet7.
 
 and fixes the following bugs:
 
@@ -45,6 +482,8 @@ and fixes the following bugs:
   Thanks to jymcheong.
 - [Fixed OpenAPI docs generation](https://github.com/wekan/wekan/pull/2783).
   Thanks to bentiss.
+- [Fixed close card button not visible on mobile web](https://github.com/wekan/wekan/36b5965dd07e3f0fd90069353310739c394c220f).
+  Thanks to xet7.
 
 Thanks to above GitHub users for their contributions and translators for their translations.
 
@@ -96,7 +535,7 @@ This release adds the following new features:
   NOTIFY_DUE_DAYS_BEFORE_AND_AFTER = 2,0 it means notification will be sent on both due day and two days before.
   Thanks to whowillcare.
 - [Added modifications the help files, related to NOTIFY_DUE_DAYS_BEFORE_AND_AFTER](https://github.com/wekan/wekan/pull/2740).
-  Thanks to whowillcare. 
+  Thanks to whowillcare.
 
 and fixes the following bugs:
 
@@ -147,7 +586,7 @@ This release adds the following new features:
 
 - [More Mobile and Desktop drag handles for Swimlanes/Lists/Cards. Part 1](https://github.com/wekan/wekan/commit/ff550e91103115e7b731dd80c4588b93b2d4c64f).
   Thanks to xet7.
-    
+
 Thanks to above GitHub users for their contributions and translators for their translations.
 
 # v3.40 2019-09-11 Wekan release
@@ -556,7 +995,7 @@ This release fixes the following bugs:
 
 - [Add missing dependencies back and revert deleting phantomjs](https://github.com/wekan/wekan/commit/32e9aa0ddaf1b015825b8c62ad17ed74b449e4b1).
   Thanks to whowillcare and xet7.
-    
+
 Thanks to above GitHub users for their contributions and translators for their translations.
 
 # v3.09 2019-08-07 Wekan release

+ 5 - 5
Dockerfile

@@ -1,13 +1,13 @@
-FROM ubuntu:disco
+FROM ubuntu:rolling
 LABEL maintainer="wekan"
 
 # Set the environment variables (defaults where required)
 # DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303
 # ENV BUILD_DEPS="paxctl"
-ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates" \
+ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \
     DEBUG=false \
-    NODE_VERSION=v8.16.1 \
-    METEOR_RELEASE=1.8.1 \
+    NODE_VERSION=v12.15.0 \
+    METEOR_RELEASE=1.9.0 \
     USE_EDGE=false \
     METEOR_EDGE=1.5-beta.17 \
     NPM_VERSION=latest \
@@ -21,7 +21,7 @@ ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 g++ build-essential
     ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3 \
     ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \
     ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \
-    RICHER_CARD_COMMENT_EDITOR=true \
+    RICHER_CARD_COMMENT_EDITOR=false \
     CARD_OPENED_WEBHOOK_ENABLED=false \
     ATTACHMENTS_STORE_PATH="" \
     MAX_IMAGE_PIXEL="" \

+ 1 - 1
Stackerfile.yml

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

+ 3 - 3
client/components/activities/activities.jade

@@ -201,20 +201,20 @@ template(name="cardActivities")
           .activity-checklist(href="{{ card.absoluteUrl }}")
             +viewer
               = checklistItem.title
-        
+
         if(currentData.timeKey)
           | {{{_ activityType }}}
           = ' '
           i(title=currentData.timeValue).activity-meta {{ moment currentData.timeValue 'LLL' }}
           if (currentData.timeOldValue)
-              = ' '                                                                                    
+              = ' '
               | {{{_ "previous_as" }}}
               = ' '
               i(title=currentData.timeOldValue).activity-meta {{ moment currentData.timeOldValue 'LLL' }}
           = ' @'
         else if(currentData.timeValue)
           | {{{_ activityType currentData.timeValue}}}
-        
+
 
         if($eq activityType 'deleteComment')
           | {{{_ 'activity-deleteComment' currentData.commentId}}}.

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

@@ -9,7 +9,7 @@
   clear: both
 
   .activity
-    margin: 10px 0
+    margin: 0.5px 0
     display: flex
 
     .member

+ 0 - 3
client/components/activities/comments.js

@@ -16,9 +16,6 @@ BlazeComponent.extendComponent({
   events() {
     return [
       {
-        'click .js-new-comment:not(.focus)'() {
-          commentFormIsOpen.set(true);
-        },
         'submit .js-new-comment-form'(evt) {
           const input = this.getInput();
           const text = input.val().trim();

+ 20 - 0
client/components/activities/comments.styl

@@ -46,3 +46,23 @@
 
     &:is-open
       cursor: auto
+
+.comment-item
+  background-color: #fff
+  border: 0
+  box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
+  color: #8c8c8c
+  height: 36px
+  margin: 4px 4px 6px 0
+  width: 92%
+
+  &:hover
+    background: darken(white, 12%)
+
+  &.add-comment
+    display: flex
+    margin: 5px
+
+    a
+      display: block
+      margin: auto

+ 52 - 13
client/components/boards/boardBody.js

@@ -1,3 +1,5 @@
+import { Cookies } from 'meteor/ostrio:cookies';
+const cookies = new Cookies();
 const subManager = new SubsManager();
 const { calculateIndex, enableClickOnTouch } = Utils;
 const swimlaneWhileSortingHeight = 150;
@@ -89,7 +91,6 @@ BlazeComponent.extendComponent({
         helper.append(list.clone());
         return helper;
       },
-      handle: '.js-swimlane-header-handle',
       items: '.swimlane:not(.placeholder)',
       placeholder: 'swimlane placeholder',
       distance: 7,
@@ -193,6 +194,32 @@ BlazeComponent.extendComponent({
     // ugly touch event hotfix
     enableClickOnTouch('.js-swimlane:not(.placeholder)');
 
+    this.autorun(() => {
+      let showDesktopDragHandles = false;
+      currentUser = Meteor.user();
+      if (currentUser) {
+        showDesktopDragHandles = (currentUser.profile || {})
+          .showDesktopDragHandles;
+      } else if (cookies.has('showDesktopDragHandles')) {
+        showDesktopDragHandles = true;
+      } else {
+        showDesktopDragHandles = false;
+      }
+      if (!Utils.isMiniScreen() && showDesktopDragHandles) {
+        $swimlanesDom.sortable({
+          handle: '.js-swimlane-header-handle',
+        });
+      } else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
+        $swimlanesDom.sortable({
+          handle: '.swimlane-header',
+        });
+      }
+
+      // Disable drag-dropping if the current user is not a board member or is miniscreen
+      $swimlanesDom.sortable('option', 'disabled', !userIsMember());
+      $swimlanesDom.sortable('option', 'disabled', Utils.isMiniScreen());
+    });
+
     function userIsMember() {
       return (
         Meteor.user() &&
@@ -210,21 +237,30 @@ BlazeComponent.extendComponent({
   },
 
   isViewSwimlanes() {
-    const currentUser = Meteor.user();
-    if (!currentUser) return false;
-    return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).boardView === 'board-view-swimlanes';
+    } else {
+      return cookies.get('boardView') === 'board-view-swimlanes';
+    }
   },
 
   isViewLists() {
-    const currentUser = Meteor.user();
-    if (!currentUser) return true;
-    return (currentUser.profile || {}).boardView === 'board-view-lists';
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).boardView === 'board-view-lists';
+    } else {
+      return cookies.get('boardView') === 'board-view-lists';
+    }
   },
 
   isViewCalendar() {
-    const currentUser = Meteor.user();
-    if (!currentUser) return false;
-    return (currentUser.profile || {}).boardView === 'board-view-cal';
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).boardView === 'board-view-cal';
+    } else {
+      return cookies.get('boardView') === 'board-view-cal';
+    }
   },
 
   openNewListForm() {
@@ -381,8 +417,11 @@ BlazeComponent.extendComponent({
     };
   },
   isViewCalendar() {
-    const currentUser = Meteor.user();
-    if (!currentUser) return false;
-    return (currentUser.profile || {}).boardView === 'board-view-cal';
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).boardView === 'board-view-cal';
+    } else {
+      return cookies.get('boardView') === 'board-view-cal';
+    }
   },
 }).register('calendarView');

+ 64 - 28
client/components/boards/boardHeader.jade

@@ -77,10 +77,11 @@ template(name="boardHeaderBar")
             i.fa.fa-archive
             span {{_ 'archives'}}
 
-      if showSort
-       a.board-header-btn.js-open-sort-view(title="{{_ 'sort-desc'}}")
-        i.fa(class="{{directionClass}}")
-        span {{_ 'sort'}}{{_ listSortShortDesc}}
+      //if showSort
+      //  a.board-header-btn.js-open-sort-view(title="{{_ 'sort-desc'}}")
+      //    i.fa(class="{{directionClass}}")
+      //    span {{_ 'sort'}}{{_ listSortShortDesc}}
+
       a.board-header-btn.js-open-filter-view(
           title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}"
           class="{{#if Filter.isActive}}emphasis{{/if}}")
@@ -89,15 +90,6 @@ template(name="boardHeaderBar")
         if Filter.isActive
           a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}")
             i.fa.fa-times-thin
-            
-      if currentUser.isAdmin
-        a.board-header-btn.js-open-rules-view(title="{{_ 'rules'}}")
-          i.fa.fa-magic
-          span {{_ 'rules'}}
-      else if currentUser.isBoardAdmin
-        a.board-header-btn.js-open-rules-view(title="{{_ 'rules'}}")
-          i.fa.fa-magic
-          span {{_ 'rules'}}
 
       a.board-header-btn.js-open-search-view(title="{{_ 'search'}}")
         i.fa.fa-search
@@ -106,8 +98,14 @@ template(name="boardHeaderBar")
       unless currentBoard.isTemplatesBoard
         a.board-header-btn.js-toggle-board-view(
           title="{{_ 'board-view'}}")
-          i.fa.fa-th-large
-          span {{#if currentUser.profile.boardView}}{{_ currentUser.profile.boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}}
+          i.fa.fa-caret-down
+          if $eq boardView 'board-view-lists'
+            i.fa.fa-trello
+          if $eq boardView 'board-view-swimlanes'
+            i.fa.fa-th-large
+          if $eq boardView 'board-view-cal'
+            i.fa.fa-calendar
+          span {{#if boardView}}{{_ boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}}
 
       if canModifyBoard
         a.board-header-btn.js-multiselection-activate(
@@ -172,6 +170,44 @@ template(name="boardChangeWatchPopup")
             i.fa.fa-check
           span.sub-name {{_ 'muted-info'}}
 
+template(name="boardChangeViewPopup")
+  ul.pop-over-list
+    li
+      with "board-view-lists"
+        a.js-open-lists-view
+          i.fa.fa-trello.colorful
+          | {{_ 'board-view-lists'}}
+          if $eq Utils.boardView "board-view-lists"
+            i.fa.fa-check
+    li
+      with "board-view-swimlanes"
+        a.js-open-swimlanes-view
+          i.fa.fa-th-large.colorful
+          | {{_ 'board-view-swimlanes'}}
+          if $eq Utils.boardView "board-view-swimlanes"
+            i.fa.fa-check
+    li
+      with "board-view-cal"
+        a.js-open-cal-view
+          i.fa.fa-calendar.colorful
+          | {{_ 'board-view-cal'}}
+          if $eq Utils.boardView "board-view-cal"
+            i.fa.fa-check
+    if currentUser.isAdmin
+      hr
+      li
+        with "board-view-rules"
+          a.js-open-rules-view(title="{{_ 'rules'}}")
+            i.fa.fa-magic
+            | {{_ 'rules'}}
+    else if currentUser.isBoardAdmin
+      hr
+      li
+        with "board-view-rules"
+          a.js-open-rules-view(title="{{_ 'rules'}}")
+            i.fa.fa-magic
+            | {{_ 'rules'}}
+
 template(name="createBoard")
   form
     label
@@ -198,19 +234,19 @@ template(name="createBoard")
       | /
       a.js-board-template {{_ 'template'}}
 
-template(name="listsortPopup")
-  h2
-   | {{_ 'list-sort-by'}}
-  hr
-  ul.pop-over-list
-    each value in allowedSortValues
-     li
-      a.js-sort-by(name="{{value.name}}")
-        if $eq sortby value.name
-           i(class="fa {{Direction}}") 
-        | {{_ value.label }}{{_ value.shortLabel}}
-        if $eq sortby value.name
-           i(class="fa fa-check")
+//template(name="listsortPopup")
+//  h2
+//   | {{_ 'list-sort-by'}}
+//  hr
+//  ul.pop-over-list
+//    each value in allowedSortValues
+//     li
+//      a.js-sort-by(name="{{value.name}}")
+//        if $eq sortby value.name
+//           i(class="fa {{Direction}}")
+//        | {{_ value.label }}{{_ value.shortLabel}}
+//        if $eq sortby value.name
+//           i(class="fa fa-check")
 
 template(name="boardChangeTitlePopup")
   form

+ 32 - 21
client/components/boards/boardHeader.js

@@ -1,5 +1,7 @@
+/*
 const DOWNCLS = 'fa-sort-down';
 const UPCLS = 'fa-sort-up';
+*/
 Template.boardMenuPopup.events({
   'click .js-rename-board': Popup.open('boardChangeTitle'),
   'click .js-custom-fields'() {
@@ -28,6 +30,7 @@ Template.boardMenuPopup.events({
   'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
   'click .js-import-board': Popup.open('chooseBoardSource'),
   'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
+  'click .js-card-settings': Popup.open('boardCardSettings'),
 });
 
 Template.boardMenuPopup.helpers({
@@ -82,6 +85,7 @@ BlazeComponent.extendComponent({
     const currentBoard = Boards.findOne(Session.get('currentBoard'));
     return currentBoard && currentBoard.stars >= 2;
   },
+  /*
   showSort() {
     return Meteor.user().hasSortBy();
   },
@@ -101,6 +105,7 @@ BlazeComponent.extendComponent({
   listSortShortDesc() {
     return `list-label-short-${this.currentListSortBy()}`;
   },
+  */
   events() {
     return [
       {
@@ -114,30 +119,14 @@ BlazeComponent.extendComponent({
         'click .js-open-archived-board'() {
           Modal.open('archivedBoards');
         },
-        'click .js-toggle-board-view'() {
-          const currentUser = Meteor.user();
-          if (
-            (currentUser.profile || {}).boardView === 'board-view-swimlanes'
-          ) {
-            currentUser.setBoardView('board-view-cal');
-          } else if (
-            (currentUser.profile || {}).boardView === 'board-view-lists'
-          ) {
-            currentUser.setBoardView('board-view-swimlanes');
-          } else if (
-            (currentUser.profile || {}).boardView === 'board-view-cal'
-          ) {
-            currentUser.setBoardView('board-view-lists');
-          } else {
-            currentUser.setBoardView('board-view-swimlanes');
-          }
-        },
+        'click .js-toggle-board-view': Popup.open('boardChangeView'),
         'click .js-toggle-sidebar'() {
           Sidebar.toggle();
         },
         'click .js-open-filter-view'() {
           Sidebar.setView('filter');
         },
+        /*
         'click .js-open-sort-view'(evt) {
           const target = evt.target;
           if (target.tagName === 'I') {
@@ -148,6 +137,7 @@ BlazeComponent.extendComponent({
             Popup.open('listsort')(evt);
           }
         },
+        */
         'click .js-filter-reset'(event) {
           event.stopPropagation();
           Sidebar.setView();
@@ -156,9 +146,6 @@ BlazeComponent.extendComponent({
         'click .js-open-search-view'() {
           Sidebar.setView('search');
         },
-        'click .js-open-rules-view'() {
-          Modal.openWide('rulesMain');
-        },
         'click .js-multiselection-activate'() {
           const currentCard = Session.get('currentCard');
           MultiSelection.activate();
@@ -186,6 +173,28 @@ Template.boardHeaderBar.helpers({
       !Meteor.user().isCommentOnly()
     );
   },
+  boardView() {
+    return Utils.boardView();
+  },
+});
+
+Template.boardChangeViewPopup.events({
+  'click .js-open-lists-view'() {
+    Utils.setBoardView('board-view-lists');
+    Popup.close();
+  },
+  'click .js-open-swimlanes-view'() {
+    Utils.setBoardView('board-view-swimlanes');
+    Popup.close();
+  },
+  'click .js-open-cal-view'() {
+    Utils.setBoardView('board-view-cal');
+    Popup.close();
+  },
+  'click .js-open-rules-view'() {
+    Modal.openWide('rulesMain');
+    Popup.close();
+  },
 });
 
 const CreateBoard = BlazeComponent.extendComponent({
@@ -308,6 +317,7 @@ BlazeComponent.extendComponent({
   },
 }).register('boardChangeWatchPopup');
 
+/*
 BlazeComponent.extendComponent({
   onCreated() {
     //this.sortBy = new ReactiveVar();
@@ -377,3 +387,4 @@ BlazeComponent.extendComponent({
     ];
   },
 }).register('listsortPopup');
+*/

+ 16 - 13
client/components/cards/attachments.jade

@@ -45,19 +45,22 @@ template(name="attachmentsGalery")
               | {{_ 'download'}}
             if currentUser.isBoardMember
               unless currentUser.isCommentOnly
-                if isImage
-                  a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}")
-                    i.fa.fa-thumb-tack
-                    if($eq ../coverId _id)
-                      | {{_ 'remove-cover'}}
-                    else
-                      | {{_ 'add-cover'}}
-                a.js-confirm-delete
-                  i.fa.fa-close
-                  | {{_ 'delete'}}
+                unless currentUser.isWorker
+                  if isImage
+                    a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}")
+                      i.fa.fa-thumb-tack
+                      if($eq ../coverId _id)
+                        | {{_ 'remove-cover'}}
+                      else
+                        | {{_ 'add-cover'}}
+                  a.js-confirm-delete
+                    i.fa.fa-close
+                    | {{_ 'delete'}}
 
     if currentUser.isBoardMember
       unless currentUser.isCommentOnly
-        li.attachment-item.add-attachment
-          a.js-add-attachment {{_ 'add-attachment' }}
-
+        unless currentUser.isWorker
+          //li.attachment-item.add-attachment
+          a.js-add-attachment
+            i.fa.fa-paperclip
+            | {{_ 'add-attachment' }}

+ 2 - 1
client/components/cards/cardDate.js

@@ -97,7 +97,8 @@ Template.dateBadge.helpers({
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 });

+ 301 - 174
client/components/cards/cardDetails.jade

@@ -4,15 +4,27 @@ template(name="cardDetails")
       +inlinedForm(classNames="js-card-details-title")
         +editCardTitleForm
       else
-        a.fa.fa-times-thin.close-card-details.js-close-card-details
-        if currentUser.isBoardMember
-          a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
+        unless isMiniScreen
+          a.fa.fa-times-thin.close-card-details.js-close-card-details
+          if currentUser.isBoardMember
+            a.fa.fa-navicon.card-details-menu.js-open-card-details-menu
+            input.inline-input(type="text" id="cardURL_copy" value="{{ absoluteUrl }}")
+            a.fa.fa-link.card-copy-button.js-copy-link(
+              class="fa-link"
+              title="{{_ 'copy-card-link-to-clipboard'}}"
+              value="{{ absoluteUrl }}"
+            )
+        if isMiniScreen
+          a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details
+          if currentUser.isBoardMember
+            a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu
+            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}}")
             +viewer
               = getTitle
-              if isWatching
-                i.fa.fa-eye.card-details-watch
+            if isWatching
+              i.card-details-watch.fa.fa-eye
         .card-details-path
           each parentList
             | &nbsp; &gt; &nbsp;
@@ -31,70 +43,105 @@ template(name="cardDetails")
         p.warning {{_ 'card-archived'}}
 
     .card-details-items
-      .card-details-item.card-details-item-received
-        h3.card-details-item-title {{_ 'card-received'}}
-        if getReceived
-          +cardReceivedDate
-        else
-          if canModifyCard
-            a.js-received-date {{_ 'add'}}
-
-      .card-details-item.card-details-item-start
-        h3.card-details-item-title {{_ 'card-start'}}
-        if getStart
-          +cardStartDate
-        else
-          if canModifyCard
-            a.js-start-date {{_ 'add'}}
-
-      .card-details-item.card-details-item-due
-        h3.card-details-item-title {{_ 'card-due'}}
-        if getDue
-          +cardDueDate
-        else
+      if currentBoard.allowsReceivedDate
+        .card-details-item.card-details-item-received
+          h3
+            i.fa.fa-sign-out
+            card-details-item-title {{_ 'card-received'}}
+          if getReceived
+            +cardReceivedDate
+          else
+            if canModifyCard
+              unless currentUser.isWorker
+                a.card-label.add-label.js-received-date
+                  i.fa.fa-plus
+
+      if currentBoard.allowsStartDate
+        .card-details-item.card-details-item-start
+          h3
+            i.fa.fa-hourglass-start
+            card-details-item-title {{_ 'card-start'}}
+          if getStart
+            +cardStartDate
+          else
+            if canModifyCard
+              unless currentUser.isWorker
+                a.card-label.add-label.js-start-date
+                  i.fa.fa-plus
+
+      if currentBoard.allowsDueDate
+        .card-details-item.card-details-item-due
+          h3
+            i.fa.fa-sign-in
+            card-details-item-title {{_ 'card-due'}}
+          if getDue
+            +cardDueDate
+          else
+            if canModifyCard
+              unless currentUser.isWorker
+                a.card-label.add-label.js-due-date
+                  i.fa.fa-plus
+
+      if currentBoard.allowsEndDate
+        .card-details-item.card-details-item-end
+          h3
+            i.fa.fa-hourglass-end
+            card-details-item-title {{_ 'card-end'}}
+          if getEnd
+            +cardEndDate
+          else
+            if canModifyCard
+              unless currentUser.isWorker
+                a.card-label.add-label.js-end-date
+                  i.fa.fa-plus
+
+      //.card-details-items
+      if currentBoard.allowsMembers
+        .card-details-item.card-details-item-members
+          h3
+            i.fa.fa-users
+            card-details-item-title {{_ 'members'}}
+          each getMembers
+            +userAvatar(userId=this cardId=../_id)
+            | {{! XXX Hack to hide syntaxic coloration /// }}
           if canModifyCard
-            a.js-due-date {{_ 'add'}}
-
-      .card-details-item.card-details-item-end
-        h3.card-details-item-title {{_ 'card-end'}}
-        if getEnd
-          +cardEndDate
-        else
+            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
+            i.fa.fa-user
+            card-details-item-title {{_ 'assignee'}}
+          each getAssignees
+            +userAvatarAssignee(userId=this cardId=../_id)
+            | {{! XXX Hack to hide syntaxic coloration /// }}
           if canModifyCard
-            a.js-end-date {{_ 'add'}}
-
-    .card-details-items
-      .card-details-item.card-details-item-members
-        h3.card-details-item-title {{_ 'members'}}
-        each getMembers
-          +userAvatar(userId=this cardId=../_id)
-          | {{! XXX Hack to hide syntaxic coloration /// }}
-        if canModifyCard
-          a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
-            i.fa.fa-plus
-
-      .card-details-item.card-details-item-assignees
-        h3.card-details-item-title {{_ 'assignee'}}
-        each getAssignees
-          +userAvatarAssignee(userId=this cardId=../_id)
-          | {{! XXX Hack to hide syntaxic coloration /// }}
-        if canModifyCard
-          unless assigneeSelected
             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
+
+      if currentBoard.allowsLabels
+        .card-details-item.card-details-item-labels
+          h3
+            i.fa.fa-tags
+            card-details-item-title {{_ '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-item.card-details-item-labels
-        h3.card-details-item-title {{_ '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
-          a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
-            i.fa.fa-plus
-
-    .card-details-items
+      //.card-details-items
       each customFieldsWD
         .card-details-item.card-details-item-customfield
           h3.card-details-item-title
@@ -102,7 +149,7 @@ template(name="cardDetails")
               = definition.name
           +cardCustomField
 
-    .card-details-items
+      //.card-details-items
       if getSpentTime
         .card-details-item.card-details-item-spent
           if getIsOvertime
@@ -111,84 +158,103 @@ template(name="cardDetails")
             h3.card-details-item-title {{_ 'spent-time-hours'}}
           +cardSpentTime
 
-    //- XXX We should use "editable" to avoid repetiting ourselves
-    if canModifyCard
-      h3.card-details-item-title {{_ 'description'}}
-      +inlinedCardDescription(classNames="card-description js-card-description")
-        +editor(autofocus=true)
-          | {{getUnsavedValue 'cardDescription' _id getDescription}}
-        .edit-controls.clearfix
-          button.primary(type="submit") {{_ 'save'}}
-          a.fa.fa-times-thin.js-close-inlined-form
-      else
-        a.js-open-inlined-form
-          if getDescription
+      //.card-details-items
+      if currentBoard.allowsRequestedBy
+        .card-details-item.card-details-item-name
+          h3
+            i.fa.fa-shopping-cart
+            card-details-item-title {{_ '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
-              = 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
-      h3.card-details-item-title {{_ 'description'}}
-      +viewer
-        = getDescription
+              = getRequestedBy
 
-    .card-details-items
-      .card-details-item.card-details-item-name
-        h3.card-details-item-title {{_ 'requested-by'}}
-        if canModifyCard
-          +inlinedForm(classNames="js-card-details-requester")
-            +editCardRequesterForm
-          else
-            a.js-open-inlined-form
-              if getRequestedBy
-                +viewer
-                  = getRequestedBy
-              else
-                | {{_ 'add'}}
-        else if getRequestedBy
-          +viewer
-            = getRequestedBy
-
-      .card-details-item.card-details-item-name
-        h3.card-details-item-title {{_ 'assigned-by'}}
-        if canModifyCard
-          +inlinedForm(classNames="js-card-details-assigner")
-            +editCardAssignerForm
-          else
-            a.js-open-inlined-form
-              if getAssignedBy
-                +viewer
-                  = getAssignedBy
+      if currentBoard.allowsAssignedBy
+        .card-details-item.card-details-item-name
+          h3
+            i.fa.fa-user-plus
+            card-details-item-title {{_ 'assigned-by'}}
+          if canModifyCard
+            unless currentUser.isWorker
+              +inlinedForm(classNames="js-card-details-assigner")
+                +editCardAssignerForm
               else
-                | {{_ 'add'}}
-        else if getRequestedBy
-          +viewer
-            = getAssignedBy
-
-    hr
-    +checklists(cardId = _id)
-
-    if currentBoard.allowsSubtasks
-      hr
-      +subtasks(cardId = _id)
-
-    hr
-    h3
-      i.fa.fa-paperclip
-      | {{_ 'attachments'}}
+                a.js-open-inlined-form
+                  if getAssignedBy
+                    +viewer
+                      = getAssignedBy
+                  else
+                    | {{_ 'add'}}
+          else if getRequestedBy
+            +viewer
+              = getAssignedBy
 
-    +attachmentsGalery
+    //- XXX We should use "editable" to avoid repetiting ourselves
+    if canModifyCard
+      unless currentUser.isWorker
+        if currentBoard.allowsDescriptionTitle
+          h3
+            i.fa.fa-align-left
+            card-details-item-title {{_ 'description'}}
+        if currentBoard.allowsDescriptionText
+          +inlinedCardDescription(classNames="card-description js-card-description")
+            +editor(autofocus=true)
+              | {{getUnsavedValue 'cardDescription' _id getDescription}}
+            .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
+        h3.card-details-item-title {{_ 'description'}}
+      if currentBoard.allowsDescriptionText
+        +viewer
+          = getDescription
+
+    .card-checklist-attachmentGalerys
+      .card-checklist-attachmentGalery.card-checklists
+        if currentBoard.allowsChecklists
+          +checklists(cardId = _id)
+        if currentBoard.allowsSubtasks
+          hr
+          +subtasks(cardId = _id)
+      if currentBoard.allowsAttachments
+        //- hr
+        //- h3
+        //- i.fa.fa-paperclip
+        //- | {{_ 'attachments'}}
+        .card-checklist-attachmentGalery.card-attachmentGalery
+          +attachmentsGalery
 
     hr
     unless currentUser.isNoComments
       .activity-title
-        h3 {{ _ 'activity'}}
+        h3
+          i.fa.fa-history
+          | {{ _ 'activity'}}
         if currentUser.isBoardMember
           .material-toggle-switch
             span.toggle-switch-title {{_ 'hide-system-messages'}}
@@ -197,9 +263,10 @@ template(name="cardDetails")
             else
               input.toggle-switch(type="checkbox" id="toggleButton")
             label.toggle-label(for="toggleButton")
-    if currentUser.isBoardMember
-      unless currentUser.isNoComments
-        +commentForm
+    if currentBoard.allowsComments
+      if currentUser.isBoardMember
+        unless currentUser.isNoComments
+          +commentForm
     unless currentUser.isNoComments
       if isLoaded.get
         if isLinkedCard
@@ -230,32 +297,79 @@ template(name="editCardAssignerForm")
 
 template(name="cardDetailsActionsPopup")
   ul.pop-over-list
-    li: a.js-toggle-watch-card {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}}
+    li
+      a.js-toggle-watch-card
+        if isWatching
+          i.fa.fa-eye
+          |  {{_ 'unwatch'}}
+        else
+          i.fa.fa-eye-slash
+          |  {{_ 'watch'}}
   if canModifyCard
-    hr
-    ul.pop-over-list
-      //li: a.js-members {{_ 'card-edit-members'}}
-      //li: a.js-labels {{_ 'card-edit-labels'}}
-      //li: a.js-attachments {{_ 'card-edit-attachments'}}
-      li: a.js-custom-fields {{_ 'card-edit-custom-fields'}}
-      //li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
-      //li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
-      //li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
-      //li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
-      li: a.js-spent-time {{_ 'editCardSpentTimePopup-title'}}
-      li: a.js-set-card-color {{_ 'setCardColorPopup-title'}}
-    hr
-    ul.pop-over-list
-      li: a.js-move-card-to-top {{_ 'moveCardToTop-title'}}
-      li: a.js-move-card-to-bottom {{_ 'moveCardToBottom-title'}}
-    hr
+    unless currentUser.isWorker
+      hr
+      ul.pop-over-list
+        //li: a.js-members {{_ 'card-edit-members'}}
+        //li: a.js-labels {{_ 'card-edit-labels'}}
+        //li: a.js-attachments {{_ 'card-edit-attachments'}}
+        li
+          a.js-custom-fields
+            i.fa.fa-list-alt
+            | {{_ 'card-edit-custom-fields'}}
+        //li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
+        //li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
+        //li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
+        //li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
+        li
+          a.js-spent-time
+            i.fa.fa-clock-o
+            | {{_ 'editCardSpentTimePopup-title'}}
+        li
+          a.js-set-card-color
+            i.fa.fa-paint-brush
+            | {{_ 'setCardColorPopup-title'}}
+      hr
     ul.pop-over-list
-      li: a.js-move-card {{_ 'moveCardPopup-title'}}
-      li: a.js-copy-card {{_ 'copyCardPopup-title'}}
-      li: a.js-copy-checklist-cards {{_ 'copyChecklistToManyCardsPopup-title'}}
+      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'}}
+    unless currentUser.isWorker
+      hr
+      ul.pop-over-list
+        li
+          a.js-move-card
+            i.fa.fa-arrow-right
+            | {{_ 'moveCardPopup-title'}}
+        li
+          a.js-copy-card
+            i.fa.fa-copy
+            | {{_ 'copyCardPopup-title'}}
+      hr
+      ul.pop-over-list
+        li
+          a.js-copy-checklist-cards
+            i.fa.fa-list
+            i.fa.fa-copy
+            | {{_ 'copyChecklistToManyCardsPopup-title'}}
       unless archived
-        li: a.js-archive {{_ 'archive-card'}}
-      li: a.js-more {{_ 'cardMorePopup-title'}}
+        hr
+        ul.pop-over-list
+          li
+            a.js-archive
+              i.fa.fa-arrow-right
+              i.fa.fa-archive
+              | {{_ 'archive-card'}}
+      hr
+      ul.pop-over-list
+        li
+          a.js-more
+            i.fa.fa-link
+            | {{_ 'cardMorePopup-title'}}
 
 template(name="moveCardPopup")
   +boardsAndLists
@@ -307,16 +421,27 @@ template(name="cardMembersPopup")
             i.fa.fa-check
 
 template(name="cardAssigneesPopup")
-  ul.pop-over-list.js-card-assignee-list
-    each board.activeMembers
-      li.item(class="{{#if isCardAssignee}}active{{/if}}")
-        a.name.js-select-assignee(href="#")
-          +userAvatar(userId=user._id)
-          span.full-name
-            = user.profile.fullname
-            | (<span class="username">{{ user.username }}</span>)
-          if isCardAssignee
-            i.fa.fa-check
+  unless currentUser.isWorker
+    ul.pop-over-list.js-card-assignee-list
+      each board.activeMembers
+        li.item(class="{{#if isCardAssignee}}active{{/if}}")
+          a.name.js-select-assignee(href="#")
+            +userAvatar(userId=user._id)
+            span.full-name
+              = user.profile.fullname
+              | (<span class="username">{{ user.username }}</span>)
+            if isCardAssignee
+              i.fa.fa-check
+  if currentUser.isWorker
+    ul.pop-over-list.js-card-assignee-list
+        li.item(class="{{#if currentUser.isCardAssignee}}active{{/if}}")
+          a.name.js-select-assignee(href="#")
+            +userAvatar(userId=currentUser._id)
+            span.full-name
+              = currentUser.profile.fullname
+              | (<span class="username">{{ currentUser.username }}</span>)
+            if currentUser.isCardAssignee
+              i.fa.fa-check
 
 template(name="userAvatarAssignee")
   a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})")
@@ -344,11 +469,13 @@ template(name="cardAssigneePopup")
         p.quiet @{{ user.username }}
     ul.pop-over-list
       if currentUser.isNotCommentOnly
+        unless currentUser.isWorker
           li: a.js-remove-assignee {{_ 'remove-member-from-card'}}
 
-      if $eq currentUser._id user._id
-        with currentUser
-          li: a.js-edit-profile {{_ 'edit-profile'}}
+      unless currentUser.isWorker
+        if $eq currentUser._id user._id
+          with currentUser
+            li: a.js-edit-profile {{_ 'edit-profile'}}
 
 template(name="userAvatarAssigneeInitials")
   svg.avatar.avatar-assignee-initials(viewBox="0 0 {{viewPortWidth}} 15")

+ 101 - 1
client/components/cards/cardDetails.js

@@ -51,7 +51,8 @@ BlazeComponent.extendComponent({
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 
@@ -252,6 +253,12 @@ BlazeComponent.extendComponent({
       if ($subtasksDom.data('sortable')) {
         $subtasksDom.sortable('option', 'disabled', !userIsMember());
       }
+      if ($checklistsDom.data('sortable')) {
+        $checklistsDom.sortable('option', 'disabled', Utils.isMiniScreen());
+      }
+      if ($subtasksDom.data('sortable')) {
+        $subtasksDom.sortable('option', 'disabled', Utils.isMiniScreen());
+      }
     });
   },
 
@@ -278,6 +285,29 @@ BlazeComponent.extendComponent({
         'click .js-close-card-details'() {
           Utils.goBoardId(this.data().boardId);
         },
+        'click .js-copy-link'() {
+          StringToCopyElement = document.getElementById('cardURL_copy');
+          StringToCopyElement.select();
+          if (document.execCommand('copy')) {
+            StringToCopyElement.blur();
+          } else {
+            document.getElementById('cardURL_copy').selectionStart = 0;
+            document.getElementById('cardURL_copy').selectionEnd = 999;
+            document.execCommand('copy');
+            if (window.getSelection) {
+              if (window.getSelection().empty) {
+                // Chrome
+                window.getSelection().empty();
+              } else if (window.getSelection().removeAllRanges) {
+                // Firefox
+                window.getSelection().removeAllRanges();
+              }
+            } else if (document.selection) {
+              // IE?
+              document.selection.empty();
+            }
+          }
+        },
         'click .js-open-card-details-menu': Popup.open('cardDetailsActions'),
         'submit .js-card-description'(event) {
           event.preventDefault();
@@ -291,6 +321,8 @@ BlazeComponent.extendComponent({
             .trim();
           if (title) {
             this.data().setTitle(title);
+          } else {
+            this.data().setTitle('');
           }
         },
         'submit .js-card-details-assigner'(event) {
@@ -300,6 +332,8 @@ BlazeComponent.extendComponent({
             .trim();
           if (assigner) {
             this.data().setAssignedBy(assigner);
+          } else {
+            this.data().setAssignedBy('');
           }
         },
         'submit .js-card-details-requester'(event) {
@@ -309,6 +343,8 @@ BlazeComponent.extendComponent({
             .trim();
           if (requester) {
             this.data().setRequestedBy(requester);
+          } else {
+            this.data().setRequestedBy('');
           }
         },
         'click .js-member': Popup.open('cardMember'),
@@ -364,6 +400,54 @@ Template.cardDetails.helpers({
     });
   },
 
+  receivedSelected() {
+    if (this.getReceived().length === 0) {
+      return false;
+    } else {
+      return true;
+    }
+  },
+
+  startSelected() {
+    if (this.getStart().length === 0) {
+      return false;
+    } else {
+      return true;
+    }
+  },
+
+  endSelected() {
+    if (this.getEnd().length === 0) {
+      return false;
+    } else {
+      return true;
+    }
+  },
+
+  dueSelected() {
+    if (this.getDue().length === 0) {
+      return false;
+    } else {
+      return true;
+    }
+  },
+
+  memberSelected() {
+    if (this.getMembers().length === 0) {
+      return false;
+    } else {
+      return true;
+    }
+  },
+
+  labelSelected() {
+    if (this.getLabels().length === 0) {
+      return false;
+    } else {
+      return true;
+    }
+  },
+
   assigneeSelected() {
     if (this.getAssignees().length === 0) {
       return false;
@@ -372,6 +456,22 @@ Template.cardDetails.helpers({
     }
   },
 
+  requestBySelected() {
+    if (this.getRequestBy().length === 0) {
+      return false;
+    } else {
+      return true;
+    }
+  },
+
+  assigneeBySelected() {
+    if (this.getAssigneeBy().length === 0) {
+      return false;
+    } else {
+      return true;
+    }
+  },
+
   memberType() {
     const user = Users.findOne(this.userId);
     return user && user.isBoardAdmin() ? 'admin' : 'normal';

+ 31 - 1
client/components/cards/cardDetails.styl

@@ -4,6 +4,12 @@
 
 avatar-radius = 50%
 
+#cardURL_copy
+  // Have clipboard text not visible by moving it to far left
+  position: absolute
+  left: -2000px
+  top: 0px
+
 .assignee
   border-radius: 3px
   display: block
@@ -107,7 +113,11 @@ avatar-radius = 50%
     border-bottom: 1px solid darken(white, 14%)
 
     .close-card-details,
-    .card-details-menu
+    .card-details-menu,
+    .card-copy-button,
+    .card-copy-mobile-button,
+    .close-card-details-mobile-web,
+    .card-details-menu-mobile-web
       float: right
 
     .close-card-details
@@ -115,10 +125,30 @@ avatar-radius = 50%
       padding: 5px
       margin-right: -8px
 
+    .close-card-details-mobile-web
+      font-size: 24px
+      padding: 5px
+      margin-right: 40px
+
+    .card-copy-button
+      font-size: 17px
+      padding: 10px
+      margin-right: 10px
+
+    .card-copy-mobile-button
+      font-size: 17px
+      padding: 10px
+      margin-right: 10px
+
     .card-details-menu
       font-size: 17px
       padding: 10px
 
+    .card-details-menu-mobile-web
+      font-size: 17px
+      padding: 10px
+      margin-right: 30px
+
     .card-details-watch
       font-size: 17px
       padding-left: 7px

+ 3 - 1
client/components/cards/checklists.jade

@@ -1,5 +1,7 @@
 template(name="checklists")
-  h3 {{_ 'checklists'}}
+  h3
+    i.fa.fa-check
+    | {{_ 'checklists'}}
   if toggleDeleteDialog.get
     .board-overlay#card-details-overlay
     +checklistDeleteDialog(checklist = checklistToDelete)

+ 9 - 3
client/components/cards/checklists.js

@@ -60,6 +60,9 @@ BlazeComponent.extendComponent({
       if ($itemsDom.data('sortable')) {
         $(self.itemsDom).sortable('option', 'disabled', !userIsMember());
       }
+      if ($itemsDom.data('sortable')) {
+        $(self.itemsDom).sortable('option', 'disabled', Utils.isMiniScreen());
+      }
     });
   },
 
@@ -67,7 +70,8 @@ BlazeComponent.extendComponent({
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 }).register('checklistDetail');
@@ -120,7 +124,8 @@ BlazeComponent.extendComponent({
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 
@@ -228,7 +233,8 @@ Template.checklistItemDetail.helpers({
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 });

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

@@ -4,8 +4,8 @@ template(name="minicard")
     class="{{#if isLinkedBoard}}linked-board{{/if}}"
     class="minicard-{{colorClass}}")
     if isMiniScreen
-      .handle
-        .fa.fa-arrows
+      //.handle
+      //  .fa.fa-arrows
     unless isMiniScreen
       if showDesktopDragHandles
         .handle
@@ -67,14 +67,15 @@ template(name="minicard")
     .minicard-custom-fields
       each customFieldsWD
         if definition.showOnCard
-          .minicard-custom-field
-            if definition.showLabelOnMiniCard
+          if trueValue
+            .minicard-custom-field
+              if definition.showLabelOnMiniCard
+                .minicard-custom-field-item
+                  +viewer
+                    = definition.name
               .minicard-custom-field-item
                 +viewer
-                  = definition.name
-            .minicard-custom-field-item
-              +viewer
-                = trueValue
+                  = trueValue
 
     if getAssignees
       .minicard-assignees.js-minicard-assignees

+ 23 - 3
client/components/cards/minicard.js

@@ -1,3 +1,5 @@
+import { Cookies } from 'meteor/ostrio:cookies';
+const cookies = new Cookies();
 // Template.cards.events({
 //   'click .member': Popup.open('cardMember')
 // });
@@ -18,7 +20,11 @@ BlazeComponent.extendComponent({
       },
       {
         'click .js-toggle-minicard-label-text'() {
-          Meteor.call('toggleMinicardLabelText');
+          if (cookies.has('hiddenMinicardLabelText')) {
+            cookies.remove('hiddenMinicardLabelText'); //true
+          } else {
+            cookies.set('hiddenMinicardLabelText', 'true'); //true
+          }
         },
       },
     ];
@@ -27,10 +33,24 @@ BlazeComponent.extendComponent({
 
 Template.minicard.helpers({
   showDesktopDragHandles() {
-    return Meteor.user().hasShowDesktopDragHandles();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).showDesktopDragHandles;
+    } else if (cookies.has('showDesktopDragHandles')) {
+      return true;
+    } else {
+      return false;
+    }
   },
   hiddenMinicardLabelText() {
-    return Meteor.user().hasHiddenMinicardLabelText();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).hiddenMinicardLabelText;
+    } else if (cookies.has('hiddenMinicardLabelText')) {
+      return true;
+    } else {
+      return false;
+    }
   },
   coverUrl() {
     return Attachments.findOne(this.coverId).link('original', '/');

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

@@ -1,5 +1,7 @@
 template(name="subtasks")
-  h3 {{_ 'subtasks'}}
+  h3
+    i.fa.fa-sitemap
+    | {{_ 'subtasks'}}
   if toggleDeleteDialog.get
     .board-overlay#card-details-overlay
     +subtaskDeleteDialog(subtask = subtaskToDelete)

+ 6 - 3
client/components/cards/subtasks.js

@@ -3,7 +3,8 @@ BlazeComponent.extendComponent({
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 }).register('subtaskDetail');
@@ -55,7 +56,8 @@ BlazeComponent.extendComponent({
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 
@@ -154,7 +156,8 @@ Template.subtaskItemDetail.helpers({
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 });

+ 56 - 20
client/components/lists/list.js

@@ -1,3 +1,5 @@
+import { Cookies } from 'meteor/ostrio:cookies';
+const cookies = new Cookies();
 const { calculateIndex, enableClickOnTouch } = Utils;
 
 BlazeComponent.extendComponent({
@@ -31,18 +33,6 @@ BlazeComponent.extendComponent({
     const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)';
     const $cards = this.$('.js-minicards');
 
-    if (Utils.isMiniScreen) {
-      $('.js-minicards').sortable({
-        handle: '.handle',
-      });
-    }
-
-    if (!Utils.isMiniScreen && showDesktopDragHandles) {
-      $('.js-minicards').sortable({
-        handle: '.handle',
-      });
-    }
-
     $cards.sortable({
       connectWith: '.js-minicards:not(.js-list-full)',
       tolerance: 'pointer',
@@ -85,16 +75,15 @@ BlazeComponent.extendComponent({
         const listId = Blaze.getData(ui.item.parents('.list').get(0))._id;
         const currentBoard = Boards.findOne(Session.get('currentBoard'));
         let swimlaneId = '';
-        const boardView = (Meteor.user().profile || {}).boardView;
         if (
-          boardView === 'board-view-swimlanes' ||
+          Utils.boardView() === 'board-view-swimlanes' ||
           currentBoard.isTemplatesBoard()
         )
           swimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))._id;
         else if (
-          boardView === 'board-view-lists' ||
-          boardView === 'board-view-cal' ||
-          !boardView
+          Utils.boardView() === 'board-view-lists' ||
+          Utils.boardView() === 'board-view-cal' ||
+          !Utils.boardView
         )
           swimlaneId = currentBoard.getDefaultSwimline()._id;
 
@@ -128,9 +117,49 @@ BlazeComponent.extendComponent({
     // ugly touch event hotfix
     enableClickOnTouch(itemsSelector);
 
-    // Disable drag-dropping if the current user is not a board member or is comment only
     this.autorun(() => {
-      $cards.sortable('option', 'disabled', !userIsMember());
+      let showDesktopDragHandles = false;
+      currentUser = Meteor.user();
+      if (currentUser) {
+        showDesktopDragHandles = (currentUser.profile || {})
+          .showDesktopDragHandles;
+      } else if (cookies.has('showDesktopDragHandles')) {
+        showDesktopDragHandles = true;
+      } else {
+        showDesktopDragHandles = false;
+      }
+
+      if (!Utils.isMiniScreen() && showDesktopDragHandles) {
+        $cards.sortable({
+          handle: '.handle',
+        });
+      } else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
+        $cards.sortable({
+          handle: '.minicard',
+        });
+      }
+
+      if ($cards.data('sortable')) {
+        $cards.sortable(
+          'option',
+          'disabled',
+          // Disable drag-dropping when user is not member/is miniscreen
+          !userIsMember(),
+          // Not disable drag-dropping while in multi-selection mode
+          // MultiSelection.isActive() || !userIsMember(),
+        );
+      }
+
+      if ($cards.data('sortable')) {
+        $cards.sortable(
+          'option',
+          'disabled',
+          // Disable drag-dropping when user is not member/is miniscreen
+          Utils.isMiniScreen(),
+          // Not disable drag-dropping while in multi-selection mode
+          // MultiSelection.isActive() || !userIsMember(),
+        );
+      }
     });
 
     // We want to re-run this function any time a card is added.
@@ -163,7 +192,14 @@ BlazeComponent.extendComponent({
 
 Template.list.helpers({
   showDesktopDragHandles() {
-    return Meteor.user().hasShowDesktopDragHandles();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).showDesktopDragHandles;
+    } else if (cookies.has('showDesktopDragHandles')) {
+      return true;
+    } else {
+      return false;
+    }
   },
 });
 

+ 16 - 22
client/components/lists/listBody.js

@@ -48,7 +48,6 @@ BlazeComponent.extendComponent({
     const board = this.data().board();
     let linkedId = '';
     let swimlaneId = '';
-    const boardView = (Meteor.user().profile || {}).boardView;
     let cardType = 'cardType-card';
     if (title) {
       if (board.isTemplatesBoard()) {
@@ -71,14 +70,14 @@ BlazeComponent.extendComponent({
           });
           cardType = 'cardType-linkedBoard';
         }
-      } else if (boardView === 'board-view-swimlanes')
+      } else if (Utils.boardView() === 'board-view-swimlanes')
         swimlaneId = this.parentComponent()
           .parentComponent()
           .data()._id;
       else if (
-        boardView === 'board-view-lists' ||
-        boardView === 'board-view-cal' ||
-        !boardView
+        Utils.boardView() === 'board-view-lists' ||
+        Utils.boardView() === 'board-view-cal' ||
+        !Utils.boardView
       )
         swimlaneId = board.getDefaultSwimline()._id;
 
@@ -157,9 +156,8 @@ BlazeComponent.extendComponent({
   },
 
   idOrNull(swimlaneId) {
-    const currentUser = Meteor.user();
     if (
-      (currentUser.profile || {}).boardView === 'board-view-swimlanes' ||
+      Utils.boardView() === 'board-view-swimlanes' ||
       this.data()
         .board()
         .isTemplatesBoard()
@@ -191,7 +189,8 @@ BlazeComponent.extendComponent({
       !this.reachedWipLimit() &&
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 
@@ -397,10 +396,9 @@ BlazeComponent.extendComponent({
       '.js-swimlane',
     );
     this.swimlaneId = '';
-    const boardView = (Meteor.user().profile || {}).boardView;
-    if (boardView === 'board-view-swimlanes')
+    if (Utils.boardView() === 'board-view-swimlanes')
       this.swimlaneId = Blaze.getData(swimlane[0])._id;
-    else if (boardView === 'board-view-lists' || !boardView)
+    else if (Utils.boardView() === 'board-view-lists' || !Utils.boardView)
       this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
   },
 
@@ -580,7 +578,7 @@ BlazeComponent.extendComponent({
       const swimlane = $(Popup._getTopStack().openerElement).parents(
         '.js-swimlane',
       );
-      if ((Meteor.user().profile || {}).boardView === 'board-view-swimlanes')
+      if (Utils.boardView() === 'board-view-swimlanes')
         this.swimlaneId = Blaze.getData(swimlane[0])._id;
       else this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id;
       // List where to insert card
@@ -709,22 +707,18 @@ BlazeComponent.extendComponent({
     if (isSandstorm) {
       const user = Meteor.user();
       if (user) {
-        const boardView = (Meteor.user().profile || {}).boardView;
-        if (boardView === 'board-view-swimlanes') {
+        if (Utils.boardView() === 'board-view-swimlanes') {
           this.swimlaneId = this.parentComponent()
             .parentComponent()
             .parentComponent()
             .data()._id;
         }
       }
-    } else {
-      const boardView = (Meteor.user().profile || {}).boardView;
-      if (boardView === 'board-view-swimlanes') {
-        this.swimlaneId = this.parentComponent()
-          .parentComponent()
-          .parentComponent()
-          .data()._id;
-      }
+    } else if (Utils.boardView() === 'board-view-swimlanes') {
+      this.swimlaneId = this.parentComponent()
+        .parentComponent()
+        .parentComponent()
+        .data()._id;
     }
   },
 

+ 40 - 17
client/components/lists/listHeader.jade

@@ -10,7 +10,7 @@ template(name="listHeader")
           a.list-header-left-icon.fa.fa-angle-left.js-unselect-list
       h2.list-header-name(
         title="{{ moment modifiedAt 'LLL' }}"
-        class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}js-open-inlined-form is-editable{{/unless}}{{/if}}")
+        class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
         +viewer
           = title
         if wipLimit.enabled
@@ -30,17 +30,17 @@ template(name="listHeader")
               if canSeeAddCard
                 a.js-add-card.fa.fa-plus.list-header-plus-icon
               a.fa.fa-navicon.js-open-list-menu
-          a.list-header-handle.handle.fa.fa-arrows.js-list-handle
+          //a.list-header-handle.handle.fa.fa-arrows.js-list-handle
         else
           a.list-header-menu-icon.fa.fa-angle-right.js-select-list
-          a.list-header-handle.handle.fa.fa-arrows.js-list-handle
+          //a.list-header-handle.handle.fa.fa-arrows.js-list-handle
       else if currentUser.isBoardMember
         if isWatching
           i.list-header-watch-icon.fa.fa-eye
         div.list-header-menu
           unless currentUser.isCommentOnly
-            if isBoardAdmin
-              a.fa.js-list-star.list-header-plus-icon(class="fa-star{{#unless starred}}-o{{/unless}}")
+            //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
@@ -56,25 +56,47 @@ template(name="editListTitleForm")
 
 template(name="listActionPopup")
   ul.pop-over-list
-    li: a.js-toggle-watch-list {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}}
+    li
+      a.js-toggle-watch-list
+        if isWatching
+          i.fa.fa-eye
+          |  {{_ 'unwatch'}}
+        else
+          i.fa.fa-eye-slash
+          |  {{_ 'watch'}}
   unless currentUser.isCommentOnly
-    hr
-    ul.pop-over-list
-      li: a.js-set-color-list {{_ 'set-color-list'}}
-    hr
+    unless currentUser.isWorker
+      ul.pop-over-list
+        li
+          a.js-set-color-list
+            i.fa.fa-paint-brush
+            | {{_ 'set-color-list'}}
     ul.pop-over-list
       if cards.count
-        li: a.js-select-cards {{_ 'list-select-cards'}}
-        hr
+        li
+          a.js-select-cards
+            i.fa.fa-check-square
+            | {{_ 'list-select-cards'}}
     if currentUser.isBoardAdmin
       ul.pop-over-list
-        li: a.js-set-wip-limit {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}}
+        li
+          a.js-set-wip-limit
+            i.fa.fa-ban
+            | {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}}
+    unless currentUser.isWorker
       hr
-    ul.pop-over-list
-      li: a.js-close-list {{_ 'archive-list'}}
+      ul.pop-over-list
+        li
+          a.js-close-list
+            i.fa.fa-arrow-right
+            i.fa.fa-archive
+            | {{_ 'archive-list'}}
     hr
     ul.pop-over-list
-      li: a.js-more {{_ 'listMorePopup-title'}}
+      li
+        a.js-more
+          i.fa.fa-link
+          | {{_ 'listMorePopup-title'}}
 
 template(name="boardLists")
   ul.pop-over-list
@@ -94,7 +116,8 @@ template(name="listMorePopup")
       input.inline-input(type="text" readonly value="{{ rootUrl }}")
     | {{_ 'added'}}
     span.date(title=list.createdAt) {{ moment createdAt 'LLL' }}
-    a.js-delete {{_ 'delete'}}
+    unless currentUser.isWorker
+      a.js-delete {{_ 'delete'}}
 
 template(name="listDeletePopup")
   p {{_ "list-delete-pop"}}

+ 21 - 7
client/components/lists/listHeader.js

@@ -1,3 +1,5 @@
+import { Cookies } from 'meteor/ostrio:cookies';
+const cookies = new Cookies();
 let listsColors;
 Meteor.startup(() => {
   listsColors = Lists.simpleSchema()._schema.color.allowedValues;
@@ -7,9 +9,10 @@ BlazeComponent.extendComponent({
   canSeeAddCard() {
     const list = Template.currentData();
     return (
-      !list.getWipLimit('enabled') ||
-      list.getWipLimit('soft') ||
-      !this.reachedWipLimit()
+      (!list.getWipLimit('enabled') ||
+        list.getWipLimit('soft') ||
+        !this.reachedWipLimit()) &&
+      !Meteor.user().isWorker()
     );
   },
 
@@ -44,14 +47,18 @@ BlazeComponent.extendComponent({
   },
 
   limitToShowCardsCount() {
-    return Meteor.user().getLimitToShowCardsCount();
+    const currentUser = Meteor.user();
+    if (currentUser) {
+      return Meteor.user().getLimitToShowCardsCount();
+    } else {
+      return false;
+    }
   },
 
   cardsCount() {
     const list = Template.currentData();
     let swimlaneId = '';
-    const boardView = (Meteor.user().profile || {}).boardView;
-    if (boardView === 'board-view-swimlanes')
+    if (Utils.boardView() === 'board-view-swimlanes')
       swimlaneId = this.parentComponent()
         .parentComponent()
         .data()._id;
@@ -100,7 +107,14 @@ BlazeComponent.extendComponent({
 
 Template.listHeader.helpers({
   showDesktopDragHandles() {
-    return Meteor.user().hasShowDesktopDragHandles();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).showDesktopDragHandles;
+    } else if (cookies.has('showDesktopDragHandles')) {
+      return true;
+    } else {
+      return false;
+    }
   },
 });
 

+ 3 - 0
client/components/main/header.styl

@@ -218,6 +218,9 @@
       padding: 10px
       margin: -10px 0 -10px -10px
 
+.announcement .viewer
+  display: inline-block
+
 .announcement,
 .offline-warning
   width: 100%

+ 25 - 12
client/components/rules/actions/boardActions.jade

@@ -1,29 +1,42 @@
 template(name="boardActions")
   div.trigger-item
     div.trigger-content
-      div.trigger-text 
+      div.trigger-text
         | {{_'r-move-card-to'}}
       div.trigger-dropdown
         select(id="move-gen-action")
           option(value="top") {{_'r-top-of'}}
           option(value="bottom") {{_'r-bottom-of'}}
-      div.trigger-text 
+      div.trigger-text
         | {{_'r-its-list'}}
     div.trigger-button.js-add-gen-move-action.js-goto-rules
       i.fa.fa-plus
 
   div.trigger-item
     div.trigger-content
-      div.trigger-text 
+      div.trigger-text
         | {{_'r-move-card-to'}}
       div.trigger-dropdown
         select(id="move-spec-action")
           option(value="top") {{_'r-top-of'}}
           option(value="bottom") {{_'r-bottom-of'}}
-      div.trigger-text 
-        | {{_'r-list'}}
+      div.trigger-text
+        | {{_'r-the-board'}}
+      div.trigger-dropdown
+        select(id="board-id")
+          each boards
+            if $eq _id currentBoard._id
+              option(value="{{_id}}" selected) {{_ 'current'}}
+            else
+              option(value="{{_id}}") {{title}}
+      div.trigger-text
+        | {{_'r-in-list'}}
       div.trigger-dropdown
         input(id="listName",type=text,placeholder="{{_'r-name'}}")
+      div.trigger-text
+        | {{_'r-in-swimlane'}}
+      div.trigger-dropdown
+        input(id="swimlaneName",type=text,placeholder="{{_'r-name'}}")
     div.trigger-button.js-add-spec-move-action.js-goto-rules
       i.fa.fa-plus
 
@@ -33,14 +46,14 @@ template(name="boardActions")
         select(id="arch-action")
           option(value="archive") {{_'r-archive'}}
           option(value="unarchive") {{_'r-unarchive'}}
-      div.trigger-text 
+      div.trigger-text
         | {{_'r-card'}}
     div.trigger-button.js-add-arch-action.js-goto-rules
       i.fa.fa-plus
 
   div.trigger-item
     div.trigger-content
-      div.trigger-text 
+      div.trigger-text
         | {{_'r-add-swimlane'}}
       div.trigger-dropdown
         input(id="swimlane-name",type=text,placeholder="{{_'r-name'}}")
@@ -49,15 +62,15 @@ template(name="boardActions")
 
   div.trigger-item
     div.trigger-content
-      div.trigger-text 
+      div.trigger-text
         | {{_'r-create-card'}}
       div.trigger-dropdown
         input(id="card-name",type=text,placeholder="{{_'r-name'}}")
-      div.trigger-text 
+      div.trigger-text
         | {{_'r-in-list'}}
       div.trigger-dropdown
         input(id="list-name",type=text,placeholder="{{_'r-name'}}")
-      div.trigger-text 
+      div.trigger-text
         | {{_'r-in-swimlane'}}
       div.trigger-dropdown
         input(id="swimlane-name2",type=text,placeholder="{{_'r-name'}}")
@@ -65,8 +78,8 @@ template(name="boardActions")
       i.fa.fa-plus
 
 
-   
-  
+
+
 
 
 

+ 25 - 5
client/components/rules/actions/boardActions.js

@@ -1,6 +1,22 @@
 BlazeComponent.extendComponent({
   onCreated() {},
 
+  boards() {
+    const boards = Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+        _id: {
+          $ne: Meteor.user().getTemplatesBoardId(),
+        },
+      },
+      {
+        sort: ['title'],
+      },
+    );
+    return boards;
+  },
+
   events() {
     return [
       {
@@ -52,15 +68,18 @@ BlazeComponent.extendComponent({
           const ruleName = this.data().ruleName.get();
           const trigger = this.data().triggerVar.get();
           const actionSelected = this.find('#move-spec-action').value;
-          const listTitle = this.find('#listName').value;
+          const swimlaneName = this.find('#swimlaneName').value;
+          const listName = this.find('#listName').value;
           const boardId = Session.get('currentBoard');
+          const destBoardId = this.find('#board-id').value;
           const desc = Utils.getTriggerActionDesc(event, this);
           if (actionSelected === 'top') {
             const triggerId = Triggers.insert(trigger);
             const actionId = Actions.insert({
               actionType: 'moveCardToTop',
-              listTitle,
-              boardId,
+              listName,
+              swimlaneName,
+              boardId: destBoardId,
               desc,
             });
             Rules.insert({
@@ -74,8 +93,9 @@ BlazeComponent.extendComponent({
             const triggerId = Triggers.insert(trigger);
             const actionId = Actions.insert({
               actionType: 'moveCardToBottom',
-              listTitle,
-              boardId,
+              listName,
+              swimlaneName,
+              boardId: destBoardId,
               desc,
             });
             Rules.insert({

+ 6 - 2
client/components/settings/informationBody.jade

@@ -4,12 +4,16 @@ template(name='information')
       | {{_ 'error-notAuthorized'}}
     else
       .content-title
-        span {{_ 'info'}}
+        span
+          i.fa.fa-info-circle
+          | {{_ 'info'}}
       .content-body
         .side-menu
           ul
             li.active
-              a.js-setting-menu(data-id="information-display") {{_ 'info'}}
+              a.js-setting-menu(data-id="information-display")
+                i.fa.fa-info-circle
+                | {{_ 'info'}}
         .main-body
           +statistics
 

+ 54 - 18
client/components/settings/peopleBody.jade

@@ -5,16 +5,22 @@ template(name="people")
     else
       .content-title.ext-box
         .ext-box-left
-          span {{_ 'people'}}
+          span
+            i.fa.fa-users
+            | {{_ 'people'}}
           input#searchInput(placeholder="{{_ 'search'}}")
-          button#searchButton {{_ 'search'}}
+          button#searchButton
+            i.fa.fa-search
+            | {{_ 'search'}}
         .ext-box-right
           span {{_ 'people-number'}} #{peopleNumber}
       .content-body
         .side-menu
           ul
             li.active
-              a.js-setting-menu(data-id="people-setting") {{_ 'people'}}
+              a.js-setting-menu(data-id="people-setting")
+                i.fa.fa-users
+                | {{_ 'people'}}
         .main-body
           if loading.get
             +spinner
@@ -39,28 +45,58 @@ template(name="peopleGeneral")
 
 template(name="peopleRow")
   tr
-    td.username {{ userData.username }}
-    td {{ userData.profile.fullname }}
-    td
-      if userData.isAdmin
-        | {{_ 'yes'}}
-      else
-        | {{_ 'no'}}
-    td {{ userData.emails.[0].address }}
-    td
-      if userData.emails.[0].verified
-        | {{_ 'yes'}}
-      else
-        | {{_ 'no'}}
-    td {{ moment userData.createdAt 'LLL' }}
+    if userData.loginDisabled
+      td.username <s>{{ userData.username }}</s>
+    else
+      td.username {{ userData.username }}
+    if userData.loginDisabled
+      td <s>{{ userData.profile.fullname }}</s>
+    else
+      td {{ userData.profile.fullname }}
+    if userData.loginDisabled
+      td
+        if userData.isAdmin
+          | <s>{{_ 'yes'}}</s>
+        else
+          | <s>{{_ 'no'}}</s>
+    else
+      td
+        if userData.isAdmin
+          | {{_ 'yes'}}
+        else
+          | {{_ 'no'}}
+    if userData.loginDisabled
+      td <s>{{ userData.emails.[0].address }}</s>
+    else
+      td {{ userData.emails.[0].address }}
+    if userData.loginDisabled
+      td
+        if userData.emails.[0].verified
+          | <s>{{_ 'yes'}}</s>
+        else
+          | <s>{{_ 'no'}}</s>
+    else
+      td
+        if userData.emails.[0].verified
+          | {{_ 'yes'}}
+        else
+          | {{_ 'no'}}
+    if userData.loginDisabled
+      td <s>{{ moment userData.createdAt 'LLL' }}</s>
+    else
+      td {{ moment userData.createdAt 'LLL' }}
     td
       if userData.loginDisabled
         | {{_ 'no'}}
       else
         | {{_ 'yes'}}
-    td {{_ userData.authenticationMethod }}
+    if userData.loginDisabled
+      td <s>{{_ userData.authenticationMethod }}</s>
+    else
+      td {{_ userData.authenticationMethod }}
     td
       a.edit-user
+        i.fa.fa-edit
         | {{_ 'edit'}}
 
 template(name="editUserPopup")

+ 1 - 1
client/components/settings/peopleBody.styl

@@ -33,7 +33,7 @@ table
     padding: 0;
 
   button
-    min-width: 60px;
+    min-width: 90px;
 
 .content-wrapper
   margin-top: 10px

+ 19 - 12
client/components/settings/settingBody.jade

@@ -4,22 +4,35 @@ template(name="setting")
       | {{_ 'error-notAuthorized'}}
     else
       .content-title
+        i.fa.fa-cog
         span {{_ 'settings'}}
       .content-body
         .side-menu
           ul
             li.active
-              a.js-setting-menu(data-id="registration-setting") {{_ 'registration'}}
+              a.js-setting-menu(data-id="registration-setting")
+                i.fa.fa-sign-in
+                | {{_ 'registration'}}
             li
-              a.js-setting-menu(data-id="email-setting") {{_ 'email'}}
+              a.js-setting-menu(data-id="email-setting")
+                i.fa.fa-envelope
+                | {{_ 'email'}}
             li
-              a.js-setting-menu(data-id="account-setting") {{_ 'accounts'}}
+              a.js-setting-menu(data-id="account-setting")
+                i.fa.fa-users
+                | {{_ 'accounts'}}
             li
-              a.js-setting-menu(data-id="announcement-setting") {{_ 'admin-announcement'}}
+              a.js-setting-menu(data-id="announcement-setting")
+                i.fa.fa-bullhorn
+                | {{_ 'admin-announcement'}}
             li
-              a.js-setting-menu(data-id="layout-setting") {{_ 'layout'}}
+              a.js-setting-menu(data-id="layout-setting")
+                i.fa.fa-object-group
+                | {{_ 'layout'}}
             li
-              a.js-setting-menu(data-id="webhook-setting") {{_ 'global-webhook'}}
+              a.js-setting-menu(data-id="webhook-setting")
+                i.fa.fa-globe
+                | {{_ 'global-webhook'}}
         .main-body
           if loading.get
             +spinner
@@ -171,12 +184,6 @@ template(name='layoutSettings')
       .title {{_ 'custom-product-name'}}
       .form-group
         input.wekan-form-control#product-name(type="text", placeholder="" value="{{currentSetting.productName}}")
-    li.layout-form
-      .title {{_ 'add-custom-html-after-body-start'}}
-      textarea#customHTMLafterBodyStart.wekan-form-control= currentSetting.customHTMLafterBodyStart
-    li.layout-form
-      .title {{_ 'add-custom-html-before-body-end'}}
-      textarea#customHTMLbeforeBodyEnd.wekan-form-control= currentSetting.customHTMLbeforeBodyEnd
     li
       button.js-save-layout.primary {{_ 'save'}}
 

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

@@ -171,20 +171,12 @@ BlazeComponent.extendComponent({
     const displayAuthenticationMethod =
       $('input[name=displayAuthenticationMethod]:checked').val() === 'true';
     const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
-    const customHTMLafterBodyStart = $('#customHTMLafterBodyStart')
-      .val()
-      .trim();
-    const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd')
-      .val()
-      .trim();
 
     try {
       Settings.update(Settings.findOne()._id, {
         $set: {
           productName,
           hideLogo: hideLogoChange,
-          customHTMLafterBodyStart,
-          customHTMLbeforeBodyEnd,
           displayAuthenticationMethod,
           defaultAuthenticationMethod,
         },

+ 4 - 1
client/components/settings/settingBody.styl

@@ -41,15 +41,18 @@
           &:hover
             background #fff
             box-shadow 0 1px 2px rgba(0,0,0,0.15);
+
           a
             @extends .flex
             padding: 1rem 0 1rem 1rem
             width: 100% - 5rem
 
-
             span
               font-size: 13px
 
+            i
+              margin-right: 20px
+
     .main-body
       padding: 0.1em 1em
       -webkit-user-select: text // Safari 3.1+

+ 188 - 26
client/components/sidebar/sidebar.jade

@@ -37,11 +37,12 @@ template(name='homeSidebar')
 template(name="membersWidget")
   .board-widget.board-widget-members
     h3
-      i.fa.fa-user
+      i.fa.fa-users
       | {{_ 'members'}}
       unless currentUser.isCommentOnly
-        a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
-          i.board-header-btn-icon.fa.fa-cog
+        unless currentUser.isWorker
+          a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
+            i.board-header-btn-icon.fa.fa-cog
 
     .board-widget-content
       each currentBoard.activeMembers
@@ -71,6 +72,108 @@ template(name="boardChangeColorPopup")
           if isSelected
             i.fa.fa-check
 
+template(name="boardCardSettingsPopup")
+  form.board-card-settings
+    h3 {{_ 'show-on-card'}}
+    div.check-div
+      a.flex.js-field-has-receiveddate(class="{{#if allowsReceivedDate}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsReceivedDate}}is-checked{{/if}}")
+        span
+          i.fa.fa-sign-out
+          | {{_ 'card-received'}}
+    div.check-div
+      a.flex.js-field-has-startdate(class="{{#if allowsStartDate}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsStartDate}}is-checked{{/if}}")
+        span
+          i.fa.fa-hourglass-start
+          | {{_ 'card-start'}}
+    div.check-div
+      a.flex.js-field-has-duedate(class="{{#if allowsDueDate}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsDueDate}}is-checked{{/if}}")
+        span
+          i.fa.fa-sign-in
+          | {{_ 'card-due'}}
+    div.check-div
+      a.flex.js-field-has-enddate(class="{{#if allowsEndDate}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsEndDate}}is-checked{{/if}}")
+        span
+          i.fa.fa-hourglass-end
+          | {{_ 'card-end'}}
+    div.check-div
+      a.flex.js-field-has-members(class="{{#if allowsMembers}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsMembers}}is-checked{{/if}}")
+        span
+          i.fa.fa-users
+          | {{_ 'members'}}
+    div.check-div
+      a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}")
+        span
+          i.fa.fa-user
+          | {{_ 'assignee'}}
+    div.check-div
+      a.flex.js-field-has-assigned-by(class="{{#if allowsAssignedBy}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsAssignedBy}}is-checked{{/if}}")
+        span
+          i.fa.fa-shopping-cart
+          | {{_ 'assigned-by'}}
+    div.check-div
+      a.flex.js-field-has-requested-by(class="{{#if allowsRequestedBy}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsRequestedBy}}is-checked{{/if}}")
+        span
+          i.fa.fa-user-plus
+          | {{_ 'requested-by'}}
+    div.check-div
+      a.flex.js-field-has-labels(class="{{#if allowsLabels}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsLabels}}is-checked{{/if}}")
+        span
+          i.fa.fa-tags
+          | {{_ 'labels'}}
+    div.check-div
+      a.flex.js-field-has-description-title(class="{{#if allowsDescriptionTitle}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsDescriptionTitle}}is-checked{{/if}}")
+        span
+          i.fa.fa-align-left
+          | {{_ 'description'}}
+          | {{_ 'title'}}
+    div.check-div
+      a.flex.js-field-has-description-text(class="{{#if allowsDescriptionText}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsDescriptionText}}is-checked{{/if}}")
+        span
+          i.fa.fa-align-left
+          | {{_ 'description'}}
+          | {{_ 'custom-field-text'}}
+    div.check-div
+      a.flex.js-field-has-checklists(class="{{#if allowsChecklists}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsChecklists}}is-checked{{/if}}")
+        span
+          i.fa.fa-check
+          | {{_ 'checklists'}}
+    div.check-div
+      a.flex.js-field-has-subtasks(class="{{#if allowsSubtasks}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsSubtasks}}is-checked{{/if}}")
+        span
+          i.fa.fa-sitemap
+          | {{_ 'subtasks'}}
+    div.check-div
+      a.flex.js-field-has-attachments(class="{{#if allowsAttachments}}is-checked{{/if}}")
+        .materialCheckBox(class="{{#if allowsAttachments}}is-checked{{/if}}")
+        span
+          i.fa.fa-paperclip
+          | {{_ 'attachments'}}
+    //div.check-div
+    //  a.flex.js-field-has-comments(class="{{#if allowsComments}}is-checked{{/if}}")
+    //    .materialCheckBox(class="{{#if allowsComments}}is-checked{{/if}}")
+    //    span
+    //      i.fa.fa-comment-o
+    //      | {{_ 'comment'}}
+    //div.check-div
+    //  a.flex.js-field-has-activities(class="{{#if allowsActivities}}is-checked{{/if}}")
+    //    .materialCheckBox(class="{{#if allowsActivities}}is-checked{{/if}}")
+    //    span
+    //      i.fa.fa-history
+    //      | {{_ 'activities'}}
+
 template(name="boardSubtaskSettingsPopup")
   form.board-subtask-settings
     h3 {{_ 'show-parent-in-minicard'}}
@@ -130,7 +233,9 @@ template(name="chooseBoardSource")
 
 template(name="archiveBoardPopup")
   p {{_ 'close-board-pop'}}
-  button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
+  button.js-confirm.negate.full(type="submit")
+    i.fa.fa-archive
+    | {{_ 'archive'}}
 
 template(name="outgoingWebhooksPopup")
   each integrations
@@ -163,37 +268,88 @@ template(name="outgoingWebhooksPopup")
 template(name="boardMenuPopup")
   ul.pop-over-list
     li: a.js-custom-fields {{_ 'custom-fields'}}
-    li: a.js-open-archives {{_ 'archived-items'}}
+    li
+      a.js-open-archives
+        i.fa.fa-archive
+        | {{_ 'archived-items'}}
     if currentUser.isBoardAdmin
-      li: a.js-change-board-color {{_ 'board-change-color'}}
+      li
+        a.js-change-board-color
+          i.fa.fa-paint-brush
+          | {{_ 'board-change-color'}}
+
     //-
       XXX Language should be handled by sandstorm, but for now display a
       language selection link in the board menu. This link is normally present
       in the header bar that is not displayed on sandstorm.
     if isSandstorm
-      li: a.js-change-language {{_ 'language'}}
+      li
+        a.js-change-language
+          i.fa.fa-flag
+          | {{_ 'language'}}
   unless isSandstorm
     if currentUser.isBoardAdmin
       hr
       ul.pop-over-list
-        li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
-        unless currentBoard.isTemplatesBoard
-          li: a.js-archive-board {{_ 'archive-board'}}
-        li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
-      hr
-      ul.pop-over-list
-        li: a.js-subtask-settings {{_ 'subtask-settings'}}
+        li
+          a(href="{{exportUrl}}", download="{{exportFilename}}")
+            i.fa.fa-share-alt
+            | {{_ 'export-board'}}
+        li
+          a.js-outgoing-webhooks
+            i.fa.fa-globe
+            | {{_ 'outgoing-webhooks'}}
+        li
+          a.js-card-settings
+            i.fa.fa-id-card-o
+            | {{_ 'card-settings'}}
+        li
+          a.js-subtask-settings
+            i.fa.fa-sitemap
+            | {{_ 'subtask-settings'}}
+      unless currentBoard.isTemplatesBoard
+        hr
+        ul.pop-over-list
+          li
+            a.js-archive-board
+              i.fa.fa-arrow-right
+              i.fa.fa-archive
+              | {{_ 'archive-board'}}
 
   if isSandstorm
     hr
     ul.pop-over-list
-      li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
-      li: a.js-import-board {{_ 'import-board-c'}}
-      li: a.js-archive-board {{_ 'archive-board'}}
-      li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
+      li
+        a(href="{{exportUrl}}", download="{{exportFilename}}")
+          i.fa.fa-share-alt
+          i.fa.fa-sign-out
+          | {{_ 'export-board'}}
+      li
+        a.js-import-board
+          i.fa.fa-share-alt
+          i.fa.fa-sign-in
+          | {{_ 'import-board-c'}}
+      li
+        a.js-archive-board
+          i.fa.fa-arrow-right
+          i.fa.fa-archive
+          | {{_ 'archive-board'}}
+      li
+        a.js-outgoing-webhooks
+          i.fa.fa-globe
+          | {{_ 'outgoing-webhooks'}}
+    hr
+    ul.pop-over-list
+      li
+        a.js-card-settings
+          i.fa.fa-id-card-o
+          | {{_ 'card-settings'}}
     hr
     ul.pop-over-list
-      li: a.js-subtask-settings {{_ 'subtask-settings'}}
+      li
+        a.js-subtask-settings
+          i.fa.fa-sitemap
+          | {{_ 'subtask-settings'}}
 
 template(name="labelsWidget")
   .board-widget.board-widget-labels
@@ -203,7 +359,7 @@ template(name="labelsWidget")
     .board-widget-content
       each currentBoard.labels
           a.card-label(class="card-label-{{color}}"
-            class="{{#if currentUser.isNotCommentOnly}}js-label{{/if}}")
+            class="{{#if currentUser.isNotCommentOnly}}{{#if currentUser.isNotWorker}}js-label{{/if}}{{/if}}")
             span.card-label-name
               +viewer
                 = name
@@ -232,12 +388,12 @@ template(name="memberPopup")
           a.js-change-role
             | {{_ 'change-permissions'}}
             span.quiet (#{memberType})
-      li
-        if $eq currentUser._id userId
-          a.js-leave-member {{_ 'leave-board'}}
-        else if currentUser.isBoardAdmin
-          a.js-remove-member {{_ 'remove-from-board'}}
-
+      unless currentUser.isWorker
+        li
+          if $eq currentUser._id userId
+            a.js-leave-member {{_ 'leave-board'}}
+          else if currentUser.isBoardAdmin
+            a.js-remove-member {{_ 'remove-from-board'}}
 
 template(name="removeMemberPopup")
   p {{_ 'remove-member-pop' name=user.profile.fullname username=user.username boardTitle=board.title}}
@@ -301,6 +457,12 @@ template(name="changePermissionsPopup")
         if isCommentOnly
           i.fa.fa-check
         span.sub-name {{_ 'comment-only-desc'}}
+    li
+      a(class="{{#if isLastAdmin}}disabled{{else}}js-set-worker{{/if}}")
+        | {{_ 'worker'}}
+        if isWorker
+          i.fa.fa-check
+        span.sub-name {{_ 'worker-desc'}}
   if isLastAdmin
     hr
     p.quiet.bottom {{_ 'last-admin-desc'}}

+ 399 - 4
client/components/sidebar/sidebar.js

@@ -1,3 +1,5 @@
+import { Cookies } from 'meteor/ostrio:cookies';
+const cookies = new Cookies();
 Sidebar = null;
 
 const defaultView = 'home';
@@ -107,7 +109,14 @@ BlazeComponent.extendComponent({
         'click .js-toggle-sidebar': this.toggle,
         'click .js-back-home': this.setView,
         'click .js-toggle-minicard-label-text'() {
-          Meteor.call('toggleMinicardLabelText');
+          currentUser = Meteor.user();
+          if (currentUser) {
+            Meteor.call('toggleMinicardLabelText');
+          } else if (cookies.has('hiddenMinicardLabelText')) {
+            cookies.remove('hiddenMinicardLabelText');
+          } else {
+            cookies.set('hiddenMinicardLabelText', 'true');
+          }
         },
         'click .js-shortcuts'() {
           FlowRouter.go('shortcuts');
@@ -121,7 +130,14 @@ Blaze.registerHelper('Sidebar', () => Sidebar);
 
 Template.homeSidebar.helpers({
   hiddenMinicardLabelText() {
-    return Meteor.user().hasHiddenMinicardLabelText();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).hiddenMinicardLabelText;
+    } else if (cookies.has('hiddenMinicardLabelText')) {
+      return true;
+    } else {
+      return false;
+    }
   },
 });
 
@@ -145,10 +161,13 @@ Template.memberPopup.helpers({
       const currentBoard = Boards.findOne(Session.get('currentBoard'));
       const commentOnly = currentBoard.hasCommentOnly(this.userId);
       const noComments = currentBoard.hasNoComments(this.userId);
+      const worker = currentBoard.hasWorker(this.userId);
       if (commentOnly) {
         return TAPi18n.__('comment-only').toLowerCase();
       } else if (noComments) {
         return TAPi18n.__('no-comments').toLowerCase();
+      } else if (worker) {
+        return TAPi18n.__('worker').toLowerCase();
       } else {
         return TAPi18n.__(type).toLowerCase();
       }
@@ -189,6 +208,7 @@ Template.boardMenuPopup.events({
   'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
   'click .js-import-board': Popup.open('chooseBoardSource'),
   'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
+  'click .js-card-settings': Popup.open('boardCardSettings'),
 });
 
 Template.boardMenuPopup.helpers({
@@ -251,6 +271,14 @@ Template.membersWidget.helpers({
     const user = Meteor.user();
     return user && user.isInvitedTo(Session.get('currentBoard'));
   },
+  isWorker() {
+    const user = Meteor.user();
+    if (user) {
+      return Meteor.call(Boards.hasWorker(user.memberId));
+    } else {
+      return false;
+    }
+  },
 });
 
 Template.membersWidget.events({
@@ -445,6 +473,10 @@ BlazeComponent.extendComponent({
     return this.currentBoard.allowsSubtasks;
   },
 
+  allowsReceivedDate() {
+    return this.currentBoard.allowsReceivedDate;
+  },
+
   isBoardSelected() {
     return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
   },
@@ -558,6 +590,359 @@ BlazeComponent.extendComponent({
   },
 }).register('boardSubtaskSettingsPopup');
 
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.currentBoard = Boards.findOne(Session.get('currentBoard'));
+  },
+
+  allowsReceivedDate() {
+    return this.currentBoard.allowsReceivedDate;
+  },
+
+  allowsStartDate() {
+    return this.currentBoard.allowsStartDate;
+  },
+
+  allowsDueDate() {
+    return this.currentBoard.allowsDueDate;
+  },
+
+  allowsEndDate() {
+    return this.currentBoard.allowsEndDate;
+  },
+
+  allowsSubtasks() {
+    return this.currentBoard.allowsSubtasks;
+  },
+
+  allowsMembers() {
+    return this.currentBoard.allowsMembers;
+  },
+
+  allowsAssignee() {
+    return this.currentBoard.allowsAssignee;
+  },
+
+  allowsAssignedBy() {
+    return this.currentBoard.allowsAssignedBy;
+  },
+
+  allowsRequestedBy() {
+    return this.currentBoard.allowsRequestedBy;
+  },
+
+  allowsLabels() {
+    return this.currentBoard.allowsLabels;
+  },
+
+  allowsChecklists() {
+    return this.currentBoard.allowsChecklists;
+  },
+
+  allowsAttachments() {
+    return this.currentBoard.allowsAttachments;
+  },
+
+  allowsComments() {
+    return this.currentBoard.allowsComments;
+  },
+
+  allowsDescriptionTitle() {
+    return this.currentBoard.allowsDescriptionTitle;
+  },
+
+  allowsDescriptionText() {
+    return this.currentBoard.allowsDescriptionText;
+  },
+
+  isBoardSelected() {
+    return this.currentBoard.dateSettingsDefaultBoardID;
+  },
+
+  isNullBoardSelected() {
+    return (
+      this.currentBoard.dateSettingsDefaultBoardId === null ||
+      this.currentBoard.dateSettingsDefaultBoardId === undefined
+    );
+  },
+
+  boards() {
+    return Boards.find(
+      {
+        archived: false,
+        'members.userId': Meteor.userId(),
+      },
+      {
+        sort: ['title'],
+      },
+    );
+  },
+
+  lists() {
+    return Lists.find(
+      {
+        boardId: this.currentBoard._id,
+        archived: false,
+      },
+      {
+        sort: ['title'],
+      },
+    );
+  },
+
+  hasLists() {
+    return this.lists().count() > 0;
+  },
+
+  isListSelected() {
+    return (
+      this.currentBoard.dateSettingsDefaultBoardId === this.currentData()._id
+    );
+  },
+
+  events() {
+    return [
+      {
+        'click .js-field-has-receiveddate'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsReceivedDate = !this.currentBoard
+            .allowsReceivedDate;
+          this.currentBoard.setAllowsReceivedDate(
+            this.currentBoard.allowsReceivedDate,
+          );
+          $(`.js-field-has-receiveddate ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsReceivedDate,
+          );
+          $('.js-field-has-receiveddate').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsReceivedDate,
+          );
+        },
+        'click .js-field-has-startdate'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsStartDate = !this.currentBoard
+            .allowsStartDate;
+          this.currentBoard.setAllowsStartDate(
+            this.currentBoard.allowsStartDate,
+          );
+          $(`.js-field-has-startdate ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsStartDate,
+          );
+          $('.js-field-has-startdate').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsStartDate,
+          );
+        },
+        'click .js-field-has-enddate'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsEndDate = !this.currentBoard.allowsEndDate;
+          this.currentBoard.setAllowsEndDate(this.currentBoard.allowsEndDate);
+          $(`.js-field-has-enddate ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsEndDate,
+          );
+          $('.js-field-has-enddate').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsEndDate,
+          );
+        },
+        'click .js-field-has-duedate'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsDueDate = !this.currentBoard.allowsDueDate;
+          this.currentBoard.setAllowsDueDate(this.currentBoard.allowsDueDate);
+          $(`.js-field-has-duedate ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsDueDate,
+          );
+          $('.js-field-has-duedate').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsDueDate,
+          );
+        },
+        'click .js-field-has-subtasks'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
+          this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
+          $(`.js-field-has-subtasks ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsSubtasks,
+          );
+          $('.js-field-has-subtasks').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsSubtasks,
+          );
+        },
+        'click .js-field-has-members'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
+          this.currentBoard.setAllowsMembers(this.currentBoard.allowsMembers);
+          $(`.js-field-has-members ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsMembers,
+          );
+          $('.js-field-has-members').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsMembers,
+          );
+        },
+        'click .js-field-has-assignee'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsAssignee = !this.currentBoard.allowsAssignee;
+          this.currentBoard.setAllowsAssignee(this.currentBoard.allowsAssignee);
+          $(`.js-field-has-assignee ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsAssignee,
+          );
+          $('.js-field-has-assignee').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsAssignee,
+          );
+        },
+        'click .js-field-has-assigned-by'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsAssignedBy = !this.currentBoard
+            .allowsAssignedBy;
+          this.currentBoard.setAllowsAssignedBy(
+            this.currentBoard.allowsAssignedBy,
+          );
+          $(`.js-field-has-assigned-by ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsAssignedBy,
+          );
+          $('.js-field-has-assigned-by').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsAssignedBy,
+          );
+        },
+        'click .js-field-has-requested-by'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsRequestedBy = !this.currentBoard
+            .allowsRequestedBy;
+          this.currentBoard.setAllowsRequestedBy(
+            this.currentBoard.allowsRequestedBy,
+          );
+          $(`.js-field-has-requested-by ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsRequestedBy,
+          );
+          $('.js-field-has-requested-by').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsRequestedBy,
+          );
+        },
+        'click .js-field-has-labels'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsLabels = !this.currentBoard.allowsLabels;
+          this.currentBoard.setAllowsLabels(this.currentBoard.allowsLabels);
+          $(`.js-field-has-labels ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsAssignee,
+          );
+          $('.js-field-has-labels').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsLabels,
+          );
+        },
+        'click .js-field-has-description-title'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsDescriptionTitle = !this.currentBoard
+            .allowsDescriptionTitle;
+          this.currentBoard.setAllowsDescriptionTitle(
+            this.currentBoard.allowsDescriptionTitle,
+          );
+          $(`.js-field-has-description-title ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsDescriptionTitle,
+          );
+          $('.js-field-has-description-title').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsDescriptionTitle,
+          );
+        },
+        'click .js-field-has-description-text'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsDescriptionText = !this.currentBoard
+            .allowsDescriptionText;
+          this.currentBoard.setAllowsDescriptionText(
+            this.currentBoard.allowsDescriptionText,
+          );
+          $(`.js-field-has-description-text ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsDescriptionText,
+          );
+          $('.js-field-has-description-text').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsDescriptionText,
+          );
+        },
+        'click .js-field-has-checklists'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsChecklists = !this.currentBoard
+            .allowsChecklists;
+          this.currentBoard.setAllowsChecklists(
+            this.currentBoard.allowsChecklists,
+          );
+          $(`.js-field-has-checklists ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsChecklists,
+          );
+          $('.js-field-has-checklists').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsChecklists,
+          );
+        },
+        'click .js-field-has-attachments'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsAttachments = !this.currentBoard
+            .allowsAttachments;
+          this.currentBoard.setAllowsAttachments(
+            this.currentBoard.allowsAttachments,
+          );
+          $(`.js-field-has-attachments ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsAttachments,
+          );
+          $('.js-field-has-attachments').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsAttachments,
+          );
+        },
+        'click .js-field-has-comments'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsComments = !this.currentBoard.allowsComments;
+          this.currentBoard.setAllowsComments(this.currentBoard.allowsComments);
+          $(`.js-field-has-comments ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsComments,
+          );
+          $('.js-field-has-comments').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsComments,
+          );
+        },
+        'click .js-field-has-activities'(evt) {
+          evt.preventDefault();
+          this.currentBoard.allowsActivities = !this.currentBoard
+            .allowsActivities;
+          this.currentBoard.setAllowsActivities(
+            this.currentBoard.allowsActivities,
+          );
+          $(`.js-field-has-activities ${MCB}`).toggleClass(
+            CKCLS,
+            this.currentBoard.allowsActivities,
+          );
+          $('.js-field-has-activities').toggleClass(
+            CKCLS,
+            this.currentBoard.allowsActivities,
+          );
+        },
+      },
+    ];
+  },
+}).register('boardCardSettingsPopup');
+
 BlazeComponent.extendComponent({
   onCreated() {
     this.error = new ReactiveVar('');
@@ -628,7 +1013,7 @@ BlazeComponent.extendComponent({
 }).register('addMemberPopup');
 
 Template.changePermissionsPopup.events({
-  'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'(
+  'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only, click .js-set-worker'(
     event,
   ) {
     const currentBoard = Boards.findOne(Session.get('currentBoard'));
@@ -638,11 +1023,13 @@ Template.changePermissionsPopup.events({
       'js-set-comment-only',
     );
     const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
+    const isWorker = $(event.currentTarget).hasClass('js-set-worker');
     currentBoard.setMemberPermission(
       memberId,
       isAdmin,
       isNoComments,
       isCommentOnly,
+      isWorker,
     );
     Popup.back(1);
   },
@@ -659,7 +1046,8 @@ Template.changePermissionsPopup.helpers({
     return (
       !currentBoard.hasAdmin(this.userId) &&
       !currentBoard.hasNoComments(this.userId) &&
-      !currentBoard.hasCommentOnly(this.userId)
+      !currentBoard.hasCommentOnly(this.userId) &&
+      !currentBoard.hasWorker(this.userId)
     );
   },
 
@@ -679,6 +1067,13 @@ Template.changePermissionsPopup.helpers({
     );
   },
 
+  isWorker() {
+    const currentBoard = Boards.findOne(Session.get('currentBoard'));
+    return (
+      !currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
+    );
+  },
+
   isLastAdmin() {
     const currentBoard = Boards.findOne(Session.get('currentBoard'));
     return (

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

@@ -109,7 +109,7 @@
     color: darken(white, 40%)
 
 .board-sidebar
-  width: 248px
+  width: 548px
   right: -@width
   transition: top .1s, right .1s, width .1s
 

+ 30 - 24
client/components/sidebar/sidebarArchives.jade

@@ -2,54 +2,60 @@ template(name="archivesSidebar")
   if isArchiveReady.get
     +basicTabs(tabs=tabs)
       +tabContent(slug="cards")
-        p.quiet
-          a.js-restore-all-cards {{_ 'restore-all'}}
-          | -
-          a.js-delete-all-cards {{_ 'delete-all'}}
+        unless isWorker
+          p.quiet
+            a.js-restore-all-cards {{_ 'restore-all'}}
+            | -
+            a.js-delete-all-cards {{_ 'delete-all'}}
         each archivedCards
           .minicard-wrapper.js-minicard
             +minicard(this)
           if currentUser.isBoardMember
-            p.quiet
-              a.js-restore-card {{_ 'restore'}}
-              | -
-              a.js-delete-card {{_ 'delete'}}
+            unless isWorker
+              p.quiet
+                a.js-restore-card {{_ 'restore'}}
+                | -
+                a.js-delete-card {{_ 'delete'}}
             if cardIsInArchivedList
               p.quiet.small ({{_ 'warn-list-archived'}})
         else
           p.no-items-message {{_ 'no-archived-cards'}}
 
       +tabContent(slug="lists")
-        p.quiet
-          a.js-restore-all-lists {{_ 'restore-all'}}
-          | -
-          a.js-delete-all-lists {{_ 'delete-all'}}
+        unless isWorker
+          p.quiet
+            a.js-restore-all-lists {{_ 'restore-all'}}
+            | -
+            a.js-delete-all-lists {{_ 'delete-all'}}
         ul.archived-lists
           each archivedLists
             li.archived-lists-item
               = title
               if currentUser.isBoardMember
-                p.quiet
-                  a.js-restore-list {{_ 'restore'}}
-                  | -
-                  a.js-delete-list {{_ 'delete'}}
+                unless isWorker
+                  p.quiet
+                    a.js-restore-list {{_ 'restore'}}
+                    | -
+                    a.js-delete-list {{_ 'delete'}}
           else
             li.no-items-message {{_ 'no-archived-lists'}}
 
       +tabContent(slug="swimlanes")
-        p.quiet
-          a.js-restore-all-swimlanes {{_ 'restore-all'}}
-          | -
-          a.js-delete-all-swimlanes {{_ 'delete-all'}}
+        unless isWorker
+          p.quiet
+            a.js-restore-all-swimlanes {{_ 'restore-all'}}
+            | -
+            a.js-delete-all-swimlanes {{_ 'delete-all'}}
         ul.archived-lists
           each archivedSwimlanes
             li.archived-lists-item
               = title
               if currentUser.isBoardMember
-                p.quiet
-                  a.js-restore-swimlane {{_ 'restore'}}
-                  | -
-                  a.js-delete-swimlane {{_ 'delete'}}
+                unless isWorker
+                  p.quiet
+                    a.js-restore-swimlane {{_ 'restore'}}
+                    | -
+                    a.js-delete-swimlane {{_ 'delete'}}
           else
             li.no-items-message {{_ 'no-archived-swimlanes'}}
   else

+ 9 - 0
client/components/sidebar/sidebarArchives.js

@@ -139,3 +139,12 @@ BlazeComponent.extendComponent({
     ];
   },
 }).register('archivesSidebar');
+
+Template.archivesSidebar.helpers({
+  isWorker() {
+    const currentBoard = Boards.findOne(Session.get('currentBoard'));
+    return (
+      !currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
+    );
+  },
+});

+ 8 - 7
client/components/sidebar/sidebarFilters.jade

@@ -117,13 +117,14 @@ template(name="multiselectionSidebar")
               i.fa.fa-check
             else if someSelectedElementHave 'member' _id
               i.fa.fa-ellipsis-h
-  hr
-  a.sidebar-btn.js-move-selection
-    i.fa.fa-share
-    span {{_ 'move-selection'}}
-  a.sidebar-btn.js-archive-selection
-    i.fa.fa-archive
-    span {{_ 'archive-selection'}}
+  unless currentUser.isWorker
+    hr
+    a.sidebar-btn.js-move-selection
+      i.fa.fa-share
+      span {{_ 'move-selection'}}
+    a.sidebar-btn.js-archive-selection
+      i.fa.fa-archive
+      span {{_ 'archive-selection'}}
 
 template(name="disambiguateMultiLabelPopup")
   p {{_ 'what-to-do'}}

+ 10 - 1
client/components/swimlanes/swimlaneHeader.js

@@ -1,3 +1,5 @@
+import { Cookies } from 'meteor/ostrio:cookies';
+const cookies = new Cookies();
 const { calculateIndexData } = Utils;
 
 let swimlaneColors;
@@ -30,7 +32,14 @@ BlazeComponent.extendComponent({
 
 Template.swimlaneHeader.helpers({
   showDesktopDragHandles() {
-    return Meteor.user().hasShowDesktopDragHandles();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).showDesktopDragHandles;
+    } else if (cookies.has('showDesktopDragHandles')) {
+      return true;
+    } else {
+      return false;
+    }
   },
 });
 

+ 32 - 30
client/components/swimlanes/swimlanes.jade

@@ -1,24 +1,25 @@
 template(name="swimlane")
   .swimlane
     +swimlaneHeader
-  .swimlane.js-lists.js-swimlane
-    if isMiniScreen
-      if currentListIsInThisSwimlane _id
-        +list(currentList)
-      unless currentList
+  unless collapseSwimlane
+    .swimlane.js-lists.js-swimlane
+      if isMiniScreen
+        if currentListIsInThisSwimlane _id
+          +list(currentList)
+        unless currentList
+          each lists
+            +miniList(this)
+          if currentUser.isBoardMember
+            unless currentUser.isCommentOnly
+              +addListForm
+      else
         each lists
-          +miniList(this)
+          +list(this)
+          if currentCardIsInThisList _id ../_id
+            +cardDetails(currentCard)
         if currentUser.isBoardMember
           unless currentUser.isCommentOnly
             +addListForm
-    else
-      each lists
-        +list(this)
-        if currentCardIsInThisList _id ../_id
-          +cardDetails(currentCard)
-      if currentUser.isBoardMember
-        unless currentUser.isCommentOnly
-          +addListForm
 
 template(name="listsGroup")
   .swimlane.list-group.js-lists
@@ -42,19 +43,20 @@ template(name="listsGroup")
           +addListForm
 
 template(name="addListForm")
-  .list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}")
-    .list-header-add
-      +inlinedForm(autoclose=false)
-        input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}"
-          autocomplete="off" autofocus)
-        .edit-controls.clearfix
-          button.primary.confirm(type="submit") {{_ 'save'}}
-          unless currentBoard.isTemplatesBoard
-            unless currentBoard.isTemplateBoard
-              span.quiet
-                | {{_ 'or'}}
-                a.js-list-template {{_ 'template'}}
-      else
-        a.open-list-composer.js-open-inlined-form
-          i.fa.fa-plus
-          | {{_ 'add-list'}}
+  unless currentUser.isWorker
+    .list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}")
+      .list-header-add
+        +inlinedForm(autoclose=false)
+          input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}"
+            autocomplete="off" autofocus)
+          .edit-controls.clearfix
+            button.primary.confirm(type="submit") {{_ 'save'}}
+            unless currentBoard.isTemplatesBoard
+              unless currentBoard.isTemplateBoard
+                span.quiet
+                  | {{_ 'or'}}
+                  a.js-list-template {{_ 'template'}}
+        else
+          a.open-list-composer.js-open-inlined-form
+            i.fa.fa-plus
+            | {{_ 'add-list'}}

+ 75 - 20
client/components/swimlanes/swimlanes.js

@@ -1,3 +1,5 @@
+import { Cookies } from 'meteor/ostrio:cookies';
+const cookies = new Cookies();
 const { calculateIndex, enableClickOnTouch } = Utils;
 
 function currentListIsInThisSwimlane(swimlaneId) {
@@ -14,7 +16,7 @@ function currentCardIsInThisList(listId, swimlaneId) {
   if (
     currentUser &&
     currentUser.profile &&
-    currentUser.profile.boardView === 'board-view-swimlanes'
+    Utils.boardView() === 'board-view-swimlanes'
   )
     return (
       currentCard &&
@@ -53,18 +55,6 @@ function initSortable(boardComponent, $listsDom) {
     },
   };
 
-  if (Utils.isMiniScreen) {
-    $listsDom.sortable({
-      handle: '.js-list-handle',
-    });
-  }
-
-  if (!Utils.isMiniScreen && showDesktopDragHandles) {
-    $listsDom.sortable({
-      handle: '.js-list-header',
-    });
-  }
-
   $listsDom.sortable({
     tolerance: 'pointer',
     helper: 'clone',
@@ -104,19 +94,64 @@ function initSortable(boardComponent, $listsDom) {
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   }
 
-  // Disable drag-dropping while in multi-selection mode, or if the current user
-  // is not a board member
   boardComponent.autorun(() => {
+    let showDesktopDragHandles = false;
+    currentUser = Meteor.user();
+    if (currentUser) {
+      showDesktopDragHandles = (currentUser.profile || {})
+        .showDesktopDragHandles;
+    } else if (cookies.has('showDesktopDragHandles')) {
+      showDesktopDragHandles = true;
+    } else {
+      showDesktopDragHandles = false;
+    }
+
+    if (!Utils.isMiniScreen() && showDesktopDragHandles) {
+      $listsDom.sortable({
+        handle: '.js-list-handle',
+      });
+    } else if (!Utils.isMiniScreen() && !showDesktopDragHandles) {
+      $listsDom.sortable({
+        handle: '.js-list-header',
+      });
+    }
+
     const $listDom = $listsDom;
     if ($listDom.data('sortable')) {
       $listsDom.sortable(
         'option',
         'disabled',
-        MultiSelection.isActive() || !userIsMember(),
+        // Disable drag-dropping when user is not member/is worker/is miniscreen
+        !userIsMember(),
+        // Not disable drag-dropping while in multi-selection mode
+        // MultiSelection.isActive() || !userIsMember(),
+      );
+    }
+
+    if ($listDom.data('sortable')) {
+      $listsDom.sortable(
+        'option',
+        'disabled',
+        // Disable drag-dropping when user is not member/is worker/is miniscreen
+        Meteor.user().isWorker(),
+        // Not disable drag-dropping while in multi-selection mode
+        // MultiSelection.isActive() || !userIsMember(),
+      );
+    }
+
+    if ($listDom.data('sortable')) {
+      $listsDom.sortable(
+        'option',
+        'disabled',
+        // Disable drag-dropping when user is not member/is worker/is miniscreen
+        Utils.isMiniScreen(),
+        // Not disable drag-dropping while in multi-selection mode
+        // MultiSelection.isActive() || !userIsMember(),
       );
     }
   });
@@ -163,8 +198,20 @@ BlazeComponent.extendComponent({
           // the user will legitimately expect to be able to select some text with
           // his mouse.
 
+          let showDesktopDragHandles = false;
+          currentUser = Meteor.user();
+          if (currentUser) {
+            showDesktopDragHandles = (currentUser.profile || {})
+              .showDesktopDragHandles;
+          } else if (cookies.has('showDesktopDragHandles')) {
+            showDesktopDragHandles = true;
+          } else {
+            showDesktopDragHandles = false;
+          }
+
           const noDragInside = ['a', 'input', 'textarea', 'p'].concat(
-            Util.isMiniScreen || (!Util.isMiniScreen && showDesktopDragHandles)
+            Utils.isMiniScreen() ||
+              (!Utils.isMiniScreen() && showDesktopDragHandles)
               ? ['.js-list-handle', '.js-swimlane-header-handle']
               : ['.js-list-header'],
           );
@@ -245,13 +292,21 @@ BlazeComponent.extendComponent({
 
 Template.swimlane.helpers({
   showDesktopDragHandles() {
-    return Meteor.user().hasShowDesktopDragHandles();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).showDesktopDragHandles;
+    } else if (cookies.has('showDesktopDragHandles')) {
+      return true;
+    } else {
+      return false;
+    }
   },
   canSeeAddList() {
     return (
       Meteor.user() &&
       Meteor.user().isBoardMember() &&
-      !Meteor.user().isCommentOnly()
+      !Meteor.user().isCommentOnly() &&
+      !Meteor.user().isWorker()
     );
   },
 });

+ 49 - 13
client/components/swimlanes/swimlanes.styl

@@ -1,5 +1,41 @@
 @import 'nib'
 
+/*
+// Minimize swimlanes start https://www.w3schools.com/howto/howto_js_accordion.asp
+
+.accordion
+  cursor: pointer
+  width: 30px
+  height: 20px
+  border: none
+  outline: none
+  font-size: 18px
+  transition: 0.4s
+  padding-top: 0px
+  margin-top: 0px
+
+.accordion:after
+  // Unicode triagle right:
+  content: '\25B6'
+  color: #777
+  font-weight: bold
+  float: left
+
+.active:after
+  // Unicode triangle down:
+  content: '\25BC'
+
+.panel
+  width: 100%
+  max-height: 0
+  overflow: hidden
+  transition: max-height 0.2s ease-out
+  margin: 0px
+  padding: 0px
+
+// Minimize swimlanes end https://www.w3schools.com/howto/howto_js_accordion.asp
+*/
+
 .swimlane
   // Even if this background color is the same as the body we can't leave it
   // transparent, because that won't work during a swimlane drag.
@@ -25,22 +61,22 @@
       cursor: grabbing
 
   .swimlane-header-wrap
-    display: flex;
-    flex-direction: row;
-    flex: 1 0 100%;
-    background-color: #ccc;
+    display: flex
+    flex-direction: row
+    flex: 1 0 100%
+    background-color: #ccc
 
     .swimlane-header
-      font-size: 14px;
+      font-size: 14px
       padding: 5px 5px
-      font-weight: bold;
-      min-height: 9px;
-      width: 100%;
-      overflow: hidden;
-      -o-text-overflow: ellipsis;
-      text-overflow: ellipsis;
-      word-wrap: break-word;
-      text-align: center;
+      font-weight: bold
+      min-height: 9px
+      width: 100%
+      overflow: hidden
+      -o-text-overflow: ellipsis
+      text-overflow: ellipsis
+      word-wrap: break-word
+      text-align: center
 
     .swimlane-header-menu
       position: absolute

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

@@ -73,6 +73,7 @@ template(name="cardMemberPopup")
         p.quiet @{{ user.username }}
     ul.pop-over-list
       if currentUser.isNotCommentOnly
+        if currentUser.isNotWorker
           li: a.js-remove-member {{_ 'remove-member-from-card'}}
 
       if $eq currentUser._id user._id

+ 47 - 18
client/components/users/userHeader.jade

@@ -13,21 +13,46 @@ template(name="headerUserBar")
 template(name="memberMenuPopup")
   ul.pop-over-list
     with currentUser
-      li: a.js-edit-profile {{_ 'edit-profile'}}
-      li: a.js-change-settings {{_ 'change-settings'}}
-      li: a.js-change-avatar {{_ 'edit-avatar'}}
+      li
+        a.js-edit-profile
+          i.fa.fa-user
+          | {{_ 'edit-profile'}}
+      li
+        a.js-change-settings
+          i.fa.fa-cog
+          | {{_ 'change-settings'}}
+      li
+        a.js-change-avatar
+          i.fa.fa-picture-o
+          | {{_ 'edit-avatar'}}
       unless isSandstorm
-        li: a.js-change-password {{_ 'changePasswordPopup-title'}}
-        li: a.js-change-language {{_ 'changeLanguagePopup-title'}}
+        li
+          a.js-change-password
+            i.fa.fa-key
+            | {{_ 'changePasswordPopup-title'}}
+        li
+          a.js-change-language
+            i.fa.fa-flag
+            | {{_ 'changeLanguagePopup-title'}}
     if currentUser.isAdmin
-      li: a.js-go-setting(href="{{pathFor 'setting'}}") {{_ 'admin-panel'}}
-  hr
-  ul.pop-over-list
-    li: a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") {{_ 'templates'}}
+      li
+        a.js-go-setting(href="{{pathFor 'setting'}}")
+          i.fa.fa-lock
+          | {{_ 'admin-panel'}}
+  unless currentUser.isWorker
+    hr
+    ul.pop-over-list
+      li
+        a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}")
+          i.fa.fa-clone
+          | {{_ 'templates'}}
   unless isSandstorm
     hr
     ul.pop-over-list
-      li: a.js-logout {{_ 'log-out'}}
+      li
+        a.js-logout
+          i.fa.fa-sign-out
+          | {{_ 'log-out'}}
 
 template(name="editProfilePopup")
   form
@@ -75,21 +100,25 @@ template(name="changeSettingsPopup")
   ul.pop-over-list
     li
       a.js-toggle-system-messages
+        i.fa.fa-comments-o
         | {{_ 'hide-system-messages'}}
         if hiddenSystemMessages
           i.fa.fa-check
     li
       a.js-toggle-desktop-drag-handles
+        i.fa.fa-arrows
         | {{_ 'show-desktop-drag-handles'}}
         if showDesktopDragHandles
           i.fa.fa-check
-    li
-      label.bold
-        | {{_ 'show-cards-minimum-count'}}
-      input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
-      input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
-
+    unless currentUser.isWorker
+      li
+        label.bold
+          i.fa.fa-sort-numeric-asc
+          | {{_ 'show-cards-minimum-count'}}
+        input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
+        input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
 
 template(name="userDeletePopup")
-  p {{_ 'delete-user-confirm-popup'}}
-  button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
+  unless currentUser.isWorker
+    p {{_ 'delete-user-confirm-popup'}}
+    button.js-confirm.negate.full(type="submit") {{_ 'delete'}}

+ 82 - 11
client/components/users/userHeader.js

@@ -1,3 +1,6 @@
+import { Cookies } from 'meteor/ostrio:cookies';
+const cookies = new Cookies();
+
 Template.headerUserBar.events({
   'click .js-open-header-member-menu': Popup.open('memberMenu'),
   'click .js-change-avatar': Popup.open('changeAvatar'),
@@ -5,10 +8,22 @@ Template.headerUserBar.events({
 
 Template.memberMenuPopup.helpers({
   templatesBoardId() {
-    return Meteor.user().getTemplatesBoardId();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return Meteor.user().getTemplatesBoardId();
+    } else {
+      // No need to getTemplatesBoardId on public board
+      return false;
+    }
   },
   templatesBoardSlug() {
-    return Meteor.user().getTemplatesBoardSlug();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return Meteor.user().getTemplatesBoardSlug();
+    } else {
+      // No need to getTemplatesBoardSlug() on public board
+      return false;
+    }
   },
 });
 
@@ -30,13 +45,31 @@ Template.memberMenuPopup.events({
 
 Template.editProfilePopup.helpers({
   allowEmailChange() {
-    return AccountSettings.findOne('accounts-allowEmailChange').booleanValue;
+    Meteor.call('AccountSettings.allowEmailChange', (_, result) => {
+      if (result) {
+        return true;
+      } else {
+        return false;
+      }
+    });
   },
   allowUserNameChange() {
-    return AccountSettings.findOne('accounts-allowUserNameChange').booleanValue;
+    Meteor.call('AccountSettings.allowUserNameChange', (_, result) => {
+      if (result) {
+        return true;
+      } else {
+        return false;
+      }
+    });
   },
   allowUserDelete() {
-    return AccountSettings.findOne('accounts-allowUserDelete').booleanValue;
+    Meteor.call('AccountSettings.allowUserDelete', (_, result) => {
+      if (result) {
+        return true;
+      } else {
+        return false;
+      }
+    });
   },
 });
 
@@ -162,22 +195,55 @@ Template.changeLanguagePopup.events({
 
 Template.changeSettingsPopup.helpers({
   showDesktopDragHandles() {
-    return Meteor.user().hasShowDesktopDragHandles();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).showDesktopDragHandles;
+    } else if (cookies.has('showDesktopDragHandles')) {
+      return true;
+    } else {
+      return false;
+    }
   },
   hiddenSystemMessages() {
-    return Meteor.user().hasHiddenSystemMessages();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).hasHiddenSystemMessages;
+    } else if (cookies.has('hasHiddenSystemMessages')) {
+      return true;
+    } else {
+      return false;
+    }
   },
   showCardsCountAt() {
-    return Meteor.user().getLimitToShowCardsCount();
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return Meteor.user().getLimitToShowCardsCount();
+    } else {
+      return cookies.get('limitToShowCardsCount');
+    }
   },
 });
 
 Template.changeSettingsPopup.events({
   'click .js-toggle-desktop-drag-handles'() {
-    Meteor.call('toggleDesktopDragHandles');
+    currentUser = Meteor.user();
+    if (currentUser) {
+      Meteor.call('toggleDesktopDragHandles');
+    } else if (cookies.has('showDesktopDragHandles')) {
+      cookies.remove('showDesktopDragHandles');
+    } else {
+      cookies.set('showDesktopDragHandles', 'true');
+    }
   },
   'click .js-toggle-system-messages'() {
-    Meteor.call('toggleSystemMessages');
+    currentUser = Meteor.user();
+    if (currentUser) {
+      Meteor.call('toggleSystemMessages');
+    } else if (cookies.has('hasHiddenSystemMessages')) {
+      cookies.remove('hasHiddenSystemMessages');
+    } else {
+      cookies.set('hasHiddenSystemMessages', 'true');
+    }
   },
   'click .js-apply-show-cards-at'(event, templateInstance) {
     event.preventDefault();
@@ -186,7 +252,12 @@ Template.changeSettingsPopup.events({
       10,
     );
     if (!isNaN(minLimit)) {
-      Meteor.call('changeLimitToShowCardsCount', minLimit);
+      currentUser = Meteor.user();
+      if (currentUser) {
+        Meteor.call('changeLimitToShowCardsCount', minLimit);
+      } else {
+        cookies.set('limitToShowCardsCount', minLimit);
+      }
       Popup.back();
     }
   },

+ 28 - 0
client/lib/keyboard.js

@@ -70,6 +70,30 @@ Mousetrap.bind('space', evt => {
   }
 });
 
+// XXX This shortcut should also work when hovering over a card in board view
+Mousetrap.bind('c', evt => {
+  if (!Session.get('currentCard')) {
+    return;
+  }
+
+  const currentUserId = Meteor.userId();
+  if (currentUserId === null) {
+    return;
+  }
+
+  if (
+    Meteor.user().isBoardMember() &&
+    !Meteor.user().isCommentOnly() &&
+    !Meteor.user().isWorker()
+  ) {
+    const card = Cards.findOne(Session.get('currentCard'));
+    card.archive();
+    // We should prevent scrolling in card when spacebar is clicked
+    // This should do it according to Mousetrap docs, but it doesn't
+    evt.preventDefault();
+  }
+});
+
 Template.keyboardShortcuts.helpers({
   mapping: [
     {
@@ -104,5 +128,9 @@ Template.keyboardShortcuts.helpers({
       keys: ['SPACE'],
       action: 'shortcut-assign-self',
     },
+    {
+      keys: ['C'],
+      action: 'archive-card',
+    },
   ],
 });

+ 8 - 3
client/lib/textComplete.js

@@ -48,6 +48,11 @@ $.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) {
   return this;
 };
 
-EscapeActions.register('textcomplete', () => {}, () => dropdownMenuIsOpened, {
-  noClickEscapeOn: '.textcomplete-dropdown',
-});
+EscapeActions.register(
+  'textcomplete',
+  () => {},
+  () => dropdownMenuIsOpened,
+  {
+    noClickEscapeOn: '.textcomplete-dropdown',
+  },
+);

+ 66 - 0
client/lib/utils.js

@@ -1,4 +1,40 @@
+import { Cookies } from 'meteor/ostrio:cookies';
+const cookies = new Cookies();
+
 Utils = {
+  setBoardView(view) {
+    currentUser = Meteor.user();
+    if (currentUser) {
+      Meteor.user().setBoardView(view);
+    } else if (view === 'board-view-lists') {
+      cookies.set('boardView', 'board-view-lists'); //true
+    } else if (view === 'board-view-swimlanes') {
+      cookies.set('boardView', 'board-view-swimlanes'); //true
+    } else if (view === 'board-view-cal') {
+      cookies.set('boardView', 'board-view-cal'); //true
+    }
+  },
+
+  unsetBoardView() {
+    cookies.remove('boardView');
+    cookies.remove('collapseSwimlane');
+  },
+
+  boardView() {
+    currentUser = Meteor.user();
+    if (currentUser) {
+      return (currentUser.profile || {}).boardView;
+    } else if (cookies.get('boardView') === 'board-view-lists') {
+      return 'board-view-lists';
+    } else if (cookies.get('boardView') === 'board-view-swimlanes') {
+      return 'board-view-swimlanes';
+    } else if (cookies.get('boardView') === 'board-view-cal') {
+      return 'board-view-cal';
+    } else {
+      return false;
+    }
+  },
+
   // XXX We should remove these two methods
   goBoardId(_id) {
     const board = Boards.findOne(_id);
@@ -117,8 +153,38 @@ Utils = {
   // in a small window (even on desktop), Wekan run in compact mode.
   // we can easily debug with a small window of desktop browser. :-)
   isMiniScreen() {
+    // OLD WINDOW WIDTH DETECTION:
     this.windowResizeDep.depend();
     return $(window).width() <= 800;
+
+    // NEW TOUCH DEVICE DETECTION:
+    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
+
+    /*
+    var hasTouchScreen = false;
+    if ("maxTouchPoints" in navigator) {
+        hasTouchScreen = navigator.maxTouchPoints > 0;
+    } else if ("msMaxTouchPoints" in navigator) {
+        hasTouchScreen = navigator.msMaxTouchPoints > 0;
+    } else {
+        var mQ = window.matchMedia && matchMedia("(pointer:coarse)");
+        if (mQ && mQ.media === "(pointer:coarse)") {
+            hasTouchScreen = !!mQ.matches;
+        } else if ('orientation' in window) {
+            hasTouchScreen = true; // deprecated, but good fallback
+        } else {
+            // Only as a last resort, fall back to user agent sniffing
+            var UA = navigator.userAgent;
+            hasTouchScreen = (
+                /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) ||
+                /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA)
+            );
+        }
+    }
+    */
+    //if (hasTouchScreen)
+    //    document.getElementById("exampleButton").style.padding="1em";
+    //return false;
   },
 
   calculateIndexData(prevData, nextData, nItems = 1) {

+ 7 - 3
config/accounts.js

@@ -38,9 +38,13 @@ AccountsTemplates.configure({
   },
 });
 
-['signIn', 'signUp', 'resetPwd', 'forgotPwd', 'enrollAccount'].forEach(
-  routeName => AccountsTemplates.configureRoute(routeName),
-);
+[
+  'signIn',
+  'signUp',
+  'resetPwd',
+  'forgotPwd',
+  'enrollAccount',
+].forEach(routeName => AccountsTemplates.configureRoute(routeName));
 
 // We display the form to change the password in a popup window that already
 // have a title, so we unset the title automatically displayed by useraccounts.

+ 32 - 8
docker-compose.yml

@@ -93,14 +93,14 @@ services:
     #-------------------------------------------------------------------------------------
     # ==== MONGODB AND METEOR VERSION ====
     # a) For Wekan Meteor 1.8.x version at master branch, use mongo 4.x
-    image: mongo:4.0.12
+    image: mongo:4.2.2
     # b) For Wekan Meteor 1.6.x version at devel branch.
     # Only for Snap and Sandstorm while they are not upgraded yet to Meteor 1.8.x
     #image: mongo:3.2.21
     #-------------------------------------------------------------------------------------
     container_name: wekan-db
     restart: always
-    command: mongod --smallfiles --oplogSize 128
+    command: mongod --oplogSize 128
     networks:
       - wekan-tier
     expose:
@@ -238,7 +238,7 @@ services:
       #---------------------------------------------------------------
       # ==== RICH TEXT EDITOR IN CARD COMMENTS ====
       # https://github.com/wekan/wekan/pull/2560
-      - RICHER_CARD_COMMENT_EDITOR=true
+      - RICHER_CARD_COMMENT_EDITOR=false
       #---------------------------------------------------------------
       # ==== CARD OPENED, SEND WEBHOOK MESSAGE ====
       # https://github.com/wekan/wekan/issues/2518
@@ -342,6 +342,31 @@ services:
       # Tthe claim name you want to map to the email field:
       #- OAUTH2_EMAIL_MAP=email
       #-----------------------------------------------------------------
+      # ==== OAUTH2 Nextcloud ====
+      # 1) Register the application with Nextcloud: https://your.nextcloud/settings/admin/security
+      #    Make sure you capture the application ID as well as generate a secret key.
+      # 2) Configure the environment variables. This differs slightly
+      #     by installation type, but make sure you have the following:
+      #- OAUTH2_ENABLED=true
+      # OAuth2 login style: popup or redirect.
+      #- OAUTH2_LOGIN_STYLE=redirect
+      # Application GUID captured during app registration:
+      #- OAUTH2_CLIENT_ID=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
+      # Secret key generated during app registration:
+      #- OAUTH2_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+      #- OAUTH2_SERVER_URL=https://your-nextcloud.tld
+      #- OAUTH2_AUTH_ENDPOINT=/index.php/apps/oauth2/authorize
+      #- OAUTH2_USERINFO_ENDPOINT=/ocs/v2.php/cloud/user?format=json
+      #- OAUTH2_TOKEN_ENDPOINT=/index.php/apps/oauth2/api/v1/token
+      # The claim name you want to map to the unique ID field:
+      #- OAUTH2_ID_MAP=id
+      # The claim name you want to map to the username field:
+      #- OAUTH2_USERNAME_MAP=id
+      # The claim name you want to map to the full name field:
+      #- OAUTH2_FULLNAME_MAP=display-name
+      # Tthe claim name you want to map to the email field:
+      #- OAUTH2_EMAIL_MAP=email
+      #-----------------------------------------------------------------
       # ==== OAUTH2 KEYCLOAK ====
       # https://github.com/wekan/wekan/wiki/Keycloak  <== MAPPING INFO, REQUIRED
       #- OAUTH2_ENABLED=true
@@ -440,10 +465,10 @@ services:
       # If the sync of the users should be done in the background
       #- LDAP_BACKGROUND_SYNC=false
       #
-      # At which interval does the background task sync in milliseconds.
-      # Leave this unset, so it uses default, and does not crash.
-      # https://github.com/wekan/wekan/issues/2354#issuecomment-515305722
-      - LDAP_BACKGROUND_SYNC_INTERVAL=''
+      # At which interval does the background task sync.
+      # The format must be as specified in:
+      # https://bunkat.github.io/later/parsers.html#text
+      #- LDAP_BACKGROUND_SYNC_INTERVAL='every 1 hour'
       #
       #- LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false
       #
@@ -559,7 +584,6 @@ services:
       # example : LOGOUT_ON_MINUTES=55
       #- LOGOUT_ON_MINUTES=
       #-------------------------------------------------------------------
-
     depends_on:
       - wekandb
 

+ 4 - 4
fix-download-unicode/cfs_access-point.txt

@@ -451,14 +451,14 @@ FS.HTTP.Handlers.Get = function (ref) {
         if(userAgent.indexOf('msie') >= 0 || userAgent.indexOf('trident') >= 0 || userAgent.indexOf('chrome') >= 0) {
             ref.filename =  encodeURIComponent(ref.filename);
         } else if(userAgent.indexOf('firefox') >= 0) {
-            ref.filename = new Buffer(ref.filename).toString('binary');
+            ref.filename = Buffer.from(ref.filename).toString('binary');
         } else {
             /* safari*/
-            ref.filename = new Buffer(ref.filename).toString('binary');
-        }   
+            ref.filename = Buffer.from(ref.filename).toString('binary');
+        }
    } catch (ex){
         ref.filename = 'tempfix';
-   } 
+   }
    return originalHandler.call(this, ref);
 };
                                                                                                                       // 221

+ 1 - 1
helm/wekan/requirements.yaml

@@ -1,5 +1,5 @@
 dependencies:
 - name: mongodb-replicaset
-  version: 3.6.x
+  version: 3.11.x
   repository: "https://kubernetes-charts.storage.googleapis.com/"
   condition: mongodb-replicaset.enabled

+ 1 - 1
helm/wekan/templates/_helpers.tpl

@@ -75,7 +75,7 @@ else use user-provided URL.
 {{- if (index .Values "mongodb-replicaset" "enabled") -}}
 {{- $count := (int (index .Values "mongodb-replicaset" "replicas")) -}}
 {{- $release := .Release.Name -}}
-mongodb://{{- range $v := until $count }}{{ $release }}-mongodb-replicaset-{{ $v }}.{{ $release }}-mongodb-replicaset:27017{{ if ne $v (sub $count 1) }},{{- end -}}{{- end -}}?replicaSet={{ index .Values "mongodb-replicaset" "replicaSetName" }}
+mongodb://{{ $release }}-mongodb-replicaset:27017/admin?replicaSet={{ index .Values "mongodb-replicaset" "replicaSetName" }}
 {{- else -}}
 {{- index .Values "mongodb-replicaset" "url" -}}
 {{- end -}}

+ 1 - 1
helm/wekan/templates/ingress.yaml

@@ -35,6 +35,6 @@ spec:
           - path: {{ $ingressPath }}
             backend:
               serviceName: {{ $fullName }}
-              servicePort: http
+              servicePort: 80
   {{- end }}
 {{- end }}

+ 9 - 2
i18n/ar.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "تعديل وضوح الرؤية",
   "boardChangeWatchPopup-title": "تغيير المتابعة",
   "boardMenuPopup-title": "Board Settings",
+  "boardChangeViewPopup-title": "عرض اللوحات",
   "boards": "لوحات",
   "board-view": "عرض اللوحات",
   "board-view-cal": "التقويم",
   "board-view-swimlanes": "خطوط السباحة",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "القائمات",
   "bucket-example": "مثل « todo list » على سبيل المثال",
   "cancel": "إلغاء",
@@ -221,6 +223,8 @@
   "comment-only-desc": "يمكن التعليق على بطاقات فقط.",
   "no-comments": "No comments",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "حاسوب",
   "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -579,8 +583,9 @@
   "default": "Default",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 9 - 2
i18n/bg.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Change Visibility",
   "boardChangeWatchPopup-title": "Промени наблюдаването",
   "boardMenuPopup-title": "Board Settings",
+  "boardChangeViewPopup-title": "Board View",
   "boards": "Табла",
   "board-view": "Board View",
   "board-view-cal": "Календар",
   "board-view-swimlanes": "Коридори",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Списъци",
   "bucket-example": "Like “Bucket List” for example",
   "cancel": "Cancel",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Може да коментира само в карти.",
   "no-comments": "Няма коментари",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Компютър",
   "confirm-subtask-delete-dialog": "Сигурен ли сте, че искате да изтриете подзадачата?",
   "confirm-checklist-delete-dialog": "Сигурни ли сте, че искате да изтриете този чеклист?",
@@ -579,8 +583,9 @@
   "default": "по подразбиране",
   "queue": "Опашка",
   "subtask-settings": "Настройки на Подзадачите",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Настройки за Подзадачите за това Табло",
-  "show-subtasks-field": "Картата може да има подзадачи",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 9 - 2
i18n/br.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Change Visibility",
   "boardChangeWatchPopup-title": "Change Watch",
   "boardMenuPopup-title": "Board Settings",
+  "boardChangeViewPopup-title": "Board View",
   "boards": "Boards",
   "board-view": "Board View",
   "board-view-cal": "Calendar",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Lists",
   "bucket-example": "Like “Bucket List” for example",
   "cancel": "Cancel",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Can comment on cards only.",
   "no-comments": "No comments",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Computer",
   "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -579,8 +583,9 @@
   "default": "Default",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 10 - 3
i18n/ca.i18n.json

@@ -78,7 +78,7 @@
   "add-attachment": "Afegeix adjunt",
   "add-board": "Afegeix Tauler",
   "add-card": "Afegeix Fitxa",
-  "add-swimlane": "Afegix Carril de Natació",
+  "add-swimlane": "Afegeix carril de natació",
   "add-subtask": "Afegir Subtasca",
   "add-checklist": "Afegeix checklist",
   "add-checklist-item": "Afegeix un ítem al checklist",
@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Canvia visibilitat",
   "boardChangeWatchPopup-title": "Canvia seguiment",
   "boardMenuPopup-title": "Board Settings",
+  "boardChangeViewPopup-title": "Visió del tauler",
   "boards": "Taulers",
   "board-view": "Visió del tauler",
   "board-view-cal": "Calendari",
   "board-view-swimlanes": "Carrils de Natació",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Llistes",
   "bucket-example": "Igual que “Bucket List”,  per exemple",
   "cancel": "Cancel·la",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Només pots fer comentaris a les fitxes",
   "no-comments": "Sense comentaris",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Ordinador",
   "confirm-subtask-delete-dialog": "Esteu segur que voleu eliminar la subtasca?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -579,8 +583,9 @@
   "default": "Default",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 15 - 8
i18n/cs.i18n.json

@@ -74,7 +74,7 @@
   "activity-checklist-completed-card": "dokončil(a) zaškrtávací seznam __checklist__ na kartě __card__ ve sloupci __list__ ve swimlane __swimlane__ na tablu __board__",
   "activity-checklist-uncompleted-card": "nedokončený seznam %s",
   "activity-editComment": "edited comment %s",
-  "activity-deleteComment": "deleted comment %s",
+  "activity-deleteComment": "smazat komentář %s",
   "add-attachment": "Přidat přílohu",
   "add-board": "Přidat tablo",
   "add-card": "Přidat kartu",
@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Upravit viditelnost",
   "boardChangeWatchPopup-title": "Změnit sledování",
   "boardMenuPopup-title": "Nastavení Tabla",
+  "boardChangeViewPopup-title": "Náhled tabla",
   "boards": "Tabla",
   "board-view": "Náhled tabla",
   "board-view-cal": "Kalendář",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Sloupce",
   "bucket-example": "Například \"O čem sním\"",
   "cancel": "Zrušit",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Může přidávat komentáře pouze do karet.",
   "no-comments": "Žádné komentáře",
   "no-comments-desc": "Nemůže vidět komentáře a aktivity",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Počítač",
   "confirm-subtask-delete-dialog": "Opravdu chcete smazat tento podúkol?",
   "confirm-checklist-delete-dialog": "Opravdu chcete smazat tento checklist?",
@@ -300,9 +304,9 @@
   "error-username-taken": "Toto uživatelské jméno již existuje",
   "error-email-taken": "Tento email byl již použit",
   "export-board": "Exportovat tablo",
-  "sort": "Sort",
+  "sort": "řadit",
   "sort-desc": "Click to Sort List",
-  "list-sort-by": "Sort the List By:",
+  "list-sort-by": "řadit seznam podle",
   "list-label-modifiedAt": "Last Access Time",
   "list-label-title": "Name of the List",
   "list-label-sort": "Your Manual Order",
@@ -532,7 +536,7 @@
   "no-name": "(Neznámé)",
   "Node_version": "Node verze",
   "Meteor_version": "Meteor version",
-  "MongoDB_version": "MongoDB version",
+  "MongoDB_version": "MongoDB verze",
   "MongoDB_storage_engine": "MongoDB storage engine",
   "MongoDB_Oplog_enabled": "MongoDB Oplog enabled",
   "OS_Arch": "OS Architektura",
@@ -579,8 +583,9 @@
   "default": "Výchozí",
   "queue": "Fronta",
   "subtask-settings": "Nastavení podúkolů",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Nastavení podúkolů tabla",
-  "show-subtasks-field": "Karty mohou mít podúkoly",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Vložit podúkoly do tohoto tabla",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Ukázat předka na minikartě",
@@ -699,9 +704,9 @@
   "r-set": "Set",
   "r-update": "Update",
   "r-datefield": "date field",
-  "r-df-start-at": "start",
+  "r-df-start-at": "začátek",
   "r-df-due-at": "due",
-  "r-df-end-at": "end",
+  "r-df-end-at": "konec",
   "r-df-received-at": "received",
   "r-to-current-datetime": "to current date/time",
   "r-remove-value-from": "Remove value from",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 9 - 2
i18n/da.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Change Visibility",
   "boardChangeWatchPopup-title": "Change Watch",
   "boardMenuPopup-title": "Board Settings",
+  "boardChangeViewPopup-title": "Board View",
   "boards": "Boards",
   "board-view": "Board View",
   "board-view-cal": "Calendar",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Lists",
   "bucket-example": "Like “Bucket List” for example",
   "cancel": "Cancel",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Can comment on cards only.",
   "no-comments": "No comments",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Computer",
   "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -579,8 +583,9 @@
   "default": "Default",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 13 - 6
i18n/de.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Sichtbarkeit ändern",
   "boardChangeWatchPopup-title": "Beobachtung ändern",
   "boardMenuPopup-title": "Boardeinstellungen",
+  "boardChangeViewPopup-title": "Boardansicht",
   "boards": "Boards",
   "board-view": "Boardansicht",
   "board-view-cal": "Kalender",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "Einklappen",
   "board-view-lists": "Listen",
   "bucket-example": "z.B. \"Löffelliste\"",
   "cancel": "Abbrechen",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Kann Karten nur kommentieren.",
   "no-comments": "Keine Kommentare",
   "no-comments-desc": "Kann keine Kommentare und Aktivitäten sehen.",
+  "worker": "Arbeiter",
+  "worker-desc": "Kann Karten nur verschieben, sich selbst zuweisen und kommentieren.",
   "computer": "Computer",
   "confirm-subtask-delete-dialog": "Wollen Sie die Teilaufgabe wirklich löschen?",
   "confirm-checklist-delete-dialog": "Wollen Sie die Checkliste wirklich löschen?",
@@ -301,11 +305,11 @@
   "error-email-taken": "E-Mail wird schon verwendet",
   "export-board": "Board exportieren",
   "sort": "Sortieren",
-  "sort-desc": "Zum sortieren der Liste, klicken",
+  "sort-desc": "Zum Sortieren der Liste klicken",
   "list-sort-by": "Sortieren der Liste nach:",
   "list-label-modifiedAt": "Letzte Zugriffszeit",
   "list-label-title": "Name der Liste",
-  "list-label-sort": "Deine manuelle Sortierung",
+  "list-label-sort": "Ihre manuelle Sortierung",
   "list-label-short-modifiedAt": "(Z)",
   "list-label-short-title": "(N)",
   "list-label-short-sort": "(M)",
@@ -579,8 +583,9 @@
   "default": "Standard",
   "queue": "Warteschlange",
   "subtask-settings": "Einstellungen für Teilaufgaben",
+  "card-settings": "Karten-Einstellungen",
   "boardSubtaskSettingsPopup-title": "Boardeinstellungen für Teilaufgaben",
-  "show-subtasks-field": "Karten können Teilaufgaben haben",
+  "boardCardSettingsPopup-title": "Karten-Einstellungen",
   "deposit-subtasks-board": "Teilaufgaben in diesem Board ablegen:",
   "deposit-subtasks-list": "Zielliste für hier abgelegte Teilaufgaben:",
   "show-parent-in-minicard": "Übergeordnetes Element auf Minikarte anzeigen:",
@@ -738,16 +743,18 @@
   "almostdue": "aktuelles Fälligkeitsdatum %s bevorstehend",
   "pastdue": "aktuelles Fälligkeitsdatum %s überschritten",
   "duenow": "aktuelles Fälligkeitsdatum %s heute",
-  "act-newDue": "__list__/__card__ hat seine 1. fällig Erinnerung [__board__]",
+  "act-newDue": "__list__/__card__ hat seine 1. fällige Erinnerung [__board__]",
   "act-withDue": "Erinnerung an Fällikgeit von __card__ [__board__]",
   "act-almostdue": "erinnernd an das aktuelle Fälligkeitszeitpunkt (__timeValue__) von __card__ ist bevorstehend",
   "act-pastdue": "erinnernd an das aktuelle Fälligkeitszeitpunkt (__timeValue__) von __card__ ist vorbei",
   "act-duenow": "erinnernd an das aktuelle Fälligkeitszeitpunkt (__timeValue__) von __card__ ist jetzt",
-  "act-atUserComment": "Du wurdest in [__board__] __list__/__card__ erwähnt",
+  "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.",
   "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",
   "assignee": "Zugewiesen",
-  "cardAssigneesPopup-title": "Zugewiesen"
+  "cardAssigneesPopup-title": "Zugewiesen",
+  "addmore-detail": "Eine detailliertere Beschreibung hinzufügen",
+  "show-on-card": "Zeige auf Karte"
 }

+ 9 - 2
i18n/el.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Αλλαγή Ορατότητας",
   "boardChangeWatchPopup-title": "Change Watch",
   "boardMenuPopup-title": "Ρυθμίσεις Πίνακα",
+  "boardChangeViewPopup-title": "Board View",
   "boards": "Πίνακες",
   "board-view": "Board View",
   "board-view-cal": "Ημερολόγιο",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Λίστες",
   "bucket-example": "Like “Bucket List” for example",
   "cancel": "Ακύρωση",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Can comment on cards only.",
   "no-comments": "Χωρίς σχόλια",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Υπολογιστής",
   "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -579,8 +583,9 @@
   "default": "Default",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 9 - 2
i18n/en-GB.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Change Visibility",
   "boardChangeWatchPopup-title": "Change Watch",
   "boardMenuPopup-title": "Board Settings",
+  "boardChangeViewPopup-title": "Board View",
   "boards": "Boards",
   "board-view": "Board View",
   "board-view-cal": "Calendar",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Lists",
   "bucket-example": "Like “Bucket List” for example",
   "cancel": "Cancel",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Can comment on cards only.",
   "no-comments": "No comments",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Computer",
   "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -579,8 +583,9 @@
   "default": "Default",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 9 - 2
i18n/en.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Change Visibility",
   "boardChangeWatchPopup-title": "Change Watch",
   "boardMenuPopup-title": "Board Settings",
+  "boardChangeViewPopup-title": "Board View",
   "boards": "Boards",
   "board-view": "Board View",
   "board-view-cal": "Calendar",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Lists",
   "bucket-example": "Like “Bucket List” for example",
   "cancel": "Cancel",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Can comment on cards only.",
   "no-comments": "No comments",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Computer",
   "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -579,8 +583,9 @@
   "default": "Default",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -752,5 +757,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 37 - 30
i18n/eo.i18n.json

@@ -128,14 +128,16 @@
   "board-private-info": "This board will be <strong>private</strong>.",
   "board-public-info": "This board will be <strong>public</strong>.",
   "boardChangeColorPopup-title": "Change Board Background",
-  "boardChangeTitlePopup-title": "Rename Board",
+  "boardChangeTitlePopup-title": "Renomi tavolon",
   "boardChangeVisibilityPopup-title": "Change Visibility",
   "boardChangeWatchPopup-title": "Change Watch",
   "boardMenuPopup-title": "Board Settings",
+  "boardChangeViewPopup-title": "Board View",
   "boards": "Boards",
   "board-view": "Board View",
   "board-view-cal": "Calendar",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Listoj",
   "bucket-example": "Like “Bucket List” for example",
   "cancel": "Cancel",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Can comment on cards only.",
   "no-comments": "No comments",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Komputilo",
   "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -246,8 +250,8 @@
   "custom-field-dropdown-options": "List Options",
   "custom-field-dropdown-options-placeholder": "Press enter to add more options",
   "custom-field-dropdown-unknown": "(unknown)",
-  "custom-field-number": "Number",
-  "custom-field-text": "Text",
+  "custom-field-number": "Nombro",
+  "custom-field-text": "Teksto",
   "custom-fields": "Custom Fields",
   "date": "Dato",
   "decline": "Decline",
@@ -323,13 +327,13 @@
   "filter-to-selection": "Filter to selection",
   "advanced-filter-label": "Advanced Filter",
   "advanced-filter-description": "Advanced Filter allows to write a string containing following operators: == != <= >= && || ( ) A space is used as a separator between the Operators. You can filter for all Custom Fields by typing their names and values. For Example: Field1 == Value1. Note: If fields or values contains spaces, you need to encapsulate them into single quotes. For Example: 'Field 1' == 'Value 1'. For single control characters (' \\/) to be skipped, you can use \\. For example: Field1 == I\\'m. Also you can combine multiple conditions. For Example: F1 == V1 || F1 == V2. Normally all operators are interpreted from left to right. You can change the order by placing brackets. For Example: F1 == V1 && ( F2 == V2 || F2 == V3 ). Also you can search text fields using regex: F1 == /Tes.*/i",
-  "fullname": "Full Name",
+  "fullname": "Plena nomo",
   "header-logo-title": "Go back to your boards page.",
   "hide-system-messages": "Hide system messages",
   "headerBarCreateBoardPopup-title": "Krei tavolon",
   "home": "Hejmo",
   "import": "Importi",
-  "link": "Link",
+  "link": "Ligilo",
   "import-board": "import board",
   "import-board-c": "Import board",
   "import-board-title-trello": "Import board from Trello",
@@ -431,11 +435,11 @@
   "remove-member-pop": "Remove __name__ (__username__) from __boardTitle__? The member will be removed from all cards on this board. They will receive a notification.",
   "removeMemberPopup-title": "Remove Member?",
   "rename": "Renomi",
-  "rename-board": "Rename Board",
+  "rename-board": "Renomi tavolon",
   "restore": "Forigi",
   "save": "Savi",
   "search": "Serĉi",
-  "rules": "Rules",
+  "rules": "Reguloj",
   "search-cards": "Search from card/list titles and descriptions on this board",
   "search-example": "Text to search for?",
   "select-color": "Select Color",
@@ -470,7 +474,7 @@
   "title": "Titolo",
   "tracking": "Tracking",
   "tracking-info": "You will be notified of any changes to those cards you are involved as creator or member.",
-  "type": "Type",
+  "type": "Tipo",
   "unassign-member": "Unassign member",
   "unsaved-description": "You have an unsaved description.",
   "unwatch": "Unwatch",
@@ -551,8 +555,8 @@
   "show-field-on-card": "Show this field on card",
   "automatically-field-on-card": "Auto create field to all cards",
   "showLabel-field-on-card": "Show field label on minicard",
-  "yes": "Yes",
-  "no": "No",
+  "yes": "Jes",
+  "no": "Ne",
   "accounts": "Accounts",
   "accounts-allowEmailChange": "Allow Email Change",
   "accounts-allowUserNameChange": "Allow Username Change",
@@ -576,11 +580,12 @@
   "boardDeletePopup-title": "Delete Board?",
   "delete-board": "Delete Board",
   "default-subtasks-board": "Subtasks for __board__ board",
-  "default": "Default",
+  "default": "Defaŭlto",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -600,13 +605,13 @@
   "activity-delete-attach-card": "deleted an attachment",
   "activity-set-customfield": "set custom field '%s' to '%s' in %s",
   "activity-unset-customfield": "unset custom field '%s' in %s",
-  "r-rule": "Rule",
+  "r-rule": "Regulo",
   "r-add-trigger": "Add trigger",
   "r-add-action": "Add action",
   "r-board-rules": "Board rules",
-  "r-add-rule": "Add rule",
+  "r-add-rule": "Aldoni regulon",
   "r-view-rule": "View rule",
-  "r-delete-rule": "Delete rule",
+  "r-delete-rule": "Forigi regulon",
   "r-new-rule-name": "New rule title",
   "r-no-rules": "No rules",
   "r-when-a-card": "When a card",
@@ -615,7 +620,7 @@
   "r-added-to": "added to",
   "r-removed-from": "Removed from",
   "r-the-board": "the board",
-  "r-list": "list",
+  "r-list": "listo",
   "set-filter": "Set Filter",
   "r-moved-to": "Moved to",
   "r-moved-from": "Moved from",
@@ -627,7 +632,7 @@
   "r-list-name": "list name",
   "r-when-a-member": "When a member is",
   "r-when-the-member": "When the member",
-  "r-name": "name",
+  "r-name": "nomo",
   "r-when-a-attach": "When an attachment",
   "r-when-a-checklist": "When a checklist is",
   "r-when-the-checklist": "When the checklist",
@@ -645,12 +650,12 @@
   "r-unarchive": "Restore from Archive",
   "r-card": "card",
   "r-add": "Aldoni",
-  "r-remove": "Remove",
+  "r-remove": "Forigi",
   "r-label": "label",
-  "r-member": "member",
+  "r-member": "membro",
   "r-remove-all": "Remove all members from the card",
   "r-set-color": "Set color to",
-  "r-checklist": "checklist",
+  "r-checklist": "kontrololisto",
   "r-check-all": "Check all",
   "r-uncheck-all": "Uncheck all",
   "r-items-check": "items of checklist",
@@ -660,7 +665,7 @@
   "r-of-checklist": "of checklist",
   "r-send-email": "Send an email",
   "r-to": "to",
-  "r-subject": "subject",
+  "r-subject": "temo",
   "r-rule-details": "Rule details",
   "r-d-move-to-top-gen": "Move card to top of its list",
   "r-d-move-to-top-spec": "Move card to top of list",
@@ -668,8 +673,8 @@
   "r-d-move-to-bottom-spec": "Move card to bottom of list",
   "r-d-send-email": "Send email",
   "r-d-send-email-to": "to",
-  "r-d-send-email-subject": "subject",
-  "r-d-send-email-message": "message",
+  "r-d-send-email-subject": "temo",
+  "r-d-send-email-message": "mesaĝo",
   "r-d-archive": "Move card to Archive",
   "r-d-unarchive": "Restore card from Archive",
   "r-d-add-label": "Add label",
@@ -677,18 +682,18 @@
   "r-create-card": "Create new card",
   "r-in-list": "in list",
   "r-in-swimlane": "in swimlane",
-  "r-d-add-member": "Add member",
-  "r-d-remove-member": "Remove member",
-  "r-d-remove-all-member": "Remove all member",
+  "r-d-add-member": "Aldoni membron",
+  "r-d-remove-member": "Forigi membron",
+  "r-d-remove-all-member": "Forigi ĉiujn membrojn",
   "r-d-check-all": "Check all items of a list",
   "r-d-uncheck-all": "Uncheck all items of a list",
   "r-d-check-one": "Check item",
   "r-d-uncheck-one": "Uncheck item",
   "r-d-check-of-list": "of checklist",
-  "r-d-add-checklist": "Add checklist",
-  "r-d-remove-checklist": "Remove checklist",
+  "r-d-add-checklist": "Aldoni kontrololiston",
+  "r-d-remove-checklist": "Forigi kontrololiston",
   "r-by": "by",
-  "r-add-checklist": "Add checklist",
+  "r-add-checklist": "Aldoni kontrololiston",
   "r-with-items": "with items",
   "r-items-list": "item1,item2,item3",
   "r-add-swimlane": "Add swimlane",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 9 - 2
i18n/es-AR.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Cambiar Visibilidad",
   "boardChangeWatchPopup-title": "Alternar Seguimiento",
   "boardMenuPopup-title": "Opciones del Tablero",
+  "boardChangeViewPopup-title": "Vista de Tablero",
   "boards": "Tableros",
   "board-view": "Vista de Tablero",
   "board-view-cal": "Calendario",
   "board-view-swimlanes": "Calles",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Listas",
   "bucket-example": "Como \"Lista de Contenedores\" por ejemplo",
   "cancel": "Cancelar",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Puede comentar en tarjetas solamente.",
   "no-comments": "Sin comentarios",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Computadora",
   "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -579,8 +583,9 @@
   "default": "Default",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 10 - 3
i18n/es.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Cambiar visibilidad",
   "boardChangeWatchPopup-title": "Cambiar vigilancia",
   "boardMenuPopup-title": "Preferencias del tablero",
+  "boardChangeViewPopup-title": "Vista del tablero",
   "boards": "Tableros",
   "board-view": "Vista del tablero",
   "board-view-cal": "Calendario",
   "board-view-swimlanes": "Carriles",
+  "board-view-collapse": "Contraer",
   "board-view-lists": "Listas",
   "bucket-example": "Como “Cosas por hacer” por ejemplo",
   "cancel": "Cancelar",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Solo puedes comentar en las tarjetas.",
   "no-comments": "No hay comentarios",
   "no-comments-desc": "No se pueden mostrar comentarios ni actividades.",
+  "worker": "Trabajador",
+  "worker-desc": "Solo puede mover tarjetas, asignarse a la tarjeta y comentar.",
   "computer": "el ordenador",
   "confirm-subtask-delete-dialog": "¿Seguro que quieres eliminar la subtarea?",
   "confirm-checklist-delete-dialog": "¿Seguro que quieres eliminar la lista de verificación?",
@@ -579,8 +583,9 @@
   "default": "Por defecto",
   "queue": "Cola",
   "subtask-settings": "Preferencias de las subtareas",
+  "card-settings": "Preferencias de la tarjeta",
   "boardSubtaskSettingsPopup-title": "Preferencias de las subtareas del tablero",
-  "show-subtasks-field": "Las tarjetas pueden tener subtareas",
+  "boardCardSettingsPopup-title": "Preferencias de la tarjeta",
   "deposit-subtasks-board": "Depositar subtareas en este tablero:",
   "deposit-subtasks-list": "Lista de destino para subtareas depositadas aquí:",
   "show-parent-in-minicard": "Mostrar el padre en una minitarjeta:",
@@ -748,6 +753,8 @@
   "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",
-  "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "assignee": "Asignado",
+  "cardAssigneesPopup-title": "Asignado",
+  "addmore-detail": "Añadir una descripción detallada",
+  "show-on-card": "Mostrar en la tarjeta"
 }

+ 9 - 2
i18n/eu.i18n.json

@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Aldatu ikusgaitasuna",
   "boardChangeWatchPopup-title": "Aldatu ikuskatzea",
   "boardMenuPopup-title": "Board Settings",
+  "boardChangeViewPopup-title": "Board View",
   "boards": "Arbelak",
   "board-view": "Board View",
   "board-view-cal": "Calendar",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "Collapse",
   "board-view-lists": "Zerrendak",
   "bucket-example": "Esaterako \"Pertz zerrenda\"",
   "cancel": "Utzi",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Iruzkinak txarteletan soilik egin ditzake",
   "no-comments": "No comments",
   "no-comments-desc": "Can not see comments and activities.",
+  "worker": "Worker",
+  "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Ordenagailua",
   "confirm-subtask-delete-dialog": "Are you sure you want to delete subtask?",
   "confirm-checklist-delete-dialog": "Are you sure you want to delete checklist?",
@@ -579,8 +583,9 @@
   "default": "Default",
   "queue": "Queue",
   "subtask-settings": "Subtasks Settings",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "Board Subtasks Settings",
-  "show-subtasks-field": "Cards can have subtasks",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "Deposit subtasks to this board:",
   "deposit-subtasks-list": "Landing list for subtasks deposited here:",
   "show-parent-in-minicard": "Show parent in minicard:",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 128 - 121
i18n/fa.i18n.json

@@ -1,45 +1,45 @@
 {
   "accept": "پذیرش",
   "act-activity-notify": "اعلان فعالیت",
-  "act-addAttachment": "added attachment __attachment__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-deleteAttachment": "deleted attachment __attachment__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addSubtask": "added subtask __subtask__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addedLabel": "Added label __label__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removeLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removedLabel": "Removed label __label__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addChecklist": "added checklist __checklist__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addChecklistItem": "added checklist item __checklistItem__ to checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removeChecklist": "removed checklist __checklist__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removeChecklistItem": "removed checklist item __checklistItem__ from checklist __checkList__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-checkedItem": "checked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-uncheckedItem": "unchecked __checklistItem__ of checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-completeChecklist": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-uncompleteChecklist": "uncompleted checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-addComment": "commented on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-editComment": "edited comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-deleteComment": "deleted comment on card __card__: __comment__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-createBoard": "created board __board__",
-  "act-createSwimlane": "created swimlane __swimlane__ to board __board__",
-  "act-createCard": "created card __card__ to list __list__ at swimlane __swimlane__ at board __board__",
-  "act-createCustomField": "created custom field __customField__ at board __board__",
-  "act-deleteCustomField": "deleted custom field __customField__ at board __board__",
-  "act-setCustomField": "edited custom field __customField__: __customFieldValue__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-createList": "added list __list__ to board __board__",
-  "act-addBoardMember": "added member __member__ to board __board__",
-  "act-archivedBoard": "Board __board__ moved to Archive",
-  "act-archivedCard": "Card __card__ at list __list__ at swimlane __swimlane__ at board __board__ moved to Archive",
-  "act-archivedList": "List __list__ at swimlane __swimlane__ at board __board__ moved to Archive",
-  "act-archivedSwimlane": "Swimlane __swimlane__ at board __board__ moved to Archive",
-  "act-importBoard": "imported board __board__",
-  "act-importCard": "imported card  __card__ to list __list__ at swimlane __swimlane__ at board __board__",
-  "act-importList": "imported list __list__ to swimlane __swimlane__ at board __board__",
-  "act-joinMember": "added member __member__ to card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
-  "act-moveCard": "moved card __card__ at board __board__ from list __oldList__ at swimlane __oldSwimlane__ to list __list__ at swimlane __swimlane__",
-  "act-moveCardToOtherBoard": "moved card __card__ from list __oldList__ at swimlane __oldSwimlane__ at board __oldBoard__ to list __list__ at swimlane __swimlane__ at board __board__",
-  "act-removeBoardMember": "removed member __member__ from board __board__",
-  "act-restoredCard": "restored card __card__ to list __list__ at swimlane __swimlane__ at board __board__",
-  "act-unjoinMember": "removed member __member__ from card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
+  "act-addAttachment": "ضمیمه __attachment__ را به کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ اضافه کرد",
+  "act-deleteAttachment": "ضمیمه __attachment__ را از کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ حذف کرد",
+  "act-addSubtask": "زیر وظیفه __subtask__ را به کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ اضافه کرد",
+  "act-addLabel": "برچسب __label__ را به کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ اضافه کرد",
+  "act-addedLabel": "برچسب __label__ را به کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ اضافه کرد",
+  "act-removeLabel": "برچسب __label__ را از کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ حذف کرد",
+  "act-removedLabel": "برچسب __label__ را از کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ حذف کرد",
+  "act-addChecklist": "سیاهه __checklist__ را به کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ اضافه کرد",
+  "act-addChecklistItem": "چک لیست __checklistItem__ را به سیاهه __checklist__ در کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ اضافه کرد",
+  "act-removeChecklist": "سیاهه __checklist__ را از کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ حذف کرد",
+  "act-removeChecklistItem": "چک لیست __checklistItem__ را از سیاهه __checkList__ در کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ حذف کرد",
+  "act-checkedItem": "چک لیست __checklistItem__ را از سیاهه __checklist__ در کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ تیک زد",
+  "act-uncheckedItem": "چک لیست __checklistItem__ را از سیاهه __checklist__ در کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ بدونِ تیک کرد",
+  "act-completeChecklist": "سیاهه __checklist__ را در کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ کامل کرد",
+  "act-uncompleteChecklist": "سیاهه __checklist__ را در کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ ناقص کرد",
+  "act-addComment": "روی کارت __card__ نظر داد: __comment__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__",
+  "act-editComment": "نظر روی کارت __card__ را ویرایش کرد: __comment__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__",
+  "act-deleteComment": "نظر روی کارت __card__ را حذف کرد: __comment__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__",
+  "act-createBoard": "برد __board__ را ایجاد کرد",
+  "act-createSwimlane": "مسیر شناور __swimlane__ را در برد __board__ ایجاد کرد",
+  "act-createCard": "کارت __card__ را در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ ایجاد کرد",
+  "act-createCustomField": "فیلد شخصی __customField__ را در برد __board__ ایجاد کرد",
+  "act-deleteCustomField": "فیلد شخصی __customField__ را در برد __board__ حذف کرد",
+  "act-setCustomField": "فیلد شخصی __customField__ را ویرایش کرد: __customFieldValue__ در کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__",
+  "act-createList": "لیست __list__ را به برد __board__ اضافه کرد",
+  "act-addBoardMember": "عضو __member__ را به برد __board__ اضافه کرد",
+  "act-archivedBoard": "برد __board__ را بایگانی کرد",
+  "act-archivedCard": "کارت __card__ را در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ بایگانی کرد",
+  "act-archivedList": "لیست __list__ را در مسیر شناور __swimlane__ در برد __board__ بایگانی کرد",
+  "act-archivedSwimlane": "مسیر شناور __swimlane__ را در برد __board__ بایگانی کرد",
+  "act-importBoard": "برد __board__ را وارد کرد",
+  "act-importCard": "کارت __card__ را به لیست __list__ در مسیر شناور __swimlane__ در برد __board__ وارد کرد",
+  "act-importList": "لیست __list__ را به مسیر شناور __swimlane__ در برد __board__ وارد کرد",
+  "act-joinMember": "عضو __member__ را به کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ اضافه کرد",
+  "act-moveCard": "کارت __card__ را در برد __board__ از لیست __oldList__ در مسیر شناور __oldSwimlane__ به لیست __list__ در مسیر شناور __swimlane__ منتقل کرد",
+  "act-moveCardToOtherBoard": "کارت __card__ را از لیست __oldList__ در مسیر شناور __oldSwimlane__ در برد __oldBoard__ به لیست __list__ در مسیر شناور __swimlane__ در برد __board__ منتقل کرد",
+  "act-removeBoardMember": "عضو __member__ را از برد __board__ حذف کرد",
+  "act-restoredCard": "کارت __card__ را به لیست __list__ در مسیر شناور __swimlane__ در برد __board__ بازگرداند",
+  "act-unjoinMember": "عضو __member__ را از کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ حذف کرد",
   "act-withBoardTitle": "__board__",
   "act-withCardTitle": "[__board__] __card__",
   "actions": "اعمال",
@@ -64,21 +64,21 @@
   "activity-unchecked-item": "چک نشده  %s در چک لیست %s از %s",
   "activity-checklist-added": "سیاهه به %s اضافه شد",
   "activity-checklist-removed": "از چک لیست حذف گردید",
-  "activity-checklist-completed": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
+  "activity-checklist-completed": "سیاهه __checklist__ را در کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ کامل کرد",
   "activity-checklist-uncompleted": "تمام نشده ها در چک لیست %s از %s",
   "activity-checklist-item-added": "added checklist item to '%s' in %s",
   "activity-checklist-item-removed": "حذف شده از چک لیست  '%s' در %s",
   "add": "افزودن",
   "activity-checked-item-card": "چک شده  %s در چک لیست %s",
   "activity-unchecked-item-card": "چک نشده  %s در چک لیست %s",
-  "activity-checklist-completed-card": "completed checklist __checklist__ at card __card__ at list __list__ at swimlane __swimlane__ at board __board__",
+  "activity-checklist-completed-card": "سیاهه __checklist__ را در کارت __card__ در لیست __list__ در مسیر شناور __swimlane__ در برد __board__ کامل کرد",
   "activity-checklist-uncompleted-card": "چک لیست تمام نشده %s",
-  "activity-editComment": "edited comment %s",
-  "activity-deleteComment": "deleted comment %s",
+  "activity-editComment": "نظر ویرایش شد %s",
+  "activity-deleteComment": "نظر حذف شد %s",
   "add-attachment": "افزودن ضمیمه",
   "add-board": "افزودن برد",
   "add-card": "افزودن کارت",
-  "add-swimlane": "Add Swimlane",
+  "add-swimlane": "اضافه کردن مسیر شناور",
   "add-subtask": "افزودن زیر وظیفه",
   "add-checklist": "افزودن چک لیست",
   "add-checklist-item": "افزودن مورد به سیاهه",
@@ -89,11 +89,11 @@
   "added": "اضافه گردید",
   "addMemberPopup-title": "اعضا",
   "admin": "مدیر",
-  "admin-desc": "امکان دیدن و ویرایش کارتها،پاک کردن کاربران و تغییر تنظیمات برای تخته",
+  "admin-desc": "امکان دیدن و ویرایش کارتها، پاک کردن کاربران و تغییر تنظیمات برای برد.",
   "admin-announcement": "اعلان",
   "admin-announcement-active": "اعلان سراسری فعال",
   "admin-announcement-title": "اعلان از سوی مدیر",
-  "all-boards": "تمام تخته‌ها",
+  "all-boards": "تمام بردها",
   "and-n-other-card": "و __count__ کارت دیگر",
   "and-n-other-card_plural": "و __count__ کارت دیگر",
   "apply": "اعمال",
@@ -108,7 +108,7 @@
   "archiveBoardPopup-title": "انتقال برد به آرشیو؟",
   "archived-items": "بایگانی",
   "archived-boards": "برد های داخل آرشیو",
-  "restore-board": "بازیابی تخته",
+  "restore-board": "بازیابی برد",
   "no-archived-boards": "هیچ بردی داخل آرشیو نیست",
   "archives": "بایگانی",
   "template": "Template",
@@ -119,23 +119,25 @@
   "attachment-delete-pop": "حذف پیوست دایمی و بی بازگشت خواهد بود.",
   "attachmentDeletePopup-title": "آیا می خواهید ضمیمه را حذف کنید؟",
   "attachments": "ضمائم",
-  "auto-watch": "اضافه شدن خودکار دیده بانی تخته زمانی که ایجاد می شوند",
+  "auto-watch": "اضافه شدن خودکار دیده‌بانی بردها زمانی که ایجاد می‌شوند",
   "avatar-too-big": "تصویر کاربر بسیار بزرگ است ـ حداکثر۷۰ کیلوبایت ـ",
   "back": "بازگشت",
   "board-change-color": "تغییر رنگ",
   "board-nb-stars": "%s ستاره",
-  "board-not-found": "تخته مورد نظر پیدا نشد",
-  "board-private-info": "این تخته <strong>خصوصی</strong> خواهد بود.",
-  "board-public-info": "این تخته <strong>عمومی</strong> خواهد بود.",
-  "boardChangeColorPopup-title": "تغییر پس زمینه تخته",
-  "boardChangeTitlePopup-title": "تغییر نام تخته",
+  "board-not-found": "برد مورد نظر پیدا نشد",
+  "board-private-info": "این برد <strong>خصوصی</strong> خواهد بود.",
+  "board-public-info": "این برد <strong>عمومی</strong> خواهد بود.",
+  "boardChangeColorPopup-title": "تغییر پس زمینه برد",
+  "boardChangeTitlePopup-title": "تغییر نام برد",
   "boardChangeVisibilityPopup-title": "تغییر وضعیت نمایش",
   "boardChangeWatchPopup-title": "تغییر دیده بانی",
   "boardMenuPopup-title": "Board Settings",
-  "boards": "تخته‌ها",
-  "board-view": "نمایش تخته",
+  "boardChangeViewPopup-title": "نمایش برد",
+  "boards": "بردها",
+  "board-view": "نمایش برد",
   "board-view-cal": "تقویم",
   "board-view-swimlanes": "Swimlanes",
+  "board-view-collapse": "بستن",
   "board-view-lists": "فهرست‌ها",
   "bucket-example": "برای مثال چیزی شبیه \"لیست سبدها\"",
   "cancel": "انصراف",
@@ -170,7 +172,7 @@
   "casSignIn": "ورود با استفاده از CAS",
   "cardType-card": "کارت",
   "cardType-linkedCard": "کارت‌های مرتبط",
-  "cardType-linkedBoard": "تخته‌های مرتبط",
+  "cardType-linkedBoard": "برد مرتبط",
   "change": "تغییر",
   "change-avatar": "تغییر تصویر",
   "change-password": "تغییر کلمه عبور",
@@ -221,6 +223,8 @@
   "comment-only-desc": "فقط می‌تواند روی کارت‌ها نظر دهد.",
   "no-comments": "هیچ کامنتی موجود نیست",
   "no-comments-desc": "نظرات و فعالیت ها را نمی توان دید.",
+  "worker": "کارگر",
+  "worker-desc": "تنها میتوانید کارت ها را جابجا کنید، این را به یک کارت اضافه کنید.",
   "computer": "رایانه",
   "confirm-subtask-delete-dialog": "از حذف این زیر وظیفه اطمینان دارید؟",
   "confirm-checklist-delete-dialog": "مطمئنا چک لیست پاک شود؟",
@@ -232,8 +236,8 @@
   "copyChecklistToManyCardsPopup-instructions": "عنوان و توضیحات کارت مقصد در این قالب JSON",
   "copyChecklistToManyCardsPopup-format": "[ {\"title\": \"First card title\", \"description\":\"First card description\"}, {\"title\":\"Second card title\",\"description\":\"Second card description\"},{\"title\":\"Last card title\",\"description\":\"Last card description\"} ]",
   "create": "ایجاد",
-  "createBoardPopup-title": "ایجاد تخته",
-  "chooseBoardSourcePopup-title": "بارگذاری تخته",
+  "createBoardPopup-title": "ایجاد برد",
+  "chooseBoardSourcePopup-title": "بارگذاری برد",
   "createLabelPopup-title": "ایجاد برچسب",
   "createCustomField": "ایجاد فیلد",
   "createCustomFieldPopup-title": "ایجاد فیلد",
@@ -281,16 +285,16 @@
   "email-invalid": "رایانامه نادرست",
   "email-invite": "دعوت از طریق رایانامه",
   "email-invite-subject": "__inviter__ برای شما دعوت نامه ارسال کرده است",
-  "email-invite-text": "__User__ عزیز\n __inviter__ شما را به عضویت تخته  \"__board__\" برای همکاری دعوت کرده است.\nلطفا لینک زیر را دنبال کنید، باتشکر:\n__url__",
+  "email-invite-text": "__User__ عزیز\n __inviter__ شما را به عضویت برد \"__board__\" برای همکاری دعوت کرده است.\nلطفا لینک زیر را دنبال کنید، باتشکر:\n__url__",
   "email-resetPassword-subject": "تنظیم مجدد کلمه عبور در __siteName__",
   "email-resetPassword-text": "سلام __user__\nجهت تنظیم مجدد کلمه عبور آدرس زیر را دنبال نمایید، باتشکر:\n__url__",
   "email-sent": "نامه الکترونیکی فرستاده شد",
   "email-verifyEmail-subject": "تایید آدرس الکترونیکی شما در __siteName__",
   "email-verifyEmail-text": "سلام __user__\nبه منظور تایید آدرس الکترونیکی حساب خود، آدرس زیر را دنبال نمایید، باتشکر:\n__url__.",
   "enable-wip-limit": "Enable WIP Limit",
-  "error-board-doesNotExist": "تخته مورد نظر وجود ندارد",
-  "error-board-notAdmin": "شما جهت انجام آن باید مدیر تخته باشید",
-  "error-board-notAMember": "شما انجام آن ،اید عضو این تخته باشید.",
+  "error-board-doesNotExist": "برد مورد نظر وجود ندارد",
+  "error-board-notAdmin": "شما جهت انجام آن باید مدیر برد باشید",
+  "error-board-notAMember": "شما برای انجام آن، باید عضو این برد باشید",
   "error-json-malformed": "متن درغالب صحیح Json نمی باشد.",
   "error-json-schema": "داده های Json شما، شامل اطلاعات صحیح در غالب درستی نمی باشد.",
   "error-list-doesNotExist": "این لیست موجود نیست",
@@ -299,40 +303,40 @@
   "error-user-notCreated": "این کاربر ایجاد نشده است",
   "error-username-taken": "این نام کاربری استفاده شده است",
   "error-email-taken": "رایانامه توسط گیرنده دریافت شده است",
-  "export-board": "انتقال به بیرون تخته",
-  "sort": "Sort",
-  "sort-desc": "Click to Sort List",
-  "list-sort-by": "Sort the List By:",
-  "list-label-modifiedAt": "Last Access Time",
-  "list-label-title": "Name of the List",
-  "list-label-sort": "Your Manual Order",
+  "export-board": "انتقال به بیرون برد",
+  "sort": "مرتب سازی",
+  "sort-desc": "برای مرتب سازی لیست کلیک کنید",
+  "list-sort-by": "مرتب سازی لیست بر اساس:",
+  "list-label-modifiedAt": "زمان دسترسی قبلی",
+  "list-label-title": "نام لیست",
+  "list-label-sort": "دلخواه شما",
   "list-label-short-modifiedAt": "(L)",
   "list-label-short-title": "(N)",
   "list-label-short-sort": "(M)",
-  "filter": "صافی ـFilterـ",
-  "filter-cards": "Filter Cards or Lists",
-  "list-filter-label": "Filter List by Title",
-  "filter-clear": "حذف صافی ـFilterـ",
+  "filter": "صافی ـ فیلتر ـ",
+  "filter-cards": "فیلتر کارت‌ها یا لیست‌ها",
+  "list-filter-label": "فیلتر لیست بر اساس عنوان",
+  "filter-clear": "حذف صافی ـ فیلتر ـ",
   "filter-no-label": "بدون برچسب",
   "filter-no-member": "بدون عضو",
   "filter-no-custom-fields": "هیچ فیلدشخصی ای وجود ندارد",
-  "filter-show-archive": "Show archived lists",
-  "filter-hide-empty": "Hide empty lists",
-  "filter-on": "صافی ـFilterـ فعال است",
-  "filter-on-desc": "شما صافی ـFilterـ برای کارتهای تخته را روشن کرده اید. جهت ویرایش کلیک نمایید.",
-  "filter-to-selection": "صافی ـFilterـ برای موارد انتخابی",
-  "advanced-filter-label": "صافی پیشرفته",
+  "filter-show-archive": "نمایش لیست‌های آرشیو شده",
+  "filter-hide-empty": "مخفی کردن لیست‌های خالی",
+  "filter-on": "صافی ـ فیلتر ـ فعال است",
+  "filter-on-desc": "شما درحال صافی ـ فیلتر ـ کارت‌های این برد هستید. برای ویرایش فیلتر کلیک نمایید.",
+  "filter-to-selection": "صافی ـ فیلتر ـ برای موارد انتخابی",
+  "advanced-filter-label": "صافی ـ فیلتر ـ پیشرفته",
   "advanced-filter-description": "فیلتر پیشرفته اجازه می دهد تا برای نوشتن رشته حاوی اپراتورهای زیر: ==! = <=> = && || () یک فضای به عنوان یک جداساز بین اپراتورها استفاده می شود. با تایپ کردن نام ها و مقادیر آنها می توانید برای تمام زمینه های سفارشی فیلتر کنید. به عنوان مثال: Field1 == Value1. نکته: اگر فیلدها یا مقادیر حاوی فضاها باشند، شما باید آنها را به یک نقل قول کپسول کنید. برای مثال: 'فیلد 1' == 'مقدار 1'. برای تک تک کاراکترهای کنترل (\\\\) که می توانید از آنها استفاده کنید، می توانید از \\ استفاده کنید. به عنوان مثال: Field1 == I \\ 'm. همچنین شما می توانید شرایط مختلف را ترکیب کنید. برای مثال: F1 == V1 || F1 == V2. به طور معمول همه اپراتورها از چپ به راست تفسیر می شوند. شما می توانید سفارش را با قرار دادن براکت تغییر دهید. برای مثال: F1 == V1 && (F2 == V2 || F2 == V3). همچنین می توانید فیلدهای متنی را با استفاده از regex جستجو کنید: F1 == /Tes.*/i",
   "fullname": "نام و نام خانوادگی",
-  "header-logo-title": "بازگشت به صفحه تخته.",
+  "header-logo-title": "بازگشت به صفحه بردها.",
   "hide-system-messages": "عدم نمایش پیامهای سیستمی",
-  "headerBarCreateBoardPopup-title": "ایجاد تخته",
+  "headerBarCreateBoardPopup-title": "ایجاد برد",
   "home": "خانه",
   "import": "وارد کردن",
   "link": "ارتباط",
-  "import-board": "وارد کردن تخته",
-  "import-board-c": "وارد کردن تخته",
-  "import-board-title-trello": "وارد کردن تخته از Trello",
+  "import-board": "وارد کردن برد",
+  "import-board-c": "وارد کردن برد",
+  "import-board-title-trello": "وارد کردن برد از Trello",
   "import-board-title-wekan": "بارگذاری برد ها از آخرین خروجی",
   "import-sandstorm-backup-warning": "قبل از بررسی این داده ها را از صفحه اصلی صادر شده یا Trello وارد نمیکنید این دانه دوباره باز می شود و یا دوباره باز می شود، یا برد را پیدا نمی کنید، این بدان معنی است که از دست دادن اطلاعات.",
   "import-sandstorm-warning": "Imported board will delete all existing data on board and replace it with imported board.",
@@ -353,7 +357,7 @@
   "invalid-time": "زمان نامعتبر",
   "invalid-user": "کاربر نامعتیر",
   "joined": "متصل",
-  "just-invited": "هم اکنون، شما به این تخته دعوت شده اید.",
+  "just-invited": "هم اکنون، شما به این برد دعوت شده‌اید.",
   "keyboard-shortcuts": "میانبر کلیدها",
   "label-create": "ایجاد برچسب",
   "label-default": "%s برچسب(پیش فرض)",
@@ -361,7 +365,7 @@
   "labels": "برچسب ها",
   "language": "زبان",
   "last-admin-desc": "شما نمی توانید نقش ـroleـ را تغییر دهید چراکه باید حداقل یک مدیری وجود داشته باشد.",
-  "leave-board": "خروج از تخته",
+  "leave-board": "خروج از برد",
   "leave-board-pop": "Are you sure you want to leave __boardTitle__? You will be removed from all cards on this board.",
   "leaveBoardPopup-title": "Leave Board ?",
   "link-card": "ارجاع به این کارت",
@@ -394,8 +398,8 @@
   "multi-selection": "امکان چند انتخابی",
   "multi-selection-on": "حالت چند انتخابی روشن است",
   "muted": "بی صدا",
-  "muted-info": "شما هیچگاه از تغییرات این تخته مطلع نخواهید شد",
-  "my-boards": "تخته‌های من",
+  "muted-info": "شما هیچگاه از تغییرات این برد مطلع نخواهید شد",
+  "my-boards": "بردهای من",
   "name": "نام",
   "no-archived-cards": "هیچ کارتی در آرشیو موجود نمی باشد",
   "no-archived-lists": "هیچ لیستی در آرشیو موجود نمی باشد",
@@ -405,7 +409,7 @@
   "normal-desc": "امکان نمایش و تنظیم کارت بدون امکان تغییر تنظیمات",
   "not-accepted-yet": "دعوت نامه هنوز پذیرفته نشده است",
   "notify-participate": "اطلاع رسانی از هرگونه تغییر در کارتهایی که ایجاد کرده اید ویا عضو آن هستید",
-  "notify-watch": "اطلاع رسانی از هرگونه تغییر در تخته، لیست یا کارتهایی که از آنها دیده بانی میکنید",
+  "notify-watch": "اطلاع رسانی از هرگونه تغییر در بردها، لیست‌ها یا کارت‌هایی که از آنها دیده‌بانی می‌کنید",
   "optional": "انتخابی",
   "or": "یا",
   "page-maybe-private": "این صفحه ممکن است خصوصی باشد. شما با<a href='%s'>ورود</a> می‌توانید آن را ببینید.",
@@ -417,26 +421,26 @@
   "previewAttachedImagePopup-title": "پیش‌نمایش",
   "previewClipboardImagePopup-title": "پیش‌نمایش",
   "private": "خصوصی",
-  "private-desc": "این تخته خصوصی است. فقط تنها افراد اضافه شده به آن  می توانند مشاهده و ویرایش کنند.",
+  "private-desc": "این برد خصوصی است. فقط افراد اضافه شده به برد می‌توانند مشاهده و ویرایش کنند.",
   "profile": "حساب کاربری",
   "public": "عمومی",
-  "public-desc": "این تخته عمومی است. برای هر کسی با آدرس ویا جستجو درموتورها مانند گوگل قابل مشاهده است . فقط افرادی که به آن اضافه شده اند  امکان ویرایش دارند.",
-  "quick-access-description": "جهت افزودن یک تخته به اینجا،آنرا ستاره دار نمایید.",
+  "public-desc": "این برد عمومی است. برای هر کسی با آدرس و یا جستجو در موتورها مانند گوگل قابل مشاهده است. فقط افرادی که به برد اضافه شده‌اند امکان ویرایش دارند.",
+  "quick-access-description": "جهت افزودن یک برد به اینجا، آن را ستاره دار نمایید.",
   "remove-cover": "حذف کاور",
-  "remove-from-board": "حذف از تخته",
+  "remove-from-board": "حذف از برد",
   "remove-label": "حذف برچسب",
   "listDeletePopup-title": "حذف فهرست؟",
   "remove-member": "حذف عضو",
   "remove-member-from-card": "حذف از کارت",
-  "remove-member-pop": "آیا __name__ (__username__) را از __boardTitle__ حذف می کنید? کاربر از تمام کارت ها در این تخته حذف خواهد شد و آنها ازین اقدام مطلع خواهند شد.",
+  "remove-member-pop": "آیا __name__ (__username__) را از __boardTitle__ حذف می‌کنید؟ کاربر از تمام کارت‌ها در این برد حذف خواهد شد. آنها از این اقدام مطلع خواهند شد.",
   "removeMemberPopup-title": "آیا می خواهید کاربر را حذف کنید؟",
   "rename": "تغیر نام",
-  "rename-board": "تغییر نام تخته",
+  "rename-board": "تغییر نام برد",
   "restore": "بازیابی",
   "save": "ذخیره",
   "search": "جستجو",
   "rules": "قوانین",
-  "search-cards": "Search from card/list titles and descriptions on this board",
+  "search-cards": "جتستجو از عنوان لیست/کارت ها و توضیحات در این برد",
   "search-example": "متن مورد جستجو؟",
   "select-color": "انتخاب رنگ",
   "set-wip-limit-value": "تعیین بیشینه تعداد وظایف در این فهرست",
@@ -444,22 +448,22 @@
   "shortcut-assign-self": "اختصاص خود به کارت فعلی",
   "shortcut-autocomplete-emoji": "تکمیل خودکار شکلکها",
   "shortcut-autocomplete-members": "تکمیل خودکار کاربرها",
-  "shortcut-clear-filters": "حذف تمامی صافی ـfilterـ",
+  "shortcut-clear-filters": "حذف تمامی صافی‌ها ـ فیلترها ـ",
   "shortcut-close-dialog": "بستن محاوره",
   "shortcut-filter-my-cards": "کارت های من",
   "shortcut-show-shortcuts": "بالا آوردن میانبر این لیست",
-  "shortcut-toggle-filterbar": "ضامن نوار جداکننده صافی ـfilterـ",
-  "shortcut-toggle-sidebar": "ضامن نوار جداکننده تخته",
+  "shortcut-toggle-filterbar": "ضامن نوار جداکننده صافی ـ فیلتر ـ",
+  "shortcut-toggle-sidebar": "ضامن نوار جداکننده برد",
   "show-cards-minimum-count": "نمایش تعداد کارتها اگر لیست شامل بیشتراز",
   "sidebar-open": "بازکردن جداکننده",
   "sidebar-close": "بستن جداکننده",
   "signupPopup-title": "ایجاد یک کاربر",
-  "star-board-title": "برای ستاره دادن، کلیک کنید.این در بالای لیست تخته های شما نمایش داده خواهد شد.",
-  "starred-boards": "تخته های ستاره دار",
-  "starred-boards-description": "تخته های ستاره دار در بالای لیست تخته ها نمایش داده می شود.",
+  "star-board-title": "برای ستاره دار کردن این برد کلیک کنید. این در بالای لیست بردهای شما نمایش داده خواهد شد.",
+  "starred-boards": "بردهای ستاره دار",
+  "starred-boards-description": "بردهای ستاره دار در بالای لیست بردها نمایش داده می‌شود.",
   "subscribe": "عضوشدن",
   "team": "تیم",
-  "this-board": "این تخته",
+  "this-board": "این برد",
   "this-card": "این کارت",
   "spent-time-hours": "زمان صرف شده (ساعت)",
   "overtime-hours": "Overtime (hours)",
@@ -482,8 +486,8 @@
   "warn-list-archived": "اخطار:این کارت در یک لیست در آرشیو موجود می باشد",
   "watch": "دیده بانی",
   "watching": "درحال دیده بانی",
-  "watching-info": "شما از هر تغییری دراین تخته آگاه خواهید شد",
-  "welcome-board": "به این تخته خوش آمدید",
+  "watching-info": "شما از هر تغییری در این برد آگاه خواهید شد",
+  "welcome-board": "به این برد خوش آمدید",
   "welcome-swimlane": "Milestone 1",
   "welcome-list1": "پایه ای ها",
   "welcome-list2": "پیشرفته",
@@ -501,7 +505,7 @@
   "disable-self-registration": "‌غیرفعال‌سازی خودثبت‌نامی",
   "invite": "دعوت",
   "invite-people": "دعوت از افراد",
-  "to-boards": "به تخته(ها)",
+  "to-boards": "به برد(ها)",
   "email-addresses": "نشانی رایانامه",
   "smtp-host-description": "آدرس سرور SMTP ای که پست الکترونیکی شما برروی آن است",
   "smtp-port-description": "شماره درگاه ـPortـ ای که سرور SMTP شما جهت ارسال از آن استفاده می کند",
@@ -520,21 +524,21 @@
   "email-smtp-test-text": "با موفقیت، یک رایانامه را فرستادید",
   "error-invitation-code-not-exist": "چنین کد دعوتی یافت نشد",
   "error-notAuthorized": "شما مجاز به دیدن این صفحه نیستید.",
-  "webhook-title": "Webhook Name",
-  "webhook-token": "Token (Optional for Authentication)",
+  "webhook-title": "نام وب‌هوک",
+  "webhook-token": "توکن",
   "outgoing-webhooks": "Outgoing Webhooks",
-  "bidirectional-webhooks": "Two-Way Webhooks",
+  "bidirectional-webhooks": "وب‌هوک two-way",
   "outgoingWebhooksPopup-title": "Outgoing Webhooks",
   "boardCardTitlePopup-title": "فیلتر موضوع کارت",
-  "disable-webhook": "Disable This Webhook",
-  "global-webhook": "Global Webhooks",
+  "disable-webhook": "حذف این وب‌هوک",
+  "global-webhook": "وب‌هوک‌های سراسری",
   "new-outgoing-webhook": "New Outgoing Webhook",
   "no-name": "(ناشناخته)",
   "Node_version": "نسخه Node",
   "Meteor_version": "Meteor version",
-  "MongoDB_version": "MongoDB version",
-  "MongoDB_storage_engine": "MongoDB storage engine",
-  "MongoDB_Oplog_enabled": "MongoDB Oplog enabled",
+  "MongoDB_version": "ورژن MongoDB",
+  "MongoDB_storage_engine": "موتور ذخیره سازی MongoDB",
+  "MongoDB_Oplog_enabled": "MongoDB Oplog فعال",
   "OS_Arch": "OS Arch",
   "OS_Cpus": "OS CPU Count",
   "OS_Freemem": "OS Free Memory",
@@ -573,14 +577,15 @@
   "requested-by": "تقاضا شده توسط",
   "board-delete-notice": "حذف دائمی است شما تمام لیست ها، کارت ها و اقدامات مرتبط با این برد را از دست خواهید داد.",
   "delete-board-confirm-popup": "تمام لیست ها، کارت ها، برچسب ها و فعالیت ها حذف خواهند شد و شما نمی توانید محتوای برد را بازیابی کنید. هیچ واکنشی وجود ندارد",
-  "boardDeletePopup-title": "حذف تخته؟",
-  "delete-board": "حذف تخته",
+  "boardDeletePopup-title": "حذف برد؟",
+  "delete-board": "حذف برد",
   "default-subtasks-board": "ریزکار برای __board__ برد",
   "default": "پیش‌فرض",
   "queue": "صف",
   "subtask-settings": "تنظیمات ریزکارها",
+  "card-settings": "Card Settings",
   "boardSubtaskSettingsPopup-title": "تنظیمات ریزکار های برد",
-  "show-subtasks-field": "کارت می تواند ریزکار داشته باشد",
+  "boardCardSettingsPopup-title": "Card Settings",
   "deposit-subtasks-board": "افزودن ریزکار به برد:",
   "deposit-subtasks-list": "لیست برای ریزکار های افزوده شده",
   "show-parent-in-minicard": "نمایش خانواده در ریز کارت",
@@ -695,10 +700,10 @@
   "r-swimlane-name": "نام مسیر شناور",
   "r-board-note": "نکته: برای نمایش موارد ممکن کادر را خالی بگذارید.",
   "r-checklist-note": "نکته: چک‌لیست‌ها باید توسط کاما از یک‌دیگر جدا شوند.",
-  "r-when-a-card-is-moved": "دمانی که یک کارت به لیست دیگری منتقل شد",
+  "r-when-a-card-is-moved": "زمانی که یک کارت به لیست دیگری منتقل شد",
   "r-set": "Set",
-  "r-update": "Update",
-  "r-datefield": "date field",
+  "r-update": "به روز رسانی",
+  "r-datefield": "تاریخ",
   "r-df-start-at": "شروع",
   "r-df-due-at": "ناشی از",
   "r-df-end-at": "پایان",
@@ -749,5 +754,7 @@
   "hide-minicard-label-text": "Hide minicard label text",
   "show-desktop-drag-handles": "Show desktop drag handles",
   "assignee": "Assignee",
-  "cardAssigneesPopup-title": "Assignee"
+  "cardAssigneesPopup-title": "Assignee",
+  "addmore-detail": "Add a more detailed description",
+  "show-on-card": "Show on Card"
 }

+ 40 - 33
i18n/fi.i18n.json

@@ -4,10 +4,10 @@
   "act-addAttachment": "lisätty liite __attachment__ kortille __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
   "act-deleteAttachment": "poistettu liite __attachment__ kortilla __card__ listalla __list__ swimlanella  __swimlane__ taululla __board__",
   "act-addSubtask": "lisätty alitehtävä __subtask__ kortille __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
-  "act-addLabel": "Lisätty tunniste __label__ kortille __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
-  "act-addedLabel": "Lisätty tunniste __label__ kortille __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
-  "act-removeLabel": "Poistettu tunniste __label__ kortilta __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
-  "act-removedLabel": "Poistettu tunniste __label__ kortilta __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
+  "act-addLabel": "Lisätty nimilappu __label__ kortille __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
+  "act-addedLabel": "Lisätty nimilappu __label__ kortille __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
+  "act-removeLabel": "Poistettu nimilappu __label__ kortilta __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
+  "act-removedLabel": "Poistettu nimilappu __label__ kortilta __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
   "act-addChecklist": "lisätty tarkistuslista __checklist__ kortille __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
   "act-addChecklistItem": "lisätty tarkistuslistan kohta __checklistItem__ tarkistuslistalle __checklist__ kortilla __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
   "act-removeChecklist": "poistettu tarkistuslista __checklist__ kortilta __card__ listalla __list__ swimlanella __swimlane__ taululla __board__",
@@ -83,7 +83,7 @@
   "add-checklist": "Lisää tarkistuslista",
   "add-checklist-item": "Lisää kohta tarkistuslistaan",
   "add-cover": "Lisää kansi",
-  "add-label": "Lisää tunniste",
+  "add-label": "Lisää nimilappu",
   "add-list": "Lisää lista",
   "add-members": "Lisää jäseniä",
   "added": "Lisätty",
@@ -132,10 +132,12 @@
   "boardChangeVisibilityPopup-title": "Muokkaa näkyvyyttä",
   "boardChangeWatchPopup-title": "Muokkaa seuraamista",
   "boardMenuPopup-title": "Tauluasetukset",
+  "boardChangeViewPopup-title": "Taulunäkymä",
   "boards": "Taulut",
   "board-view": "Taulunäkymä",
   "board-view-cal": "Kalenteri",
   "board-view-swimlanes": "Swimlanet",
+  "board-view-collapse": "Pienennä",
   "board-view-lists": "Listat",
   "bucket-example": "Kuten “Laatikko lista” esimerkiksi",
   "cancel": "Peruuta",
@@ -150,9 +152,9 @@
   "card-spent": "Käytetty aika",
   "card-edit-attachments": "Muokkaa liitetiedostoja",
   "card-edit-custom-fields": "Muokkaa mukautettuja kenttiä",
-  "card-edit-labels": "Muokkaa tunnisteita",
+  "card-edit-labels": "Muokkaa nimilappuja",
   "card-edit-members": "Muokkaa jäseniä",
-  "card-labels-title": "Muokkaa kortin tunnisteita.",
+  "card-labels-title": "Muokkaa kortin nimilappuja.",
   "card-members-title": "Lisää tai poista taulun jäseniä tältä kortilta.",
   "card-start": "Alkaa",
   "card-start-on": "Alkaa",
@@ -161,7 +163,7 @@
   "cardCustomFieldsPopup-title": "Muokkaa mukautettuja kenttiä",
   "cardDeletePopup-title": "Poista kortti?",
   "cardDetailsActionsPopup-title": "Korttitoimet",
-  "cardLabelsPopup-title": "Tunnisteet",
+  "cardLabelsPopup-title": "Nimilaput",
   "cardMembersPopup-title": "Jäsenet",
   "cardMorePopup-title": "Lisää",
   "cardTemplatePopup-title": "Luo malli",
@@ -221,6 +223,8 @@
   "comment-only-desc": "Voi vain kommentoida kortteja",
   "no-comments": "Ei kommentteja",
   "no-comments-desc": "Ei voi nähdä kommentteja ja toimintaa.",
+  "worker": "Työntekijä",
+  "worker-desc": "Voi vain siirtää kortteja, ilmoittautua kortin käsittelijäksi ja kommentoida.",
   "computer": "Tietokone",
   "confirm-subtask-delete-dialog": "Haluatko varmasti poistaa alitehtävän?",
   "confirm-checklist-delete-dialog": "Haluatko varmasti poistaa tarkistuslistan?",
@@ -234,7 +238,7 @@
   "create": "Luo",
   "createBoardPopup-title": "Luo taulu",
   "chooseBoardSourcePopup-title": "Tuo taulu",
-  "createLabelPopup-title": "Luo tunniste",
+  "createLabelPopup-title": "Luo nimilappu",
   "createCustomField": "Luo kenttä",
   "createCustomFieldPopup-title": "Luo kenttä",
   "current": "nykyinen",
@@ -254,9 +258,9 @@
   "default-avatar": "Oletusprofiilikuva",
   "delete": "Poista",
   "deleteCustomFieldPopup-title": "Poista mukautettu kenttä?",
-  "deleteLabelPopup-title": "Poista tunniste?",
+  "deleteLabelPopup-title": "Poista nimilappu?",
   "description": "Kuvaus",
-  "disambiguateMultiLabelPopup-title": "Yksikäsitteistä tunnistetoiminta",
+  "disambiguateMultiLabelPopup-title": "Yksikäsitteistä nimilapputoiminta",
   "disambiguateMultiMemberPopup-title": "Yksikäsitteistä jäsentoiminta",
   "discard": "Hylkää",
   "done": "Valmis",
@@ -270,7 +274,7 @@
   "editCardDueDatePopup-title": "Muokkaa eräpäivää",
   "editCustomFieldPopup-title": "Muokkaa kenttää",
   "editCardSpentTimePopup-title": "Muuta käytettyä aikaa",
-  "editLabelPopup-title": "Muokkaa tunnistetta",
+  "editLabelPopup-title": "Muokkaa nimilappua",
   "editNotificationPopup-title": "Muokkaa ilmoituksia",
   "editProfilePopup-title": "Muokkaa profiilia",
   "email": "Sähköposti",
@@ -313,7 +317,7 @@
   "filter-cards": "Suodata kortit tai listat",
   "list-filter-label": "Suodata listat otsikon mukaan",
   "filter-clear": "Poista suodatin",
-  "filter-no-label": "Ei tunnistetta",
+  "filter-no-label": "Ei nimilappua",
   "filter-no-member": "Ei jäseniä",
   "filter-no-custom-fields": "Ei mukautettuja kenttiä",
   "filter-show-archive": "Näytä arkistoidut listat",
@@ -355,10 +359,10 @@
   "joined": "liittyi",
   "just-invited": "Sinut on juuri kutsuttu tälle taululle",
   "keyboard-shortcuts": "Pikanäppäimet",
-  "label-create": "Luo tunniste",
-  "label-default": "%s tunniste (oletus)",
-  "label-delete-pop": "Tätä ei voi peruuttaa. Tämä poistaa tämän tunnisteen kaikista korteista ja tuhoaa sen historian.",
-  "labels": "Tunnisteet",
+  "label-create": "Luo nimilappu",
+  "label-default": "%s nimilappu (oletus)",
+  "label-delete-pop": "Tätä ei voi peruuttaa. Tämä poistaa tämän nimilapun kaikista korteista ja tuhoaa sen historian.",
+  "labels": "Nimilaput",
   "language": "Kieli",
   "last-admin-desc": "Et voi vaihtaa rooleja koska täytyy olla olemassa ainakin yksi ylläpitäjä.",
   "leave-board": "Jää pois taululta",
@@ -424,7 +428,7 @@
   "quick-access-description": "Merkkaa taulu tähdellä lisätäksesi pikavalinta tähän palkkiin.",
   "remove-cover": "Poista kansi",
   "remove-from-board": "Poista taululta",
-  "remove-label": "Poista tunniste",
+  "remove-label": "Poista nimilappu",
   "listDeletePopup-title": "Poista lista?",
   "remove-member": "Poista jäsen",
   "remove-member-from-card": "Poista kortilta",
@@ -550,7 +554,7 @@
   "seconds": "sekuntia",
   "show-field-on-card": "Näytä tämä kenttä kortilla",
   "automatically-field-on-card": "Luo kenttä automaattisesti kaikille korteille",
-  "showLabel-field-on-card": "Näytä kentän tunniste minikortilla",
+  "showLabel-field-on-card": "Näytä kentän nimilappu minikortilla",
   "yes": "Kyllä",
   "no": "Ei",
   "accounts": "Tilit",
@@ -572,15 +576,16 @@
   "assigned-by": "Tehtävänantaja",
   "requested-by": "Pyytäjä",
   "board-delete-notice": "Poistaminen on lopullista. Menetät kaikki listat, kortit ja toimet tällä taululla.",
-  "delete-board-confirm-popup": "Kaikki listat, kortit, tunnisteet ja toimet poistetaan ja et pysty palauttamaan taulun sisältöä. Tätä ei voi peruuttaa.",
+  "delete-board-confirm-popup": "Kaikki listat, kortit, nimilaput ja toimet poistetaan ja et pysty palauttamaan taulun sisältöä. Tätä ei voi peruuttaa.",
   "boardDeletePopup-title": "Poista taulu?",
   "delete-board": "Poista taulu",
   "default-subtasks-board": "Alitehtävät taululle __board__",
   "default": "Oletus",
   "queue": "Jono",
   "subtask-settings": "Alitehtävä-asetukset",
+  "card-settings": "Kortin asetukset",
   "boardSubtaskSettingsPopup-title": "Taulualitehtävien asetukset",
-  "show-subtasks-field": "Korteilla voi olla alitehtäviä",
+  "boardCardSettingsPopup-title": "Kortin asetukset",
   "deposit-subtasks-board": "Talleta alitehtävät tälle taululle:",
   "deposit-subtasks-list": "Laskeutumislista alatehtäville tallennettu tänne:",
   "show-parent-in-minicard": "Näytä ylätehtävä minikortilla:",
@@ -592,11 +597,11 @@
   "parent-card": "Ylätehtäväkortti",
   "source-board": "Lähdetaulu",
   "no-parent": "Älä näytä ylätehtävää",
-  "activity-added-label": "lisätty tunniste '%s' kohteeseen %s",
-  "activity-removed-label": "poistettu tunniste '%s' kohteesta %s",
+  "activity-added-label": "lisätty nimilappu '%s' kohteeseen %s",
+  "activity-removed-label": "poistettu nimilappu '%s' kohteesta %s",
   "activity-delete-attach": "poistettu liitetiedosto kohteesta %s",
-  "activity-added-label-card": "lisätty tunniste '%s'",
-  "activity-removed-label-card": "poistettu tunniste '%s'",
+  "activity-added-label-card": "lisätty nimilappu '%s'",
+  "activity-removed-label-card": "poistettu nimilappu '%s'",
   "activity-delete-attach-card": "poistettu liitetiedosto",
   "activity-set-customfield": "asetettu mukautettu kentän '%s' sisällöksi '%s' kortilla %s",
   "activity-unset-customfield": "poistettu mukautettu kenttä '%s' kortilla %s",
@@ -622,8 +627,8 @@
   "r-archived": "Siirretty Arkistoon",
   "r-unarchived": "Palautettu Arkistosta",
   "r-a-card": "kortti",
-  "r-when-a-label-is": "Kun tunniste on",
-  "r-when-the-label": "Kun tunniste on",
+  "r-when-a-label-is": "Kun nimilappu on",
+  "r-when-the-label": "Kun nimilappu on",
   "r-list-name": "listan nimi",
   "r-when-a-member": "Kun jäsen on",
   "r-when-the-member": "Kun käyttäjä",
@@ -646,7 +651,7 @@
   "r-card": "kortti",
   "r-add": "Lisää",
   "r-remove": "Poista",
-  "r-label": "tunniste",
+  "r-label": "nimilappu",
   "r-member": "jäsen",
   "r-remove-all": "Poista kaikki jäsenet kortilta",
   "r-set-color": "Aseta väriksi",
@@ -672,8 +677,8 @@
   "r-d-send-email-message": "viesti",
   "r-d-archive": "Siirrä kortti Arkistoon",
   "r-d-unarchive": "Palauta kortti Arkistosta",
-  "r-d-add-label": "Lisää tunniste",
-  "r-d-remove-label": "Poista tunniste",
+  "r-d-add-label": "Lisää nimilappu",
+  "r-d-remove-label": "Poista nimilappu",
   "r-create-card": "Luo uusi kortti",
   "r-in-list": "listassa",
   "r-in-swimlane": "swimlanessa",
@@ -746,8 +751,10 @@
   "act-atUserComment": "Sinut mainittiin [__board__] __list__/__card__",
   "delete-user-confirm-popup": "Haluatko varmasti poistaa tämän käyttäjätilin? Tätä ei voi peruuttaa.",
   "accounts-allowUserDelete": "Salli käyttäjien poistaa tilinsä itse",
-  "hide-minicard-label-text": "Piilota minikortin tunniste teksti",
+  "hide-minicard-label-text": "Piilota minikortin nimilappu teksti",
   "show-desktop-drag-handles": "Näytä työpöydän vedon kahvat",
-  "assignee": "Valtuutettu",
-  "cardAssigneesPopup-title": "Valtuutettu"
+  "assignee": "Käsittelijä",
+  "cardAssigneesPopup-title": "Käsittelijä",
+  "addmore-detail": "Lisää tarkempi kuvaus",
+  "show-on-card": "Näytä kortilla"
 }

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