summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.meteor/.finished-upgraders1
-rw-r--r--.meteor/packages36
-rw-r--r--.meteor/release2
-rw-r--r--.meteor/versions121
-rw-r--r--CHANGELOG.md176
-rw-r--r--Dockerfile89
-rw-r--r--README.md2
-rw-r--r--client/components/activities/activities.jade51
-rw-r--r--client/components/activities/activities.js16
-rw-r--r--client/components/boards/boardHeader.jade9
-rw-r--r--client/components/boards/boardHeader.js3
-rw-r--r--client/components/cards/cardDetails.js2
-rw-r--r--client/components/forms/forms.styl1
-rw-r--r--client/components/main/layouts.jade21
-rw-r--r--client/components/main/layouts.js72
-rw-r--r--client/components/main/layouts.styl17
-rw-r--r--client/components/rules/.DS_Storebin0 -> 6148 bytes
-rw-r--r--client/components/rules/actions/boardActions.jade46
-rw-r--r--client/components/rules/actions/boardActions.js122
-rw-r--r--client/components/rules/actions/cardActions.jade43
-rw-r--r--client/components/rules/actions/cardActions.js118
-rw-r--r--client/components/rules/actions/checklistActions.jade51
-rw-r--r--client/components/rules/actions/checklistActions.js128
-rw-r--r--client/components/rules/actions/mailActions.jade11
-rw-r--r--client/components/rules/actions/mailActions.js35
-rw-r--r--client/components/rules/ruleDetails.jade20
-rw-r--r--client/components/rules/ruleDetails.js38
-rw-r--r--client/components/rules/rules.styl167
-rw-r--r--client/components/rules/rulesActions.jade29
-rw-r--r--client/components/rules/rulesActions.js58
-rw-r--r--client/components/rules/rulesList.jade27
-rw-r--r--client/components/rules/rulesList.js15
-rw-r--r--client/components/rules/rulesMain.jade9
-rw-r--r--client/components/rules/rulesMain.js70
-rw-r--r--client/components/rules/rulesTriggers.jade25
-rw-r--r--client/components/rules/rulesTriggers.js53
-rw-r--r--client/components/rules/triggers/boardTriggers.jade68
-rw-r--r--client/components/rules/triggers/boardTriggers.js116
-rw-r--r--client/components/rules/triggers/cardTriggers.jade79
-rw-r--r--client/components/rules/triggers/cardTriggers.js128
-rw-r--r--client/components/rules/triggers/checklistTriggers.jade83
-rw-r--r--client/components/rules/triggers/checklistTriggers.js146
-rw-r--r--client/components/settings/connectionMethod.jade6
-rw-r--r--client/components/settings/connectionMethod.js34
-rw-r--r--client/components/settings/peopleBody.jade22
-rw-r--r--client/components/settings/peopleBody.js31
-rw-r--r--client/lib/modal.js14
-rw-r--r--client/lib/utils.js29
-rw-r--r--docker-compose.yml127
-rw-r--r--models/actions.js19
-rw-r--r--models/activities.js8
-rw-r--r--models/attachments.js7
-rw-r--r--models/boards.js34
-rw-r--r--models/cards.js410
-rw-r--r--models/checklistItems.js85
-rw-r--r--models/checklists.js23
-rw-r--r--models/export.js74
-rw-r--r--models/lists.js11
-rw-r--r--models/rules.js48
-rw-r--r--models/settings.js33
-rw-r--r--models/triggers.js58
-rw-r--r--models/users.js136
-rw-r--r--models/wekanCreator.js205
-rw-r--r--package.json6
-rw-r--r--sandstorm-pkgdef.capnp8
-rw-r--r--server/authentication.js45
-rw-r--r--server/migrations.js13
-rw-r--r--server/notifications/email.js2
-rw-r--r--server/notifications/notifications.js1
-rw-r--r--server/publications/people.js1
-rw-r--r--server/publications/rules.js18
-rw-r--r--server/publications/users.js9
-rw-r--r--server/rulesHelper.js138
-rw-r--r--server/triggersDef.js58
-rwxr-xr-xsnap-src/bin/config165
-rwxr-xr-xsnap-src/bin/wekan-help150
-rw-r--r--snapcraft.yaml18
77 files changed, 4049 insertions, 301 deletions
diff --git a/.meteor/.finished-upgraders b/.meteor/.finished-upgraders
index 2a56593d..8f397c7d 100644
--- a/.meteor/.finished-upgraders
+++ b/.meteor/.finished-upgraders
@@ -16,3 +16,4 @@ notices-for-facebook-graph-api-2
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
diff --git a/.meteor/packages b/.meteor/packages
index 0bb90513..65d54fd2 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -3,17 +3,18 @@
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
-meteor-base@1.2.0
+meteor-base@1.4.0
# Build system
-ecmascript
+ecmascript@0.12.0
stylus@2.513.13
-standard-minifier-css@1.3.5
-standard-minifier-js@2.2.0
+standard-minifier-css@1.5.0
+standard-minifier-js@2.4.0
mquandalle:jade
+coffeescript@2.3.1_2!
# Polyfills
-es5-shim@4.6.15
+es5-shim@4.8.0
# Collections
aldeed:collection2
@@ -23,7 +24,7 @@ dburles:collection-helpers
idmontie:migrations
matb33:collection-hooks
matteodem:easy-search
-mongo@1.3.1
+mongo@1.6.0
mquandalle:collection-mutations
# Account system
@@ -31,14 +32,15 @@ kenton:accounts-sandstorm
service-configuration@1.0.11
useraccounts:unstyled
useraccounts:flow-routing
+salleman:accounts-oidc
# Utilities
-check@1.2.5
+check@1.3.1
jquery@1.11.10
-random@1.0.10
-reactive-dict@1.2.0
-session@1.1.7
-tracker@1.1.3
+random@1.1.0
+reactive-dict@1.2.1
+session@1.1.8
+tracker@1.2.0
underscore@1.0.10
3stack:presence
alethes:pages
@@ -52,7 +54,7 @@ mquandalle:autofocus
ongoworks:speakingurl
raix:handlebar-helpers
tap:i18n
-http@1.3.0
+http@1.4.1
# UI components
blaze
@@ -69,20 +71,22 @@ templates:tabs
verron:autosize
simple:json-routes
rajit:bootstrap3-datepicker
-shell-server@0.3.0
+shell-server@0.4.0
simple:rest-accounts-password
useraccounts:core
email@1.2.3
horka:swipebox
-dynamic-import@0.2.0
+dynamic-import@0.5.0
staringatlights:fast-render
mixmax:smart-disconnect
-accounts-password@1.5.0
+accounts-password@1.5.1
cfs:gridfs
eluck:accounts-lockout
rzymek:fullcalendar
momentjs:moment@2.22.2
-browser-policy-framing
+browser-policy-framing@1.1.0
mquandalle:moment
msavin:usercache
+wekan:wekan-ldap
+wekan:accounts-cas
diff --git a/.meteor/release b/.meteor/release
index 56a7a07f..02806a3f 100644
--- a/.meteor/release
+++ b/.meteor/release
@@ -1 +1 @@
-METEOR@1.6.0.1
+METEOR@1.8.1-beta.0
diff --git a/.meteor/versions b/.meteor/versions
index fc7d64b0..8d10ad73 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -1,6 +1,7 @@
3stack:presence@1.1.2
-accounts-base@1.4.0
-accounts-password@1.5.0
+accounts-base@1.4.3
+accounts-oauth@1.1.16
+accounts-password@1.5.1
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
@@ -10,19 +11,19 @@ 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.3.12
-babel-compiler@6.24.7
-babel-runtime@1.1.1
-base64@1.0.10
-binary-heap@1.0.10
-blaze@2.3.2
+autoupdate@1.5.0
+babel-compiler@7.2.0
+babel-runtime@1.3.0
+base64@1.0.11
+binary-heap@1.0.11
+blaze@2.3.3
blaze-tools@1.0.10
-boilerplate-generator@1.3.1
+boilerplate-generator@1.6.0
browser-policy-common@1.0.11
browser-policy-framing@1.1.0
-caching-compiler@1.1.9
+caching-compiler@1.2.0
caching-html-compiler@1.1.2
-callback-hook@1.0.10
+callback-hook@1.1.0
cfs:access-point@0.1.49
cfs:base-package@0.0.30
cfs:collection@0.5.5
@@ -40,38 +41,40 @@ cfs:storage-adapter@0.2.3
cfs:tempstore@0.1.5
cfs:upload-http@0.0.20
cfs:worker@0.1.4
-check@1.2.5
+check@1.3.1
chuangbo:cookie@1.1.0
-coffeescript@1.12.7_3
-coffeescript-compiler@1.12.7_3
+coffeescript@2.3.1_2
+coffeescript-compiler@2.3.1_2
cottz:publish-relations@2.0.8
dburles:collection-helpers@1.1.0
ddp@1.4.0
-ddp-client@2.2.0
-ddp-common@1.3.0
+ddp-client@2.3.3
+ddp-common@1.4.0
ddp-rate-limiter@1.0.7
-ddp-server@2.1.1
+ddp-server@2.2.0
deps@1.0.12
-diff-sequence@1.0.7
-dynamic-import@0.2.1
-ecmascript@0.9.0
-ecmascript-runtime@0.5.0
-ecmascript-runtime-client@0.5.0
-ecmascript-runtime-server@0.5.0
+diff-sequence@1.1.0
+dynamic-import@0.5.0
+ecmascript@0.12.0
+ecmascript-runtime@0.7.0
+ecmascript-runtime-client@0.8.0
+ecmascript-runtime-server@0.7.1
ejson@1.1.0
eluck:accounts-lockout@0.9.0
email@1.2.3
-es5-shim@4.6.15
+es5-shim@4.8.0
fastclick@1.0.13
+fetch@0.1.0
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.3.0
-id-map@1.0.9
+http@1.4.1
+id-map@1.1.0
idmontie:migrations@1.0.3
+inter-process-messaging@0.1.0
jquery@1.11.10
kadira:blaze-layout@2.3.0
kadira:dochead@1.5.0
@@ -80,12 +83,12 @@ kenton:accounts-sandstorm@0.7.0
launch-screen@1.1.1
livedata@1.0.18
localstorage@1.2.0
-logging@1.1.19
+logging@1.1.20
matb33:collection-hooks@0.8.4
matteodem:easy-search@1.6.4
mdg:validation-error@0.5.1
-meteor@1.8.2
-meteor-base@1.2.0
+meteor@1.9.2
+meteor-base@1.4.0
meteor-platform@1.2.6
meteorhacks:aggregate@1.3.0
meteorhacks:collection-utils@1.2.0
@@ -93,18 +96,20 @@ meteorhacks:meteorx@1.4.1
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
meteorspark:util@0.2.0
-minifier-css@1.2.16
-minifier-js@2.2.2
+minifier-css@1.4.0
+minifier-js@2.4.0
minifiers@1.1.8-faster-rebuild.0
-minimongo@1.4.3
+minimongo@1.4.5
mixmax:smart-disconnect@0.0.4
mobile-status-bar@1.0.14
-modules@0.11.0
-modules-runtime@0.9.1
+modern-browsers@0.1.2
+modules@0.13.0
+modules-runtime@0.10.2
momentjs:moment@2.22.2
-mongo@1.3.1
+mongo@1.6.0
+mongo-decimal@0.1.0
mongo-dev-server@1.1.0
-mongo-id@1.0.6
+mongo-id@1.0.7
mongo-livedata@1.0.12
mousetrap:mousetrap@1.4.6_1
mquandalle:autofocus@1.0.0
@@ -118,43 +123,48 @@ mquandalle:mousetrap-bindglobal@0.0.1
mquandalle:perfect-scrollbar@0.6.5_2
msavin:usercache@1.0.0
npm-bcrypt@0.9.3
-npm-mongo@2.2.33
+npm-mongo@3.1.1
+oauth@1.2.3
+oauth2@1.2.1
observe-sequence@1.0.16
ongoworks:speakingurl@1.1.0
-ordered-dict@1.0.9
+ordered-dict@1.1.0
peerlibrary:assert@0.2.5
peerlibrary:base-component@0.16.0
peerlibrary:blaze-components@0.15.1
-peerlibrary:computed-field@0.7.0
+peerlibrary:computed-field@0.9.0
peerlibrary:reactive-field@0.3.0
perak:markdown@1.0.5
-promise@0.10.0
+promise@0.11.1
raix:eventemitter@0.1.3
raix:handlebar-helpers@0.2.5
rajit:bootstrap3-datepicker@1.7.1
-random@1.0.10
-rate-limit@1.0.8
-reactive-dict@1.2.0
+random@1.1.0
+rate-limit@1.0.9
+reactive-dict@1.2.1
reactive-var@1.0.11
-reload@1.1.11
-retry@1.0.9
-routepolicy@1.0.12
+reload@1.2.0
+retry@1.1.0
+routepolicy@1.1.0
rzymek:fullcalendar@3.8.0
+salleman:accounts-oidc@1.0.9
+salleman:oidc@1.0.9
service-configuration@1.0.11
-session@1.1.7
+session@1.1.8
sha@1.0.9
-shell-server@0.3.1
+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.10
-standard-minifier-css@1.3.5
-standard-minifier-js@2.2.3
+srp@1.0.12
+standard-minifier-css@1.5.0
+standard-minifier-js@2.4.0
staringatlights:fast-render@2.16.5
staringatlights:inject-data@2.0.5
stylus@2.513.13
@@ -164,14 +174,17 @@ templating@1.3.2
templating-compiler@1.3.3
templating-runtime@1.3.2
templating-tools@1.1.2
-tracker@1.1.3
+tracker@1.2.0
ui@1.0.13
underscore@1.0.10
-url@1.1.0
+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.4.0
+webapp@1.7.0
webapp-hashing@1.0.9
+wekan:accounts-cas@0.1.0
+wekan:wekan-ldap@0.0.2
+yasaricli:slugify@0.0.7
zimme:active-route@2.3.2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4740127..b4bab0ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-# v1.55 2018-10-16 Wekan release
+# v1.55.1 2018-10-16 Wekan Edge release
This release adds the following new features:
@@ -6,66 +6,178 @@ This release adds the following new features:
and fixes the following bugs:
-- [Update broke the ability to mute notifications](https://github.com/wekan/wekan/issues/1952).
+- [LDAP: Include missing LDAP PR so that LDAP works](https://github.com/wekan/wekan-ldap/pull/6);
+- [Improve notifications](https://github.com/wekan/wekan/pull/1948);
+- [Fix deleting Custom Fields, removing broken references](https://github.com/wekan/wekan/issues/1872);
+- [Fix vertical text for swimlanes in IE11](https://github.com/wekan/wekan/issues/1798);
+- [Update broke the ability to mute notifications](https://github.com/wekan/wekan/pull/1954).
-Thanks to GitHub user Akuket for contributions.
+Thanks to GitHub users Akuket, Clement87 and tomodwyer for their contributions.
-# v1.54 2018-10-14 Wekan release
+# v1.53.9 2018-10-11 Wekan Edge release
-This release fixes the following bugs:
+This release adds the following new features:
-- [Fix vertical text for swimlanes in IE11](https://github.com/wekan/wekan/issues/1798).
-- [Fix deleting Custom Fields, removing broken references](https://github.com/wekan/wekan/issues/1872).
-- [Improve notifications](https://github.com/wekan/wekan/pull/1948).
-- [REST API: Get cards by swimlane id](https://github.com/wekan/wekan/pull/1944). Please [add docs](https://github.com/wekan/wekan/wiki/REST-API-Swimlanes).
+- docker-compose.yml in this Edge branch now works with Wekan Edge + Meteor 1.8.1-beta.0 + MongoDB 4.0.3;
+- [Snap is still broken](https://forum.snapcraft.io/t/how-to-connect-to-localhost-mongodb-in-snap-apparmor-prevents/7793/2). Please use latest Snap release on Edge branch, until this is fixed.
-Thanks to GitHub users Akuket, Clement87, dcmcand and tomodwyer for their contributions.
+Thanks to GitHub user xet7 for contributions.
-# v1.53 2018-10-03 Wekan release
+# v1.53.8 2018-10-10 Wekan Edge release
-This release fixes the following bugs:
+This release tries to fix the following bugs:
+
+- Try to fix Docker.
+
+Thanks to GitHub user xet7 for contributions.
+
+# v1.53.7 2018-10-10 Wekan Edge release
+
+This release adds the following new features:
+
+- Try MongoDB 4.0.3
+
+Thanks to GitHub user xet7 for contributions.
+
+# v1.53.6 2018-10-10 Wekan Edge release
+
+This release adds the following new features:
+
+- [Add LDAP to Snap Help](https://github.com/wekan/wekan/commit/809c8f64f69721d51b7d963248a77585867fac53).
+
+and tries to fix the following bugs:
+
+- Try to fix snap.
+
+Thanks to GitHub users Akuket and xet7 for their contributions.
+
+# v1.53.5 2018-10-10 Wekan Edge relase
+
+This release tries to fix the following bugs:
+
+- Try to fix snap.
+
+Thanks to GitHub user xet7 for contributions.
+
+# v1.53.4 2018-10-10 Wekan Edge release
+
+This release adds the following new features:
+
+- [Upgrade Hoek](https://github.com/wekan/wekan/commit/0b971b6ddb1ffc4adad6b6b09ae7f42dd376fe2c).
+
+Thanks to GitHub user xet7 for contributions.
+
+# v1.53.3 2018-10-10 Wekan Edge release
+
+This release adds the following new features:
+
+- [Upgrade](https://github.com/wekan/wekan/issues/1522) to [Meteor](https://blog.meteor.com/meteor-1-8-erases-the-debts-of-1-7-77af4c931fe3) [1.8.1-beta.0](https://github.com/meteor/meteor/issues/10216).
+ with [these](https://github.com/wekan/wekan/commit/079e45eb52a0f62ddb6051bf2ea80fac8860d3d5)
+ [commits](https://github.com/wekan/wekan/commit/dd47d46f4341a8c4ced05749633f783e88623e1b). So now it's possible to use MongoDB 2.6 - 4.0.
+
+Thanks to GitHub user xet7 for contributions.
+# v1.53.2 2018-10-10 Wekan Edge release
+
+This release adds the following new features:
+
+- [Add LDAP package to Docker and Snap](https://github.com/wekan/wekan/commit/f599391419bc7422a6ead52cdefc7d380e787897).
+
+Thanks to GitHub user xet7 for contributions.
+
+# v1.53.1 2018-10-10 Wekan Edge release
+
+This release adds the following new features:
+
+- [LDAP](https://github.com/wekan/wekan/commit/288800eafc91d07f859c4f59588e0b646137ccb9).
+ Please test and [add info about bugs](https://github.com/wekan/wekan/issues/119);
+- [Add LDAP support and authentications dropdown menu on login page](https://github.com/wekan/wekan/pull/1943);
+- [REST API: Get cards by swimlane id](https://github.com/wekan/wekan/pull/1944). Please [add docs](https://github.com/wekan/wekan/wiki/REST-API-Swimlanes).
+
+and fixes the following bugs:
+
+- [OpenShift: Drop default namespace value and duplicate WEKAN_SERVICE_NAME parameter.commit](https://github.com/wekan/wekan/commit/fcc3560df4dbcc418c63470776376238af4f6ddc);
- [Fix Card URL](https://github.com/wekan/wekan/pull/1932);
-- [OpenShift: Drop default namespace value and duplicate WEKAN_SERVICE_NAME parameter](https://github.com/wekan/wekan/pull/1930);
-- [Add whole packages/ directory to .gitignore](https://github.com/wekan/wekan/commit/f437d8370e03439d7ba5649496ec188c5d7b7e0c);
-- [Add info about root-url to GitHub issue template](https://github.com/wekan/wekan/commit/001c8f2b0138fb26a8c84acab62a604d0c6e5dda).
+- [Add info about root-url to GitHub issue template](https://github.com/wekan/wekan/commit/4c0eb7dcc19ca9ae8c5d2d0276e0d024269de236);
+- [Feature rules: fixes and enhancements](https://github.com/wekan/wekan/pull/1936).
+
+Thanks to GitHub users Akuket, Angtrim, dcmcand, lberk, maximest-pierre, InfoSec812, schulz and xet7 for their contributions.
+
+# v1.52.1 2018-10-02 Wekan Edge release
-Thanks to GitHub users lberk, InfoSec812 and xet7 for their contributions.
+This release adds the following new features:
-# v1.52 2018-10-01 Wekan release
+- REST API: [Add member with role to board. Remove member from board](https://github.com/wekan/wekan/commit/33caf1809a459b136b671f7061f08eb5e8d5e920).
+ [Docs](https://github.com/wekan/wekan/wiki/REST-API-Role). Related to [role issue](https://github.com/wekan/wekan/issues/1861).
-This release removes the following new features:
+and reverts previous change:
-- [Removed CAS from Wekan stable](https://github.com/wekan/wekan/commit/5923585584f9cb8121476bf5e5d0abf7891e86f6),
- because [it does not work correctly](https://github.com/wekan/wekan/issues/1925).
- CAS developent continues at edge.
+- OAuth2: [Revert Oidc preferred_username back to username](https://github.com/wekan/wekan/commit/33caf1809a459b136b671f7061f08eb5e8d5e920).
+ This [does not fix or break anything](https://github.com/wekan/wekan/issues/1874#issuecomment-425179291),
+ Oidc already works with [doorkeeper](https://github.com/doorkeeper-gem/doorkeeper-provider-app).
Thanks to GitHub user xet7 for contributions.
+
+# v1.51.2 2018-09-30 Wekan Edge release
+
+This release adds the following new features:
-# v1.51 2018-09-28 Wekan release
+- [REST API: Change role of board member](https://github.com/wekan/wekan/commit/51ac6c839ecf2226b2a81b0d4f985d3b942f0938).
+ Docs: https://github.com/wekan/wekan/wiki/REST-API-Role
+
+Thanks to GitHub users entrptaher and xet7 for their contributions.
+
+# v1.51.1 2018-09-28 Wekan Edge release
This release adds the following new features:
-- [Add CAS with attributes](https://github.com/wekan/wekan/commit/c6cea2fb4e9e17403fe0ce2ba5bf2d20dcf81a8f);
-- [Move Add Board button to top left, so there is no need to scroll to bottom when there is a lot of boards](https://github.com/wekan/wekan/commit/a10b6fb173d529220861668cfb1c341ec45e2a53).
+- [Add CAS with attributes](https://github.com/wekan/wekan/commit/bd6e4a351b984b032e17c57793a70923eb17d8f5);
+- [Move Add Board button to top left, so there is no need to scroll to bottom when there is a lot of boards](https://github.com/wekan/wekan/commit/fb46a88a0f01f7f74ae6b941dd6f2060e020f09d).
Thanks to GitHub users ppoulard and xet7 for their contributions.
-# v1.50 2018-09-22 Wekan release
+# v1.50.3 2018-09-23 Wekan Edge release
+
+This release tries to fix the following bugs:
+
+- [Remove "Fix Cannot setup mail server via snap variables"](https://github.com/wekan/wekan/commit/6d88baebc7e297ffdbbd5bb6971190b18f79d21f)
+ to see does Wekan Snap start correctly after removing it.
+
+Thanks to GitHub user xet7 for contributions.
+
+# v1.50.2 2018-09-23 Wekan Edge release
+
+This release tries to fix the following bugs:
+
+- Build Wekan and release again, to see does it work.
+
+Thanks to GitHub user xet7 for contributions.
+
+# v1.50.1 2018-09-22 Wekan Edge release
This release adds the following new features:
-- [Change from Node v8.12.0 prerelease to use official Node v8.12.0](https://github.com/wekan/wekan/commit/bfabd6346033c3d3887a4693de8f13bc1705b582).
+- [Change from Node v8.12.0 prerelease to use official Node v8.12.0](https://github.com/wekan/wekan/commit/7ec7a5f27c381e90f3da6bddc3773ed87b1c1a1f).
and fixes the following bugs:
-- [Fix Dockerfile Meteor install by changing tar to bsdtar](https://github.com/wekan/wekan/commit/352e9033b6efb212e65e34bb9c407bb1d7dce824);
-- Add [npm-debug.log](https://github.com/wekan/wekan/commit/f7731f4f5ec27e63e74a3265d105427ef3c0985a) and
- [.DS_Store](https://github.com/wekan/wekan/commit/d652eb5cee3fd648a6023e38db444ad460ddef7e) to .gitignore;
-- [Add more debug log requirements to GitHub issue template](https://github.com/wekan/wekan/commit/94cd2ce69098f02e4ac4bebb1a2b5eaf919f1020);
-- [Add default Wekan Snap MongoDB bind IP 127.0.0.1](https://github.com/wekan/wekan/commit/12656ee9a13d2464cdc183590c76d3e09486c607).
+- [Fix Dockerfile Meteor install by changing tar to bsdtar](https://github.com/wekan/wekan/commit/1bad81ca86ca87c02148764cc03a3070882a8a33);
+- Add [npm-debug.log and .DS_Store](https://github.com/wekan/wekan/commit/44f4a1c3bf8033b6b658703a0ccaed5fdb183ab4) to .gitignore;
+- [Add more debug log requirements to GitHub issue template](https://github.com/wekan/wekan/commit/1c4ce56b0f18e00e01b54c7059cbbf8d3e196154);
+- [Add default Wekan Snap MongoDB bind IP 127.0.0.1](https://github.com/wekan/wekan/commit/6ac726e198933ee41c129d22a7118fcfbf4ca9a2);
+- [Fix Feature Rules](https://github.com/wekan/wekan/pull/1909);
+- [Fix Cannot setup mail server via snap variables](https://github.com/wekan/wekan/issues/1906);
+- [Try to fix OAuth2: Change oidc username to preferred_username](https://github.com/wekan/wekan/commit/734e4e5f3ff2c3dabf94c0fbfca561db066c4565).
+
+Thanks to GitHub users Angtrim, maurice-schleussinger, suprovsky and xet7 for their contributions.
-Thanks to GitHub users maurice-schleussinger and xet7 for their contributions.
+# v1.49.1 2018-09-17 Wekan Edge release
+
+This release adds the following new features:
+
+- Change from Node v8.12.0 prerelease to use official Node v8.12.0.
+
+Thanks to GitHub user xet7 for contributions.
# v1.49 2018-09-17 Wekan release
diff --git a/Dockerfile b/Dockerfile
index 1d0f20e6..76517c07 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,19 +18,59 @@ ARG MATOMO_WITH_USERNAME
ARG BROWSER_POLICY_ENABLED
ARG TRUSTED_URL
ARG WEBHOOKS_ATTRIBUTES
+ARG OAUTH2_ENABLED
ARG OAUTH2_CLIENT_ID
ARG OAUTH2_SECRET
ARG OAUTH2_SERVER_URL
ARG OAUTH2_AUTH_ENDPOINT
ARG OAUTH2_USERINFO_ENDPOINT
ARG OAUTH2_TOKEN_ENDPOINT
+ARG LDAP_ENABLE
+ARG LDAP_PORT
+ARG LDAP_HOST
+ARG LDAP_BASEDN
+ARG LDAP_LOGIN_FALLBACK
+ARG LDAP_RECONNECT
+ARG LDAP_TIMEOUT
+ARG LDAP_IDLE_TIMEOUT
+ARG LDAP_CONNECT_TIMEOUT
+ARG LDAP_AUTHENTIFICATION
+ARG LDAP_AUTHENTIFICATION_USERDN
+ARG LDAP_AUTHENTIFICATION_PASSWORD
+ARG LDAP_LOG_ENABLED
+ARG LDAP_BACKGROUND_SYNC
+ARG LDAP_BACKGROUND_SYNC_INTERVAL
+ARG LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED
+ARG LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS
+ARG LDAP_ENCRYPTION
+ARG LDAP_CA_CERT
+ARG LDAP_REJECT_UNAUTHORIZED
+ARG LDAP_USER_SEARCH_FILTER
+ARG LDAP_USER_SEARCH_SCOPE
+ARG LDAP_USER_SEARCH_FIELD
+ARG LDAP_SEARCH_PAGE_SIZE
+ARG LDAP_SEARCH_SIZE_LIMIT
+ARG LDAP_GROUP_FILTER_ENABLE
+ARG LDAP_GROUP_FILTER_OBJECTCLASS
+ARG LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE
+ARG LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE
+ARG LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT
+ARG LDAP_GROUP_FILTER_GROUP_NAME
+ARG LDAP_UNIQUE_IDENTIFIER_FIELD
+ARG LDAP_UTF8_NAMES_SLUGIFY
+ARG LDAP_USERNAME_FIELD
+ARG LDAP_MERGE_EXISTING_USERS
+ARG LDAP_SYNC_USER_DATA
+ARG LDAP_SYNC_USER_DATA_FIELDMAP
+ARG LDAP_SYNC_GROUP_ROLES
+ARG LDAP_DEFAULT_DOMAIN
# 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 build-essential python git ca-certificates gcc-7" \
NODE_VERSION=v8.12.0 \
- METEOR_RELEASE=1.6.0.1 \
+ METEOR_RELEASE=1.8.1-beta.0 \
USE_EDGE=false \
METEOR_EDGE=1.5-beta.17 \
NPM_VERSION=latest \
@@ -45,12 +85,52 @@ ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 build-essential pyth
BROWSER_POLICY_ENABLED=true \
TRUSTED_URL="" \
WEBHOOKS_ATTRIBUTES="" \
+ OAUTH2_ENABLED=false \
OAUTH2_CLIENT_ID="" \
OAUTH2_SECRET="" \
OAUTH2_SERVER_URL="" \
OAUTH2_AUTH_ENDPOINT="" \
OAUTH2_USERINFO_ENDPOINT="" \
- OAUTH2_TOKEN_ENDPOINT=""
+ OAUTH2_TOKEN_ENDPOINT="" \
+ LDAP_ENABLE=false \
+ LDAP_PORT=389 \
+ LDAP_HOST="" \
+ LDAP_BASEDN="" \
+ LDAP_LOGIN_FALLBACK=false \
+ LDAP_RECONNECT=true \
+ LDAP_TIMEOUT=10000 \
+ LDAP_IDLE_TIMEOUT=10000 \
+ LDAP_CONNECT_TIMEOUT=10000 \
+ LDAP_AUTHENTIFICATION=false \
+ LDAP_AUTHENTIFICATION_USERDN="" \
+ LDAP_AUTHENTIFICATION_PASSWORD="" \
+ LDAP_LOG_ENABLED=false \
+ LDAP_BACKGROUND_SYNC=false \
+ LDAP_BACKGROUND_SYNC_INTERVAL=100 \
+ LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false \
+ LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false \
+ LDAP_ENCRYPTION=false \
+ LDAP_CA_CERT="" \
+ LDAP_REJECT_UNAUTHORIZED=false \
+ LDAP_USER_SEARCH_FILTER="" \
+ LDAP_USER_SEARCH_SCOPE="" \
+ LDAP_USER_SEARCH_FIELD="" \
+ LDAP_SEARCH_PAGE_SIZE=0 \
+ LDAP_SEARCH_SIZE_LIMIT=0 \
+ LDAP_GROUP_FILTER_ENABLE=false \
+ LDAP_GROUP_FILTER_OBJECTCLASS="" \
+ LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" \
+ LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" \
+ LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" \
+ LDAP_GROUP_FILTER_GROUP_NAME="" \
+ LDAP_UNIQUE_IDENTIFIER_FIELD="" \
+ LDAP_UTF8_NAMES_SLUGIFY=true \
+ LDAP_USERNAME_FIELD="" \
+ LDAP_MERGE_EXISTING_USERS=false \
+ LDAP_SYNC_USER_DATA=false \
+ LDAP_SYNC_USER_DATA_FIELDMAP="" \
+ LDAP_SYNC_GROUP_ROLES="" \
+ LDAP_DEFAULT_DOMAIN=""
# Copy the app to the image
COPY ${SRC_PATH} /home/wekan/app
@@ -128,7 +208,8 @@ RUN \
# Change user to wekan and install meteor
cd /home/wekan/ && \
chown wekan:wekan --recursive /home/wekan && \
- curl "https://install.meteor.com/?release=${METEOR_RELEASE}" -o /home/wekan/install_meteor.sh && \
+ curl "https://install.meteor.com" -o /home/wekan/install_meteor.sh && \
+ #curl "https://install.meteor.com/?release=${METEOR_RELEASE}" -o /home/wekan/install_meteor.sh && \
# OLD: sed -i "s|RELEASE=.*|RELEASE=${METEOR_RELEASE}\"\"|g" ./install_meteor.sh && \
# Install Meteor forcing its progress
sed -i 's/VERBOSITY="--silent"/VERBOSITY="--progress-bar"/' ./install_meteor.sh && \
@@ -148,6 +229,8 @@ RUN \
cd /home/wekan/app/packages && \
gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/flow-router.git kadira-flow-router && \
gosu wekan:wekan git clone --depth 1 -b master git://github.com/meteor-useraccounts/core.git meteor-useraccounts-core && \
+ gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/meteor-accounts-cas.git && \
+ gosu wekan:wekan git clone --depth 1 -b master git://github.com/wekan/wekan-ldap.git && \
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \
cd /home/wekan/.meteor && \
gosu wekan:wekan /home/wekan/.meteor/meteor -- help; \
diff --git a/README.md b/README.md
index 5fd6dc70..54804197 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@
- master+devel branch. At release, devel is merged to master.
- Receives fixes and features that have been tested at edge that they work.
+- If you want automatic updates, [use Snap](https://github.com/wekan/wekan-snap/wiki/Install).
+- If you want to test before update, [use Docker quay.io release tags](https://github.com/wekan/wekan/wiki/Docker).
## Edge
diff --git a/client/components/activities/activities.jade b/client/components/activities/activities.jade
index d3e3d5ba..bddc4dad 100644
--- a/client/components/activities/activities.jade
+++ b/client/components/activities/activities.jade
@@ -14,6 +14,9 @@ template(name="boardActivities")
p.activity-desc
+memberName(user=user)
+ if($eq activityType 'deleteAttachment')
+ | {{{_ 'activity-delete-attach' cardLink}}}.
+
if($eq activityType 'addAttachment')
| {{{_ 'activity-attached' attachmentLink cardLink}}}.
@@ -31,12 +34,28 @@ template(name="boardActivities")
.activity-checklist(href="{{ card.absoluteUrl }}")
+viewer
= checklist.title
+ if($eq activityType 'removeChecklist')
+ | {{{_ 'activity-checklist-removed' cardLink}}}.
+
+ if($eq activityType 'checkedItem')
+ | {{{_ 'activity-checked-item' checkItem checklist.title cardLink}}}.
+
+ if($eq activityType 'uncheckedItem')
+ | {{{_ 'activity-unchecked-item' checkItem checklist.title cardLink}}}.
+
+ if($eq activityType 'checklistCompleted')
+ | {{{_ 'activity-checklist-completed' checklist.title cardLink}}}.
+
+ if($eq activityType 'checklistUncompleted')
+ | {{{_ 'activity-checklist-uncompleted' checklist.title cardLink}}}.
if($eq activityType 'addChecklistItem')
| {{{_ 'activity-checklist-item-added' checklist.title cardLink}}}.
.activity-checklist(href="{{ card.absoluteUrl }}")
+viewer
= checklistItem.title
+ if($eq activityType 'removedChecklistItem')
+ | {{{_ 'activity-checklist-item-removed' checklist.title cardLink}}}.
if($eq activityType 'archivedCard')
| {{{_ 'activity-archived' cardLink}}}.
@@ -89,6 +108,12 @@ template(name="boardActivities")
if($eq activityType 'restoredCard')
| {{{_ 'activity-sent' cardLink boardLabel}}}.
+ if($eq activityType 'addedLabel')
+ | {{{_ 'activity-added-label' lastLabel cardLink}}}.
+
+ if($eq activityType 'removedLabel')
+ | {{{_ 'activity-removed-label' lastLabel cardLink}}}.
+
if($eq activityType 'unjoinMember')
if($eq user._id member._id)
| {{{_ 'activity-unjoined' cardLink}}}.
@@ -119,6 +144,28 @@ template(name="cardActivities")
| {{{_ 'activity-removed' cardLabel memberLink}}}.
if($eq activityType 'archivedCard')
| {{_ 'activity-archived' cardLabel}}.
+
+ if($eq activityType 'addedLabel')
+ | {{{_ 'activity-added-label-card' lastLabel }}}.
+
+ if($eq activityType 'removedLabel')
+ | {{{_ 'activity-removed-label-card' lastLabel }}}.
+
+ if($eq activityType 'removeChecklist')
+ | {{{_ 'activity-checklist-removed' cardLabel}}}.
+
+ if($eq activityType 'checkedItem')
+ | {{{_ 'activity-checked-item-card' checkItem checklist.title }}}.
+
+ if($eq activityType 'uncheckedItem')
+ | {{{_ 'activity-unchecked-item-card' checkItem checklist.title }}}.
+
+ if($eq activityType 'checklistCompleted')
+ | {{{_ 'activity-checklist-completed-card' checklist.title }}}.
+
+ if($eq activityType 'checklistUncompleted')
+ | {{{_ 'activity-checklist-uncompleted-card' checklist.title }}}.
+
if($eq activityType 'restoredCard')
| {{_ 'activity-sent' cardLabel boardLabel}}.
if($eq activityType 'moveCard')
@@ -127,6 +174,10 @@ template(name="cardActivities")
| {{{_ 'activity-attached' attachmentLink cardLabel}}}.
if attachment.isImage
img.attachment-image-preview(src=attachment.url)
+ if($eq activityType 'deleteAttachment')
+ | {{{_ 'activity-delete-attach' cardLabel}}}.
+ if($eq activityType 'removedChecklist')
+ | {{{_ 'activity-checklist-removed' cardLabel}}}.
if($eq activityType 'addChecklist')
| {{{_ 'activity-checklist-added' cardLabel}}}.
.activity-checklist
diff --git a/client/components/activities/activities.js b/client/components/activities/activities.js
index 25e151fd..b3fe8f50 100644
--- a/client/components/activities/activities.js
+++ b/client/components/activities/activities.js
@@ -50,6 +50,12 @@ BlazeComponent.extendComponent({
}
},
+ checkItem(){
+ const checkItemId = this.currentData().checklistItemId;
+ const checkItem = ChecklistItems.findOne({_id:checkItemId});
+ return checkItem.title;
+ },
+
boardLabel() {
return TAPi18n.__('this-board');
},
@@ -66,6 +72,16 @@ BlazeComponent.extendComponent({
}, card.title));
},
+ lastLabel(){
+ const lastLabelId = this.currentData().labelId;
+ const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById(lastLabelId);
+ if(lastLabel.name === undefined || lastLabel.name === ''){
+ return lastLabel.color;
+ }else{
+ return lastLabel.name;
+ }
+ },
+
listLabel() {
return this.currentData().list().title;
},
diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade
index 1c6c8f8c..75b2f02b 100644
--- a/client/components/boards/boardHeader.jade
+++ b/client/components/boards/boardHeader.jade
@@ -87,6 +87,10 @@ 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'}}
a.board-header-btn.js-open-search-view(title="{{_ 'search'}}")
i.fa.fa-search
@@ -290,6 +294,11 @@ template(name="boardChangeTitlePopup")
textarea.js-board-desc= description
input.primary.wide(type="submit" value="{{_ 'rename'}}")
+template(name="boardCreateRulePopup")
+ p {{_ 'close-board-pop'}}
+ button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
+
+
template(name="archiveBoardPopup")
p {{_ 'close-board-pop'}}
button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js
index 2dfd58c1..89f686ab 100644
--- a/client/components/boards/boardHeader.js
+++ b/client/components/boards/boardHeader.js
@@ -108,6 +108,9 @@ 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();
diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js
index d1bb3a1e..da0f126a 100644
--- a/client/components/cards/cardDetails.js
+++ b/client/components/cards/cardDetails.js
@@ -110,7 +110,7 @@ BlazeComponent.extendComponent({
},
onRendered() {
- if (!Utils.isMiniScreen()){
+ if (!Utils.isMiniScreen()) {
Meteor.setTimeout(() => {
this.scrollParentContainer();
}, 500);
diff --git a/client/components/forms/forms.styl b/client/components/forms/forms.styl
index 5be70b7a..892a6e74 100644
--- a/client/components/forms/forms.styl
+++ b/client/components/forms/forms.styl
@@ -1,5 +1,6 @@
@import 'nib'
+select,
textarea,
input:not([type=file]),
button
diff --git a/client/components/main/layouts.jade b/client/components/main/layouts.jade
index bd9fac23..68876dc5 100644
--- a/client/components/main/layouts.jade
+++ b/client/components/main/layouts.jade
@@ -18,6 +18,10 @@ template(name="userFormsLayout")
img(src="{{pathFor '/wekan-logo.png'}}" alt="Wekan")
section.auth-dialog
+Template.dynamic(template=content)
+ +connectionMethod
+ if isCas
+ .at-form
+ button#cas(class='at-btn submit' type='submit') {{casSignInLabel}}
div.at-form-lang
select.select-lang.js-userform-set-language
each languages
@@ -33,11 +37,18 @@ template(name="defaultLayout")
if (Modal.isOpen)
#modal
.overlay
- .modal-content
- a.modal-close-btn.js-close-modal
- i.fa.fa-times-thin
- +Template.dynamic(template=Modal.getHeaderName)
- +Template.dynamic(template=Modal.getTemplateName)
+ if (Modal.isWide)
+ .modal-content-wide.modal-container
+ a.modal-close-btn.js-close-modal
+ i.fa.fa-times-thin
+ +Template.dynamic(template=Modal.getHeaderName)
+ +Template.dynamic(template=Modal.getTemplateName)
+ else
+ .modal-content.modal-container
+ a.modal-close-btn.js-close-modal
+ i.fa.fa-times-thin
+ +Template.dynamic(template=Modal.getHeaderName)
+ +Template.dynamic(template=Modal.getTemplateName)
template(name="notFound")
+message(label='page-not-found')
diff --git a/client/components/main/layouts.js b/client/components/main/layouts.js
index f12718a7..393f890b 100644
--- a/client/components/main/layouts.js
+++ b/client/components/main/layouts.js
@@ -6,7 +6,23 @@ const i18nTagToT9n = (i18nTag) => {
return i18nTag;
};
+const validator = {
+ set(obj, prop, value) {
+ if (prop === 'state' && value !== 'signIn') {
+ $('.at-form-authentication').hide();
+ } else if (prop === 'state' && value === 'signIn') {
+ $('.at-form-authentication').show();
+ }
+ // The default behavior to store the value
+ obj[prop] = value;
+ // Indicate success
+ return true;
+ },
+};
+
Template.userFormsLayout.onRendered(() => {
+ AccountsTemplates.state.form.keys = new Proxy(AccountsTemplates.state.form.keys, validator);
+
const i18nTag = navigator.language;
if (i18nTag) {
T9n.setLanguage(i18nTagToT9n(i18nTag));
@@ -39,6 +55,17 @@ Template.userFormsLayout.helpers({
const curLang = T9n.getLanguage() || 'en';
return t9nTag === curLang;
},
+/*
+ isCas() {
+ return Meteor.settings.public &&
+ Meteor.settings.public.cas &&
+ Meteor.settings.public.cas.loginUrl;
+ },
+
+ casSignInLabel() {
+ return TAPi18n.__('casSignIn', {}, T9n.getLanguage() || 'en');
+ },
+*/
});
Template.userFormsLayout.events({
@@ -47,6 +74,51 @@ Template.userFormsLayout.events({
T9n.setLanguage(i18nTagToT9n(i18nTag));
evt.preventDefault();
},
+ 'click button#cas'() {
+ Meteor.loginWithCas(function() {
+ if (FlowRouter.getRouteName() === 'atSignIn') {
+ FlowRouter.go('/');
+ }
+ });
+ },
+ 'click #at-btn'(event) {
+ /* All authentication method can be managed/called here.
+ !! DON'T FORGET to correctly fill the fields of the user during its creation if necessary authenticationMethod : String !!
+ */
+ const authenticationMethodSelected = $('.select-authentication').val();
+ // Local account
+ if (authenticationMethodSelected === 'password') {
+ return;
+ }
+
+ // Stop submit #at-pwd-form
+ event.preventDefault();
+ event.stopImmediatePropagation();
+
+ const email = $('#at-field-username_and_email').val();
+ const password = $('#at-field-password').val();
+
+ // Ldap account
+ if (authenticationMethodSelected === 'ldap') {
+ // Check if the user can use the ldap connection
+ Meteor.subscribe('user-authenticationMethod', email, {
+ onReady() {
+ const user = Users.findOne();
+ if (user === undefined || user.authenticationMethod === 'ldap') {
+ // Use the ldap connection package
+ Meteor.loginWithLDAP(email, password, function(error) {
+ if (!error) {
+ // Connection
+ return FlowRouter.go('/');
+ }
+ return error;
+ });
+ }
+ return this.stop();
+ },
+ });
+ }
+ },
});
Template.defaultLayout.events({
diff --git a/client/components/main/layouts.styl b/client/components/main/layouts.styl
index a79ff337..3457a028 100644
--- a/client/components/main/layouts.styl
+++ b/client/components/main/layouts.styl
@@ -61,6 +61,23 @@ body
display: block
float: right
font-size: 24px
+
+ .modal-content-wide
+ width: 800px
+ min-height: 0px
+ margin: 42px auto
+ padding: 12px
+ border-radius: 4px
+ background: darken(white, 13%)
+ z-index: 110
+
+ h2
+ margin-bottom: 25px
+
+ .modal-close-btn
+ display: block
+ float: right
+ font-size: 24px
h1
font-size: 22px
diff --git a/client/components/rules/.DS_Store b/client/components/rules/.DS_Store
new file mode 100644
index 00000000..5008ddfc
--- /dev/null
+++ b/client/components/rules/.DS_Store
Binary files differ
diff --git a/client/components/rules/actions/boardActions.jade b/client/components/rules/actions/boardActions.jade
new file mode 100644
index 00000000..768d77cf
--- /dev/null
+++ b/client/components/rules/actions/boardActions.jade
@@ -0,0 +1,46 @@
+template(name="boardActions")
+ div.trigger-item
+ div.trigger-content
+ 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
+ | {{_'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
+ | {{_'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-dropdown
+ input(id="listName",type=text,placeholder="{{_'r-name'}}")
+ div.trigger-button.js-add-spec-move-action.js-goto-rules
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-dropdown
+ select(id="arch-action")
+ option(value="archive") {{_'r-archive'}}
+ option(value="unarchive") {{_'r-unarchive'}}
+ div.trigger-text
+ | {{_'r-card'}}
+ div.trigger-button.js-add-arch-action.js-goto-rules
+ i.fa.fa-plus
+
+
+
+
+
+
+
diff --git a/client/components/rules/actions/boardActions.js b/client/components/rules/actions/boardActions.js
new file mode 100644
index 00000000..95771fce
--- /dev/null
+++ b/client/components/rules/actions/boardActions.js
@@ -0,0 +1,122 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+
+ },
+
+ events() {
+ return [{
+ 'click .js-add-spec-move-action' (event) {
+ 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 boardId = Session.get('currentBoard');
+ const desc = Utils.getTriggerActionDesc(event, this);
+ if (actionSelected === 'top') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'moveCardToTop',
+ listTitle,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ if (actionSelected === 'bottom') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'moveCardToBottom',
+ listTitle,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ },
+ 'click .js-add-gen-move-action' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const boardId = Session.get('currentBoard');
+ const ruleName = this.data().ruleName.get();
+ const trigger = this.data().triggerVar.get();
+ const actionSelected = this.find('#move-gen-action').value;
+ if (actionSelected === 'top') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'moveCardToTop',
+ 'listTitle': '*',
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ if (actionSelected === 'bottom') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'moveCardToBottom',
+ 'listTitle': '*',
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ },
+ 'click .js-add-arch-action' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const boardId = Session.get('currentBoard');
+ const ruleName = this.data().ruleName.get();
+ const trigger = this.data().triggerVar.get();
+ const actionSelected = this.find('#arch-action').value;
+ if (actionSelected === 'archive') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'archive',
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ if (actionSelected === 'unarchive') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'unarchive',
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ },
+ }];
+ },
+
+}).register('boardActions');
+/* eslint-no-undef */
diff --git a/client/components/rules/actions/cardActions.jade b/client/components/rules/actions/cardActions.jade
new file mode 100644
index 00000000..74ad9ab5
--- /dev/null
+++ b/client/components/rules/actions/cardActions.jade
@@ -0,0 +1,43 @@
+template(name="cardActions")
+ div.trigger-item
+ div.trigger-content
+ div.trigger-dropdown
+ select(id="label-action")
+ option(value="add") {{{_'r-add'}}}
+ option(value="remove") {{{_'r-remove'}}}
+ div.trigger-text
+ | {{{_'r-label'}}}
+ div.trigger-dropdown
+ select(id="label-id")
+ each labels
+ option(value="#{_id}")
+ = name
+ div.trigger-button.js-add-label-action.js-goto-rules
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-dropdown
+ select(id="member-action")
+ option(value="add") {{{_'r-add'}}}
+ option(value="remove") {{{_'r-remove'}}}
+ div.trigger-text
+ | {{{_'r-member'}}}
+ div.trigger-dropdown
+ input(id="member-name",type=text,placeholder="{{{_'r-name'}}}")
+ div.trigger-button.js-add-member-action.js-goto-rules
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{{_'r-remove-all'}}}
+ div.trigger-button.js-add-removeall-action.js-goto-rules
+ i.fa.fa-plus
+
+
+
+
+
+
+
diff --git a/client/components/rules/actions/cardActions.js b/client/components/rules/actions/cardActions.js
new file mode 100644
index 00000000..b04440bd
--- /dev/null
+++ b/client/components/rules/actions/cardActions.js
@@ -0,0 +1,118 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.subscribe('allRules');
+ },
+
+ labels() {
+ const labels = Boards.findOne(Session.get('currentBoard')).labels;
+ for (let i = 0; i < labels.length; i++) {
+ if (labels[i].name === '' || labels[i].name === undefined) {
+ labels[i].name = labels[i].color.toUpperCase();
+ }
+ }
+ return labels;
+ },
+
+ events() {
+ return [{
+ 'click .js-add-label-action' (event) {
+ const ruleName = this.data().ruleName.get();
+ const trigger = this.data().triggerVar.get();
+ const actionSelected = this.find('#label-action').value;
+ const labelId = this.find('#label-id').value;
+ const boardId = Session.get('currentBoard');
+ const desc = Utils.getTriggerActionDesc(event, this);
+ if (actionSelected === 'add') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'addLabel',
+ labelId,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ if (actionSelected === 'remove') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'removeLabel',
+ labelId,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+
+ },
+ 'click .js-add-member-action' (event) {
+ const ruleName = this.data().ruleName.get();
+ const trigger = this.data().triggerVar.get();
+ const actionSelected = this.find('#member-action').value;
+ const username = this.find('#member-name').value;
+ const boardId = Session.get('currentBoard');
+ const desc = Utils.getTriggerActionDesc(event, this);
+ if (actionSelected === 'add') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'addMember',
+ username,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ desc,
+ });
+ }
+ if (actionSelected === 'remove') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'removeMember',
+ username,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ },
+ 'click .js-add-removeall-action' (event) {
+ const ruleName = this.data().ruleName.get();
+ const trigger = this.data().triggerVar.get();
+ const triggerId = Triggers.insert(trigger);
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const boardId = Session.get('currentBoard');
+ const actionId = Actions.insert({
+ actionType: 'removeMember',
+ 'username': '*',
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ },
+ }];
+ },
+
+}).register('cardActions');
diff --git a/client/components/rules/actions/checklistActions.jade b/client/components/rules/actions/checklistActions.jade
new file mode 100644
index 00000000..8414a1a5
--- /dev/null
+++ b/client/components/rules/actions/checklistActions.jade
@@ -0,0 +1,51 @@
+template(name="checklistActions")
+ div.trigger-item
+ div.trigger-content
+ div.trigger-dropdown
+ select(id="check-action")
+ option(value="add") {{{_'r-add'}}}
+ option(value="remove") {{{_'r-remove'}}}
+ div.trigger-text
+ | {{{_'r-checklist'}}}
+ div.trigger-dropdown
+ input(id="checklist-name",type=text,placeholder="{{{_'r-name'}}}")
+ div.trigger-button.js-add-checklist-action.js-goto-rules
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-dropdown
+ select(id="checkall-action")
+ option(value="check") {{{_'r-check-all'}}}
+ option(value="uncheck") {{{_'r-uncheck-all'}}}
+ div.trigger-text
+ | {{{_'r-items-check'}}}
+ div.trigger-dropdown
+ input(id="checklist-name2",type=text,placeholder="{{{_'r-name'}}}")
+ div.trigger-button.js-add-checkall-action.js-goto-rules
+ i.fa.fa-plus
+
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-dropdown
+ select(id="check-item-action")
+ option(value="check") {{{_'r-check'}}}
+ option(value="uncheck") {{{_'r-uncheck'}}}
+ div.trigger-text
+ | {{{_'r-item'}}}
+ div.trigger-dropdown
+ input(id="checkitem-name",type=text,placeholder="{{{_'r-name'}}}")
+ div.trigger-text
+ | {{{_'r-of-checklist'}}}
+ div.trigger-dropdown
+ input(id="checklist-name3",type=text,placeholder="{{{_'r-name'}}}")
+ div.trigger-button.js-add-check-item-action.js-goto-rules
+ i.fa.fa-plus
+
+
+
+
+
+
+
diff --git a/client/components/rules/actions/checklistActions.js b/client/components/rules/actions/checklistActions.js
new file mode 100644
index 00000000..4b70f959
--- /dev/null
+++ b/client/components/rules/actions/checklistActions.js
@@ -0,0 +1,128 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.subscribe('allRules');
+ },
+ events() {
+ return [{
+ 'click .js-add-checklist-action' (event) {
+ const ruleName = this.data().ruleName.get();
+ const trigger = this.data().triggerVar.get();
+ const actionSelected = this.find('#check-action').value;
+ const checklistName = this.find('#checklist-name').value;
+ const boardId = Session.get('currentBoard');
+ const desc = Utils.getTriggerActionDesc(event, this);
+ if (actionSelected === 'add') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'addChecklist',
+ checklistName,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ if (actionSelected === 'remove') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'removeChecklist',
+ checklistName,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+
+ },
+ 'click .js-add-checkall-action' (event) {
+ const ruleName = this.data().ruleName.get();
+ const trigger = this.data().triggerVar.get();
+ const actionSelected = this.find('#checkall-action').value;
+ const checklistName = this.find('#checklist-name2').value;
+ const boardId = Session.get('currentBoard');
+ const desc = Utils.getTriggerActionDesc(event, this);
+ if (actionSelected === 'check') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'checkAll',
+ checklistName,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ if (actionSelected === 'uncheck') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'uncheckAll',
+ checklistName,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ },
+ 'click .js-add-check-item-action' (event) {
+ const ruleName = this.data().ruleName.get();
+ const trigger = this.data().triggerVar.get();
+ const checkItemName = this.find('#checkitem-name');
+ const checklistName = this.find('#checklist-name3');
+ const actionSelected = this.find('#check-item-action').value;
+ const boardId = Session.get('currentBoard');
+ const desc = Utils.getTriggerActionDesc(event, this);
+ if (actionSelected === 'check') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'checkItem',
+ checklistName,
+ checkItemName,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ if (actionSelected === 'uncheck') {
+ const triggerId = Triggers.insert(trigger);
+ const actionId = Actions.insert({
+ actionType: 'uncheckItem',
+ checklistName,
+ checkItemName,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ }
+ },
+ }];
+ },
+
+}).register('checklistActions');
diff --git a/client/components/rules/actions/mailActions.jade b/client/components/rules/actions/mailActions.jade
new file mode 100644
index 00000000..7be78c75
--- /dev/null
+++ b/client/components/rules/actions/mailActions.jade
@@ -0,0 +1,11 @@
+template(name="mailActions")
+ div.trigger-item.trigger-item-mail
+ div.trigger-content.trigger-content-mail
+ div.trigger-text.trigger-text-email
+ | {{_'r-send-email'}}
+ div.trigger-dropdown-mail
+ input(id="email-to",type=text,placeholder="{{_'r-to'}}")
+ input(id="email-subject",type=text,placeholder="{{_'r-subject'}}")
+ textarea(id="email-msg")
+ div.trigger-button.trigger-button-email.js-mail-action.js-goto-rules
+ i.fa.fa-plus
diff --git a/client/components/rules/actions/mailActions.js b/client/components/rules/actions/mailActions.js
new file mode 100644
index 00000000..40cbc280
--- /dev/null
+++ b/client/components/rules/actions/mailActions.js
@@ -0,0 +1,35 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+
+ },
+
+ events() {
+ return [{
+ 'click .js-mail-action' (event) {
+ const emailTo = this.find('#email-to').value;
+ const emailSubject = this.find('#email-subject').value;
+ const emailMsg = this.find('#email-msg').value;
+ const trigger = this.data().triggerVar.get();
+ const ruleName = this.data().ruleName.get();
+ const triggerId = Triggers.insert(trigger);
+ const boardId = Session.get('currentBoard');
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const actionId = Actions.insert({
+ actionType: 'sendEmail',
+ emailTo,
+ emailSubject,
+ emailMsg,
+ boardId,
+ desc,
+ });
+ Rules.insert({
+ title: ruleName,
+ triggerId,
+ actionId,
+ boardId,
+ });
+ },
+ }];
+ },
+
+}).register('mailActions');
diff --git a/client/components/rules/ruleDetails.jade b/client/components/rules/ruleDetails.jade
new file mode 100644
index 00000000..7183cf96
--- /dev/null
+++ b/client/components/rules/ruleDetails.jade
@@ -0,0 +1,20 @@
+template(name="ruleDetails")
+ .rules
+ h2
+ i.fa.fa-magic
+ | {{{_ 'r-rule-details' }}}
+ .triggers-content
+ .triggers-body
+ .triggers-main-body
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ = trigger
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ = action
+ div.rules-back
+ button.js-goback
+ i.fa.fa-chevron-left
+ | {{{_ 'back'}}}
diff --git a/client/components/rules/ruleDetails.js b/client/components/rules/ruleDetails.js
new file mode 100644
index 00000000..17c86dc3
--- /dev/null
+++ b/client/components/rules/ruleDetails.js
@@ -0,0 +1,38 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.subscribe('allRules');
+ this.subscribe('allTriggers');
+ this.subscribe('allActions');
+
+ },
+
+ trigger() {
+ const ruleId = this.data().ruleId;
+ const rule = Rules.findOne({
+ _id: ruleId.get(),
+ });
+ const trigger = Triggers.findOne({
+ _id: rule.triggerId,
+ });
+ const desc = trigger.description();
+ const upperdesc = desc.charAt(0).toUpperCase() + desc.substr(1);
+ return upperdesc;
+ },
+ action() {
+ const ruleId = this.data().ruleId;
+ const rule = Rules.findOne({
+ _id: ruleId.get(),
+ });
+ const action = Actions.findOne({
+ _id: rule.actionId,
+ });
+ const desc = action.description();
+ const upperdesc = desc.charAt(0).toUpperCase() + desc.substr(1);
+ return upperdesc;
+ },
+
+ events() {
+ return [{}];
+ },
+
+}).register('ruleDetails');
diff --git a/client/components/rules/rules.styl b/client/components/rules/rules.styl
new file mode 100644
index 00000000..b52f84a7
--- /dev/null
+++ b/client/components/rules/rules.styl
@@ -0,0 +1,167 @@
+.rules-list
+ overflow:hidden
+ overflow-y:scroll
+ max-height: 400px
+.rules-lists-item
+ display: block
+ position: relative
+ overflow: auto
+ p
+ display: inline-block
+ float: left
+ margin: revert
+
+.rules-btns-group
+ position: absolute
+ right: 0
+ top: 50%
+ transform: translateY(-50%)
+ button
+ margin: auto
+.rules-add
+ display: block
+ overflow: auto
+ margin-top: 15px
+ margin-bottom: 5px
+ input
+ display: inline-block
+ float: right
+ margin: auto
+ margin-right: 10px
+ button
+ display: inline-block
+ float: right
+ margin: auto
+.rules-back
+ display: block
+ overflow: auto
+ margin-top: 15px
+ margin-bottom: 5px
+ button
+ display: inline-block
+ float: right
+ margin: auto
+ margin-right:14px
+
+.flex
+ display: -webkit-box
+ display: -moz-box
+ display: -webkit-flex
+ display: -moz-flex
+ display: -ms-flexbox
+ display: flex
+
+
+
+.triggers-content
+ color: #727479
+ background: #dedede
+ .triggers-body
+ display flex
+ padding-top 15px
+ height 100%
+
+ .triggers-side-menu
+ background-color: #f7f7f7
+ border: 1px solid #f0f0f0
+ border-radius: 4px
+ height: intrinsic
+ box-shadow: inset -1px -1px 3px rgba(0,0,0,.05)
+
+ ul
+
+ li
+ margin: 0.1rem 0.2rem;
+ width:50px
+ height:50px
+ text-align:center
+ font-size: 25px
+ position: relative
+
+ i
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ box-shadow: none
+ transform: translate(-50%,-50%);
+
+
+ &.active
+ background #fff
+ box-shadow 0 1px 2px rgba(0,0,0,0.15);
+
+ &: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
+ .triggers-main-body
+ padding: 0.1em 1em
+ width:100%
+ .trigger-item
+ overflow:auto
+ padding:10px
+ height:40px
+ margin-bottom:5px
+ border-radius: 3px
+ position: relative
+ background-color: white
+ .trigger-content
+ position:absolute
+ top:50%
+ transform: translateY(-50%)
+ left:10px
+ .trigger-text
+ font-size: 16px
+ display:inline-block
+ .trigger-text.trigger-text-email
+ margin-left: 5px;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ .trigger-dropdown
+ display:inline-block
+ select
+ width:auto
+ height:30px
+ margin:0px
+ margin-left:5px
+ input
+ display: inline-block
+ width: 80px;
+ margin: 0;
+ .trigger-content-mail
+ left:20px
+ right:100px
+ .trigger-button
+ position:absolute
+ top:50%
+ transform: translateY(-50%)
+ width:30px
+ height:30px
+ border: 1px solid #eee
+ border-radius: 4px
+ box-shadow: inset -1px -1px 3px rgba(0,0,0,.05)
+ text-align:center
+ font-size: 20px
+ right:10px
+ i
+ position: absolute
+ top: 50%
+ left: 50%
+ box-shadow: none
+ transform: translate(-50%,-50%)
+ &:hover, &.is-active
+ box-shadow: 0 0 0 2px darken(white, 60%) inset
+ .trigger-button.trigger-button-email
+ top:30px
+ .trigger-item.trigger-item-mail
+ height:300px
+
+
+
diff --git a/client/components/rules/rulesActions.jade b/client/components/rules/rulesActions.jade
new file mode 100644
index 00000000..3ac04e1c
--- /dev/null
+++ b/client/components/rules/rulesActions.jade
@@ -0,0 +1,29 @@
+template(name="rulesActions")
+ h2
+ i.fa.fa-magic
+ | {{{_ 'r-rule' }}} "#{data.ruleName.get}" - {{{_ 'r-add-action'}}}
+ .triggers-content
+ .triggers-body
+ .triggers-side-menu
+ ul
+ li.active.js-set-board-actions
+ i.fa.fa-columns
+ li.js-set-card-actions
+ i.fa.fa-sticky-note
+ li.js-set-checklist-actions
+ i.fa.fa-check
+ li.js-set-mail-actions
+ i.fa.fa-at
+ .triggers-main-body
+ if ($eq currentActions.get 'board')
+ +boardActions(ruleName=data.ruleName triggerVar=data.triggerVar)
+ else if ($eq currentActions.get 'card')
+ +cardActions(ruleName=data.ruleName triggerVar=data.triggerVar)
+ else if ($eq currentActions.get 'checklist')
+ +checklistActions(ruleName=data.ruleName triggerVar=data.triggerVar)
+ else if ($eq currentActions.get 'mail')
+ +mailActions(ruleName=data.ruleName triggerVar=data.triggerVar)
+ div.rules-back
+ button.js-goback
+ i.fa.fa-chevron-left
+ | {{{_ 'back'}}}
diff --git a/client/components/rules/rulesActions.js b/client/components/rules/rulesActions.js
new file mode 100644
index 00000000..64a5c70e
--- /dev/null
+++ b/client/components/rules/rulesActions.js
@@ -0,0 +1,58 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.currentActions = new ReactiveVar('board');
+ },
+
+ setBoardActions() {
+ this.currentActions.set('board');
+ $('.js-set-card-actions').removeClass('active');
+ $('.js-set-board-actions').addClass('active');
+ $('.js-set-checklist-actions').removeClass('active');
+ $('.js-set-mail-actions').removeClass('active');
+ },
+ setCardActions() {
+ this.currentActions.set('card');
+ $('.js-set-card-actions').addClass('active');
+ $('.js-set-board-actions').removeClass('active');
+ $('.js-set-checklist-actions').removeClass('active');
+ $('.js-set-mail-actions').removeClass('active');
+ },
+ setChecklistActions() {
+ this.currentActions.set('checklist');
+ $('.js-set-card-actions').removeClass('active');
+ $('.js-set-board-actions').removeClass('active');
+ $('.js-set-checklist-actions').addClass('active');
+ $('.js-set-mail-actions').removeClass('active');
+ },
+ setMailActions() {
+ this.currentActions.set('mail');
+ $('.js-set-card-actions').removeClass('active');
+ $('.js-set-board-actions').removeClass('active');
+ $('.js-set-checklist-actions').removeClass('active');
+ $('.js-set-mail-actions').addClass('active');
+ },
+
+ rules() {
+ return Rules.find({});
+ },
+
+ name() {
+ // console.log(this.data());
+ },
+ events() {
+ return [{
+ 'click .js-set-board-actions'(){
+ this.setBoardActions();
+ },
+ 'click .js-set-card-actions'() {
+ this.setCardActions();
+ },
+ 'click .js-set-mail-actions'() {
+ this.setMailActions();
+ },
+ 'click .js-set-checklist-actions'() {
+ this.setChecklistActions();
+ },
+ }];
+ },
+}).register('rulesActions');
diff --git a/client/components/rules/rulesList.jade b/client/components/rules/rulesList.jade
new file mode 100644
index 00000000..c2676aa7
--- /dev/null
+++ b/client/components/rules/rulesList.jade
@@ -0,0 +1,27 @@
+template(name="rulesList")
+ .rules
+ h2
+ i.fa.fa-magic
+ | {{{_ 'r-board-rules' }}}
+
+ ul.rules-list
+ each rules
+ li.rules-lists-item
+ p
+ = title
+ div.rules-btns-group
+ button.js-goto-details
+ i.fa.fa-eye
+ | {{{_ 'r-view-rule'}}}
+ if currentUser.isAdmin
+ button.js-delete-rule
+ i.fa.fa-trash-o
+ | {{{_ 'r-delete-rule'}}}
+ else
+ li.no-items-message {{{_ 'r-no-rules' }}}
+ if currentUser.isAdmin
+ div.rules-add
+ button.js-goto-trigger
+ i.fa.fa-plus
+ | {{{_ 'r-add-rule'}}}
+ input(type=text,placeholder="{{{_ 'r-new-rule-name' }}}",id="ruleTitle") \ No newline at end of file
diff --git a/client/components/rules/rulesList.js b/client/components/rules/rulesList.js
new file mode 100644
index 00000000..d3923bf9
--- /dev/null
+++ b/client/components/rules/rulesList.js
@@ -0,0 +1,15 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.subscribe('allRules');
+ },
+
+ rules() {
+ const boardId = Session.get('currentBoard');
+ return Rules.find({
+ boardId,
+ });
+ },
+ events() {
+ return [{}];
+ },
+}).register('rulesList');
diff --git a/client/components/rules/rulesMain.jade b/client/components/rules/rulesMain.jade
new file mode 100644
index 00000000..dc33ee4e
--- /dev/null
+++ b/client/components/rules/rulesMain.jade
@@ -0,0 +1,9 @@
+template(name="rulesMain")
+ if($eq rulesCurrentTab.get 'rulesList')
+ +rulesList
+ if($eq rulesCurrentTab.get 'trigger')
+ +rulesTriggers(ruleName=ruleName triggerVar=triggerVar)
+ if($eq rulesCurrentTab.get 'action')
+ +rulesActions(ruleName=ruleName triggerVar=triggerVar)
+ if($eq rulesCurrentTab.get 'ruleDetails')
+ +ruleDetails(ruleId=ruleId) \ No newline at end of file
diff --git a/client/components/rules/rulesMain.js b/client/components/rules/rulesMain.js
new file mode 100644
index 00000000..0752a541
--- /dev/null
+++ b/client/components/rules/rulesMain.js
@@ -0,0 +1,70 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.rulesCurrentTab = new ReactiveVar('rulesList');
+ this.ruleName = new ReactiveVar('');
+ this.triggerVar = new ReactiveVar();
+ this.ruleId = new ReactiveVar();
+ },
+
+ setTrigger() {
+ this.rulesCurrentTab.set('trigger');
+ },
+
+ setRulesList() {
+ this.rulesCurrentTab.set('rulesList');
+ },
+
+ setAction() {
+ this.rulesCurrentTab.set('action');
+ },
+
+ setRuleDetails() {
+ this.rulesCurrentTab.set('ruleDetails');
+ },
+
+ events() {
+ return [{
+ 'click .js-delete-rule' () {
+ const rule = this.currentData();
+ Rules.remove(rule._id);
+ Actions.remove(rule.actionId);
+ Triggers.remove(rule.triggerId);
+
+ },
+ 'click .js-goto-trigger' (event) {
+ event.preventDefault();
+ const ruleTitle = this.find('#ruleTitle').value;
+ if(ruleTitle !== undefined && ruleTitle !== ''){
+ this.find('#ruleTitle').value = '';
+ this.ruleName.set(ruleTitle);
+ this.setTrigger();
+ }
+ },
+ 'click .js-goto-action' (event) {
+ event.preventDefault();
+ this.setAction();
+ },
+ 'click .js-goto-rules' (event) {
+ event.preventDefault();
+ this.setRulesList();
+ },
+ 'click .js-goback' (event) {
+ event.preventDefault();
+ if(this.rulesCurrentTab.get() === 'trigger' || this.rulesCurrentTab.get() === 'ruleDetails' ){
+ this.setRulesList();
+ }
+ if(this.rulesCurrentTab.get() === 'action'){
+ this.setTrigger();
+ }
+ },
+ 'click .js-goto-details' (event) {
+ event.preventDefault();
+ const rule = this.currentData();
+ this.ruleId.set(rule._id);
+ this.setRuleDetails();
+ },
+
+ }];
+ },
+
+}).register('rulesMain');
diff --git a/client/components/rules/rulesTriggers.jade b/client/components/rules/rulesTriggers.jade
new file mode 100644
index 00000000..79d9d98e
--- /dev/null
+++ b/client/components/rules/rulesTriggers.jade
@@ -0,0 +1,25 @@
+template(name="rulesTriggers")
+ h2
+ i.fa.fa-magic
+ | {{{_ 'r-rule' }}} "#{data.ruleName.get}" - {{{_ 'r-add-trigger'}}}
+ .triggers-content
+ .triggers-body
+ .triggers-side-menu
+ ul
+ li.active.js-set-board-triggers
+ i.fa.fa-columns
+ li.js-set-card-triggers
+ i.fa.fa-sticky-note
+ li.js-set-checklist-triggers
+ i.fa.fa-check
+ .triggers-main-body
+ if showBoardTrigger.get
+ +boardTriggers
+ else if showCardTrigger.get
+ +cardTriggers
+ else if showChecklistTrigger.get
+ +checklistTriggers
+ div.rules-back
+ button.js-goback
+ i.fa.fa-chevron-left
+ | {{{_ 'back'}}}
diff --git a/client/components/rules/rulesTriggers.js b/client/components/rules/rulesTriggers.js
new file mode 100644
index 00000000..e3c16221
--- /dev/null
+++ b/client/components/rules/rulesTriggers.js
@@ -0,0 +1,53 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.showBoardTrigger = new ReactiveVar(true);
+ this.showCardTrigger = new ReactiveVar(false);
+ this.showChecklistTrigger = new ReactiveVar(false);
+ },
+
+ setBoardTriggers() {
+ this.showBoardTrigger.set(true);
+ this.showCardTrigger.set(false);
+ this.showChecklistTrigger.set(false);
+ $('.js-set-card-triggers').removeClass('active');
+ $('.js-set-board-triggers').addClass('active');
+ $('.js-set-checklist-triggers').removeClass('active');
+ },
+ setCardTriggers() {
+ this.showBoardTrigger.set(false);
+ this.showCardTrigger.set(true);
+ this.showChecklistTrigger.set(false);
+ $('.js-set-card-triggers').addClass('active');
+ $('.js-set-board-triggers').removeClass('active');
+ $('.js-set-checklist-triggers').removeClass('active');
+ },
+ setChecklistTriggers() {
+ this.showBoardTrigger.set(false);
+ this.showCardTrigger.set(false);
+ this.showChecklistTrigger.set(true);
+ $('.js-set-card-triggers').removeClass('active');
+ $('.js-set-board-triggers').removeClass('active');
+ $('.js-set-checklist-triggers').addClass('active');
+ },
+
+ rules() {
+ return Rules.find({});
+ },
+
+ name() {
+ // console.log(this.data());
+ },
+ events() {
+ return [{
+ 'click .js-set-board-triggers' () {
+ this.setBoardTriggers();
+ },
+ 'click .js-set-card-triggers' () {
+ this.setCardTriggers();
+ },
+ 'click .js-set-checklist-triggers' () {
+ this.setChecklistTriggers();
+ },
+ }];
+ },
+}).register('rulesTriggers');
diff --git a/client/components/rules/triggers/boardTriggers.jade b/client/components/rules/triggers/boardTriggers.jade
new file mode 100644
index 00000000..48b9345c
--- /dev/null
+++ b/client/components/rules/triggers/boardTriggers.jade
@@ -0,0 +1,68 @@
+template(name="boardTriggers")
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-card-is'}}
+ div.trigger-dropdown
+ select(id="gen-action")
+ option(value="created") {{_'r-added-to'}}
+ option(value="removed") {{_'r-removed-from'}}
+ div.trigger-text
+ | {{_'r-the-board'}}
+ div.trigger-button.js-add-gen-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-card-is'}}
+ div.trigger-dropdown
+ select(id="create-action")
+ option(value="created") {{_'r-added-to'}}
+ option(value="removed") {{_'r-removed-from'}}
+ div.trigger-text
+ | {{_'r-list'}}
+ div.trigger-dropdown
+ input(id="create-list-name",type=text,placeholder="{{_'r-list-name'}}")
+ div.trigger-button.js-add-create-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-card-is-moved'}}
+ div.trigger-button.js-add-gen-moved-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-card-is'}}
+ div.trigger-dropdown
+ select(id="move-action")
+ option(value="moved-to") {{_'r-moved-to'}}
+ option(value="moved-from") {{_'r-moved-from'}}
+ div.trigger-text
+ | {{_'r-list'}}
+ div.trigger-dropdown
+ input(id="move-list-name",type=text,placeholder="{{_'r-list-name'}}")
+ div.trigger-button.js-add-moved-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-card-is'}}
+ div.trigger-dropdown
+ select(id="arch-action")
+ option(value="archived") {{_'r-archived'}}
+ option(value="unarchived") {{_'r-unarchived'}}
+ div.trigger-button.js-add-arch-trigger.js-goto-action
+ i.fa.fa-plus
+
+
+
+
+
+
+
diff --git a/client/components/rules/triggers/boardTriggers.js b/client/components/rules/triggers/boardTriggers.js
new file mode 100644
index 00000000..40c5b07e
--- /dev/null
+++ b/client/components/rules/triggers/boardTriggers.js
@@ -0,0 +1,116 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+
+ },
+
+ events() {
+ return [{
+ 'click .js-add-gen-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#gen-action').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'created') {
+ datas.triggerVar.set({
+ activityType: 'createCard',
+ boardId,
+ 'listName': '*',
+ desc,
+ });
+ }
+ if (actionSelected === 'removed') {
+ datas.triggerVar.set({
+ activityType: 'removeCard',
+ boardId,
+ desc,
+ });
+ }
+
+ },
+ 'click .js-add-create-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#create-action').value;
+ const listName = this.find('#create-list-name').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'created') {
+ datas.triggerVar.set({
+ activityType: 'createCard',
+ boardId,
+ listName,
+ desc,
+ });
+ }
+ if (actionSelected === 'removed') {
+ datas.triggerVar.set({
+ activityType: 'removeCard',
+ boardId,
+ listName,
+ desc,
+ });
+ }
+ },
+ 'click .js-add-moved-trigger' (event) {
+ const datas = this.data();
+ const desc = Utils.getTriggerActionDesc(event, this);
+
+ const actionSelected = this.find('#move-action').value;
+ const listName = this.find('#move-list-name').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'moved-to') {
+ datas.triggerVar.set({
+ activityType: 'moveCard',
+ boardId,
+ listName,
+ 'oldListName': '*',
+ desc,
+ });
+ }
+ if (actionSelected === 'moved-from') {
+ datas.triggerVar.set({
+ activityType: 'moveCard',
+ boardId,
+ 'listName': '*',
+ 'oldListName': listName,
+ desc,
+ });
+ }
+ },
+ 'click .js-add-gen-moved-trigger' (event){
+ const datas = this.data();
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const boardId = Session.get('currentBoard');
+
+ datas.triggerVar.set({
+ activityType: 'moveCard',
+ boardId,
+ 'listName':'*',
+ 'oldListName': '*',
+ desc,
+ });
+ },
+ 'click .js-add-arc-trigger' (event) {
+ const datas = this.data();
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const actionSelected = this.find('#arch-action').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'archived') {
+ datas.triggerVar.set({
+ activityType: 'archivedCard',
+ boardId,
+ desc,
+ });
+ }
+ if (actionSelected === 'unarchived') {
+ datas.triggerVar.set({
+ activityType: 'restoredCard',
+ boardId,
+ desc,
+ });
+ }
+ },
+
+ }];
+ },
+
+}).register('boardTriggers');
diff --git a/client/components/rules/triggers/cardTriggers.jade b/client/components/rules/triggers/cardTriggers.jade
new file mode 100644
index 00000000..5226e3c4
--- /dev/null
+++ b/client/components/rules/triggers/cardTriggers.jade
@@ -0,0 +1,79 @@
+template(name="cardTriggers")
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-label-is'}}
+ div.trigger-dropdown
+ select(id="label-action")
+ option(value="added") {{_'r-added-to'}}
+ option(value="removed") {{_'r-removed-from'}}
+ div.trigger-text
+ | {{_'r-a-card'}}
+ div.trigger-button.js-add-gen-label-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-the-label-is'}}
+ div.trigger-dropdown
+ select(id="spec-label")
+ each labels
+ option(value="#{_id}")
+ = name
+ div.trigger-text
+ | {{_'r-is'}}
+ div.trigger-dropdown
+ select(id="spec-label-action")
+ option(value="added") {{_'r-added-to'}}
+ option(value="removed") {{_'r-removed-from'}}
+ div.trigger-text
+ | {{_'r-a-card'}}
+ div.trigger-button.js-add-spec-label-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-member'}}
+ div.trigger-dropdown
+ select(id="gen-member-action")
+ option(value="added") {{_'r-added-to'}}
+ option(value="removed") {{_'r-removed-from'}}
+ div.trigger-text
+ | {{_'r-a-card'}}
+ div.trigger-button.js-add-gen-member-trigger.js-goto-action
+ i.fa.fa-plus
+
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-the-member'}}
+ div.trigger-dropdown
+ input(id="spec-member",type=text,placeholder="{{_'r-name'}}")
+ div.trigger-text
+ | {{_'r-is'}}
+ div.trigger-dropdown
+ select(id="spec-member-action")
+ option(value="added") {{_'r-added-to'}}
+ option(value="removed") {{_'r-removed-from'}}
+ div.trigger-text
+ | {{_'r-a-card'}}
+ div.trigger-button.js-add-spec-member-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-attach'}}
+ div.trigger-text
+ | {{_'r-is'}}
+ div.trigger-dropdown
+ select(id="attach-action")
+ option(value="added") {{_'r-added-to'}}
+ option(value="removed") {{_'r-removed-from'}}
+ div.trigger-text
+ | {{_'r-a-card'}}
+ div.trigger-button.js-add-attachment-trigger.js-goto-action
+ i.fa.fa-plus
diff --git a/client/components/rules/triggers/cardTriggers.js b/client/components/rules/triggers/cardTriggers.js
new file mode 100644
index 00000000..2303a85b
--- /dev/null
+++ b/client/components/rules/triggers/cardTriggers.js
@@ -0,0 +1,128 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.subscribe('allRules');
+ },
+ labels() {
+ const labels = Boards.findOne(Session.get('currentBoard')).labels;
+ for (let i = 0; i < labels.length; i++) {
+ if (labels[i].name === '' || labels[i].name === undefined) {
+ labels[i].name = labels[i].color.toUpperCase();
+ }
+ }
+ return labels;
+ },
+ events() {
+ return [{
+ 'click .js-add-gen-label-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#label-action').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'added') {
+ datas.triggerVar.set({
+ activityType: 'addedLabel',
+ boardId,
+ 'labelId': '*',
+ desc,
+ });
+ }
+ if (actionSelected === 'removed') {
+ datas.triggerVar.set({
+ activityType: 'removedLabel',
+ boardId,
+ 'labelId': '*',
+ desc,
+ });
+ }
+ },
+ 'click .js-add-spec-label-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#spec-label-action').value;
+ const labelId = this.find('#spec-label').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'added') {
+ datas.triggerVar.set({
+ activityType: 'addedLabel',
+ boardId,
+ labelId,
+ desc,
+ });
+ }
+ if (actionSelected === 'removed') {
+ datas.triggerVar.set({
+ activityType: 'removedLabel',
+ boardId,
+ labelId,
+ desc,
+ });
+ }
+ },
+ 'click .js-add-gen-member-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#gen-member-action').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'added') {
+ datas.triggerVar.set({
+ activityType: 'joinMember',
+ boardId,
+ 'username': '*',
+ desc,
+ });
+ }
+ if (actionSelected === 'removed') {
+ datas.triggerVar.set({
+ activityType: 'unjoinMember',
+ boardId,
+ 'username': '*',
+ desc,
+ });
+ }
+ },
+ 'click .js-add-spec-member-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#spec-member-action').value;
+ const username = this.find('#spec-member').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'added') {
+ datas.triggerVar.set({
+ activityType: 'joinMember',
+ boardId,
+ username,
+ desc,
+ });
+ }
+ if (actionSelected === 'removed') {
+ datas.triggerVar.set({
+ activityType: 'unjoinMember',
+ boardId,
+ username,
+ desc,
+ });
+ }
+ },
+ 'click .js-add-attachment-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#attach-action').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'added') {
+ datas.triggerVar.set({
+ activityType: 'addAttachment',
+ boardId,
+ desc,
+ });
+ }
+ if (actionSelected === 'removed') {
+ datas.triggerVar.set({
+ activityType: 'deleteAttachment',
+ boardId,
+ desc,
+ });
+ }
+ },
+ }];
+ },
+}).register('cardTriggers');
diff --git a/client/components/rules/triggers/checklistTriggers.jade b/client/components/rules/triggers/checklistTriggers.jade
new file mode 100644
index 00000000..c6cd99a6
--- /dev/null
+++ b/client/components/rules/triggers/checklistTriggers.jade
@@ -0,0 +1,83 @@
+template(name="checklistTriggers")
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-checklist'}}
+ div.trigger-dropdown
+ select(id="gen-check-action")
+ option(value="created") {{_'r-added-to'}}
+ option(value="removed") {{_'r-removed-from'}}
+ div.trigger-text
+ | {{_'r-a-card'}}
+ div.trigger-button.js-add-gen-check-trigger.js-goto-action
+ i.fa.fa-plus
+
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-the-checklist'}}
+ div.trigger-dropdown
+ input(id="check-name",type=text,placeholder="{{_'r-name'}}")
+ div.trigger-text
+ | {{_'r-is'}}
+ div.trigger-dropdown
+ select(id="spec-check-action")
+ option(value="created") {{_'r-added-to'}}
+ option(value="removed") {{_'r-removed-from'}}
+ div.trigger-text
+ | {{_'r-a-card'}}
+ div.trigger-button.js-add-spec-check-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-checklist'}}
+ div.trigger-dropdown
+ select(id="gen-comp-check-action")
+ option(value="completed") {{_'r-completed'}}
+ option(value="uncompleted") {{_'r-made-incomplete'}}
+ div.trigger-button.js-add-gen-comp-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-the-checklist'}}
+ div.trigger-dropdown
+ input(id="spec-comp-check-name",type=text,placeholder="{{_'r-name'}}")
+ div.trigger-text
+ | {{_'r-is'}}
+ div.trigger-dropdown
+ select(id="spec-comp-check-action")
+ option(value="completed") {{_'r-completed'}}
+ option(value="uncompleted") {{_'r-made-incomplete'}}
+ div.trigger-button.js-add-spec-comp-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-a-item'}}
+ div.trigger-dropdown
+ select(id="check-item-gen-action")
+ option(value="checked") {{_'r-checked'}}
+ option(value="unchecked") {{_'r-unchecked'}}
+ div.trigger-button.js-add-gen-check-item-trigger.js-goto-action
+ i.fa.fa-plus
+
+ div.trigger-item
+ div.trigger-content
+ div.trigger-text
+ | {{_'r-when-the-item'}}
+ div.trigger-dropdown
+ input(id="check-item-name",type=text,placeholder="{{_'r-name'}}")
+ div.trigger-text
+ | {{_'r-is'}}
+ div.trigger-dropdown
+ select(id="check-item-spec-action")
+ option(value="checked") {{_'r-checked'}}
+ option(value="unchecked") {{_'r-unchecked'}}
+ div.trigger-button.js-add-spec-check-item-trigger.js-goto-action
+ i.fa.fa-plus
diff --git a/client/components/rules/triggers/checklistTriggers.js b/client/components/rules/triggers/checklistTriggers.js
new file mode 100644
index 00000000..2272be29
--- /dev/null
+++ b/client/components/rules/triggers/checklistTriggers.js
@@ -0,0 +1,146 @@
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.subscribe('allRules');
+ },
+ events() {
+ return [{
+ 'click .js-add-gen-check-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#gen-check-action').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'created') {
+ datas.triggerVar.set({
+ activityType: 'addChecklist',
+ boardId,
+ 'checklistName': '*',
+ desc,
+ });
+ }
+ if (actionSelected === 'removed') {
+ datas.triggerVar.set({
+ activityType: 'removeChecklist',
+ boardId,
+ 'checklistName': '*',
+ desc,
+ });
+ }
+ },
+ 'click .js-add-spec-check-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#spec-check-action').value;
+ const checklistId = this.find('#check-name').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'created') {
+ datas.triggerVar.set({
+ activityType: 'addChecklist',
+ boardId,
+ 'checklistName': checklistId,
+ desc,
+ });
+ }
+ if (actionSelected === 'removed') {
+ datas.triggerVar.set({
+ activityType: 'removeChecklist',
+ boardId,
+ 'checklistName': checklistId,
+ desc,
+ });
+ }
+ },
+ 'click .js-add-gen-comp-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+
+ const datas = this.data();
+ const actionSelected = this.find('#gen-comp-check-action').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'completed') {
+ datas.triggerVar.set({
+ activityType: 'completeChecklist',
+ boardId,
+ 'checklistName': '*',
+ desc,
+ });
+ }
+ if (actionSelected === 'uncompleted') {
+ datas.triggerVar.set({
+ activityType: 'uncompleteChecklist',
+ boardId,
+ 'checklistName': '*',
+ desc,
+ });
+ }
+ },
+ 'click .js-add-spec-comp-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#spec-comp-check-action').value;
+ const checklistId = this.find('#spec-comp-check-name').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'completed') {
+ datas.triggerVar.set({
+ activityType: 'completeChecklist',
+ boardId,
+ 'checklistName': checklistId,
+ desc,
+ });
+ }
+ if (actionSelected === 'uncompleted') {
+ datas.triggerVar.set({
+ activityType: 'uncompleteChecklist',
+ boardId,
+ 'checklistName': checklistId,
+ desc,
+ });
+ }
+ },
+ 'click .js-add-gen-check-item-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#check-item-gen-action').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'checked') {
+ datas.triggerVar.set({
+ activityType: 'checkedItem',
+ boardId,
+ 'checklistItemName': '*',
+ desc,
+ });
+ }
+ if (actionSelected === 'unchecked') {
+ datas.triggerVar.set({
+ activityType: 'uncheckedItem',
+ boardId,
+ 'checklistItemName': '*',
+ desc,
+ });
+ }
+ },
+ 'click .js-add-spec-check-item-trigger' (event) {
+ const desc = Utils.getTriggerActionDesc(event, this);
+ const datas = this.data();
+ const actionSelected = this.find('#check-item-spec-action').value;
+ const checklistItemId = this.find('#check-item-name').value;
+ const boardId = Session.get('currentBoard');
+ if (actionSelected === 'checked') {
+ datas.triggerVar.set({
+ activityType: 'checkedItem',
+ boardId,
+ 'checklistItemName': checklistItemId,
+ desc,
+ });
+ }
+ if (actionSelected === 'unchecked') {
+ datas.triggerVar.set({
+ activityType: 'uncheckedItem',
+ boardId,
+ 'checklistItemName': checklistItemId,
+ desc,
+ });
+ }
+ },
+ }];
+ },
+
+}).register('checklistTriggers');
diff --git a/client/components/settings/connectionMethod.jade b/client/components/settings/connectionMethod.jade
new file mode 100644
index 00000000..ac4c8c64
--- /dev/null
+++ b/client/components/settings/connectionMethod.jade
@@ -0,0 +1,6 @@
+template(name='connectionMethod')
+ div.at-form-authentication
+ label {{_ 'authentication-method'}}
+ select.select-authentication
+ each authentications
+ option(value="{{value}}") {{_ value}}
diff --git a/client/components/settings/connectionMethod.js b/client/components/settings/connectionMethod.js
new file mode 100644
index 00000000..9fe8f382
--- /dev/null
+++ b/client/components/settings/connectionMethod.js
@@ -0,0 +1,34 @@
+Template.connectionMethod.onCreated(function() {
+ this.authenticationMethods = new ReactiveVar([]);
+
+ Meteor.call('getAuthenticationsEnabled', (_, result) => {
+ if (result) {
+ // TODO : add a management of different languages
+ // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
+ this.authenticationMethods.set([
+ {value: 'password'},
+ // Gets only the authentication methods availables
+ ...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
+ ]);
+ }
+
+ // If only the default authentication available, hides the select boxe
+ const content = $('.at-form-authentication');
+ if (!(this.authenticationMethods.get().length > 1)) {
+ content.hide();
+ } else {
+ content.show();
+ }
+ });
+});
+
+Template.connectionMethod.onRendered(() => {
+ // Moves the select boxe in the first place of the at-pwd-form div
+ $('.at-form-authentication').detach().prependTo('.at-pwd-form');
+});
+
+Template.connectionMethod.helpers({
+ authentications() {
+ return Template.instance().authenticationMethods.get();
+ },
+});
diff --git a/client/components/settings/peopleBody.jade b/client/components/settings/peopleBody.jade
index a3506a24..4d06637e 100644
--- a/client/components/settings/peopleBody.jade
+++ b/client/components/settings/peopleBody.jade
@@ -27,6 +27,7 @@ template(name="peopleGeneral")
th {{_ 'verified'}}
th {{_ 'createdAt'}}
th {{_ 'active'}}
+ th {{_ 'authentication-method'}}
th
each user in peopleList
+peopleRow(userId=user._id)
@@ -52,6 +53,7 @@ template(name="peopleRow")
| {{_ 'no'}}
else
| {{_ 'yes'}}
+ td {{_ userData.authenticationMethod }}
td
a.edit-user
| {{_ 'edit'}}
@@ -66,12 +68,18 @@ template(name="editUserPopup")
| {{_ 'username'}}
span.error.hide.username-taken
| {{_ 'error-username-taken'}}
- input.js-profile-username(type="text" value=user.username)
+ if isLdap
+ input.js-profile-username(type="text" value=user.username readonly)
+ else
+ input.js-profile-username(type="text" value=user.username)
label
| {{_ 'email'}}
span.error.hide.email-taken
| {{_ 'error-email-taken'}}
- input.js-profile-email(type="email" value="{{user.emails.[0].address}}")
+ if isLdap
+ input.js-profile-email(type="email" value="{{user.emails.[0].address}}" readonly)
+ else
+ input.js-profile-email(type="email" value="{{user.emails.[0].address}}")
label
| {{_ 'admin'}}
select.select-role.js-profile-isadmin
@@ -82,9 +90,17 @@ template(name="editUserPopup")
select.select-active.js-profile-isactive
option(value="false") {{_ 'yes'}}
option(value="true" selected="{{user.loginDisabled}}") {{_ 'no'}}
+ label
+ | {{_ 'authentication-type'}}
+ select.select-authenticationMethod.js-authenticationMethod
+ each authentications
+ if isSelected value
+ option(value="{{value}}" selected) {{_ value}}
+ else
+ option(value="{{value}}") {{_ value}}
hr
label
| {{_ 'password'}}
input.js-profile-password(type="password")
- input.primary.wide(type="submit" value="{{_ 'save'}}")
+ input.primary.wide(type="submit" value="{{_ 'save'}}") \ No newline at end of file
diff --git a/client/components/settings/peopleBody.js b/client/components/settings/peopleBody.js
index 7cc992f2..a4d70974 100644
--- a/client/components/settings/peopleBody.js
+++ b/client/components/settings/peopleBody.js
@@ -62,10 +62,39 @@ Template.peopleRow.helpers({
},
});
+Template.editUserPopup.onCreated(function() {
+ this.authenticationMethods = new ReactiveVar([]);
+
+ Meteor.call('getAuthenticationsEnabled', (_, result) => {
+ if (result) {
+ // TODO : add a management of different languages
+ // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
+ this.authenticationMethods.set([
+ {value: 'password'},
+ // Gets only the authentication methods availables
+ ...Object.entries(result).filter((e) => e[1]).map((e) => ({value: e[0]})),
+ ]);
+ }
+ });
+});
+
Template.editUserPopup.helpers({
user() {
return Users.findOne(this.userId);
},
+ authentications() {
+ return Template.instance().authenticationMethods.get();
+ },
+ isSelected(match) {
+ const userId = Template.instance().data.userId;
+ const selected = Users.findOne(userId).authenticationMethod;
+ return selected === match;
+ },
+ isLdap() {
+ const userId = Template.instance().data.userId;
+ const selected = Users.findOne(userId).authenticationMethod;
+ return selected === 'ldap';
+ },
});
BlazeComponent.extendComponent({
@@ -91,6 +120,7 @@ Template.editUserPopup.events({
const isAdmin = tpl.find('.js-profile-isadmin').value.trim();
const isActive = tpl.find('.js-profile-isactive').value.trim();
const email = tpl.find('.js-profile-email').value.trim();
+ const authentication = tpl.find('.js-authenticationMethod').value.trim();
const isChangePassword = password.length > 0;
const isChangeUserName = username !== user.username;
@@ -101,6 +131,7 @@ Template.editUserPopup.events({
'profile.fullname': fullname,
'isAdmin': isAdmin === 'true',
'loginDisabled': isActive === 'true',
+ 'authenticationMethod': authentication,
},
});
diff --git a/client/lib/modal.js b/client/lib/modal.js
index d5350264..3c27a179 100644
--- a/client/lib/modal.js
+++ b/client/lib/modal.js
@@ -4,6 +4,7 @@ window.Modal = new class {
constructor() {
this._currentModal = new ReactiveVar(closedValue);
this._onCloseGoTo = '';
+ this._isWideModal = false;
}
getHeaderName() {
@@ -20,6 +21,10 @@ window.Modal = new class {
return this.getTemplateName() !== closedValue;
}
+ isWide(){
+ return this._isWideModal;
+ }
+
close() {
this._currentModal.set(closedValue);
if (this._onCloseGoTo) {
@@ -27,9 +32,16 @@ window.Modal = new class {
}
}
+ openWide(modalName, { header = '', onCloseGoTo = ''} = {}) {
+ this._currentModal.set({ header, modalName });
+ this._onCloseGoTo = onCloseGoTo;
+ this._isWideModal = true;
+ }
+
open(modalName, { header = '', onCloseGoTo = ''} = {}) {
this._currentModal.set({ header, modalName });
this._onCloseGoTo = onCloseGoTo;
+
}
}();
@@ -38,5 +50,5 @@ Blaze.registerHelper('Modal', Modal);
EscapeActions.register('modalWindow',
() => Modal.close(),
() => Modal.isOpen(),
- { noClickEscapeOn: '.modal-content' }
+ { noClickEscapeOn: '.modal-container' }
);
diff --git a/client/lib/utils.js b/client/lib/utils.js
index 5349e500..525cfb83 100644
--- a/client/lib/utils.js
+++ b/client/lib/utils.js
@@ -39,11 +39,11 @@ Utils = {
if (!prevData && !nextData) {
base = 0;
increment = 1;
- // If we drop the card in the first position
+ // If we drop the card in the first position
} else if (!prevData) {
base = nextData.sort - 1;
increment = -1;
- // If we drop the card in the last position
+ // If we drop the card in the last position
} else if (!nextData) {
base = prevData.sort + 1;
increment = 1;
@@ -71,11 +71,11 @@ Utils = {
if (!prevCardDomElement && !nextCardDomElement) {
base = 0;
increment = 1;
- // If we drop the card in the first position
+ // If we drop the card in the first position
} else if (!prevCardDomElement) {
base = Blaze.getData(nextCardDomElement).sort - 1;
increment = -1;
- // If we drop the card in the last position
+ // If we drop the card in the last position
} else if (!nextCardDomElement) {
base = Blaze.getData(prevCardDomElement).sort + 1;
increment = 1;
@@ -189,6 +189,27 @@ Utils = {
window._paq.push(['trackPageView']);
}
},
+
+ getTriggerActionDesc(event, tempInstance) {
+ const jqueryEl = tempInstance.$(event.currentTarget.parentNode);
+ const triggerEls = jqueryEl.find('.trigger-content').children();
+ let finalString = '';
+ for (let i = 0; i < triggerEls.length; i++) {
+ const element = tempInstance.$(triggerEls[i]);
+ if (element.hasClass('trigger-text')) {
+ finalString += element.text().toLowerCase();
+ } else if (element.find('select').length > 0) {
+ finalString += element.find('select option:selected').text().toLowerCase();
+ } else if (element.find('input').length > 0) {
+ finalString += element.find('input').val();
+ }
+ // Add space
+ if (i !== length - 1) {
+ finalString += ' ';
+ }
+ }
+ return finalString;
+ },
};
// A simple tracker dependency that we invalidate every time the window is
diff --git a/docker-compose.yml b/docker-compose.yml
index 7509bbc9..a2228dac 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,9 +1,11 @@
version: '2'
+# Using prebuilt image: docker-compose up -d --no-build
+
services:
wekandb:
- image: mongo:3.2.21
+ image: mongo:4.0.3
container_name: wekan-db
restart: always
command: mongod --smallfiles --oplogSize 128
@@ -16,7 +18,7 @@ services:
- wekan-db-dump:/dump
wekan:
- image: quay.io/wekan/wekan
+ image: quay.io/wekan/wekan:edge
container_name: wekan-app
restart: always
networks:
@@ -63,6 +65,9 @@ services:
# What to send to Outgoing Webhook, or leave out. Example, that includes all that are default: cardId,listId,oldListId,boardId,comment,user,card,commentId .
# example: WEBHOOKS_ATTRIBUTES=cardId,listId,oldListId,boardId,comment,user,card,commentId
- WEBHOOKS_ATTRIBUTES=''
+ # Enable the OAuth2 connection
+ # example: OAUTH2_ENABLED=true
+ - OAUTH2_ENABLED=false
# OAuth2 docs: https://github.com/wekan/wekan/wiki/OAuth2
# OAuth2 Client ID, for example from Rocket.Chat. Example: abcde12345
# example: OAUTH2_CLIENT_ID=abcde12345
@@ -82,6 +87,124 @@ services:
# OAuth2 Token Endpoint. Example: /oauth/token
# example: OAUTH2_TOKEN_ENDPOINT=/oauth/token
- OAUTH2_TOKEN_ENDPOINT=''
+ # LDAP_ENABLE : Enable or not the connection by the LDAP
+ # example : LDAP_ENABLE=true
+ - LDAP_ENABLE=false
+ # LDAP_PORT : The port of the LDAP server
+ # example : LDAP_PORT=389
+ - LDAP_PORT=389
+ # LDAP_HOST : The host server for the LDAP server
+ # example : LDAP_HOST=localhost
+ - LDAP_HOST=''
+ # LDAP_BASEDN : The base DN for the LDAP Tree
+ # example : LDAP_BASEDN=ou=user,dc=example,dc=org
+ - LDAP_BASEDN=''
+ # LDAP_LOGIN_FALLBACK : Fallback on the default authentication method
+ # example : LDAP_LOGIN_FALLBACK=true
+ - LDAP_LOGIN_FALLBACK=false
+ # LDAP_RECONNECT : Reconnect to the server if the connection is lost
+ # example : LDAP_RECONNECT=false
+ - LDAP_RECONNECT=true
+ # LDAP_TIMEOUT : Overall timeout, in milliseconds
+ # example : LDAP_TIMEOUT=12345
+ - LDAP_TIMEOUT=10000
+ # LDAP_IDLE_TIMEOUT : Specifies the timeout for idle LDAP connections in milliseconds
+ # example : LDAP_IDLE_TIMEOUT=12345
+ - LDAP_IDLE_TIMEOUT=10000
+ # LDAP_CONNECT_TIMEOUT : Connection timeout, in milliseconds
+ # example : LDAP_CONNECT_TIMEOUT=12345
+ - LDAP_CONNECT_TIMEOUT=10000
+ # LDAP_AUTHENTIFICATION : If the LDAP needs a user account to search
+ # example : LDAP_AUTHENTIFICATION=true
+ - LDAP_AUTHENTIFICATION=false
+ # LDAP_AUTHENTIFICATION_USERDN : The search user DN
+ # example : LDAP_AUTHENTIFICATION_USERDN=cn=admin,dc=example,dc=org
+ - LDAP_AUTHENTIFICATION_USERDN=''
+ # LDAP_AUTHENTIFICATION_PASSWORD : The password for the search user
+ # example : AUTHENTIFICATION_PASSWORD=admin
+ - LDAP_AUTHENTIFICATION_PASSWORD=''
+ # LDAP_LOG_ENABLED : Enable logs for the module
+ # example : LDAP_LOG_ENABLED=true
+ - LDAP_LOG_ENABLED=false
+ # LDAP_BACKGROUND_SYNC : If the sync of the users should be done in the background
+ # example : LDAP_BACKGROUND_SYNC=true
+ - LDAP_BACKGROUND_SYNC=false
+ # LDAP_BACKGROUND_SYNC_INTERVAL : At which interval does the background task sync in milliseconds
+ # example : LDAP_BACKGROUND_SYNC_INTERVAL=12345
+ - LDAP_BACKGROUND_SYNC_INTERVAL=100
+ # LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED :
+ # example : LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=true
+ - LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false
+ # LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS :
+ # example : LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=true
+ - LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false
+ # LDAP_ENCRYPTION : If using LDAPS
+ # example : LDAP_ENCRYPTION=true
+ - LDAP_ENCRYPTION=false
+ # LDAP_CA_CERT : The certification for the LDAPS server
+ # example : LDAP_CA_CERT=-----BEGIN CERTIFICATE-----MIIE+zCCA+OgAwIBAgIkAhwR/6TVLmdRY6hHxvUFWc0+Enmu/Hu6cj+G2FIdAgIC...-----END CERTIFICATE-----
+ - LDAP_CA_CERT=''
+ # LDAP_REJECT_UNAUTHORIZED : Reject Unauthorized Certificate
+ # example : LDAP_REJECT_UNAUTHORIZED=true
+ - LDAP_REJECT_UNAUTHORIZED=false
+ # LDAP_USER_SEARCH_FILTER : Optional extra LDAP filters. Don't forget the outmost enclosing parentheses if needed
+ # example : LDAP_USER_SEARCH_FILTER=
+ - LDAP_USER_SEARCH_FILTER=''
+ # LDAP_USER_SEARCH_SCOPE : Base (search only in the provided DN), one (search only in the provided DN and one level deep), or subtree (search the whole subtree)
+ # example : LDAP_USER_SEARCH_SCOPE=one
+ - LDAP_USER_SEARCH_SCOPE=''
+ # LDAP_USER_SEARCH_FIELD : Which field is used to find the user
+ # example : LDAP_USER_SEARCH_FIELD=uid
+ - LDAP_USER_SEARCH_FIELD=''
+ # LDAP_SEARCH_PAGE_SIZE : Used for pagination (0=unlimited)
+ # example : LDAP_SEARCH_PAGE_SIZE=12345
+ - LDAP_SEARCH_PAGE_SIZE=0
+ # LDAP_SEARCH_SIZE_LIMIT : The limit number of entries (0=unlimited)
+ # example : LDAP_SEARCH_SIZE_LIMIT=12345
+ - LDAP_SEARCH_SIZE_LIMIT=0
+ # LDAP_GROUP_FILTER_ENABLE : Enable group filtering
+ # example : LDAP_GROUP_FILTER_ENABLE=true
+ - LDAP_GROUP_FILTER_ENABLE=false
+ # LDAP_GROUP_FILTER_OBJECTCLASS : The object class for filtering
+ # example : LDAP_GROUP_FILTER_OBJECTCLASS=group
+ - LDAP_GROUP_FILTER_OBJECTCLASS=''
+ # LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE :
+ # example :
+ - LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=''
+ # LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE :
+ # example :
+ - LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=''
+ # LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT :
+ # example :
+ - LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=''
+ # LDAP_GROUP_FILTER_GROUP_NAME :
+ # example :
+ - LDAP_GROUP_FILTER_GROUP_NAME=''
+ # LDAP_UNIQUE_IDENTIFIER_FIELD : This field is sometimes class GUID (Globally Unique Identifier)
+ # example : LDAP_UNIQUE_IDENTIFIER_FIELD=guid
+ - LDAP_UNIQUE_IDENTIFIER_FIELD=''
+ # LDAP_UTF8_NAMES_SLUGIFY : Convert the username to utf8
+ # example : LDAP_UTF8_NAMES_SLUGIFY=false
+ - LDAP_UTF8_NAMES_SLUGIFY=true
+ # LDAP_USERNAME_FIELD : Which field contains the ldap username
+ # example : LDAP_USERNAME_FIELD=username
+ - LDAP_USERNAME_FIELD=''
+ # LDAP_MERGE_EXISTING_USERS :
+ # example : LDAP_MERGE_EXISTING_USERS=true
+ - LDAP_MERGE_EXISTING_USERS=false
+ # LDAP_SYNC_USER_DATA :
+ # example : LDAP_SYNC_USER_DATA=true
+ - LDAP_SYNC_USER_DATA=false
+ # LDAP_SYNC_USER_DATA_FIELDMAP :
+ # example : LDAP_SYNC_USER_DATA_FIELDMAP={\"cn\":\"name\", \"mail\":\"email\"}
+ - LDAP_SYNC_USER_DATA_FIELDMAP=''
+ # LDAP_SYNC_GROUP_ROLES :
+ # example :
+ - LDAP_SYNC_GROUP_ROLES=''
+ # LDAP_DEFAULT_DOMAIN : The default domain of the ldap it is used to create email if the field is not map correctly with the LDAP_SYNC_USER_DATA_FIELDMAP
+ # example :
+ - LDAP_DEFAULT_DOMAIN=''
+
depends_on:
- wekandb
diff --git a/models/actions.js b/models/actions.js
new file mode 100644
index 00000000..0430b044
--- /dev/null
+++ b/models/actions.js
@@ -0,0 +1,19 @@
+Actions = new Mongo.Collection('actions');
+
+Actions.allow({
+ insert(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+ update(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+ remove(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+});
+
+Actions.helpers({
+ description() {
+ return this.desc;
+ },
+});
diff --git a/models/activities.js b/models/activities.js
index e49cbf0a..47e3ff1e 100644
--- a/models/activities.js
+++ b/models/activities.js
@@ -56,6 +56,14 @@ Activities.before.insert((userId, doc) => {
doc.createdAt = new Date();
});
+
+Activities.after.insert((userId, doc) => {
+ const activity = Activities._transform(doc);
+ RulesHelper.executeRules(activity);
+
+});
+
+
if (Meteor.isServer) {
// For efficiency create indexes on the date of creation, and on the date of
// creation in conjunction with the card or board id, as corresponding views
diff --git a/models/attachments.js b/models/attachments.js
index 91dd0dbc..3da067de 100644
--- a/models/attachments.js
+++ b/models/attachments.js
@@ -86,5 +86,12 @@ if (Meteor.isServer) {
Activities.remove({
attachmentId: doc._id,
});
+ Activities.insert({
+ userId,
+ type: 'card',
+ activityType: 'deleteAttachment',
+ boardId: doc.boardId,
+ cardId: doc.cardId,
+ });
});
}
diff --git a/models/boards.js b/models/boards.js
index 2a21d6da..52d0ca87 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -280,6 +280,10 @@ Boards.helpers({
return _.findWhere(this.labels, { name, color });
},
+ getLabelById(labelId){
+ return _.findWhere(this.labels, { _id: labelId });
+ },
+
labelIndex(labelId) {
return _.pluck(this.labels, '_id').indexOf(labelId);
},
@@ -537,11 +541,10 @@ Boards.mutations({
};
},
- setMemberPermission(memberId, isAdmin, isNoComments, isCommentOnly) {
+ setMemberPermission(memberId, isAdmin, isNoComments, isCommentOnly, currentUserId = Meteor.userId()) {
const memberIndex = this.memberIndex(memberId);
-
// do not allow change permission of self
- if (memberId === Meteor.userId()) {
+ if (memberId === currentUserId) {
isAdmin = this.members[memberIndex].isAdmin;
}
@@ -923,4 +926,29 @@ if (Meteor.isServer) {
});
}
});
+
+ JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function (req, res) {
+ try {
+ const boardId = req.params.boardId;
+ const memberId = req.params.memberId;
+ const {isAdmin, isNoComments, isCommentOnly} = req.body;
+ Authentication.checkBoardAccess(req.userId, boardId);
+ const board = Boards.findOne({ _id: boardId });
+ function isTrue(data){
+ return data.toLowerCase() === 'true';
+ }
+ board.setMemberPermission(memberId, isTrue(isAdmin), isTrue(isNoComments), isTrue(isCommentOnly), req.userId);
+
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: query,
+ });
+ }
+ catch (error) {
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: error,
+ });
+ }
+ });
}
diff --git a/models/cards.js b/models/cards.js
index 7a0bcd5c..25692c25 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -276,14 +276,22 @@ Cards.helpers({
return Cards.find({
parentId: this._id,
archived: false,
- }, {sort: { sort: 1 } });
+ }, {
+ sort: {
+ sort: 1,
+ },
+ });
},
allSubtasks() {
return Cards.find({
parentId: this._id,
archived: false,
- }, {sort: { sort: 1 } });
+ }, {
+ sort: {
+ sort: 1,
+ },
+ });
},
subtasksCount() {
@@ -296,7 +304,8 @@ Cards.helpers({
subtasksFinishedCount() {
return Cards.find({
parentId: this._id,
- archived: true}).count();
+ archived: true,
+ }).count();
},
subtasksFinished() {
@@ -328,12 +337,9 @@ Cards.helpers({
});
//search for "True Value" which is for DropDowns other then the Value (which is the id)
let trueValue = customField.value;
- if (definition.settings.dropdownItems && definition.settings.dropdownItems.length > 0)
- {
- for (let i = 0; i < definition.settings.dropdownItems.length; i++)
- {
- if (definition.settings.dropdownItems[i]._id === customField.value)
- {
+ if (definition.settings.dropdownItems && definition.settings.dropdownItems.length > 0) {
+ for (let i = 0; i < definition.settings.dropdownItems.length; i++) {
+ if (definition.settings.dropdownItems[i]._id === customField.value) {
trueValue = definition.settings.dropdownItems[i].name;
}
}
@@ -358,8 +364,10 @@ Cards.helpers({
},
canBeRestored() {
- const list = Lists.findOne({_id: this.listId});
- if(!list.getWipLimit('soft') && list.getWipLimit('enabled') && list.getWipLimit('value') === list.cards().count()){
+ const list = Lists.findOne({
+ _id: this.listId,
+ });
+ if (!list.getWipLimit('soft') && list.getWipLimit('enabled') && list.getWipLimit('value') === list.cards().count()) {
return false;
}
return true;
@@ -424,7 +432,7 @@ Cards.helpers({
},
parentString(sep) {
- return this.parentList().map(function(elem){
+ return this.parentList().map(function(elem) {
return elem.title;
}).join(sep);
},
@@ -826,19 +834,65 @@ Cards.helpers({
Cards.mutations({
applyToChildren(funct) {
- Cards.find({ parentId: this._id }).forEach((card) => {
+ Cards.find({
+ parentId: this._id,
+ }).forEach((card) => {
funct(card);
});
},
archive() {
- this.applyToChildren((card) => { return card.archive(); });
- return {$set: {archived: true}};
+ this.applyToChildren((card) => {
+ return card.archive();
+ });
+ return {
+ $set: {
+ archived: true,
+ },
+ };
},
restore() {
- this.applyToChildren((card) => { return card.restore(); });
- return {$set: {archived: false}};
+ this.applyToChildren((card) => {
+ return card.restore();
+ });
+ return {
+ $set: {
+ archived: false,
+ },
+ };
+ },
+
+ setTitle(title) {
+ return {
+ $set: {
+ title,
+ },
+ };
+ },
+
+ setDescription(description) {
+ return {
+ $set: {
+ description,
+ },
+ };
+ },
+
+ setRequestedBy(requestedBy) {
+ return {
+ $set: {
+ requestedBy,
+ },
+ };
+ },
+
+ setAssignedBy(assignedBy) {
+ return {
+ $set: {
+ assignedBy,
+ },
+ };
},
move(swimlaneId, listId, sortIndex) {
@@ -850,15 +904,25 @@ Cards.mutations({
sort: sortIndex,
};
- return {$set: mutatedFields};
+ return {
+ $set: mutatedFields,
+ };
},
addLabel(labelId) {
- return {$addToSet: {labelIds: labelId}};
+ return {
+ $addToSet: {
+ labelIds: labelId,
+ },
+ };
},
removeLabel(labelId) {
- return {$pull: {labelIds: labelId}};
+ return {
+ $pull: {
+ labelIds: labelId,
+ },
+ };
},
toggleLabel(labelId) {
@@ -869,12 +933,49 @@ Cards.mutations({
}
},
+ assignMember(memberId) {
+ return {
+ $addToSet: {
+ members: memberId,
+ },
+ };
+ },
+
+ unassignMember(memberId) {
+ return {
+ $pull: {
+ members: memberId,
+ },
+ };
+ },
+
+ toggleMember(memberId) {
+ if (this.members && this.members.indexOf(memberId) > -1) {
+ return this.unassignMember(memberId);
+ } else {
+ return this.assignMember(memberId);
+ }
+ },
+
assignCustomField(customFieldId) {
- return {$addToSet: {customFields: {_id: customFieldId, value: null}}};
+ return {
+ $addToSet: {
+ customFields: {
+ _id: customFieldId,
+ value: null,
+ },
+ },
+ };
},
unassignCustomField(customFieldId) {
- return {$pull: {customFields: {_id: customFieldId}}};
+ return {
+ $pull: {
+ customFields: {
+ _id: customFieldId,
+ },
+ },
+ };
},
toggleCustomField(customFieldId) {
@@ -889,7 +990,9 @@ Cards.mutations({
// todo
const index = this.customFieldIndex(customFieldId);
if (index > -1) {
- const update = {$set: {}};
+ const update = {
+ $set: {},
+ };
update.$set[`customFields.${index}.value`] = value;
return update;
}
@@ -899,19 +1002,119 @@ Cards.mutations({
},
setCover(coverId) {
- return {$set: {coverId}};
+ return {
+ $set: {
+ coverId,
+ },
+ };
},
unsetCover() {
- return {$unset: {coverId: ''}};
+ return {
+ $unset: {
+ coverId: '',
+ },
+ };
+ },
+
+ setReceived(receivedAt) {
+ return {
+ $set: {
+ receivedAt,
+ },
+ };
+ },
+
+ unsetReceived() {
+ return {
+ $unset: {
+ receivedAt: '',
+ },
+ };
+ },
+
+ setStart(startAt) {
+ return {
+ $set: {
+ startAt,
+ },
+ };
+ },
+
+ unsetStart() {
+ return {
+ $unset: {
+ startAt: '',
+ },
+ };
+ },
+
+ setDue(dueAt) {
+ return {
+ $set: {
+ dueAt,
+ },
+ };
+ },
+
+ unsetDue() {
+ return {
+ $unset: {
+ dueAt: '',
+ },
+ };
+ },
+
+ setEnd(endAt) {
+ return {
+ $set: {
+ endAt,
+ },
+ };
+ },
+
+ unsetEnd() {
+ return {
+ $unset: {
+ endAt: '',
+ },
+ };
+ },
+
+ setOvertime(isOvertime) {
+ return {
+ $set: {
+ isOvertime,
+ },
+ };
+ },
+
+ setSpentTime(spentTime) {
+ return {
+ $set: {
+ spentTime,
+ },
+ };
+ },
+
+ unsetSpentTime() {
+ return {
+ $unset: {
+ spentTime: '',
+ isOvertime: false,
+ },
+ };
},
setParentId(parentId) {
- return {$set: {parentId}};
+ return {
+ $set: {
+ parentId,
+ },
+ };
},
});
-
//FUNCTIONS FOR creation of Activities
function cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId) {
@@ -921,6 +1124,7 @@ function cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId) {
userId,
oldListId,
activityType: 'moveCard',
+ listName: Lists.findOne(doc.listId).title,
listId: doc.listId,
boardId: doc.boardId,
cardId: doc._id,
@@ -936,6 +1140,7 @@ function cardState(userId, doc, fieldNames) {
Activities.insert({
userId,
activityType: 'archivedCard',
+ listName: Lists.findOne(doc.listId).title,
boardId: doc.boardId,
listId: doc.listId,
cardId: doc._id,
@@ -945,6 +1150,7 @@ function cardState(userId, doc, fieldNames) {
userId,
activityType: 'restoredCard',
boardId: doc.boardId,
+ listName: Lists.findOne(doc.listId).title,
listId: doc.listId,
cardId: doc._id,
});
@@ -959,10 +1165,11 @@ function cardMembers(userId, doc, fieldNames, modifier) {
// Say hello to the new member
if (modifier.$addToSet && modifier.$addToSet.members) {
memberId = modifier.$addToSet.members;
+ const username = Users.findOne(memberId).username;
if (!_.contains(doc.members, memberId)) {
Activities.insert({
userId,
- memberId,
+ username,
activityType: 'joinMember',
boardId: doc.boardId,
cardId: doc._id,
@@ -973,11 +1180,12 @@ function cardMembers(userId, doc, fieldNames, modifier) {
// Say goodbye to the former member
if (modifier.$pull && modifier.$pull.members) {
memberId = modifier.$pull.members;
+ const username = Users.findOne(memberId).username;
// Check that the former member is member of the card
if (_.contains(doc.members, memberId)) {
Activities.insert({
userId,
- memberId,
+ username,
activityType: 'unjoinMember',
boardId: doc.boardId,
cardId: doc._id,
@@ -986,11 +1194,47 @@ function cardMembers(userId, doc, fieldNames, modifier) {
}
}
+function cardLabels(userId, doc, fieldNames, modifier) {
+ if (!_.contains(fieldNames, 'labelIds'))
+ return;
+ let labelId;
+ // Say hello to the new label
+ if (modifier.$addToSet && modifier.$addToSet.labelIds) {
+ labelId = modifier.$addToSet.labelIds;
+ if (!_.contains(doc.labelIds, labelId)) {
+ const act = {
+ userId,
+ labelId,
+ activityType: 'addedLabel',
+ boardId: doc.boardId,
+ cardId: doc._id,
+ };
+ Activities.insert(act);
+ }
+ }
+
+ // Say goodbye to the label
+ if (modifier.$pull && modifier.$pull.labelIds) {
+ labelId = modifier.$pull.labelIds;
+ // Check that the former member is member of the card
+ if (_.contains(doc.labelIds, labelId)) {
+ Activities.insert({
+ userId,
+ labelId,
+ activityType: 'removedLabel',
+ boardId: doc.boardId,
+ cardId: doc._id,
+ });
+ }
+ }
+}
+
function cardCreation(userId, doc) {
Activities.insert({
userId,
activityType: 'createCard',
boardId: doc.boardId,
+ listName: Lists.findOne(doc.listId).title,
listId: doc.listId,
cardId: doc._id,
swimlaneId: doc.swimlaneId,
@@ -1015,7 +1259,6 @@ function cardRemover(userId, doc) {
});
}
-
if (Meteor.isServer) {
// Cards are often fetched within a board, so we create an index to make these
// queries more efficient.
@@ -1039,7 +1282,7 @@ if (Meteor.isServer) {
});
//New activity for card moves
- Cards.after.update(function (userId, doc, fieldNames) {
+ Cards.after.update(function(userId, doc, fieldNames) {
const oldListId = this.previous.listId;
const oldSwimlaneId = this.previous.swimlaneId;
cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId);
@@ -1050,6 +1293,11 @@ if (Meteor.isServer) {
cardMembers(userId, doc, fieldNames, modifier);
});
+ // Add a new activity if we add or remove a label to the card
+ Cards.before.update((userId, doc, fieldNames, modifier) => {
+ cardLabels(userId, doc, fieldNames, modifier);
+ });
+
// Remove all activities associated with a card if we remove the card
// Remove also card_comments / checklists / attachments
Cards.after.remove((userId, doc) => {
@@ -1081,13 +1329,17 @@ if (Meteor.isServer) {
}
//LISTS REST API
if (Meteor.isServer) {
- JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', function (req, res) {
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', function(req, res) {
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: Cards.find({boardId: paramBoardId, listId: paramListId, archived: false}).map(function (doc) {
+ data: Cards.find({
+ boardId: paramBoardId,
+ listId: paramListId,
+ archived: false,
+ }).map(function(doc) {
return {
_id: doc._id,
title: doc.title,
@@ -1097,24 +1349,31 @@ if (Meteor.isServer) {
});
});
- JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res) {
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards/:cardId', function(req, res) {
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
const paramCardId = req.params.cardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: Cards.findOne({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false}),
+ data: Cards.findOne({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }),
});
});
- JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function (req, res) {
+ JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function(req, res) {
Authentication.checkUserId(req.userId);
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
- const check = Users.findOne({_id: req.body.authorId});
+ const check = Users.findOne({
+ _id: req.body.authorId,
+ });
const members = req.body.members || [req.body.authorId];
- if (typeof check !== 'undefined') {
+ if (typeof check !== 'undefined') {
const id = Cards.direct.insert({
title: req.body.title,
boardId: paramBoardId,
@@ -1132,7 +1391,9 @@ if (Meteor.isServer) {
},
});
- const card = Cards.findOne({_id:id});
+ const card = Cards.findOne({
+ _id: id,
+ });
cardCreation(req.body.authorId, card);
} else {
@@ -1142,7 +1403,7 @@ if (Meteor.isServer) {
}
});
- JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res) {
+ JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId/cards/:cardId', function(req, res) {
Authentication.checkUserId(req.userId);
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
@@ -1150,27 +1411,63 @@ if (Meteor.isServer) {
if (req.body.hasOwnProperty('title')) {
const newTitle = req.body.title;
- Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
- {$set: {title: newTitle}});
+ Cards.direct.update({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }, {
+ $set: {
+ title: newTitle,
+ },
+ });
}
if (req.body.hasOwnProperty('listId')) {
const newParamListId = req.body.listId;
- Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
- {$set: {listId: newParamListId}});
+ Cards.direct.update({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }, {
+ $set: {
+ listId: newParamListId,
+ },
+ });
- const card = Cards.findOne({_id: paramCardId} );
- cardMove(req.body.authorId, card, {fieldName: 'listId'}, paramListId);
+ const card = Cards.findOne({
+ _id: paramCardId,
+ });
+ cardMove(req.body.authorId, card, {
+ fieldName: 'listId',
+ }, paramListId);
}
if (req.body.hasOwnProperty('description')) {
const newDescription = req.body.description;
- Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
- {$set: {description: newDescription}});
+ Cards.direct.update({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }, {
+ $set: {
+ description: newDescription,
+ },
+ });
}
if (req.body.hasOwnProperty('labelIds')) {
const newlabelIds = req.body.labelIds;
- Cards.direct.update({_id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false},
- {$set: {labelIds: newlabelIds}});
+ Cards.direct.update({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ archived: false,
+ }, {
+ $set: {
+ labelIds: newlabelIds,
+ },
+ });
}
if (req.body.hasOwnProperty('requestedBy')) {
const newrequestedBy = req.body.requestedBy;
@@ -1225,15 +1522,20 @@ if (Meteor.isServer) {
});
});
-
- JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res) {
+ JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId/cards/:cardId', function(req, res) {
Authentication.checkUserId(req.userId);
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
const paramCardId = req.params.cardId;
- Cards.direct.remove({_id: paramCardId, listId: paramListId, boardId: paramBoardId});
- const card = Cards.find({_id: paramCardId} );
+ Cards.direct.remove({
+ _id: paramCardId,
+ listId: paramListId,
+ boardId: paramBoardId,
+ });
+ const card = Cards.find({
+ _id: paramCardId,
+ });
cardRemover(req.body.authorId, card);
JsonRoutes.sendResult(res, {
code: 200,
diff --git a/models/checklistItems.js b/models/checklistItems.js
index e075eda2..c85c0260 100644
--- a/models/checklistItems.js
+++ b/models/checklistItems.js
@@ -44,6 +44,12 @@ ChecklistItems.mutations({
setTitle(title) {
return { $set: { title } };
},
+ check(){
+ return { $set: { isFinished: true } };
+ },
+ uncheck(){
+ return { $set: { isFinished: false } };
+ },
toggleItem() {
return { $set: { isFinished: !this.isFinished } };
},
@@ -70,21 +76,100 @@ function itemCreation(userId, doc) {
boardId,
checklistId: doc.checklistId,
checklistItemId: doc._id,
+ checklistItemName:doc.title,
});
}
function itemRemover(userId, doc) {
+ const card = Cards.findOne(doc.cardId);
+ const boardId = card.boardId;
+ Activities.insert({
+ userId,
+ activityType: 'removedChecklistItem',
+ cardId: doc.cardId,
+ boardId,
+ checklistId: doc.checklistId,
+ checklistItemId: doc._id,
+ checklistItemName:doc.title,
+ });
Activities.remove({
checklistItemId: doc._id,
});
}
+function publishCheckActivity(userId, doc){
+ const card = Cards.findOne(doc.cardId);
+ const boardId = card.boardId;
+ let activityType;
+ if(doc.isFinished){
+ activityType = 'checkedItem';
+ }else{
+ activityType = 'uncheckedItem';
+ }
+ const act = {
+ userId,
+ activityType,
+ cardId: doc.cardId,
+ boardId,
+ checklistId: doc.checklistId,
+ checklistItemId: doc._id,
+ checklistItemName:doc.title,
+ };
+ Activities.insert(act);
+}
+
+function publishChekListCompleted(userId, doc){
+ const card = Cards.findOne(doc.cardId);
+ const boardId = card.boardId;
+ const checklistId = doc.checklistId;
+ const checkList = Checklists.findOne({_id:checklistId});
+ if(checkList.isFinished()){
+ const act = {
+ userId,
+ activityType: 'checklistCompleted',
+ cardId: doc.cardId,
+ boardId,
+ checklistId: doc.checklistId,
+ checklistName: checkList.title,
+ };
+ Activities.insert(act);
+ }
+}
+
+function publishChekListUncompleted(userId, doc){
+ const card = Cards.findOne(doc.cardId);
+ const boardId = card.boardId;
+ const checklistId = doc.checklistId;
+ const checkList = Checklists.findOne({_id:checklistId});
+ if(checkList.isFinished()){
+ const act = {
+ userId,
+ activityType: 'checklistUncompleted',
+ cardId: doc.cardId,
+ boardId,
+ checklistId: doc.checklistId,
+ checklistName: checkList.title,
+ };
+ Activities.insert(act);
+ }
+}
+
// Activities
if (Meteor.isServer) {
Meteor.startup(() => {
ChecklistItems._collection._ensureIndex({ checklistId: 1 });
});
+ ChecklistItems.after.update((userId, doc, fieldNames) => {
+ publishCheckActivity(userId, doc);
+ publishChekListCompleted(userId, doc, fieldNames);
+ });
+
+ ChecklistItems.before.update((userId, doc, fieldNames) => {
+ publishChekListUncompleted(userId, doc, fieldNames);
+ });
+
+
ChecklistItems.after.insert((userId, doc) => {
itemCreation(userId, doc);
});
diff --git a/models/checklists.js b/models/checklists.js
index c58453ef..425a10b2 100644
--- a/models/checklists.js
+++ b/models/checklists.js
@@ -47,6 +47,18 @@ Checklists.helpers({
isFinished() {
return 0 !== this.itemCount() && this.itemCount() === this.finishedCount();
},
+ checkAllItems(){
+ const checkItems = ChecklistItems.find({checklistId: this._id});
+ checkItems.forEach(function(item){
+ item.check();
+ });
+ },
+ uncheckAllItems(){
+ const checkItems = ChecklistItems.find({checklistId: this._id});
+ checkItems.forEach(function(item){
+ item.uncheck();
+ });
+ },
itemIndex(itemId) {
const items = self.findOne({_id : this._id}).items;
return _.pluck(items, '_id').indexOf(itemId);
@@ -91,6 +103,7 @@ if (Meteor.isServer) {
cardId: doc.cardId,
boardId: Cards.findOne(doc.cardId).boardId,
checklistId: doc._id,
+ checklistName:doc.title,
});
});
@@ -101,6 +114,16 @@ if (Meteor.isServer) {
Activities.remove(activity._id);
});
}
+ Activities.insert({
+ userId,
+ activityType: 'removeChecklist',
+ cardId: doc.cardId,
+ boardId: Cards.findOne(doc.cardId).boardId,
+ checklistId: doc._id,
+ checklistName:doc.title,
+ });
+
+
});
}
diff --git a/models/export.js b/models/export.js
index 6c0b43fd..0911a631 100644
--- a/models/export.js
+++ b/models/export.js
@@ -14,7 +14,7 @@ if (Meteor.isServer) {
* See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
* for detailed explanations
*/
- JsonRoutes.add('get', '/api/boards/:boardId/export', function (req, res) {
+ JsonRoutes.add('get', '/api/boards/:boardId/export', function(req, res) {
const boardId = req.params.boardId;
let user = null;
// todo XXX for real API, first look for token in Authentication: header
@@ -28,8 +28,11 @@ if (Meteor.isServer) {
}
const exporter = new Exporter(boardId);
- if(exporter.canExport(user)) {
- JsonRoutes.sendResult(res, { code: 200, data: exporter.build() });
+ 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.
@@ -47,24 +50,49 @@ class Exporter {
const byBoard = { boardId: this._boardId };
const byBoardNoLinked = { boardId: this._boardId, linkedId: '' };
// we do not want to retrieve boardId in related elements
- const noBoardId = { fields: { boardId: 0 } };
+ const noBoardId = {
+ fields: {
+ boardId: 0,
+ },
+ };
const result = {
_format: 'wekan-board-1.0.0',
};
- _.extend(result, Boards.findOne(this._boardId, { fields: { stars: 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(byBoard, noBoardId).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.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
@@ -101,18 +129,34 @@ class Exporter {
// 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.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; });
+ 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) } };
+ 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 = {
diff --git a/models/lists.js b/models/lists.js
index 9bcb9ba1..b99fe8f5 100644
--- a/models/lists.js
+++ b/models/lists.js
@@ -86,6 +86,17 @@ Lists.helpers({
{ sort: ['sort'] });
},
+ cardsUnfiltered(swimlaneId) {
+ const selector = {
+ listId: this._id,
+ archived: false,
+ };
+ if (swimlaneId)
+ selector.swimlaneId = swimlaneId;
+ return Cards.find(selector,
+ { sort: ['sort'] });
+ },
+
allCards() {
return Cards.find({ listId: this._id });
},
diff --git a/models/rules.js b/models/rules.js
new file mode 100644
index 00000000..7d971980
--- /dev/null
+++ b/models/rules.js
@@ -0,0 +1,48 @@
+Rules = new Mongo.Collection('rules');
+
+Rules.attachSchema(new SimpleSchema({
+ title: {
+ type: String,
+ optional: false,
+ },
+ triggerId: {
+ type: String,
+ optional: false,
+ },
+ actionId: {
+ type: String,
+ optional: false,
+ },
+ boardId: {
+ type: String,
+ optional: false,
+ },
+}));
+
+Rules.mutations({
+ rename(description) {
+ return { $set: { description } };
+ },
+});
+
+Rules.helpers({
+ getAction(){
+ return Actions.findOne({_id:this.actionId});
+ },
+ getTrigger(){
+ return Triggers.findOne({_id:this.triggerId});
+ },
+});
+
+
+Rules.allow({
+ insert(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+ update(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+ remove(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+});
diff --git a/models/settings.js b/models/settings.js
index 5d01f2ea..2f82e52f 100644
--- a/models/settings.js
+++ b/models/settings.js
@@ -129,6 +129,18 @@ if (Meteor.isServer) {
}
}
+ function isLdapEnabled() {
+ return process.env.LDAP_ENABLE === 'true';
+ }
+
+ function isOauth2Enabled() {
+ return process.env.OAUTH2_ENABLED === 'true';
+ }
+
+ function isCasEnabled() {
+ return process.env.CAS_ENABLED === 'true';
+ }
+
Meteor.methods({
sendInvitation(emails, boards) {
check(emails, [String]);
@@ -198,5 +210,26 @@ if (Meteor.isServer) {
withUserName: process.env.MATOMO_WITH_USERNAME || false,
};
},
+
+ _isLdapEnabled() {
+ return isLdapEnabled();
+ },
+
+ _isOauth2Enabled() {
+ return isOauth2Enabled();
+ },
+
+ _isCasEnabled() {
+ return isCasEnabled();
+ },
+
+ // Gets all connection methods to use it in the Template
+ getAuthenticationsEnabled() {
+ return {
+ ldap: isLdapEnabled(),
+ oauth2: isOauth2Enabled(),
+ cas: isCasEnabled(),
+ };
+ },
});
}
diff --git a/models/triggers.js b/models/triggers.js
new file mode 100644
index 00000000..15982b6e
--- /dev/null
+++ b/models/triggers.js
@@ -0,0 +1,58 @@
+Triggers = new Mongo.Collection('triggers');
+
+Triggers.mutations({
+ rename(description) {
+ return {
+ $set: {
+ description,
+ },
+ };
+ },
+});
+
+Triggers.allow({
+ insert(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+ update(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+ remove(userId, doc) {
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+ },
+});
+
+Triggers.helpers({
+
+ description() {
+ return this.desc;
+ },
+
+ getRule() {
+ return Rules.findOne({
+ triggerId: this._id,
+ });
+ },
+
+ fromList() {
+ return Lists.findOne(this.fromId);
+ },
+
+ toList() {
+ return Lists.findOne(this.toId);
+ },
+
+ findList(title) {
+ return Lists.findOne({
+ title,
+ });
+ },
+
+ labels() {
+ const boardLabels = this.board().labels;
+ const cardLabels = _.filter(boardLabels, (label) => {
+ return _.contains(this.labelIds, label._id);
+ });
+ return cardLabels;
+ },
+});
diff --git a/models/users.js b/models/users.js
index dc0ab608..4f2184e4 100644
--- a/models/users.js
+++ b/models/users.js
@@ -123,6 +123,11 @@ Users.attachSchema(new SimpleSchema({
type: Boolean,
optional: true,
},
+ 'authenticationMethod': {
+ type: String,
+ optional: false,
+ defaultValue: 'password',
+ },
}));
Users.allow({
@@ -484,29 +489,30 @@ if (Meteor.isServer) {
return user;
}
- // if (user.services.oidc) {
- // const email = user.services.oidc.email.toLowerCase();
- //
- // user.username = user.services.oidc.username;
- // user.emails = [{ address: email, verified: true }];
- // const initials = user.services.oidc.fullname.match(/\b[a-zA-Z]/g).join('').toUpperCase();
- // user.profile = { initials, fullname: user.services.oidc.fullname };
- //
- // // see if any existing user has this email address or username, otherwise create new
- // const existingUser = Meteor.users.findOne({$or: [{'emails.address': email}, {'username':user.username}]});
- // if (!existingUser)
- // return user;
- //
- // // copy across new service info
- // const service = _.keys(user.services)[0];
- // existingUser.services[service] = user.services[service];
- // existingUser.emails = user.emails;
- // existingUser.username = user.username;
- // existingUser.profile = user.profile;
- //
- // Meteor.users.remove({_id: existingUser._id}); // remove existing record
- // return existingUser;
- // }
+ if (user.services.oidc) {
+ const email = user.services.oidc.email.toLowerCase();
+ user.username = user.services.oidc.username;
+ user.emails = [{ address: email, verified: true }];
+ const initials = user.services.oidc.fullname.match(/\b[a-zA-Z]/g).join('').toUpperCase();
+ user.profile = { initials, fullname: user.services.oidc.fullname };
+ user.authenticationMethod = 'oauth2';
+
+ // see if any existing user has this email address or username, otherwise create new
+ const existingUser = Meteor.users.findOne({$or: [{'emails.address': email}, {'username':user.username}]});
+ if (!existingUser)
+ return user;
+
+ // copy across new service info
+ const service = _.keys(user.services)[0];
+ existingUser.services[service] = user.services[service];
+ existingUser.emails = user.emails;
+ existingUser.username = user.username;
+ existingUser.profile = user.profile;
+ existingUser.authenticationMethod = user.authenticationMethod;
+
+ Meteor.users.remove({_id: existingUser._id}); // remove existing record
+ return existingUser;
+ }
if (options.from === 'admin') {
user.createdThroughApi = true;
@@ -514,7 +520,10 @@ if (Meteor.isServer) {
}
const disableRegistration = Settings.findOne().disableRegistration;
- if (!disableRegistration) {
+ // If ldap, bypass the inviation code if the self registration isn't allowed.
+ // TODO : pay attention if ldap field in the user model change to another content ex : ldap field to connection_type
+ if (options.ldap || !disableRegistration) {
+ user.authenticationMethod = 'ldap';
return user;
}
@@ -632,7 +641,9 @@ if (Meteor.isServer) {
//invite user to corresponding boards
const disableRegistration = Settings.findOne().disableRegistration;
- if (disableRegistration) {
+ // If ldap, bypass the inviation code if the self registration isn't allowed.
+ // TODO : pay attention if ldap field in the user model change to another content ex : ldap field to connection_type
+ if (doc.authenticationMethod !== 'ldap' && disableRegistration) {
const invitationCode = InvitationCodes.findOne({code: doc.profile.icode, valid: true});
if (!invitationCode) {
throw new Meteor.Error('error-invitation-code-not-exist');
@@ -762,6 +773,81 @@ if (Meteor.isServer) {
}
});
+ JsonRoutes.add('POST', '/api/boards/:boardId/members/:userId/add', function (req, res) {
+ try {
+ Authentication.checkUserId(req.userId);
+ const userId = req.params.userId;
+ const boardId = req.params.boardId;
+ const action = req.body.action;
+ const {isAdmin, isNoComments, isCommentOnly} = req.body;
+ let data = Meteor.users.findOne({ _id: userId });
+ if (data !== undefined) {
+ if (action === 'add') {
+ data = Boards.find({
+ _id: boardId,
+ }).map(function(board) {
+ if (!board.hasMember(userId)) {
+ board.addMember(userId);
+ function isTrue(data){
+ return data.toLowerCase() === 'true';
+ }
+ board.setMemberPermission(userId, isTrue(isAdmin), isTrue(isNoComments), isTrue(isCommentOnly), userId);
+ }
+ return {
+ _id: board._id,
+ title: board.title,
+ };
+ });
+ }
+ }
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: query,
+ });
+ }
+ catch (error) {
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: error,
+ });
+ }
+ });
+
+ JsonRoutes.add('POST', '/api/boards/:boardId/members/:userId/remove', function (req, res) {
+ try {
+ Authentication.checkUserId(req.userId);
+ const userId = req.params.userId;
+ const boardId = req.params.boardId;
+ const action = req.body.action;
+ let data = Meteor.users.findOne({ _id: userId });
+ if (data !== undefined) {
+ if (action === 'remove') {
+ data = Boards.find({
+ _id: boardId,
+ }).map(function(board) {
+ if (board.hasMember(userId)) {
+ board.removeMember(userId);
+ }
+ return {
+ _id: board._id,
+ title: board.title,
+ };
+ });
+ }
+ }
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: query,
+ });
+ }
+ catch (error) {
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: error,
+ });
+ }
+ });
+
JsonRoutes.add('POST', '/api/users/', function (req, res) {
try {
Authentication.checkUserId(req.userId);
diff --git a/models/wekanCreator.js b/models/wekanCreator.js
index d144821f..59d0cfd5 100644
--- a/models/wekanCreator.js
+++ b/models/wekanCreator.js
@@ -1,4 +1,4 @@
-const DateString = Match.Where(function (dateAsString) {
+const DateString = Match.Where(function(dateAsString) {
check(dateAsString, String);
return moment(dateAsString, moment.ISO_8601).isValid();
});
@@ -42,6 +42,10 @@ export class WekanCreator {
this.comments = {};
// 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 = {};
@@ -57,10 +61,10 @@ export class WekanCreator {
* @param {String} dateString a properly formatted Date
*/
_now(dateString) {
- if(dateString) {
+ if (dateString) {
return new Date(dateString);
}
- if(!this._nowDate) {
+ if (!this._nowDate) {
this._nowDate = new Date();
}
return this._nowDate;
@@ -72,9 +76,9 @@ export class WekanCreator {
* Otherwise return current logged user.
* @param wekanUserId
* @private
- */
+ */
_user(wekanUserId) {
- if(wekanUserId && this.members[wekanUserId]) {
+ if (wekanUserId && this.members[wekanUserId]) {
return this.members[wekanUserId];
}
return Meteor.userId();
@@ -96,7 +100,7 @@ export class WekanCreator {
// allowed values (is it worth the maintenance?)
color: String,
permission: Match.Where((value) => {
- return ['private', 'public'].indexOf(value)>= 0;
+ return ['private', 'public'].indexOf(value) >= 0;
}),
}));
}
@@ -147,6 +151,30 @@ export class WekanCreator {
})]);
}
+ 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,
+ })]);
+ }
+
+ 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 = {
@@ -172,12 +200,12 @@ export class WekanCreator {
title: boardToImport.title,
};
// now add other members
- if(boardToImport.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))
+ if (!boardToCreate.members.some((member) => member.wekanId === wekanMember.wekanId))
boardToCreate.members.push({
- ... wekanMember,
+ ...wekanMember,
userId: wekanMember.wekanId,
});
});
@@ -194,7 +222,11 @@ export class WekanCreator {
boardToCreate.labels.push(labelToCreate);
});
const boardId = Boards.direct.insert(boardToCreate);
- Boards.direct.update(boardId, {$set: {modifiedAt: this._now()}});
+ Boards.direct.update(boardId, {
+ $set: {
+ modifiedAt: this._now(),
+ },
+ });
// log activity
Activities.direct.insert({
activityType: 'importBoard',
@@ -246,21 +278,21 @@ export class WekanCreator {
});
}
// add members {
- if(card.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]) {
+ 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)){
+ if (!wekanMembers.find((wId) => wId === wekanId)) {
wekanMembers.push(wekanId);
}
}
return true;
});
- if(wekanMembers.length>0) {
+ if (wekanMembers.length > 0) {
cardToCreate.members = wekanMembers;
}
}
@@ -321,9 +353,9 @@ export class WekanCreator {
// - 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 (Meteor.isServer) {
if (att.url) {
- file.attachData(att.url, function (error) {
+ file.attachData(att.url, function(error) {
file.boardId = boardId;
file.cardId = cardId;
file.userId = self._user(att.userId);
@@ -331,20 +363,26 @@ export class WekanCreator {
// attachments' related activities automatically
file.source = 'import';
if (error) {
- throw(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}});
+ 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.attachData(new Buffer(att.file, 'base64'), {
+ type: att.type,
+ }, (error) => {
file.name(att.name);
file.boardId = boardId;
file.cardId = cardId;
@@ -353,15 +391,19 @@ export class WekanCreator {
// attachments' related activities automatically
file.source = 'import';
if (error) {
- throw(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}});
+ if (wekanCoverId === att._id) {
+ Cards.direct.update(cardId, {
+ $set: {
+ coverId: wekanAtt._id,
+ },
+ });
}
}
});
@@ -404,7 +446,11 @@ export class WekanCreator {
sort: list.sort ? list.sort : listIndex,
};
const listId = Lists.direct.insert(listToCreate);
- Lists.direct.update(listId, {$set: {'updatedAt': this._now()}});
+ Lists.direct.update(listId, {
+ $set: {
+ 'updatedAt': this._now(),
+ },
+ });
this.lists[list._id] = listId;
// // log activity
// Activities.direct.insert({
@@ -437,7 +483,11 @@ export class WekanCreator {
sort: swimlane.sort ? swimlane.sort : swimlaneIndex,
};
const swimlaneId = Swimlanes.direct.insert(swimlaneToCreate);
- Swimlanes.direct.update(swimlaneId, {$set: {'updatedAt': this._now()}});
+ Swimlanes.direct.update(swimlaneId, {
+ $set: {
+ 'updatedAt': this._now(),
+ },
+ });
this.swimlanes[swimlane._id] = swimlaneId;
});
}
@@ -459,6 +509,47 @@ export class WekanCreator {
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
@@ -477,7 +568,8 @@ export class WekanCreator {
parseActivities(wekanBoard) {
wekanBoard.activities.forEach((activity) => {
switch (activity.activityType) {
- case 'addAttachment': {
+ 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
@@ -485,12 +577,12 @@ export class WekanCreator {
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.
+ 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]) {
+ if (!this.attachments[wekanCardId]) {
this.attachments[wekanCardId] = [];
}
this.attachments[wekanCardId].push(wekanAttachment);
@@ -498,7 +590,8 @@ export class WekanCreator {
}
break;
}
- case 'addComment': {
+ case 'addComment':
+ {
const wekanComment = wekanBoard.comments.filter((comment) => {
return comment._id === activity.commentId;
})[0];
@@ -509,26 +602,31 @@ export class WekanCreator {
this.comments[id].push(wekanComment);
break;
}
- case 'createBoard': {
+ case 'createBoard':
+ {
this.createdAt.board = activity.createdAt;
break;
}
- case 'createCard': {
+ case 'createCard':
+ {
const cardId = activity.cardId;
this.createdAt.cards[cardId] = activity.createdAt;
this.createdBy.cards[cardId] = activity.userId;
break;
}
- case 'createList': {
+ case 'createList':
+ {
const listId = activity.listId;
this.createdAt.lists[listId] = activity.createdAt;
break;
}
- case 'createSwimlane': {
+ case 'createSwimlane':
+ {
const swimlaneId = activity.swimlaneId;
this.createdAt.swimlanes[swimlaneId] = activity.createdAt;
break;
- }}
+ }
+ }
});
}
@@ -537,7 +635,8 @@ export class WekanCreator {
switch (activity.activityType) {
// Board related activities
// TODO: addBoardMember, removeBoardMember
- case 'createBoard': {
+ case 'createBoard':
+ {
Activities.direct.insert({
userId: this._user(activity.userId),
type: 'board',
@@ -550,7 +649,8 @@ export class WekanCreator {
}
// List related activities
// TODO: removeList, archivedList
- case 'createList': {
+ case 'createList':
+ {
Activities.direct.insert({
userId: this._user(activity.userId),
type: 'list',
@@ -563,7 +663,8 @@ export class WekanCreator {
}
// Card related activities
// TODO: archivedCard, restoredCard, joinMember, unjoinMember
- case 'createCard': {
+ case 'createCard':
+ {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
@@ -574,7 +675,8 @@ export class WekanCreator {
});
break;
}
- case 'moveCard': {
+ case 'moveCard':
+ {
Activities.direct.insert({
userId: this._user(activity.userId),
oldListId: this.lists[activity.oldListId],
@@ -587,7 +689,8 @@ export class WekanCreator {
break;
}
// Comment related activities
- case 'addComment': {
+ case 'addComment':
+ {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
@@ -599,7 +702,8 @@ export class WekanCreator {
break;
}
// Attachment related activities
- case 'addAttachment': {
+ case 'addAttachment':
+ {
Activities.direct.insert({
userId: this._user(activity.userId),
type: 'card',
@@ -612,7 +716,8 @@ export class WekanCreator {
break;
}
// Checklist related activities
- case 'addChecklist': {
+ case 'addChecklist':
+ {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
@@ -623,7 +728,8 @@ export class WekanCreator {
});
break;
}
- case 'addChecklistItem': {
+ case 'addChecklistItem':
+ {
Activities.direct.insert({
userId: this._user(activity.userId),
activityType: activity.activityType,
@@ -636,7 +742,8 @@ export class WekanCreator {
createdAt: this._now(activity.createdAt),
});
break;
- }}
+ }
+ }
});
}
@@ -652,6 +759,9 @@ export class WekanCreator {
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');
@@ -674,6 +784,9 @@ export class WekanCreator {
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;
}
diff --git a/package.json b/package.json
index 6247c4a4..7bea1112 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "wekan",
- "version": "1.55.0",
- "description": "Open-Source kanban",
+ "version": "v1.55.1",
+ "description": "The open-source kanban",
"private": true,
"scripts": {
"lint": "eslint --ignore-pattern 'packages/*' .",
@@ -23,9 +23,11 @@
"eslint": "^4.19.1"
},
"dependencies": {
+ "@babel/runtime": "^7.1.2",
"babel-runtime": "^6.26.0",
"bson-ext": "^2.0.0",
"es6-promise": "^4.2.4",
+ "hoek": "^5.0.4",
"meteor-node-stubs": "^0.4.1",
"os": "^0.1.1",
"page": "^1.8.6",
diff --git a/sandstorm-pkgdef.capnp b/sandstorm-pkgdef.capnp
index f66b6d0c..4c38b6f7 100644
--- a/sandstorm-pkgdef.capnp
+++ b/sandstorm-pkgdef.capnp
@@ -22,10 +22,10 @@ const pkgdef :Spk.PackageDefinition = (
appTitle = (defaultText = "Wekan"),
# The name of the app as it is displayed to the user.
- appVersion = 154,
+ appVersion = 156,
# Increment this for every release.
- appMarketingVersion = (defaultText = "1.55.0~2018-10-16"),
+ appMarketingVersion = (defaultText = "1.55.1~2018-10-16"),
# Human-readable presentation of the app version.
minUpgradableAppVersion = 0,
@@ -245,12 +245,14 @@ const myCommand :Spk.Manifest.Command = (
(key = "BROWSER_POLICY_ENABLED", value="true"),
(key = "TRUSTED_URL", value=""),
(key = "WEBHOOKS_ATTRIBUTES", value=""),
- (key = "OAUTH2_CLIENT_ID", value=""),
+ (key = "OAUTH2_ENABLED", value=""),
+ (key = "OAUTH2_CLIENT_ID", value="false"),
(key = "OAUTH2_SECRET", value=""),
(key = "OAUTH2_SERVER_URL", value=""),
(key = "OAUTH2_AUTH_ENDPOINT", value=""),
(key = "OAUTH2_USERINFO_ENDPOINT", value=""),
(key = "OAUTH2_TOKEN_ENDPOINT", value=""),
+ (key = "LDAP_ENABLE", value="false"),
(key = "SANDSTORM", value = "1"),
(key = "METEOR_SETTINGS", value = "{\"public\": {\"sandstorm\": true}}")
]
diff --git a/server/authentication.js b/server/authentication.js
index 8a74ebf7..6310e8df 100644
--- a/server/authentication.js
+++ b/server/authentication.js
@@ -62,27 +62,28 @@ Meteor.startup(() => {
Authentication.checkAdminOrCondition(userId, normalAccess);
};
-// if (Meteor.isServer) {
-//
-// if(process.env.OAUTH2_CLIENT_ID !== '') {
-//
-// ServiceConfiguration.configurations.upsert( // eslint-disable-line no-undef
-// { service: 'oidc' },
-// {
-// $set: {
-// loginStyle: 'redirect',
-// clientId: process.env.OAUTH2_CLIENT_ID,
-// secret: process.env.OAUTH2_SECRET,
-// serverUrl: process.env.OAUTH2_SERVER_URL,
-// authorizationEndpoint: process.env.OAUTH2_AUTH_ENDPOINT,
-// userinfoEndpoint: process.env.OAUTH2_USERINFO_ENDPOINT,
-// tokenEndpoint: process.env.OAUTH2_TOKEN_ENDPOINT,
-// idTokenWhitelistFields: [],
-// requestPermissions: ['openid'],
-// },
-// }
-// );
-// }
-// }
+ if (Meteor.isServer) {
+
+ if(process.env.OAUTH2_CLIENT_ID !== '') {
+
+ ServiceConfiguration.configurations.upsert( // eslint-disable-line no-undef
+ { service: 'oidc' },
+ {
+ $set: {
+ loginStyle: 'redirect',
+ clientId: process.env.OAUTH2_CLIENT_ID,
+ secret: process.env.OAUTH2_SECRET,
+ serverUrl: process.env.OAUTH2_SERVER_URL,
+ authorizationEndpoint: process.env.OAUTH2_AUTH_ENDPOINT,
+ userinfoEndpoint: process.env.OAUTH2_USERINFO_ENDPOINT,
+ tokenEndpoint: process.env.OAUTH2_TOKEN_ENDPOINT,
+ idTokenWhitelistFields: [],
+ requestPermissions: ['openid'],
+ },
+ }
+ );
+ }
+ }
});
+
diff --git a/server/migrations.js b/server/migrations.js
index ac33d836..2ccda54d 100644
--- a/server/migrations.js
+++ b/server/migrations.js
@@ -322,6 +322,18 @@ Migrations.add('add-subtasks-allowed', () => {
}, noValidateMulti);
});
+Migrations.add('add-authenticationMethod', () => {
+ Users.update({
+ 'authenticationMethod': {
+ $exists: false,
+ },
+ }, {
+ $set: {
+ 'authenticationMethod': 'password',
+ },
+ }, noValidateMulti);
+});
+
Migrations.add('remove-tag', () => {
Users.update({
}, {
@@ -338,4 +350,3 @@ Migrations.add('remove-customFields-references-broken', () => {
},
}, noValidateMulti);
});
-
diff --git a/server/notifications/email.js b/server/notifications/email.js
index 2af6381e..b2b7fab8 100644
--- a/server/notifications/email.js
+++ b/server/notifications/email.js
@@ -39,3 +39,5 @@ Meteor.startup(() => {
}, 30000);
});
});
+
+
diff --git a/server/notifications/notifications.js b/server/notifications/notifications.js
index 19d43bc4..fa8b2ee2 100644
--- a/server/notifications/notifications.js
+++ b/server/notifications/notifications.js
@@ -19,7 +19,6 @@ Notifications = {
delete notifyServices[serviceName];
},
- // filter recipients according to user settings for notification
getUsers: (watchers) => {
const users = [];
watchers.forEach((userId) => {
diff --git a/server/publications/people.js b/server/publications/people.js
index 7c13bdcc..56187732 100644
--- a/server/publications/people.js
+++ b/server/publications/people.js
@@ -17,6 +17,7 @@ Meteor.publish('people', function(limit) {
'emails': 1,
'createdAt': 1,
'loginDisabled': 1,
+ 'authenticationMethod': 1,
},
});
} else {
diff --git a/server/publications/rules.js b/server/publications/rules.js
new file mode 100644
index 00000000..d0864893
--- /dev/null
+++ b/server/publications/rules.js
@@ -0,0 +1,18 @@
+Meteor.publish('rules', (ruleId) => {
+ check(ruleId, String);
+ return Rules.find({
+ _id: ruleId,
+ });
+});
+
+Meteor.publish('allRules', () => {
+ return Rules.find({});
+});
+
+Meteor.publish('allTriggers', () => {
+ return Triggers.find({});
+});
+
+Meteor.publish('allActions', () => {
+ return Actions.find({});
+});
diff --git a/server/publications/users.js b/server/publications/users.js
index 4fd98e13..f0c94153 100644
--- a/server/publications/users.js
+++ b/server/publications/users.js
@@ -17,3 +17,12 @@ Meteor.publish('user-admin', function() {
},
});
});
+
+Meteor.publish('user-authenticationMethod', function(match) {
+ check(match, String);
+ return Users.find({$or: [{_id: match}, {email: match}, {username: match}]}, {
+ fields: {
+ 'authenticationMethod': 1,
+ },
+ });
+});
diff --git a/server/rulesHelper.js b/server/rulesHelper.js
new file mode 100644
index 00000000..833092d0
--- /dev/null
+++ b/server/rulesHelper.js
@@ -0,0 +1,138 @@
+RulesHelper = {
+ executeRules(activity){
+ const matchingRules = this.findMatchingRules(activity);
+ for(let i = 0; i< matchingRules.length; i++){
+ const action = matchingRules[i].getAction();
+ if(action !== undefined){
+ this.performAction(activity, action);
+ }
+ }
+ },
+ findMatchingRules(activity){
+ const activityType = activity.activityType;
+ if(TriggersDef[activityType] === undefined){
+ return [];
+ }
+ const matchingFields = TriggersDef[activityType].matchingFields;
+ const matchingMap = this.buildMatchingFieldsMap(activity, matchingFields);
+ const matchingTriggers = Triggers.find(matchingMap);
+ const matchingRules = [];
+ matchingTriggers.forEach(function(trigger){
+ const rule = trigger.getRule();
+ // Check that for some unknown reason there are some leftover triggers
+ // not connected to any rules
+ if(rule !== undefined){
+ matchingRules.push(trigger.getRule());
+ }
+ });
+ return matchingRules;
+ },
+ buildMatchingFieldsMap(activity, matchingFields){
+ const matchingMap = {'activityType':activity.activityType};
+ for(let i = 0; i< matchingFields.length; i++){
+ // Creating a matching map with the actual field of the activity
+ // and with the wildcard (for example: trigger when a card is added
+ // in any [*] board
+ matchingMap[matchingFields[i]] = { $in: [activity[matchingFields[i]], '*']};
+ }
+ return matchingMap;
+ },
+ performAction(activity, action){
+ const card = Cards.findOne({_id:activity.cardId});
+ const boardId = activity.boardId;
+ if(action.actionType === 'moveCardToTop'){
+ let listId;
+ let list;
+ if(action.listTitle === '*'){
+ listId = card.listId;
+ list = card.list();
+ }else{
+ list = Lists.findOne({title: action.listTitle, boardId });
+ listId = list._id;
+ }
+ const minOrder = _.min(list.cardsUnfiltered(card.swimlaneId).map((c) => c.sort));
+ card.move(card.swimlaneId, listId, minOrder - 1);
+ }
+ if(action.actionType === 'moveCardToBottom'){
+ let listId;
+ let list;
+ if(action.listTitle === '*'){
+ listId = card.listId;
+ list = card.list();
+ }else{
+ list = Lists.findOne({title: action.listTitle, boardId});
+ listId = list._id;
+ }
+ const maxOrder = _.max(list.cardsUnfiltered(card.swimlaneId).map((c) => c.sort));
+ card.move(card.swimlaneId, listId, maxOrder + 1);
+ }
+ if(action.actionType === 'sendEmail'){
+ const emailTo = action.emailTo;
+ const emailMsg = action.emailMsg;
+ const emailSubject = action.emailSubject;
+ try {
+ Email.send({
+ emailTo,
+ from: Accounts.emailTemplates.from,
+ emailSubject,
+ emailMsg,
+ });
+ } catch (e) {
+ return;
+ }
+ }
+ if(action.actionType === 'archive'){
+ card.archive();
+ }
+ if(action.actionType === 'unarchive'){
+ card.restore();
+ }
+ if(action.actionType === 'addLabel'){
+ card.addLabel(action.labelId);
+ }
+ if(action.actionType === 'removeLabel'){
+ card.removeLabel(action.labelId);
+ }
+ if(action.actionType === 'addMember'){
+ const memberId = Users.findOne({username:action.username})._id;
+ card.assignMember(memberId);
+ }
+ if(action.actionType === 'removeMember'){
+ if(action.memberName === '*'){
+ const members = card.members;
+ for(let i = 0; i< members.length; i++){
+ card.unassignMember(members[i]);
+ }
+ }else{
+ const memberId = Users.findOne({username:action.username})._id;
+ card.unassignMember(memberId);
+ }
+ }
+ if(action.actionType === 'checkAll'){
+ const checkList = Checklists.findOne({'title':action.checklistName, 'cardId':card._id});
+ checkList.checkAllItems();
+ }
+ if(action.actionType === 'uncheckAll'){
+ const checkList = Checklists.findOne({'title':action.checklistName, 'cardId':card._id});
+ checkList.uncheckAllItems();
+ }
+ if(action.actionType === 'checkItem'){
+ const checkList = Checklists.findOne({'title':action.checklistName, 'cardId':card._id});
+ const checkItem = ChecklistItems.findOne({'title':action.checkItemName, 'checkListId':checkList._id});
+ checkItem.check();
+ }
+ if(action.actionType === 'uncheckItem'){
+ const checkList = Checklists.findOne({'title':action.checklistName, 'cardId':card._id});
+ const checkItem = ChecklistItems.findOne({'title':action.checkItemName, 'checkListId':checkList._id});
+ checkItem.uncheck();
+ }
+ if(action.actionType === 'addChecklist'){
+ Checklists.insert({'title':action.checklistName, 'cardId':card._id, 'sort':0});
+ }
+ if(action.actionType === 'removeChecklist'){
+ Checklists.remove({'title':action.checklistName, 'cardId':card._id, 'sort':0});
+ }
+
+ },
+
+};
diff --git a/server/triggersDef.js b/server/triggersDef.js
new file mode 100644
index 00000000..f6d5333b
--- /dev/null
+++ b/server/triggersDef.js
@@ -0,0 +1,58 @@
+TriggersDef = {
+ createCard:{
+ matchingFields: ['boardId', 'listName'],
+ },
+ moveCard:{
+ matchingFields: ['boardId', 'listName', 'oldListName'],
+ },
+ archivedCard:{
+ matchingFields: ['boardId'],
+ },
+ restoredCard:{
+ matchingFields: ['boardId'],
+ },
+ joinMember:{
+ matchingFields: ['boardId', 'username'],
+ },
+ unjoinMember:{
+ matchingFields: ['boardId', 'username'],
+ },
+ addChecklist:{
+ matchingFields: ['boardId', 'checklistName'],
+ },
+ removeChecklist:{
+ matchingFields: ['boardId', 'checklistName'],
+ },
+ completeChecklist:{
+ matchingFields: ['boardId', 'checklistName'],
+ },
+ uncompleteChecklist:{
+ matchingFields: ['boardId', 'checklistName'],
+ },
+ addedChecklistItem:{
+ matchingFields: ['boardId', 'checklistItemName'],
+ },
+ removedChecklistItem:{
+ matchingFields: ['boardId', 'checklistItemName'],
+ },
+ checkedItem:{
+ matchingFields: ['boardId', 'checklistItemName'],
+ },
+ uncheckedItem:{
+ matchingFields: ['boardId', 'checklistItemName'],
+ },
+ addAttachment:{
+ matchingFields: ['boardId'],
+ },
+ deleteAttachment:{
+ matchingFields: ['boardId'],
+ },
+ addedLabel:{
+ matchingFields: ['boardId', 'labelId'],
+ },
+ removedLabel:{
+ matchingFields: ['boardId', 'labelId'],
+ },
+};
+
+
diff --git a/snap-src/bin/config b/snap-src/bin/config
index a54b13c2..b81925ac 100755
--- a/snap-src/bin/config
+++ b/snap-src/bin/config
@@ -3,7 +3,7 @@
# All supported keys are defined here together with descriptions and default values
# list of supported keys
-keys="MONGODB_BIND_UNIX_SOCKET MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT"
+keys="MONGODB_BIND_UNIX_SOCKET MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_ENABLED OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN"
# default values
DESCRIPTION_MONGODB_BIND_UNIX_SOCKET="mongodb binding unix socket:\n"\
@@ -13,11 +13,11 @@ DEFAULT_MONGODB_BIND_UNIX_SOCKET="$SNAP_DATA/share"
KEY_MONGODB_BIND_UNIX_SOCKET="mongodb-bind-unix-socket"
DESCRIPTION_MONGODB_PORT="mongodb binding port: eg 27017 when using localhost"
-DEFAULT_MONGODB_PORT="27019"
+DEFAULT_MONGODB_PORT=""
KEY_MONGODB_PORT='mongodb-port'
DESCRIPTION_MONGODB_BIND_IP="mongodb binding ip address: eg 127.0.0.1 for localhost\n\t\tIf not defined default unix socket is used instead"
-DEFAULT_MONGODB_BIND_IP="127.0.0.1"
+DEFAULT_MONGODB_BIND_IP=""
KEY_MONGODB_BIND_IP="mongodb-bind-ip"
DESCRIPTION_MAIL_URL="wekan mail binding"
@@ -82,6 +82,10 @@ DESCRIPTION_WEBHOOKS_ATTRIBUTES="What to send to Outgoing Webhook, or leave out.
DEFAULT_WEBHOOKS_ATTRIBUTES=""
KEY_WEBHOOKS_ATTRIBUTES="webhooks-attributes"
+DESCRIPTION_OAUTH2_ENABLED="Enable the OAuth2 connection"
+DEFAULT_OAUTH2_ENABLED="false"
+KEY_OAUTH2_ENABLED="oauth2-enabled"
+
DESCRIPTION_OAUTH2_CLIENT_ID="OAuth2 Client ID, for example from Rocket.Chat. Example: abcde12345"
DEFAULT_OAUTH2_CLIENT_ID=""
KEY_OAUTH2_CLIENT_ID="oauth2-client-id"
@@ -106,3 +110,158 @@ DESCRIPTION_OAUTH2_TOKEN_ENDPOINT="OAuth2 token endpoint. Example: /oauth/token"
DEFAULT_OAUTH2_TOKEN_ENDPOINT=""
KEY_OAUTH2_TOKEN_ENDPOINT="oauth2-token-endpoint"
+DESCRIPTION_LDAP_ENABLE="Enable or not the connection by the LDAP"
+DEFAULT_LDAP_ENABLE="false"
+KEY_LDAP_ENABLE="ldap-enable"
+
+DESCRIPTION_LDAP_PORT="The port of the LDAP server"
+DEFAULT_LDAP_PORT="389"
+KEY_LDAP_PORT="ldap-port"
+
+DESCRIPTION_LDAP_HOST="The host server for the LDAP server"
+DEFAULT_LDAP_HOST=""
+KEY_LDAP_HOST="ldap-host"
+
+DESCRIPTION_LDAP_BASEDN="The base DN for the LDAP Tree"
+DEFAULT_LDAP_BASEDN=""
+KEY_LDAP_BASEDN="ldap-basedn"
+
+DESCRIPTION_LDAP_LOGIN_FALLBACK="Fallback on the default authentication method"
+DEFAULT_LDAP_LOGIN_FALLBACK="false"
+KEY_LDAP_LOGIN_FALLBACK="ldap-login-fallback"
+
+DESCRIPTION_LDAP_RECONNECT="Reconnect to the server if the connection is lost"
+DEFAULT_LDAP_RECONNECT="true"
+KEY_LDAP_RECONNECT="ldap-reconnect"
+
+DESCRIPTION_LDAP_TIMEOUT="Overall timeout, in milliseconds."
+DEFAULT_LDAP_TIMEOUT="10000"
+KEY_LDAP_TIMEOUT="ldap-timeout"
+
+DESCRIPTION_LDAP_IDLE_TIMEOUT="Specifies the timeout for idle LDAP connections in milliseconds"
+DEFAULT_LDAP_IDLE_TIMEOUT="10000"
+KEY_LDAP_IDLE_TIMEOUT="ldap-idle-timeout"
+
+DESCRIPTION_LDAP_CONNECT_TIMEOUT="Connection timeout, in milliseconds."
+DEFAULT_LDAP_CONNECT_TIMEOUT="10000"
+KEY_LDAP_CONNECT_TIMEOUT="ldap-connect-timeout"
+
+DESCRIPTION_LDAP_AUTHENTIFICATION="If the LDAP needs a user account to search"
+DEFAULT_LDAP_AUTHENTIFICATION="false"
+KEY_LDAP_AUTHENTIFICATION="ldap-authentication"
+
+DESCRIPTION_LDAP_AUTHENTIFICATION_USERDN="The search user DN"
+DEFAULT_LDAP_AUTHENTIFICATION_USERDN=""
+KEY_LDAP_AUTHENTIFICATION_USERDN="ldap-authentication-userdn"
+
+DESCRIPTION_LDAP_AUTHENTIFICATION_PASSWORD="The password for the search user"
+DEFAULT_LDAP_AUTHENTIFICATION_PASSWORD=""
+KEY_LDAP_AUTHENTIFICATION_PASSWORD="ldap-authentication-password"
+
+DESCRIPTION_LDAP_LOG_ENABLED="Enable logs for the module"
+DEFAULT_LDAP_LOG_ENABLED="false"
+KEY_LDAP_LOG_ENABLED="ldap-log-enabled"
+
+DESCRIPTION_LDAP_BACKGROUND_SYNC="If the sync of the users should be done in the background"
+DEFAULT_LDAP_BACKGROUND_SYNC="false"
+KEY_LDAP_BACKGROUND_SYNC="ldap-background-sync"
+
+DESCRIPTION_LDAP_BACKGROUND_SYNC_INTERVAL="At which interval does the background task sync in milliseconds"
+DEFAULT_LDAP_BACKGROUND_SYNC_INTERVAL="100"
+KEY_LDAP_BACKGROUND_SYNC_INTERVAL="ldap-background-sync-interval"
+
+DESCRIPTION_LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=""
+DEFAULT_LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED="false"
+KEY_LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED="ldap-background-sync-keep-existant-users-updated"
+
+DESCRIPTION_LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=""
+DEFAULT_LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS="false"
+KEY_LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS="ldap-background-sync-import-new-users"
+
+DESCRIPTION_LDAP_ENCRYPTION="If using LDAPS"
+DEFAULT_LDAP_ENCRYPTION="false"
+KEY_LDAP_ENCRYPTION="ldap-encryption"
+
+DESCRIPTION_LDAP_CA_CERT="The certification for the LDAPS server"
+DEFAULT_LDAP_CA_CERT=""
+KEY_LDAP_CA_CERT="ldap-ca-cert"
+
+DESCRIPTION_LDAP_REJECT_UNAUTHORIZED="Reject Unauthorized Certificate"
+DEFAULT_LDAP_REJECT_UNAUTHORIZED="false"
+KEY_LDAP_REJECT_UNAUTHORIZED="ldap-reject-unauthorized"
+
+DESCRIPTION_LDAP_USER_SEARCH_FILTER="Optional extra LDAP filters. Don't forget the outmost enclosing parentheses if needed"
+DEFAULT_LDAP_USER_SEARCH_FILTER=""
+KEY_LDAP_USER_SEARCH_FILTER="ldap-user-search-filter"
+
+DESCRIPTION_LDAP_USER_SEARCH_SCOPE="Base (search only in the provided DN), one (search only in the provided DN and one level deep), or subtree (search the whole subtree)."
+DEFAULT_LDAP_USER_SEARCH_SCOPE=""
+KEY_LDAP_USER_SEARCH_SCOPE="ldap-user-search-scope"
+
+DESCRIPTION_LDAP_USER_SEARCH_FIELD="Which field is used to find the user"
+DEFAULT_LDAP_USER_SEARCH_FIELD=""
+KEY_LDAP_USER_SEARCH_FIELD="ldap-user-search-field"
+
+DESCRIPTION_LDAP_SEARCH_PAGE_SIZE="Used for pagination (0=unlimited)"
+DEFAULT_LDAP_SEARCH_PAGE_SIZE="0"
+KEY_LDAP_SEARCH_PAGE_SIZE="ldap-search-page-size"
+
+DESCRIPTION_LDAP_SEARCH_SIZE_LIMIT="The limit number of entries (0=unlimited)"
+DEFAULT_LDAP_SEARCH_SIZE_LIMIT="0"
+KEY_LDAP_SEARCH_SIZE_LIMIT="ldap-search-size-limit"
+
+DESCRIPTION_LDAP_GROUP_FILTER_ENABLE="Enable group filtering"
+DEFAULT_LDAP_GROUP_FILTER_ENABLE="false"
+KEY_LDAP_GROUP_FILTER_ENABLE="ldap-group-filter-enable"
+
+DESCRIPTION_LDAP_GROUP_FILTER_OBJECTCLASS="The object class for filtering"
+DEFAULT_LDAP_GROUP_FILTER_OBJECTCLASS=""
+KEY_LDAP_GROUP_FILTER_OBJECTCLASS="ldap-group-filter-objectclass"
+
+DESCRIPTION_LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=""
+DEFAULT_LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=""
+KEY_LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="ldap-group-filter-id-attribute"
+
+DESCRIPTION_LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=""
+DEFAULT_LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=""
+KEY_LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="ldap-group-filter-member-attribute"
+
+DESCRIPTION_LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=""
+DEFAULT_LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=""
+KEY_LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="ldap-group-filter-member-format"
+
+DESCRIPTION_LDAP_GROUP_FILTER_GROUP_NAME=""
+DEFAULT_LDAP_GROUP_FILTER_GROUP_NAME=""
+KEY_LDAP_GROUP_FILTER_GROUP_NAME="ldap-group-filter-member-format"
+
+DESCRIPTION_LDAP_UNIQUE_IDENTIFIER_FIELD="This field is sometimes class GUID (Globally Unique Identifier)"
+DEFAULT_LDAP_UNIQUE_IDENTIFIER_FIELD=""
+KEY_LDAP_UNIQUE_IDENTIFIER_FIELD="ldap-unique-identifier-field"
+
+DESCRIPTION_LDAP_UTF8_NAMES_SLUGIFY="Convert the username to utf8"
+DEFAULT_LDAP_UTF8_NAMES_SLUGIFY="true"
+KEY_LDAP_UTF8_NAMES_SLUGIFY="ldap-utf8-names-slugify"
+
+DESCRIPTION_LDAP_USERNAME_FIELD="Which field contains the ldap username"
+DEFAULT_LDAP_USERNAME_FIELD=""
+KEY_LDAP_USERNAME_FIELD="ldap-username-field"
+
+DESCRIPTION_LDAP_MERGE_EXISTING_USERS=""
+DEFAULT_LDAP_MERGE_EXISTING_USERS="false"
+KEY_LDAP_MERGE_EXISTING_USERS="ldap-merge-existing-users"
+
+DESCRIPTION_LDAP_SYNC_USER_DATA=""
+DEFAULT_LDAP_SYNC_USER_DATA="false"
+KEY_LDAP_SYNC_USER_DATA="ldap-sync-user-data"
+
+DESCRIPTION_LDAP_SYNC_USER_DATA_FIELDMAP=""
+DEFAULT_LDAP_SYNC_USER_DATA_FIELDMAP=""
+KEY_LDAP_SYNC_USER_DATA_FIELDMAP="ldap-sync-user-data-fieldmap"
+
+DESCRIPTION_LDAP_SYNC_GROUP_ROLES=""
+DEFAULT_LDAP_SYNC_GROUP_ROLES=""
+KEY_LDAP_SYNC_GROUP_ROLES="ldap-sync-group-roles"
+
+DESCRIPTION_LDAP_DEFAULT_DOMAIN="The default domain of the ldap it is used to create email if the field is not map correctly with the LDAP_SYNC_USER_DATA_FIELDMAP"
+DEFAULT_LDAP_DEFAULT_DOMAIN=""
+KEY_LDAP_DEFAULT_DOMAIN="ldap-default-domain"
diff --git a/snap-src/bin/wekan-help b/snap-src/bin/wekan-help
index 95814d36..3af19336 100755
--- a/snap-src/bin/wekan-help
+++ b/snap-src/bin/wekan-help
@@ -95,6 +95,156 @@ echo -e "\t$ snap set $SNAP_NAME OAUTH2_TOKEN_ENDPOINT='/oauth/token'"
echo -e "\t-Disable the OAuth2 Token Endpoint of Wekan:"
echo -e "\t$ snap set $SNAP_NAME OAUTH2_TOKEN_ENDPOINT=''"
echo -e "\n"
+echo -e "Ldap Enable."
+echo -e "To enable the ldap of Wekan:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_ENABLE='true'"
+echo -e "\t-Disable the ldap of Wekan:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_ENABLE='false'"
+echo -e "\n"
+echo -e "Ldap Port."
+echo -e "The port of the ldap server:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_PORT='12345'"
+echo -e "\n"
+echo -e "Ldap Host."
+echo -e "The host server for the LDAP server:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_HOST='localhost'"
+echo -e "\n"
+echo -e "Ldap Base Dn."
+echo -e "The base DN for the LDAP Tree:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_BASEDN='ou=user,dc=example,dc=org'"
+echo -e "\n"
+echo -e "Ldap Login Fallback."
+echo -e "Fallback on the default authentication method:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_LOGIN_FALLBACK='true'"
+echo -e "\n"
+echo -e "Ldap Reconnect."
+echo -e "Reconnect to the server if the connection is lost:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_RECONNECT='false'"
+echo -e "\n"
+echo -e "Ldap Timeout."
+echo -e "Overall timeout, in milliseconds:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_TIMEOUT='12345'"
+echo -e "\n"
+echo -e "Ldap Idle Timeout."
+echo -e "Specifies the timeout for idle LDAP connections in milliseconds:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_IDLE_TIMEOUT='12345'"
+echo -e "\n"
+echo -e "Ldap Connect Timeout."
+echo -e "Connection timeout, in milliseconds:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_CONNECT_TIMEOUT='12345'"
+echo -e "\n"
+echo -e "Ldap Authentication."
+echo -e "If the LDAP needs a user account to search:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_AUTHENTIFICATION='true'"
+echo -e "\n"
+echo -e "Ldap Authentication User Dn."
+echo -e "The search user Dn:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_AUTHENTIFICATION_USERDN='cn=admin,dc=example,dc=org'"
+echo -e "\n"
+echo -e "Ldap Authentication Password."
+echo -e "The password for the search user:"
+echo -e "\t$ snap set $SNAP_NAME AUTHENTIFICATION_PASSWORD='admin'"
+echo -e "\n"
+echo -e "Ldap Log Enabled."
+echo -e "Enable logs for the module:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_LOG_ENABLED='true'"
+echo -e "\n"
+echo -e "Ldap Background Sync."
+echo -e "If the sync of the users should be done in the background:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_BACKGROUND_SYNC='true'"
+echo -e "\n"
+echo -e "Ldap Background Sync Interval."
+echo -e "At which interval does the background task sync in milliseconds:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_BACKGROUND_SYNC_INTERVAL='12345'"
+echo -e "\n"
+echo -e "Ldap Background Sync Keep Existant Users Updated."
+echo -e "\t$ snap set $SNAP_NAME LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED='true'"
+echo -e "\n"
+echo -e "Ldap Background Sync Import New Users."
+echo -e "\t$ snap set $SNAP_NAME LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS='true'"
+echo -e "\n"
+echo -e "Ldap Encryption."
+echo -e "Allow LDAPS:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_ENCRYPTION='true'"
+echo -e "\n"
+echo -e "Ldap Ca Cert."
+echo -e "The certification for the LDAPS server:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_CA_CERT=-----BEGIN CERTIFICATE-----MIIE+zCCA+OgAwIBAgIkAhwR/6TVLmdRY6hHxvUFWc0+Enmu/Hu6cj+G2FIdAgIC...-----END CERTIFICATE-----"
+echo -e "\n"
+echo -e "Ldap Reject Unauthorized."
+echo -e "Reject Unauthorized Certificate:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_REJECT_UNAUTHORIZED='true'"
+echo -e "\n"
+echo -e "Ldap User Search Filter."
+echo -e "Optional extra LDAP filters. Don't forget the outmost enclosing parentheses if needed:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_USER_SEARCH_FILTER=''"
+echo -e "\n"
+echo -e "Ldap User Search Scope."
+echo -e "Base (search only in the provided DN), one (search only in the provided DN and one level deep), or subtree (search the whole subtree):"
+echo -e "\t$ snap set $SNAP_NAME LDAP_USER_SEARCH_SCOPE=one"
+echo -e "\n"
+echo -e "Ldap User Search Field."
+echo -e "Which field is used to find the user:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_USER_SEARCH_FIELD='uid'"
+echo -e "\n"
+echo -e "Ldap Search Page Size."
+echo -e "Used for pagination (0=unlimited):"
+echo -e "\t$ snap set $SNAP_NAME LDAP_SEARCH_PAGE_SIZE='12345'"
+echo -e "\n"
+echo -e "Ldap Search Size Limit."
+echo -e "The limit number of entries (0=unlimited):"
+echo -e "\t$ snap set $SNAP_NAME LDAP_SEARCH_SIZE_LIMIT='12345'"
+echo -e "\n"
+echo -e "Ldap Group Filter Enable."
+echo -e "Enable group filtering:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_GROUP_FILTER_ENABLE='true'"
+echo -e "\n"
+echo -e "Ldap Group Filter ObjectClass."
+echo -e "The object class for filtering:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_GROUP_FILTER_OBJECTCLASS='group'"
+echo -e "\n"
+echo -e "Ldap Group Filter Id Attribute."
+echo -e "\t$ snap set $SNAP_NAME LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE=''"
+echo -e "\n"
+echo -e "Ldap Group Filter Member Attribute."
+echo -e "\t$ snap set $SNAP_NAME LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE=''"
+echo -e "\n"
+echo -e "Ldap Group Filter Member Format."
+echo -e "\t$ snap set $SNAP_NAME LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT=''"
+echo -e "\n"
+echo -e "Ldap Group Filter Group Name."
+echo -e "\t$ snap set $SNAP_NAME LDAP_GROUP_FILTER_GROUP_NAME=''"
+echo -e "\n"
+echo -e "Ldap Unique Identifier Field."
+echo -e "This field is sometimes class GUID (Globally Unique Identifier):"
+echo -e "\t$ snap set $SNAP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD=guid"
+echo -e "\n"
+echo -e "Ldap Utf8 Names Slugify."
+echo -e "Convert the username to utf8:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_UTF8_NAMES_SLUGIFY='false'"
+echo -e "\n"
+echo -e "Ldap Username Field."
+echo -e "Which field contains the ldap username:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_USERNAME_FIELD='username'"
+echo -e "\n"
+echo -e "Ldap Merge Existing Users."
+echo -e "\t$ snap set $SNAP_NAME LDAP_MERGE_EXISTING_USERS='true'"
+echo -e "\n"
+echo -e "Ldap Sync User Data."
+echo -e "Enable synchronization of user data:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_SYNC_USER_DATA='true'"
+echo -e "\n"
+echo -e "Ldap Sync User Data Fieldmap."
+echo -e "A field map for the matching:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_SYNC_USER_DATA_FIELDMAP={\"cn\":\"name\", \"mail\":\"email\"}"
+echo -e "\n"
+echo -e "Ldap Sync Group Roles."
+echo -e "\t$ snap set $SNAP_NAME LDAP_SYNC_GROUP_ROLES=''"
+echo -e "\n"
+echo -e "Ldap Default Domain."
+echo -e "The default domain of the ldap it is used to create email if the field is not map correctly with the LDAP_SYNC_USER_DATA_FIELDMAP:"
+echo -e "\t$ snap set $SNAP_NAME LDAP_DEFAULT_DOMAIN=''"
+echo -e "\n"
# parse config file for supported settings keys
echo -e "wekan supports settings keys"
echo -e "values can be changed by calling\n$ snap set $SNAP_NAME <key name>='<key value>'"
diff --git a/snapcraft.yaml b/snapcraft.yaml
index dbe738d0..9ad94fe5 100644
--- a/snapcraft.yaml
+++ b/snapcraft.yaml
@@ -65,7 +65,7 @@ apps:
parts:
mongodb:
- source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.2.21.tgz
+ source: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.0.3.tgz
plugin: dump
stage-packages: [libssl1.0.0]
filesets:
@@ -83,7 +83,6 @@ parts:
plugin: nodejs
node-engine: 8.12.0
node-packages:
- - npm
- node-gyp
- node-pre-gyp
- fibers@2.0.0
@@ -93,7 +92,6 @@ parts:
- python
- g++
- capnproto
- - npm
- curl
- execstack
stage-packages:
@@ -122,9 +120,11 @@ parts:
# Removed from build-packages: - paxctl
#echo "Applying paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303"
#paxctl -mC `which node`
+ #echo "Installing npm"
+ #curl -L https://www.npmjs.com/install.sh | sh
echo "Installing meteor"
curl https://install.meteor.com/ -o install_meteor.sh
- sed -i "s|RELEASE=.*|RELEASE=\"1.6.0.1\"|g" install_meteor.sh
+ #sed -i "s|RELEASE=.*|RELEASE=\"1.8.1-beta.0\"|g" install_meteor.sh
chmod +x install_meteor.sh
sh install_meteor.sh
rm install_meteor.sh
@@ -142,6 +142,16 @@ parts:
sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' meteor-useraccounts-core/package.js
cd ..
fi
+ if [ ! -d "packages/meteor-accounts-cas" ]; then
+ cd packages
+ git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-cas.git meteor-accounts-cas
+ cd ..
+ fi
+ if [ ! -d "packages/wekan-ldap" ]; then
+ cd packages
+ git clone --depth 1 -b master https://github.com/wekan/wekan-ldap.git
+ cd ..
+ fi
rm -rf package-lock.json .build
meteor add standard-minifier-js --allow-superuser
meteor npm install --allow-superuser