summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.json1
-rw-r--r--.meteor/.finished-upgraders1
-rw-r--r--.meteor/.gitignore1
-rw-r--r--.meteor/packages3
-rw-r--r--.meteor/release2
-rw-r--r--.meteor/versions199
-rw-r--r--client/components/boards/boardHeader.jade2
-rw-r--r--client/components/cards/cardDetails.js4
-rw-r--r--client/components/cards/labels.js4
-rw-r--r--client/components/import/import.jade4
-rwxr-xr-xclient/components/main/editor.js20
-rw-r--r--client/components/sidebar/sidebar.jade2
-rw-r--r--client/config/gecko-fix.js4
-rw-r--r--client/lib/popup.js2
-rwxr-xr-xi18n/en.i18n.json3
-rw-r--r--meta/icons/wekan-150.pngbin4323 -> 3634 bytes
-rw-r--r--models/activities.js10
-rw-r--r--models/boards.js109
-rw-r--r--models/cardComments.js17
-rw-r--r--models/cards.js38
-rw-r--r--models/lists.js37
-rw-r--r--models/unsavedEdits.js9
-rw-r--r--models/users.js139
-rw-r--r--public/wekan-favicon.pngbin919 -> 756 bytes
-rw-r--r--public/wekan-logo-header.pngbin2504 -> 1827 bytes
-rw-r--r--public/wekan-logo.pngbin11813 -> 8433 bytes
26 files changed, 387 insertions, 224 deletions
diff --git a/.eslintrc.json b/.eslintrc.json
index 939f7b46..87c2e2cf 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -74,6 +74,7 @@
"Avatars": true,
"BlazeComponent": false,
"BlazeLayout": false,
+ "CollectionHooks": false,
"DocHead": false,
"ESSearchResults": false,
"FastRender": false,
diff --git a/.meteor/.finished-upgraders b/.meteor/.finished-upgraders
index 61ee3132..dacc2c0d 100644
--- a/.meteor/.finished-upgraders
+++ b/.meteor/.finished-upgraders
@@ -10,3 +10,4 @@ notices-for-facebook-graph-api-2
1.2.0-meteor-platform-split
1.2.0-cordova-changes
1.2.0-breaking-changes
+1.3.0-split-minifiers-package
diff --git a/.meteor/.gitignore b/.meteor/.gitignore
index 40830374..501f92e4 100644
--- a/.meteor/.gitignore
+++ b/.meteor/.gitignore
@@ -1 +1,2 @@
+dev_bundle
local
diff --git a/.meteor/packages b/.meteor/packages
index f6e11fcf..e57bdb19 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -8,7 +8,8 @@ meteor-base
# Build system
ecmascript
stylus
-standard-minifiers
+standard-minifier-css
+standard-minifier-js
mquandalle:jade
# Polyfills
diff --git a/.meteor/release b/.meteor/release
index aa93b69d..e5b4dc18 100644
--- a/.meteor/release
+++ b/.meteor/release
@@ -1 +1 @@
-METEOR@1.2.2-faster-rebuilds.0
+METEOR@1.3.4.4
diff --git a/.meteor/versions b/.meteor/versions
index 6c8051a7..843cd138 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -1,26 +1,26 @@
3stack:presence@1.0.5
-accounts-base@1.2.2
-accounts-password@1.1.4
-aldeed:collection2@2.8.0
-aldeed:collection2-core@1.0.0
+accounts-base@1.2.8
+accounts-password@1.1.12
+aldeed:collection2@2.9.1
+aldeed:collection2-core@1.1.1
aldeed:schema-deny@1.0.1
aldeed:schema-index@1.0.1
aldeed:simple-schema@1.5.3
-alethes:pages@1.8.4
-arillo:flow-router-helpers@0.4.7
-audit-argument-checks@1.0.4
-autoupdate@1.2.4
-babel-compiler@5.8.25-faster-rebuild.0
-babel-runtime@0.1.4
-base64@1.0.4
-binary-heap@1.0.4
-blaze@2.1.3
-blaze-html-templates@1.0.1
-blaze-tools@1.0.4
-boilerplate-generator@1.0.4
-caching-compiler@1.0.1-faster-rebuild.0
-caching-html-compiler@1.0.2
-callback-hook@1.0.4
+alethes:pages@1.8.6
+allow-deny@1.0.5
+arillo:flow-router-helpers@0.5.2
+audit-argument-checks@1.0.7
+autoupdate@1.2.11
+babel-compiler@6.8.4
+babel-runtime@0.1.9_1
+base64@1.0.9
+binary-heap@1.0.9
+blaze@2.1.8
+blaze-tools@1.0.9
+boilerplate-generator@1.0.9
+caching-compiler@1.0.6
+caching-html-compiler@1.0.6
+callback-hook@1.0.9
cfs:access-point@0.1.49
cfs:base-package@0.0.30
cfs:collection@0.5.5
@@ -28,7 +28,7 @@ cfs:collection-filters@0.2.4
cfs:data-man@0.0.6
cfs:file@0.1.17
cfs:gridfs@0.0.33
-cfs:http-methods@0.0.30
+cfs:http-methods@0.0.32
cfs:http-publish@0.0.13
cfs:power-queue@0.9.11
cfs:reactive-list@0.0.9
@@ -38,115 +38,120 @@ cfs:storage-adapter@0.2.3
cfs:tempstore@0.1.5
cfs:upload-http@0.0.20
cfs:worker@0.1.4
-check@1.1.0
+check@1.2.3
chuangbo:cookie@1.1.0
-coffeescript@1.0.12-faster-rebuild.0
-cosmos:browserify@0.9.3
-cottz:publish-relations@2.0.0
+coffeescript@1.1.3
+cottz:publish-relations@2.0.6
dburles:collection-helpers@1.0.4
-ddp@1.2.2
-ddp-client@1.2.2-faster-rebuild.0
-ddp-common@1.2.2
-ddp-rate-limiter@1.0.0
-ddp-server@1.2.3-faster-rebuild.0
-deps@1.0.9
-diff-sequence@1.0.1
-ecmascript@0.1.7-faster-rebuild.0
-ecmascript-runtime@0.2.6
-ejson@1.0.7
-email@1.0.9-faster-rebuild.0
-es5-shim@4.1.14
-fastclick@1.0.7
+ddp@1.2.5
+ddp-client@1.2.9
+ddp-common@1.2.6
+ddp-rate-limiter@1.0.5
+ddp-server@1.2.9
+deps@1.0.12
+diff-sequence@1.0.6
+ecmascript@0.4.7
+ecmascript-runtime@0.2.12
+ejson@1.0.12
+email@1.0.15
+es5-shim@4.5.13
+fastclick@1.0.12
fortawesome:fontawesome@4.5.0
-geojson-utils@1.0.4
-hot-code-push@1.0.0
-html-tools@1.0.5
-htmljs@1.0.5
-http@1.1.2-faster-rebuild.0
-id-map@1.0.4
+geojson-utils@1.0.9
+hot-code-push@1.0.4
+html-tools@1.0.10
+htmljs@1.0.10
+http@1.1.8
+id-map@1.0.8
idmontie:migrations@1.0.1
-jquery@1.11.4
+jquery@1.11.9
kadira:blaze-layout@2.3.0
-kadira:dochead@1.4.0
-kadira:flow-router@2.10.1
-kenton:accounts-sandstorm@0.1.8
-launch-screen@1.0.4
-livedata@1.0.15
-localstorage@1.0.5
-logging@1.0.9-faster-rebuild.0
+kadira:dochead@1.5.0
+kadira:flow-router@2.12.1
+kenton:accounts-sandstorm@0.5.1
+launch-screen@1.0.12
+livedata@1.0.18
+localstorage@1.0.11
+logging@1.0.14
matb33:collection-hooks@0.8.1
matteodem:easy-search@1.6.4
mdg:validation-error@0.2.0
-meteor@1.1.11-faster-rebuild.0
-meteor-base@1.0.1
-meteor-platform@1.2.3
+meteor@1.1.16
+meteor-base@1.0.4
+meteor-platform@1.2.6
meteorhacks:aggregate@1.3.0
meteorhacks:collection-utils@1.2.0
-meteorhacks:fast-render@2.11.0
-meteorhacks:inject-data@1.4.1
+meteorhacks:fast-render@2.14.0
+meteorhacks:inject-data@2.0.0
+meteorhacks:meteorx@1.4.1
meteorhacks:picker@1.0.3
-meteorhacks:subs-manager@1.6.3
+meteorhacks:subs-manager@1.6.4
meteorspark:util@0.2.0
+minifier-css@1.1.13
+minifier-js@1.1.13
minifiers@1.1.8-faster-rebuild.0
-minimongo@1.0.10
-mobile-status-bar@1.0.6
-mongo@1.1.4-faster-rebuild.0
-mongo-id@1.0.1
-mongo-livedata@1.0.9
+minimongo@1.0.17
+mobile-status-bar@1.0.12
+modules@0.6.5
+modules-runtime@0.6.5
+mongo@1.1.9_1
+mongo-id@1.0.5
+mongo-livedata@1.0.12
mousetrap:mousetrap@1.4.6_1
mquandalle:autofocus@1.0.0
mquandalle:collection-mutations@0.1.0
-mquandalle:jade@0.4.8
+mquandalle:jade@0.4.9
mquandalle:jade-compiler@0.4.5
mquandalle:jquery-textcomplete@0.8.0_1
mquandalle:jquery-ui-drag-drop-sort@0.2.0
mquandalle:moment@1.0.1
mquandalle:mousetrap-bindglobal@0.0.1
mquandalle:perfect-scrollbar@0.6.5_2
-npm-bcrypt@0.7.8_2
-npm-mongo@1.4.40-faster-rebuild.0
-observe-sequence@1.0.7
+npm-bcrypt@0.8.6_2
+npm-mongo@1.4.45
+observe-sequence@1.0.12
ongoworks:speakingurl@1.1.0
-ordered-dict@1.0.4
+ordered-dict@1.0.8
peerlibrary:assert@0.2.5
peerlibrary:base-component@0.14.0
peerlibrary:blaze-components@0.15.1
peerlibrary:computed-field@0.3.1
peerlibrary:reactive-field@0.1.0
perak:markdown@1.0.5
-promise@0.5.2-faster-rebuild.0
+promise@0.7.3
raix:eventemitter@0.1.3
raix:handlebar-helpers@0.2.5
-random@1.0.5
-rate-limit@1.0.0
-reactive-dict@1.1.3
-reactive-var@1.0.6
-reload@1.1.4
-retry@1.0.4
-routepolicy@1.0.6
+random@1.0.10
+rate-limit@1.0.5
+reactive-dict@1.1.8
+reactive-var@1.0.10
+reload@1.1.10
+retry@1.0.8
+routepolicy@1.0.11
seriousm:emoji-continued@1.4.0
-service-configuration@1.0.5
-session@1.1.1
-sha@1.0.4
+service-configuration@1.0.10
+session@1.1.6
+sha@1.0.8
simple:json-routes@1.0.4
-softwarerero:accounts-t9n@1.1.7
-spacebars@1.0.7
-spacebars-compiler@1.0.7
-srp@1.0.4
-standard-minifiers@1.0.3-faster-rebuild.0
-stylus@2.511.2-faster-rebuild.0
-tap:i18n@1.7.0
-templates:tabs@2.2.0
-templating@1.1.6-faster-rebuild.0
-templating-tools@1.0.0
-tracker@1.0.9
-ui@1.0.8
-underscore@1.0.4
-url@1.0.5
-useraccounts:core@1.13.1
-useraccounts:flow-routing@1.13.1
-useraccounts:unstyled@1.13.1
+softwarerero:accounts-t9n@1.3.4
+spacebars@1.0.12
+spacebars-compiler@1.0.12
+srp@1.0.9
+standard-minifier-css@1.0.8
+standard-minifier-js@1.0.8
+stylus@2.512.4
+tap:i18n@1.8.2
+templates:tabs@2.2.2
+templating@1.1.13
+templating-tools@1.0.4
+tracker@1.0.14
+ui@1.0.11
+underscore@1.0.9
+url@1.0.10
+useraccounts:core@1.14.2
+useraccounts:flow-routing@1.14.2
+useraccounts:unstyled@1.14.2
verron:autosize@3.0.8
-webapp@1.2.4-faster-rebuild.0
-webapp-hashing@1.0.5
+webapp@1.2.10
+webapp-hashing@1.0.9
zimme:active-route@2.3.2
diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade
index e61831a1..03768b36 100644
--- a/client/components/boards/boardHeader.jade
+++ b/client/components/boards/boardHeader.jade
@@ -89,7 +89,7 @@ template(name="boardHeaderBar")
span {{#if MultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}
if MultiSelection.isActive
a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}")
- i.fa.fa-times-thin
+ i.fa.fa-times-thin
.separator
a.board-header-btn.js-open-board-menu
diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js
index 299bbf34..5dd0b762 100644
--- a/client/components/cards/cardDetails.js
+++ b/client/components/cards/cardDetails.js
@@ -147,12 +147,12 @@ Template.cardDetailsActionsPopup.events({
'click .js-move-card-to-top'(evt) {
evt.preventDefault();
const minOrder = _.min(this.list().cards().map((c) => c.sort));
- this.move(this.listId, minOrder / 2);
+ this.move(this.listId, minOrder - 1);
},
'click .js-move-card-to-bottom'(evt) {
evt.preventDefault();
const maxOrder = _.max(this.list().cards().map((c) => c.sort));
- this.move(this.listId, Math.floor(maxOrder) + 1);
+ this.move(this.listId, maxOrder + 1);
},
'click .js-archive'(evt) {
evt.preventDefault();
diff --git a/client/components/cards/labels.js b/client/components/cards/labels.js
index 20d95bc6..cdd5a700 100644
--- a/client/components/cards/labels.js
+++ b/client/components/cards/labels.js
@@ -9,9 +9,7 @@ BlazeComponent.extendComponent({
},
labels() {
- return labelColors.map((color) => {
- return { color, name: '' };
- });
+ return labelColors.map((color) => ({ color, name: '' }));
},
isSelected(color) {
diff --git a/client/components/import/import.jade b/client/components/import/import.jade
index e10072b4..d4def7d8 100644
--- a/client/components/import/import.jade
+++ b/client/components/import/import.jade
@@ -55,10 +55,10 @@ template(name="importMapMembersAddPopup")
ul.pop-over-list
+esEach(index="users")
li.item.js-member-item
- a.name.js-select-import(title="{{profile.name}} ({{username}})" data-id="{{_id}}")
+ a.name.js-select-import(title="{{profile.fullname}} ({{username}})" data-id="{{_id}}")
+userAvatar(userId=_id esSearch=true)
span.full-name
- = profile.name
+ = profile.fullname
| (<span class="username">{{username}}</span>)
+ifEsIsSearching(index='users')
+spinner
diff --git a/client/components/main/editor.js b/client/components/main/editor.js
index da66bb74..17429067 100755
--- a/client/components/main/editor.js
+++ b/client/components/main/editor.js
@@ -90,15 +90,10 @@ Blaze.Template.registerHelper('mentions', new Template('mentions', function() {
}));
Template.viewer.events({
- 'click .js-open-member'(evt, tpl) {
- const userId = evt.currentTarget.dataset.userid;
- Popup.open('member').call({ userId }, evt, tpl);
- },
-
// Viewer sometimes have click-able wrapper around them (for instance to edit
// the corresponding text). Clicking a link shouldn't fire these actions, stop
// we stop these event at the viewer component level.
- 'click a'(evt) {
+ 'click a'(evt, tpl) {
evt.stopPropagation();
// XXX We hijack the build-in browser action because we currently don't have
@@ -106,9 +101,16 @@ Template.viewer.events({
// handled by a third party package that we can't configure easily. Fix that
// by using directly `_blank` attribute in the rendered HTML.
evt.preventDefault();
- const href = evt.currentTarget.href;
- if (href) {
- window.open(href, '_blank');
+
+ const userId = evt.currentTarget.dataset.userid;
+ if (userId) {
+ Popup.open('member').call({ userId }, evt, tpl);
+ }
+ else {
+ const href = evt.currentTarget.href;
+ if (href) {
+ window.open(href, '_blank');
+ }
}
},
});
diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade
index 893b5164..4f5586cb 100644
--- a/client/components/sidebar/sidebar.jade
+++ b/client/components/sidebar/sidebar.jade
@@ -102,7 +102,7 @@ template(name="addMemberPopup")
ul.pop-over-list
+esEach(index="users")
li.item.js-member-item(class="{{#if isBoardMember}}disabled{{/if}}")
- a.name.js-select-member(title="{{profile.name}} ({{username}})")
+ a.name.js-select-member(title="{{profile.fullname}} ({{username}})")
+userAvatar(userId=_id esSearch=true)
span.full-name
= profile.fullname
diff --git a/client/config/gecko-fix.js b/client/config/gecko-fix.js
new file mode 100644
index 00000000..00e28970
--- /dev/null
+++ b/client/config/gecko-fix.js
@@ -0,0 +1,4 @@
+if (Object.prototype.hasOwnProperty('watch')) {
+ Object.prototype.watch = undefined;
+ Object.prototype.unwatch = undefined;
+}
diff --git a/client/lib/popup.js b/client/lib/popup.js
index 3166ca30..3658d883 100644
--- a/client/lib/popup.js
+++ b/client/lib/popup.js
@@ -75,7 +75,7 @@ window.Popup = new class {
// If there are no popup currently opened we use the Blaze API to render
// one into the DOM. We use a reactive function as the data parameter that
- // return the the complete along with its top element and depends on our
+ // return the complete along with its top element and depends on our
// internal dependency that is being invalidated every time the top
// element of the stack has changed and we want to update the popup.
//
diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index b171bdf9..2047d8e1 100755
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -294,5 +294,8 @@
"watch": "Watch",
"watching": "Watching",
"watching-info": "You will be notified of any change in this board",
+ "welcome-board": "Welcome Board",
+ "welcome-list1": "Basics",
+ "welcome-list2": "Advanced",
"what-to-do": "What do you want to do?"
}
diff --git a/meta/icons/wekan-150.png b/meta/icons/wekan-150.png
index baca8d9d..e8e89c62 100644
--- a/meta/icons/wekan-150.png
+++ b/meta/icons/wekan-150.png
Binary files differ
diff --git a/models/activities.js b/models/activities.js
index ad920149..aa2ea3ec 100644
--- a/models/activities.js
+++ b/models/activities.js
@@ -41,12 +41,14 @@ Activities.before.insert((userId, doc) => {
doc.createdAt = new Date();
});
-// For efficiency create an index on the date of creation.
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
+ // are largely used in the App. See #524.
Meteor.startup(() => {
- Activities._collection._ensureIndex({
- createdAt: -1,
- });
+ Activities._collection._ensureIndex({ createdAt: -1 });
+ Activities._collection._ensureIndex({ cardId: 1, createdAt: -1 });
+ Activities._collection._ensureIndex({ boardId: 1, createdAt: -1 });
});
Activities.after.insert((userId, doc) => {
diff --git a/models/boards.js b/models/boards.js
index 52272cce..715bb838 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -6,25 +6,77 @@ Boards.attachSchema(new SimpleSchema({
},
slug: {
type: String,
+ autoValue() { // eslint-disable-line consistent-return
+ // XXX We need to improve slug management. Only the id should be necessary
+ // to identify a board in the code.
+ // XXX If the board title is updated, the slug should also be updated.
+ // In some cases (Chinese and Japanese for instance) the `getSlug` function
+ // return an empty string. This is causes bugs in our application so we set
+ // a default slug in this case.
+ if (this.isInsert && !this.isSet) {
+ let slug = 'board';
+ const title = this.field('title');
+ if (title.isSet) {
+ slug = getSlug(title.value) || slug;
+ }
+ return slug;
+ }
+ },
},
archived: {
type: Boolean,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ return false;
+ }
+ },
},
createdAt: {
type: Date,
- denyUpdate: true,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert) {
+ return new Date();
+ } else {
+ this.unset();
+ }
+ },
},
// XXX Inconsistent field naming
modifiedAt: {
type: Date,
- denyInsert: true,
optional: true,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isUpdate) {
+ return new Date();
+ } else {
+ this.unset();
+ }
+ },
},
// De-normalized number of users that have starred this board
stars: {
type: Number,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert) {
+ return 0;
+ }
+ },
},
// De-normalized label system
+ 'labels': {
+ type: [Object],
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ const colors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
+ const defaultLabelsColors = _.clone(colors).splice(0, 6);
+ return defaultLabelsColors.map((color) => ({
+ color,
+ _id: Random.id(6),
+ name: '',
+ }));
+ }
+ },
+ },
'labels.$._id': {
// We don't specify that this field must be unique in the board because that
// will cause performance penalties and is not necessary since this field is
@@ -47,6 +99,19 @@ Boards.attachSchema(new SimpleSchema({
// XXX We might want to maintain more informations under the member sub-
// documents like de-normalized meta-data (the date the member joined the
// board, the number of contributions, etc.).
+ 'members': {
+ type: [Object],
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ return [{
+ userId: this.userId,
+ isAdmin: true,
+ isActive: true,
+ isInvited: false,
+ }];
+ }
+ },
+ },
'members.$.userId': {
type: String,
},
@@ -70,6 +135,11 @@ Boards.attachSchema(new SimpleSchema({
'wisteria',
'midnight',
],
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ return Boards.simpleSchema()._schema.color.allowedValues[0];
+ }
+ },
},
description: {
type: String,
@@ -338,41 +408,6 @@ if (Meteor.isServer) {
});
}
-Boards.before.insert((userId, doc) => {
- // XXX We need to improve slug management. Only the id should be necessary
- // to identify a board in the code.
- // XXX If the board title is updated, the slug should also be updated.
- // In some cases (Chinese and Japanese for instance) the `getSlug` function
- // return an empty string. This is causes bugs in our application so we set
- // a default slug in this case.
- doc.slug = doc.slug || getSlug(doc.title) || 'board';
- doc.createdAt = new Date();
- doc.archived = false;
- doc.members = doc.members || [{
- userId,
- isAdmin: true,
- isActive: true,
- }];
- doc.stars = 0;
- doc.color = Boards.simpleSchema()._schema.color.allowedValues[0];
-
- // Handle labels
- const colors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
- const defaultLabelsColors = _.clone(colors).splice(0, 6);
- doc.labels = defaultLabelsColors.map((color) => {
- return {
- color,
- _id: Random.id(6),
- name: '',
- };
- });
-});
-
-Boards.before.update((userId, doc, fieldNames, modifier) => {
- modifier.$set = modifier.$set || {};
- modifier.$set.modifiedAt = new Date();
-});
-
if (Meteor.isServer) {
// Let MongoDB ensure that a member is not included twice in the same board
Meteor.startup(() => {
diff --git a/models/cardComments.js b/models/cardComments.js
index 224deb03..ce6edf3c 100644
--- a/models/cardComments.js
+++ b/models/cardComments.js
@@ -16,10 +16,22 @@ CardComments.attachSchema(new SimpleSchema({
createdAt: {
type: Date,
denyUpdate: false,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert) {
+ return new Date();
+ } else {
+ this.unset();
+ }
+ },
},
// XXX Should probably be called `authorId`
userId: {
type: String,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ return this.userId;
+ }
+ },
},
}));
@@ -44,11 +56,6 @@ CardComments.helpers({
CardComments.hookOptions.after.update = { fetchPrevious: false };
-CardComments.before.insert((userId, doc) => {
- doc.createdAt = new Date();
- doc.userId = userId;
-});
-
if (Meteor.isServer) {
CardComments.after.insert((userId, doc) => {
Activities.insert({
diff --git a/models/cards.js b/models/cards.js
index 09c86191..84fbb6c2 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -9,6 +9,11 @@ Cards.attachSchema(new SimpleSchema({
},
archived: {
type: Boolean,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ return false;
+ }
+ },
},
listId: {
type: String,
@@ -25,10 +30,19 @@ Cards.attachSchema(new SimpleSchema({
},
createdAt: {
type: Date,
- denyUpdate: true,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert) {
+ return new Date();
+ } else {
+ this.unset();
+ }
+ },
},
dateLastActivity: {
type: Date,
+ autoValue() {
+ return new Date();
+ },
},
description: {
type: String,
@@ -46,6 +60,11 @@ Cards.attachSchema(new SimpleSchema({
// the `members` field?
userId: {
type: String,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ return this.userId;
+ }
+ },
},
sort: {
type: Number,
@@ -190,18 +209,13 @@ Cards.mutations({
},
});
-Cards.before.insert((userId, doc) => {
- doc.createdAt = new Date();
- doc.dateLastActivity = new Date();
- if(!doc.hasOwnProperty('archived')){
- doc.archived = false;
- }
- if (!doc.userId) {
- doc.userId = userId;
- }
-});
-
if (Meteor.isServer) {
+ // Cards are often fetched within a board, so we create an index to make these
+ // queries more efficient.
+ Meteor.startup(() => {
+ Cards._collection._ensureIndex({ boardId: 1 });
+ });
+
Cards.after.insert((userId, doc) => {
Activities.insert({
userId,
diff --git a/models/lists.js b/models/lists.js
index 4e4a1134..9ae2e4f7 100644
--- a/models/lists.js
+++ b/models/lists.js
@@ -6,13 +6,24 @@ Lists.attachSchema(new SimpleSchema({
},
archived: {
type: Boolean,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ return false;
+ }
+ },
},
boardId: {
type: String,
},
createdAt: {
type: Date,
- denyUpdate: true,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert) {
+ return new Date();
+ } else {
+ this.unset();
+ }
+ },
},
sort: {
type: Number,
@@ -22,8 +33,14 @@ Lists.attachSchema(new SimpleSchema({
},
updatedAt: {
type: Date,
- denyInsert: true,
optional: true,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isUpdate) {
+ return new Date();
+ } else {
+ this.unset();
+ }
+ },
},
}));
@@ -73,19 +90,11 @@ Lists.mutations({
Lists.hookOptions.after.update = { fetchPrevious: false };
-Lists.before.insert((userId, doc) => {
- doc.createdAt = new Date();
- doc.archived = false;
- if (!doc.userId)
- doc.userId = userId;
-});
-
-Lists.before.update((userId, doc, fieldNames, modifier) => {
- modifier.$set = modifier.$set || {};
- modifier.$set.modifiedAt = new Date();
-});
-
if (Meteor.isServer) {
+ Meteor.startup(() => {
+ Lists._collection._ensureIndex({ boardId: 1 });
+ });
+
Lists.after.insert((userId, doc) => {
Activities.insert({
userId,
diff --git a/models/unsavedEdits.js b/models/unsavedEdits.js
index 87a70e22..25952fb5 100644
--- a/models/unsavedEdits.js
+++ b/models/unsavedEdits.js
@@ -14,6 +14,11 @@ UnsavedEditCollection.attachSchema(new SimpleSchema({
},
userId: {
type: String,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ return this.userId;
+ }
+ },
},
}));
@@ -28,7 +33,3 @@ if (Meteor.isServer) {
fetch: ['userId'],
});
}
-
-UnsavedEditCollection.before.insert((userId, doc) => {
- doc.userId = userId;
-});
diff --git a/models/users.js b/models/users.js
index 4e4a0fac..790ee0a1 100644
--- a/models/users.js
+++ b/models/users.js
@@ -1,5 +1,95 @@
Users = Meteor.users;
+Users.attachSchema(new SimpleSchema({
+ username: {
+ type: String,
+ optional: true,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ const name = this.field('profile.fullname');
+ if (name.isSet) {
+ return name.value.toLowerCase().replace(/\s/g, '');
+ }
+ }
+ },
+ },
+ emails: {
+ type: [Object],
+ optional: true,
+ },
+ 'emails.$.address': {
+ type: String,
+ regEx: SimpleSchema.RegEx.Email,
+ },
+ 'emails.$.verified': {
+ type: Boolean,
+ },
+ createdAt: {
+ type: Date,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert) {
+ return new Date();
+ } else {
+ this.unset();
+ }
+ },
+ },
+ profile: {
+ type: Object,
+ optional: true,
+ autoValue() { // eslint-disable-line consistent-return
+ if (this.isInsert && !this.isSet) {
+ return {};
+ }
+ },
+ },
+ 'profile.avatarUrl': {
+ type: String,
+ optional: true,
+ },
+ 'profile.emailBuffer': {
+ type: [String],
+ optional: true,
+ },
+ 'profile.fullname': {
+ type: String,
+ optional: true,
+ },
+ 'profile.initials': {
+ type: String,
+ optional: true,
+ },
+ 'profile.invitedBoards': {
+ type: [String],
+ optional: true,
+ },
+ 'profile.language': {
+ type: String,
+ optional: true,
+ },
+ 'profile.notifications': {
+ type: [String],
+ optional: true,
+ },
+ 'profile.starredBoards': {
+ type: [String],
+ optional: true,
+ },
+ 'profile.tags': {
+ type: [String],
+ optional: true,
+ },
+ services: {
+ type: Object,
+ optional: true,
+ blackbox: true,
+ },
+ heartbeat: {
+ type: Date,
+ optional: true,
+ },
+}));
+
// Search a user in the complete server database by its name or username. This
// is used for instance to add a new user to a board.
const searchInFields = ['username', 'profile.fullname'];
@@ -259,14 +349,6 @@ if (Meteor.isServer) {
});
}
-Users.before.insert((userId, doc) => {
- doc.profile = doc.profile || {};
-
- if (!doc.username && doc.profile.name) {
- doc.username = doc.profile.name.toLowerCase().replace(/\s/g, '');
- }
-});
-
if (Meteor.isServer) {
// Let mongoDB ensure username unicity
Meteor.startup(() => {
@@ -306,32 +388,29 @@ if (Meteor.isServer) {
incrementBoards(_.difference(newIds, oldIds), +1);
});
- // XXX i18n
+ const fakeUserId = new Meteor.EnvironmentVariable();
+ const getUserId = CollectionHooks.getUserId;
+ CollectionHooks.getUserId = () => {
+ return fakeUserId.get() || getUserId();
+ };
+
Users.after.insert((userId, doc) => {
- const ExampleBoard = {
- title: 'Welcome Board',
- userId: doc._id,
- permission: 'private',
+ const fakeUser = {
+ extendAutoValueContext: {
+ userId: doc._id,
+ },
};
- // Insert the Welcome Board
- Boards.insert(ExampleBoard, (err, boardId) => {
-
- ['Basics', 'Advanced'].forEach((title) => {
- const list = {
- title,
- boardId,
- userId: ExampleBoard.userId,
-
- // XXX Not certain this is a bug, but we except these fields get
- // inserted by the Lists.before.insert collection-hook. Since this
- // hook is not called in this case, we have to dublicate the logic and
- // set them here.
- archived: false,
- createdAt: new Date(),
- };
+ fakeUserId.withValue(doc._id, () => {
+ // Insert the Welcome Board
+ Boards.insert({
+ title: TAPi18n.__('welcome-board'),
+ permission: 'private',
+ }, fakeUser, (err, boardId) => {
- Lists.insert(list);
+ ['welcome-list1', 'welcome-list2'].forEach((title) => {
+ Lists.insert({ title: TAPi18n.__(title), boardId }, fakeUser);
+ });
});
});
});
diff --git a/public/wekan-favicon.png b/public/wekan-favicon.png
index bd66f554..8beb85f4 100644
--- a/public/wekan-favicon.png
+++ b/public/wekan-favicon.png
Binary files differ
diff --git a/public/wekan-logo-header.png b/public/wekan-logo-header.png
index 441b6007..16ffa102 100644
--- a/public/wekan-logo-header.png
+++ b/public/wekan-logo-header.png
Binary files differ
diff --git a/public/wekan-logo.png b/public/wekan-logo.png
index 1d98f176..6a2740f2 100644
--- a/public/wekan-logo.png
+++ b/public/wekan-logo.png
Binary files differ