summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.json8
-rw-r--r--.meteor/.finished-upgraders3
-rw-r--r--.meteor/packages39
-rw-r--r--.meteor/release2
-rw-r--r--.meteor/versions181
-rw-r--r--.travis.yml8
-rw-r--r--Dockerfile30
-rw-r--r--client/components/boards/boardHeader.js22
-rw-r--r--models/boards.js101
-rw-r--r--models/cards.js98
-rw-r--r--models/export.js80
-rw-r--r--models/lists.js58
-rw-r--r--models/users.js108
-rw-r--r--package.json10
-rw-r--r--server/logger.js59
-rw-r--r--server/observableChanges.js100
16 files changed, 660 insertions, 247 deletions
diff --git a/.eslintrc.json b/.eslintrc.json
index 416548eb..0e2fd495 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -51,7 +51,9 @@
"object-shorthand": 2,
"prefer-const": 2,
"prefer-spread": 2,
- "prefer-template": 2
+ "prefer-template": 2,
+ "no-console": 0,
+ "no-unused-vars" : "warn"
},
"globals": {
"Meteor": false,
@@ -122,6 +124,8 @@
"Emoji": true,
"Checklists": true,
"Settings": true,
- "InvitationCodes": true
+ "InvitationCodes": true,
+ "Winston":true
+ "JsonRoutes" : true
}
}
diff --git a/.meteor/.finished-upgraders b/.meteor/.finished-upgraders
index 7bd71978..a8020370 100644
--- a/.meteor/.finished-upgraders
+++ b/.meteor/.finished-upgraders
@@ -12,3 +12,6 @@ notices-for-facebook-graph-api-2
1.2.0-breaking-changes
1.3.0-split-minifiers-package
1.3.5-remove-old-dev-bundle-link
+1.4.0-remove-old-dev-bundle-link
+1.4.1-add-shell-server-package
+1.4.3-split-account-service-packages
diff --git a/.meteor/packages b/.meteor/packages
index 7ede4008..67678ccb 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -3,17 +3,17 @@
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
-meteor-base
+meteor-base@1.0.4
# Build system
-ecmascript
-stylus
-standard-minifier-css
-standard-minifier-js
+ecmascript@0.7.2
+stylus@2.513.9
+standard-minifier-css@1.3.4
+standard-minifier-js@2.0.0
mquandalle:jade
# Polyfills
-es5-shim
+es5-shim@4.6.15
# Collections
aldeed:collection2
@@ -24,30 +24,30 @@ dburles:collection-helpers
idmontie:migrations
matb33:collection-hooks
matteodem:easy-search
-mongo
+mongo@1.1.16
mquandalle:collection-mutations
# Account system
-accounts-password
+accounts-password@1.3.5
kenton:accounts-sandstorm
-service-configuration
+service-configuration@1.0.11
useraccounts:core
useraccounts:unstyled
useraccounts:flow-routing
-email
+email@1.2.0
# Utilities
-check
-jquery
-random
-reactive-dict
-session
-tracker
-underscore
+check@1.2.5
+jquery@1.11.10
+random@1.0.10
+reactive-dict@1.1.8
+session@1.1.7
+tracker@1.1.2
+underscore@1.0.10
3stack:presence
alethes:pages
arillo:flow-router-helpers
-audit-argument-checks
+audit-argument-checks@1.0.7
kadira:blaze-layout
kadira:dochead
meteorhacks:fast-render
@@ -61,7 +61,7 @@ tap:i18n
# UI components
blaze
-reactive-var
+reactive-var@1.0.11
fortawesome:fontawesome
mousetrap:mousetrap
mquandalle:jquery-textcomplete
@@ -76,3 +76,4 @@ verron:autosize
simple:json-routes
rajit:bootstrap3-datepicker
kadira:flow-router
+shell-server@0.2.3
diff --git a/.meteor/release b/.meteor/release
index fcf9d2d7..605b4e1f 100644
--- a/.meteor/release
+++ b/.meteor/release
@@ -1 +1 @@
-METEOR@1.3.5.1
+METEOR@1.4.4.1
diff --git a/.meteor/versions b/.meteor/versions
index 4ca2f780..1a040534 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -1,26 +1,26 @@
3stack:presence@1.0.5
-accounts-base@1.2.8
-accounts-password@1.1.13
-aldeed:collection2@2.9.1
-aldeed:collection2-core@1.1.1
-aldeed:schema-deny@1.0.1
-aldeed:schema-index@1.0.1
+accounts-base@1.2.16
+accounts-password@1.3.5
+aldeed:collection2@2.10.0
+aldeed:collection2-core@1.2.0
+aldeed:schema-deny@1.1.0
+aldeed:schema-index@1.1.1
aldeed:simple-schema@1.5.3
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.5
-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
+autoupdate@1.3.12
+babel-compiler@6.18.2
+babel-runtime@1.0.1
+base64@1.0.10
+binary-heap@1.0.10
+blaze@2.3.2
+blaze-tools@1.0.10
+boilerplate-generator@1.0.11
+caching-compiler@1.1.9
+caching-html-compiler@1.1.2
+callback-hook@1.0.10
cfs:access-point@0.1.49
cfs:base-package@0.0.30
cfs:collection@0.5.5
@@ -38,64 +38,64 @@ 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.3
+check@1.2.5
chuangbo:cookie@1.1.0
-coffeescript@1.1.4
-cottz:publish-relations@2.0.6
-dburles:collection-helpers@1.0.4
+coffeescript@1.12.3_1
+cottz:publish-relations@2.0.7
+dburles:collection-helpers@1.1.0
ddp@1.2.5
-ddp-client@1.2.9
-ddp-common@1.2.6
-ddp-rate-limiter@1.0.5
-ddp-server@1.2.10
+ddp-client@1.3.4
+ddp-common@1.2.8
+ddp-rate-limiter@1.0.7
+ddp-server@1.3.14
deps@1.0.12
-diff-sequence@1.0.6
-ecmascript@0.4.8
-ecmascript-runtime@0.2.12
-ejson@1.0.12
-email@1.0.16
-es5-shim@4.5.13
-fastclick@1.0.12
-fortawesome:fontawesome@4.5.0
-geojson-utils@1.0.9
+diff-sequence@1.0.7
+ecmascript@0.7.3
+ecmascript-runtime@0.3.15
+ejson@1.0.13
+email@1.2.1
+es5-shim@4.6.15
+fastclick@1.0.13
+fortawesome:fontawesome@4.7.0
+geojson-utils@1.0.10
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.9
+html-tools@1.0.11
+htmljs@1.0.11
+http@1.2.12
+id-map@1.0.9
+idmontie:migrations@1.0.3
+jquery@1.11.10
kadira:blaze-layout@2.3.0
kadira:dochead@1.5.0
kadira:flow-router@2.12.1
-kenton:accounts-sandstorm@0.5.1
-launch-screen@1.0.12
+kenton:accounts-sandstorm@0.6.0
+launch-screen@1.1.1
livedata@1.0.18
-localstorage@1.0.11
-logging@1.0.14
-matb33:collection-hooks@0.8.1
+localstorage@1.0.12
+logging@1.1.17
+matb33:collection-hooks@0.8.4
matteodem:easy-search@1.6.4
-mdg:validation-error@0.2.0
-meteor@1.1.16
+mdg:validation-error@0.5.1
+meteor@1.6.1
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.14.0
+meteorhacks:fast-render@2.16.0
meteorhacks:inject-data@2.0.0
meteorhacks:meteorx@1.4.1
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
meteorspark:util@0.2.0
-minifier-css@1.1.13
-minifier-js@1.1.13
+minifier-css@1.2.16
+minifier-js@2.0.0
minifiers@1.1.8-faster-rebuild.0
-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
+minimongo@1.0.21
+mobile-status-bar@1.0.14
+modules@0.8.2
+modules-runtime@0.7.10
+mongo@1.1.16
+mongo-id@1.0.6
mongo-livedata@1.0.12
mousetrap:mousetrap@1.4.6_1
mquandalle:autofocus@1.0.0
@@ -107,52 +107,55 @@ 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.8.6_3
-npm-mongo@1.4.45
-observe-sequence@1.0.12
+npm-bcrypt@0.9.2
+npm-mongo@2.2.24
+observe-sequence@1.0.16
ongoworks:speakingurl@1.1.0
-ordered-dict@1.0.8
+ordered-dict@1.0.9
peerlibrary:assert@0.2.5
-peerlibrary:base-component@0.14.0
+peerlibrary:base-component@0.16.0
peerlibrary:blaze-components@0.15.1
-peerlibrary:computed-field@0.3.1
-peerlibrary:reactive-field@0.1.0
+peerlibrary:computed-field@0.6.1
+peerlibrary:reactive-field@0.3.0
perak:markdown@1.0.5
-promise@0.7.3
+promise@0.8.8
raix:eventemitter@0.1.3
raix:handlebar-helpers@0.2.5
-rajit:bootstrap3-datepicker@1.5.1
+rajit:bootstrap3-datepicker@1.6.4
random@1.0.10
-rate-limit@1.0.5
+rate-limit@1.0.8
reactive-dict@1.1.8
-reactive-var@1.0.10
-reload@1.1.10
-retry@1.0.8
-routepolicy@1.0.11
+reactive-var@1.0.11
+reload@1.1.11
+retry@1.0.9
+routepolicy@1.0.12
seriousm:emoji-continued@1.4.0
-service-configuration@1.0.10
-session@1.1.6
-sha@1.0.8
-simple:json-routes@1.0.4
-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.5
+service-configuration@1.0.11
+session@1.1.7
+sha@1.0.9
+shell-server@0.2.3
+simple:json-routes@2.1.0
+softwarerero:accounts-t9n@1.3.9
+spacebars@1.0.15
+spacebars-compiler@1.1.2
+srp@1.0.10
+standard-minifier-css@1.3.4
+standard-minifier-js@2.0.0
+stylus@2.513.9
tap:i18n@1.8.2
-templates:tabs@2.2.2
-templating@1.1.14
-templating-tools@1.0.4
-tracker@1.0.15
-ui@1.0.11
-underscore@1.0.9
-url@1.0.10
+templates:tabs@2.3.0
+templating@1.3.2
+templating-compiler@1.3.2
+templating-runtime@1.3.2
+templating-tools@1.1.2
+tracker@1.1.2
+ui@1.0.13
+underscore@1.0.10
+url@1.1.0
useraccounts:core@1.14.2
useraccounts:flow-routing@1.14.2
useraccounts:unstyled@1.14.2
verron:autosize@3.0.8
-webapp@1.2.11
+webapp@1.3.15
webapp-hashing@1.0.9
zimme:active-route@2.3.2
diff --git a/.travis.yml b/.travis.yml
index 9a760beb..01d60165 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,13 +10,15 @@ before_install:
- sudo chmod +x docker-compose
- sudo mv docker-compose /usr/local/bin
- sudo docker-compose build --no-cache --force-rm
- - sudo docker-compose up && docker ps -a
- - sudo docker run wekan-app /bin/sh -c "npm test"
+ - sudo docker-compose up -d wekandb
+ - sudo docker-compose up -d wekan
+ # ^^ Note - need to come up with some way of checking the output from docker run
+ # that it was a success... perhaps the nodejs server can output a success message?
language: node_js
node_js:
- - "0.10.48"
+ - "6.10.2"
install:
- "npm install"
diff --git a/Dockerfile b/Dockerfile
index 4d67e5ee..e49b6dfd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,9 +1,10 @@
-FROM debian:wheezy
+FROM debian:8.7
MAINTAINER wekan
# Declare Arguments
ARG NODE_VERSION
ARG METEOR_RELEASE
+ARG METEOR_EDGE
ARG NPM_VERSION
ARG ARCHITECTURE
ARG SRC_PATH
@@ -11,9 +12,10 @@ ARG SRC_PATH
# Set the environment variables (defaults where required)
ENV BUILD_DEPS="wget curl bzip2 build-essential python git ca-certificates"
ENV GOSU_VERSION=1.10
-ENV NODE_VERSION ${NODE_VERSION:-v0.10.48}
-ENV METEOR_RELEASE ${METEOR_RELEASE:-1.3.5.1}
-ENV NPM_VERSION ${NPM_VERSION:-4.2.0}
+ENV NODE_VERSION ${NODE_VERSION:-v6.10.2}
+ENV METEOR_RELEASE ${METEOR_RELEASE:-1.4.5}
+ENV METEOR_EDGE ${METEOR_EDGE:-1.4.4-rc.6}
+ENV NPM_VERSION ${NPM_VERSION:-3.10.10}
ENV ARCHITECTURE ${ARCHITECTURE:-linux-x64}
ENV SRC_PATH ${SRC_PATH:-./}
@@ -63,7 +65,7 @@ RUN \
ln -s /opt/nodejs/bin/npm /usr/bin/npm && \
\
# Install Node dependencies
- npm install npm@${NPM_VERSION} -g && \
+ npm install -g npm@${NPM_VERSION} && \
npm install -g node-gyp && \
npm install -g fibers && \
\
@@ -74,11 +76,23 @@ RUN \
sed -i "s|RELEASE=.*|RELEASE=${METEOR_RELEASE}\"\"|g" ./install_meteor.sh && \
echo "Starting meteor ${METEOR_RELEASE} installation... \n" && \
chown wekan:wekan ./install_meteor.sh && \
- gosu wekan:wekan sh ./install_meteor.sh && \
+ ###########################
+ ###########################
+ # Block for ensuring installation of release candidates - perhaps remove later.
+ gosu wekan:wekan sh ./install_meteor.sh || \
+ ( \
+ gosu wekan:wekan git clone --recursive git://github.com/meteor/meteor.git /home/wekan/.meteor && \
+ cd /home/wekan/.meteor && \
+ gosu wekan:wekan git checkout release/METEOR@${METEOR_EDGE} && \
+ gosu wekan /home/wekan/.meteor/meteor -- help \
+ ) && \
+ ###########################
+ ###########################
\
# Build app
cd /home/wekan/app && \
- gosu wekan /home/wekan/.meteor/meteor npm install --save xss && \
+ gosu wekan /home/wekan/.meteor/meteor add standard-minifier-js && \
+ gosu wekan /home/wekan/.meteor/meteor npm install && \
gosu wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build && \
cd /home/wekan/app_build/bundle/programs/server/ && \
gosu wekan npm install && \
@@ -96,4 +110,4 @@ RUN \
ENV PORT=80
EXPOSE $PORT
-CMD ["node", "/build/main.js"]
+CMD ["node", "/build/main.js"] \ No newline at end of file
diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js
index 44532c3e..10d9925a 100644
--- a/client/components/boards/boardHeader.js
+++ b/client/components/boards/boardHeader.js
@@ -15,17 +15,17 @@ Template.boardMenuPopup.events({
}),
});
-Template.boardMenuPopup.helpers({
- exportUrl() {
- const boardId = Session.get('currentBoard');
- const loginToken = Accounts._storedLoginToken();
- return FlowRouter.url(`api/boards/${boardId}?authToken=${loginToken}`);
- },
- exportFilename() {
- const boardId = Session.get('currentBoard');
- return `wekan-export-board-${boardId}.json`;
- },
-});
+// Template.boardMenuPopup.helpers({
+// exportUrl() {
+// const boardId = Session.get('currentBoard');
+// const loginToken = Accounts._storedLoginToken();
+// return FlowRouter.url(`api/boards/${boardId}?authToken=${loginToken}`);
+// },
+// exportFilename() {
+// const boardId = Session.get('currentBoard');
+// return `wekan-export-board-${boardId}.json`;
+// },
+// });
Template.boardChangeTitlePopup.events({
submit(evt, tpl) {
diff --git a/models/boards.js b/models/boards.js
index f4296a6c..9cbb5b63 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -156,7 +156,7 @@ Boards.helpers({
* Is supplied user authorized to view this board?
*/
isVisibleBy(user) {
- if(this.isPublic()) {
+ if (this.isPublic()) {
// public boards are visible to everyone
return true;
} else {
@@ -172,7 +172,7 @@ Boards.helpers({
* @returns {boolean} the member that matches, or undefined/false
*/
isActiveMember(userId) {
- if(userId) {
+ if (userId) {
return this.members.find((member) => (member.userId === userId && member.isActive));
} else {
return false;
@@ -184,23 +184,23 @@ Boards.helpers({
},
lists() {
- return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 }});
+ return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } });
},
activities() {
- return Activities.find({ boardId: this._id }, { sort: { createdAt: -1 }});
+ return Activities.find({ boardId: this._id }, { sort: { createdAt: -1 } });
},
activeMembers() {
- return _.where(this.members, {isActive: true});
+ return _.where(this.members, { isActive: true });
},
activeAdmins() {
- return _.where(this.members, {isActive: true, isAdmin: true});
+ return _.where(this.members, { isActive: true, isAdmin: true });
},
memberUsers() {
- return Users.find({ _id: {$in: _.pluck(this.members, 'userId')} });
+ return Users.find({ _id: { $in: _.pluck(this.members, 'userId') } });
},
getLabel(name, color) {
@@ -216,15 +216,15 @@ Boards.helpers({
},
hasMember(memberId) {
- return !!_.findWhere(this.members, {userId: memberId, isActive: true});
+ return !!_.findWhere(this.members, { userId: memberId, isActive: true });
},
hasAdmin(memberId) {
- return !!_.findWhere(this.members, {userId: memberId, isActive: true, isAdmin: true});
+ return !!_.findWhere(this.members, { userId: memberId, isActive: true, isAdmin: true });
},
hasCommentOnly(memberId) {
- return !!_.findWhere(this.members, {userId: memberId, isActive: true, isAdmin: false, isCommentOnly: true});
+ return !!_.findWhere(this.members, { userId: memberId, isActive: true, isAdmin: false, isCommentOnly: true });
},
absoluteUrl() {
@@ -239,34 +239,34 @@ Boards.helpers({
// XXX waiting on https://github.com/mquandalle/meteor-collection-mutations/issues/1 to remove...
pushLabel(name, color) {
const _id = Random.id(6);
- Boards.direct.update(this._id, { $push: {labels: { _id, name, color }}});
+ Boards.direct.update(this._id, { $push: { labels: { _id, name, color } } });
return _id;
},
});
Boards.mutations({
archive() {
- return { $set: { archived: true }};
+ return { $set: { archived: true } };
},
restore() {
- return { $set: { archived: false }};
+ return { $set: { archived: false } };
},
rename(title) {
- return { $set: { title }};
+ return { $set: { title } };
},
setDescription(description) {
- return { $set: {description} };
+ return { $set: { description } };
},
setColor(color) {
- return { $set: { color }};
+ return { $set: { color } };
},
setVisibility(visibility) {
- return { $set: { permission: visibility }};
+ return { $set: { permission: visibility } };
},
addLabel(name, color) {
@@ -276,7 +276,7 @@ Boards.mutations({
// user).
if (!this.getLabel(name, color)) {
const _id = Random.id(6);
- return { $push: {labels: { _id, name, color }}};
+ return { $push: { labels: { _id, name, color } } };
}
return {};
},
@@ -295,7 +295,7 @@ Boards.mutations({
},
removeLabel(labelId) {
- return { $pull: { labels: { _id: labelId }}};
+ return { $pull: { labels: { _id: labelId } } };
},
addMember(memberId) {
@@ -386,7 +386,7 @@ if (Meteor.isServer) {
return false;
// If there is more than one admin, it's ok to remove anyone
- const nbAdmins = _.where(doc.members, {isActive: true, isAdmin: true}).length;
+ const nbAdmins = _.where(doc.members, { isActive: true, isAdmin: true }).length;
if (nbAdmins > 1)
return false;
@@ -408,7 +408,7 @@ if (Meteor.isServer) {
if (board) {
const userId = Meteor.userId();
const index = board.memberIndex(userId);
- if (index>=0) {
+ if (index >= 0) {
board.removeMember(userId);
return true;
} else throw new Meteor.Error('error-board-notAMember');
@@ -424,7 +424,7 @@ if (Meteor.isServer) {
_id: 1,
'members.userId': 1,
}, { unique: true });
- Boards._collection._ensureIndex({'members.userId': 1});
+ Boards._collection._ensureIndex({ 'members.userId': 1 });
});
// Genesis: the first activity of the newly created board
@@ -553,3 +553,60 @@ if (Meteor.isServer) {
}
});
}
+
+//BOARDS REST API
+if (Meteor.isServer) {
+ JsonRoutes.add('GET', '/api/boards', function (req, res, next) {
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: Boards.find({ permission: 'public' }).map(function (doc) {
+ return {
+ _id: doc._id,
+ title: doc.title,
+ };
+ }),
+ });
+ });
+
+ JsonRoutes.add('GET', '/api/boards/:id', function (req, res, next) {
+ const id = req.params.id;
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: Boards.findOne({ _id: id }),
+ });
+ });
+
+ JsonRoutes.add('POST', '/api/boards', function (req, res, next) {
+ const id = Boards.insert({
+ title: req.body.title,
+ members: [
+ {
+ userId: req.body.owner,
+ isAdmin: true,
+ isActive: true,
+ isCommentOnly: false,
+ },
+ ],
+ permission: 'public',
+ color: 'belize',
+ });
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: {
+ _id: id,
+ },
+ });
+ });
+
+ JsonRoutes.add('DELETE', '/api/boards/:id', function (req, res, next) {
+ const id = req.params.id;
+ Boards.remove({ _id: id });
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data:{
+ _id: id,
+ },
+ });
+ });
+
+}
diff --git a/models/cards.js b/models/cards.js
index 922fbecd..2d585825 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -123,15 +123,15 @@ Cards.helpers({
},
activities() {
- return Activities.find({ cardId: this._id }, { sort: { createdAt: -1 }});
+ return Activities.find({ cardId: this._id }, { sort: { createdAt: -1 } });
},
comments() {
- return CardComments.find({ cardId: this._id }, { sort: { createdAt: -1 }});
+ return CardComments.find({ cardId: this._id }, { sort: { createdAt: -1 } });
},
attachments() {
- return Attachments.find({ cardId: this._id }, { sort: { uploadedAt: -1 }});
+ return Attachments.find({ cardId: this._id }, { sort: { uploadedAt: -1 } });
},
cover() {
@@ -142,7 +142,7 @@ Cards.helpers({
},
checklists() {
- return Checklists.find({ cardId: this._id }, { sort: { createdAt: 1 }});
+ return Checklists.find({ cardId: this._id }, { sort: { createdAt: 1 } });
},
checklistItemCount() {
@@ -183,19 +183,19 @@ Cards.helpers({
Cards.mutations({
archive() {
- return { $set: { archived: true }};
+ return { $set: { archived: true } };
},
restore() {
- return { $set: { archived: false }};
+ return { $set: { archived: false } };
},
setTitle(title) {
- return { $set: { title }};
+ return { $set: { title } };
},
setDescription(description) {
- return { $set: { description }};
+ return { $set: { description } };
},
move(listId, sortIndex) {
@@ -207,11 +207,11 @@ Cards.mutations({
},
addLabel(labelId) {
- return { $addToSet: { labelIds: labelId }};
+ return { $addToSet: { labelIds: labelId } };
},
removeLabel(labelId) {
- return { $pull: { labelIds: labelId }};
+ return { $pull: { labelIds: labelId } };
},
toggleLabel(labelId) {
@@ -223,11 +223,11 @@ Cards.mutations({
},
assignMember(memberId) {
- return { $addToSet: { members: memberId }};
+ return { $addToSet: { members: memberId } };
},
unassignMember(memberId) {
- return { $pull: { members: memberId }};
+ return { $pull: { members: memberId } };
},
toggleMember(memberId) {
@@ -239,27 +239,27 @@ Cards.mutations({
},
setCover(coverId) {
- return { $set: { coverId }};
+ return { $set: { coverId } };
},
unsetCover() {
- return { $unset: { coverId: '' }};
+ return { $unset: { coverId: '' } };
},
setStart(startAt) {
- return { $set: { startAt }};
+ return { $set: { startAt } };
},
unsetStart() {
- return { $unset: { startAt: '' }};
+ return { $unset: { startAt: '' } };
},
setDue(dueAt) {
- return { $set: { dueAt }};
+ return { $set: { dueAt } };
},
unsetDue() {
- return { $unset: { dueAt: '' }};
+ return { $unset: { dueAt: '' } };
},
});
@@ -304,7 +304,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;
if (_.contains(fieldNames, 'listId') && doc.listId !== oldListId) {
Activities.insert({
@@ -370,3 +370,63 @@ if (Meteor.isServer) {
});
});
}
+//LISTS REST API
+if (Meteor.isServer) {
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', function (req, res, next) {
+ const paramBoardId = req.params.boardId;
+ const paramListId = req.params.listId;
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: Cards.find({ boardId: paramBoardId, listId: paramListId, archived: false }).map(function (doc) {
+ return {
+ _id: doc._id,
+ title: doc.title,
+ description: doc.description,
+ };
+ }),
+ });
+ });
+
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res, next) {
+ const paramBoardId = req.params.boardId;
+ const paramListId = req.params.listId;
+ const paramCardId = req.params.cardId;
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: Cards.findOne({ _id: paramCardId, listId: paramListId, boardId: paramBoardId, archived: false }),
+ });
+ });
+
+ JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function (req, res, next) {
+ const paramBoardId = req.params.boardId;
+ const paramListId = req.params.listId;
+ const id = Cards.insert({
+ title: req.body.title,
+ boardId: paramBoardId,
+ listId: paramListId,
+ description: req.body.description,
+ userId : req.body.authorId,
+ sort: 0,
+ members:[ req.body.authorId ],
+ });
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: {
+ _id: id,
+ },
+ });
+ });
+
+ JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId/cards/:cardId', function (req, res, next) {
+ const paramBoardId = req.params.boardId;
+ const paramListId = req.params.listId;
+ const paramCardId = req.params.cardId;
+ Cards.remove({ _id: paramCardId, listId: paramListId, boardId: paramBoardId });
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: {
+ _id: paramCardId,
+ },
+ });
+ });
+}
diff --git a/models/export.js b/models/export.js
index b774cf15..7a363dd3 100644
--- a/models/export.js
+++ b/models/export.js
@@ -1,5 +1,5 @@
/* global JsonRoutes */
-if(Meteor.isServer) {
+if (Meteor.isServer) {
// todo XXX once we have a real API in place, move that route there
// todo XXX also share the route definition between the client and the server
// so that we could use something like
@@ -14,28 +14,28 @@ if(Meteor.isServer) {
* See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
* for detailed explanations
*/
- JsonRoutes.add('get', '/api/boards/:boardId', function (req, res) {
- const boardId = req.params.boardId;
- let user = null;
- // todo XXX for real API, first look for token in Authentication: header
- // then fallback to parameter
- const loginToken = req.query.authToken;
- if (loginToken) {
- const hashToken = Accounts._hashLoginToken(loginToken);
- user = Meteor.users.findOne({
- 'services.resume.loginTokens.hashedToken': hashToken,
- });
- }
+ // JsonRoutes.add('get', '/api/boards/:boardId', function (req, res) {
+ // const boardId = req.params.boardId;
+ // let user = null;
+ // // todo XXX for real API, first look for token in Authentication: header
+ // // then fallback to parameter
+ // const loginToken = req.query.authToken;
+ // if (loginToken) {
+ // const hashToken = Accounts._hashLoginToken(loginToken);
+ // user = Meteor.users.findOne({
+ // 'services.resume.loginTokens.hashedToken': hashToken,
+ // });
+ // }
- const exporter = new Exporter(boardId);
- if(exporter.canExport(user)) {
- JsonRoutes.sendResult(res, 200, exporter.build());
- } else {
- // we could send an explicit error message, but on the other hand the only
- // way to get there is by hacking the UI so let's keep it raw.
- JsonRoutes.sendResult(res, 403);
- }
- });
+ // const exporter = new Exporter(boardId);
+ // if(exporter.canExport(user)) {
+ // JsonRoutes.sendResult(res, 200, exporter.build());
+ // } else {
+ // // we could send an explicit error message, but on the other hand the only
+ // // way to get there is by hacking the UI so let's keep it raw.
+ // JsonRoutes.sendResult(res, 403);
+ // }
+ // });
}
class Exporter {
@@ -44,13 +44,13 @@ class Exporter {
}
build() {
- const byBoard = {boardId: this._boardId};
+ const byBoard = { boardId: this._boardId };
// 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(byBoard, noBoardId).fetch();
result.comments = CardComments.find(byBoard, noBoardId).fetch();
@@ -69,29 +69,31 @@ 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;});
- const byUserIds = {_id: {$in: Object.getOwnPropertyNames(users)}};
+ result.comments.forEach((comment) => { users[comment.userId] = true; });
+ result.activities.forEach((activity) => { users[activity.userId] = true; });
+ const byUserIds = { _id: { $in: Object.getOwnPropertyNames(users) } };
// we use whitelist to be sure we do not expose inadvertently
// some secret fields that gets added to User later.
- const userFields = {fields: {
- _id: 1,
- username: 1,
- 'profile.fullname': 1,
- 'profile.initials': 1,
- 'profile.avatarUrl': 1,
- }};
+ const userFields = {
+ fields: {
+ _id: 1,
+ username: 1,
+ 'profile.fullname': 1,
+ 'profile.initials': 1,
+ 'profile.avatarUrl': 1,
+ },
+ };
result.users = Users.find(byUserIds, userFields).fetch().map((user) => {
// user avatar is stored as a relative url, we export absolute
- if(user.profile.avatarUrl) {
+ if (user.profile.avatarUrl) {
user.profile.avatarUrl = FlowRouter.url(user.profile.avatarUrl);
}
return user;
diff --git a/models/lists.js b/models/lists.js
index 0ae3ca5f..a10e23b6 100644
--- a/models/lists.js
+++ b/models/lists.js
@@ -76,15 +76,15 @@ Lists.helpers({
Lists.mutations({
rename(title) {
- return { $set: { title }};
+ return { $set: { title } };
},
archive() {
- return { $set: { archived: true }};
+ return { $set: { archived: true } };
},
restore() {
- return { $set: { archived: false }};
+ return { $set: { archived: false } };
},
});
@@ -128,3 +128,55 @@ if (Meteor.isServer) {
}
});
}
+
+//LISTS REST API
+if (Meteor.isServer) {
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists', function (req, res, next) {
+ const paramBoardId = req.params.boardId;
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: Lists.find({ boardId: paramBoardId, archived: false }).map(function (doc) {
+ return {
+ _id: doc._id,
+ title: doc.title,
+ };
+ }),
+ });
+ });
+
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId', function (req, res, next) {
+ const paramBoardId = req.params.boardId;
+ const paramListId = req.params.listId;
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: Lists.findOne({ _id: paramListId, boardId: paramBoardId, archived: false }),
+ });
+ });
+
+ JsonRoutes.add('POST', '/api/boards/:boardId/lists', function (req, res, next) {
+ const paramBoardId = req.params.boardId;
+ const id = Lists.insert({
+ title: req.body.title,
+ boardId: paramBoardId,
+ });
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: {
+ _id: id,
+ },
+ });
+ });
+
+ JsonRoutes.add('DELETE', '/api/boards/:boardId/lists/:listId', function (req, res, next) {
+ const paramBoardId = req.params.boardId;
+ const paramListId = req.params.listId;
+ Lists.remove({ _id: paramListId, boardId: paramBoardId });
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: {
+ _id: paramListId,
+ },
+ });
+ });
+
+}
diff --git a/models/users.js b/models/users.js
index 236feb45..c1ce146a 100644
--- a/models/users.js
+++ b/models/users.js
@@ -1,7 +1,7 @@
// Sandstorm context is detected using the METEOR_SETTINGS environment variable
// in the package definition.
const isSandstorm = Meteor.settings && Meteor.settings.public &&
- Meteor.settings.public.sandstorm;
+ Meteor.settings.public.sandstorm;
Users = Meteor.users;
Users.attachSchema(new SimpleSchema({
@@ -106,8 +106,8 @@ Users.attachSchema(new SimpleSchema({
},
isAdmin: {
type: Boolean,
- optional: true
- }
+ optional: true,
+ },
}));
// Search a user in the complete server database by its name or username. This
@@ -148,32 +148,32 @@ Users.helpers({
},
starredBoards() {
- const {starredBoards = []} = this.profile;
- return Boards.find({archived: false, _id: {$in: starredBoards}});
+ const { starredBoards = [] } = this.profile;
+ return Boards.find({ archived: false, _id: { $in: starredBoards } });
},
hasStarred(boardId) {
- const {starredBoards = []} = this.profile;
+ const { starredBoards = [] } = this.profile;
return _.contains(starredBoards, boardId);
},
invitedBoards() {
- const {invitedBoards = []} = this.profile;
- return Boards.find({archived: false, _id: {$in: invitedBoards}});
+ const { invitedBoards = [] } = this.profile;
+ return Boards.find({ archived: false, _id: { $in: invitedBoards } });
},
isInvitedTo(boardId) {
- const {invitedBoards = []} = this.profile;
+ const { invitedBoards = [] } = this.profile;
return _.contains(invitedBoards, boardId);
},
hasTag(tag) {
- const {tags = []} = this.profile;
+ const { tags = [] } = this.profile;
return _.contains(tags, tag);
},
hasNotification(activityId) {
- const {notifications = []} = this.profile;
+ const { notifications = [] } = this.profile;
return _.contains(notifications, activityId);
},
@@ -183,7 +183,7 @@ Users.helpers({
},
getEmailBuffer() {
- const {emailBuffer = []} = this.profile;
+ const { emailBuffer = [] } = this.profile;
return emailBuffer;
},
@@ -308,7 +308,7 @@ Users.mutations({
},
setAvatarUrl(avatarUrl) {
- return { $set: { 'profile.avatarUrl': avatarUrl }};
+ return { $set: { 'profile.avatarUrl': avatarUrl } };
},
setShowCardsCountAt(limit) {
@@ -323,7 +323,7 @@ Meteor.methods({
if (nUsersWithUsername > 0) {
throw new Meteor.Error('username-already-taken');
} else {
- Users.update(this.userId, {$set: { username }});
+ Users.update(this.userId, { $set: { username } });
}
},
toggleSystemMessages() {
@@ -346,19 +346,19 @@ if (Meteor.isServer) {
const inviter = Meteor.user();
const board = Boards.findOne(boardId);
const allowInvite = inviter &&
- board &&
- board.members &&
- _.contains(_.pluck(board.members, 'userId'), inviter._id) &&
- _.where(board.members, {userId: inviter._id})[0].isActive &&
- _.where(board.members, {userId: inviter._id})[0].isAdmin;
+ board &&
+ board.members &&
+ _.contains(_.pluck(board.members, 'userId'), inviter._id) &&
+ _.where(board.members, { userId: inviter._id })[0].isActive &&
+ _.where(board.members, { userId: inviter._id })[0].isAdmin;
if (!allowInvite) throw new Meteor.Error('error-board-notAMember');
this.unblock();
const posAt = username.indexOf('@');
let user = null;
- if (posAt>=0) {
- user = Users.findOne({emails: {$elemMatch: {address: username}}});
+ if (posAt >= 0) {
+ user = Users.findOne({ emails: { $elemMatch: { address: username } } });
} else {
user = Users.findOne(username) || Users.findOne({ username });
}
@@ -409,7 +409,7 @@ if (Meteor.isServer) {
});
Accounts.onCreateUser((options, user) => {
const userCount = Users.find().count();
- if (!isSandstorm && userCount === 0 ){
+ if (!isSandstorm && userCount === 0) {
user.isAdmin = true;
return user;
}
@@ -421,11 +421,11 @@ if (Meteor.isServer) {
if (!options || !options.profile) {
throw new Meteor.Error('error-invitation-code-blank', 'The invitation code is required');
}
- const invitationCode = InvitationCodes.findOne({code: options.profile.invitationcode, email: options.email, valid: true});
+ const invitationCode = InvitationCodes.findOne({ code: options.profile.invitationcode, email: options.email, valid: true });
if (!invitationCode) {
throw new Meteor.Error('error-invitation-code-not-exist', 'The invitation code doesn\'t exist');
- }else{
- user.profile = {icode: options.profile.invitationcode};
+ } else {
+ user.profile = { icode: options.profile.invitationcode };
}
return user;
@@ -445,7 +445,7 @@ if (Meteor.isServer) {
// counter.
// We need to run this code on the server only, otherwise the incrementation
// will be done twice.
- Users.after.update(function(userId, user, fieldNames) {
+ Users.after.update(function (userId, user, fieldNames) {
// The `starredBoards` list is hosted on the `profile` field. If this
// field hasn't been modificated we don't need to run this hook.
if (!_.contains(fieldNames, 'profile'))
@@ -464,7 +464,7 @@ if (Meteor.isServer) {
// direction and then in the other.
function incrementBoards(boardsIds, inc) {
boardsIds.forEach((boardId) => {
- Boards.update(boardId, {$inc: {stars: inc}});
+ Boards.update(boardId, { $inc: { stars: inc } });
});
}
incrementBoards(_.difference(oldIds, newIds), -1);
@@ -505,10 +505,10 @@ if (Meteor.isServer) {
//invite user to corresponding boards
const disableRegistration = Settings.findOne().disableRegistration;
if (disableRegistration) {
- const invitationCode = InvitationCodes.findOne({code: doc.profile.icode, valid:true});
+ const invitationCode = InvitationCodes.findOne({ code: doc.profile.icode, valid: true });
if (!invitationCode) {
throw new Meteor.Error('error-invitation-code-not-exist');
- }else{
+ } else {
invitationCode.boardsToBeInvited.forEach((boardId) => {
const board = Boards.findOne(boardId);
board.addMember(doc._id);
@@ -517,9 +517,55 @@ if (Meteor.isServer) {
doc.profile = {};
}
doc.profile.invitedBoards = invitationCode.boardsToBeInvited;
- Users.update(doc._id, {$set:{profile: doc.profile}});
- InvitationCodes.update(invitationCode._id, {$set: {valid:false}});
+ Users.update(doc._id, { $set: { profile: doc.profile } });
+ InvitationCodes.update(invitationCode._id, { $set: { valid: false } });
}
}
});
}
+
+
+// USERS REST API
+if (Meteor.isServer) {
+ JsonRoutes.add('GET', '/api/users', function (req, res, next) {
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: Meteor.users.find({}).map(function (doc) {
+ return { _id: doc._id, username: doc.username };
+ }),
+ });
+ });
+ JsonRoutes.add('GET', '/api/users/:id', function (req, res, next) {
+ const id = req.params.id;
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: Meteor.users.findOne({ _id: id }),
+ });
+ });
+ JsonRoutes.add('POST', '/api/users/', function (req, res, next) {
+ const id = Accounts.createUser({
+ username: req.body.username,
+ email: req.body.email,
+ password: 'default',
+ });
+
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: {
+ _id: id,
+ },
+ });
+ });
+
+ JsonRoutes.add('DELETE', '/api/users/:id', function (req, res, next) {
+ const id = req.params.id;
+ Meteor.users.remove({ _id: id });
+ JsonRoutes.sendResult(res, {
+ code: 200,
+ data: {
+ _id: id,
+ },
+ });
+ });
+}
+
diff --git a/package.json b/package.json
index a3b44826..d591a213 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,9 @@
"lint": "eslint --ignore-pattern 'packages/*' .",
"test": "npm run --silent lint"
},
+ "eslintConfig": {
+ "extends": "@meteorjs/eslint-config-meteor"
+ },
"repository": {
"type": "git",
"url": "git+https://github.com/wekan/wekan.git"
@@ -20,6 +23,13 @@
"eslint": "^2.0.0"
},
"dependencies": {
+ "babel-runtime": "^6.23.0",
+ "bcrypt": "^1.0.2",
+ "bson": "^1.0.4",
+ "es6-promise": "^4.1.0",
+ "meteor-node-stubs": "^0.2.6",
+ "winston": "^2.3.1",
+ "winston-zulip": "0.0.6",
"xss": "^0.3.3"
}
}
diff --git a/server/logger.js b/server/logger.js
new file mode 100644
index 00000000..376e30aa
--- /dev/null
+++ b/server/logger.js
@@ -0,0 +1,59 @@
+Meteor.startup(() => {
+ Winston = require('winston');
+ require('winston-zulip');
+ const fs = require('fs');
+
+ //remove default logger
+ Winston.remove(Winston.transports.Console);
+
+
+ const loggerEnable = process.env.LOGGER_ENABLE || false;
+ console.log('here1');
+ console.log(loggerEnable);
+ if (loggerEnable) {
+ console.log('here2');
+ const loggers = process.env.LOGGERS.split(',') || 'console';
+
+ if (loggers.includes('console')) {
+ Winston.add(Winston.transports.Console, {
+ json: true,
+ timestamp: true,
+ });
+ }
+
+ if (loggers.includes('file')) {
+ //create logs directory
+ fs.mkdir('logs', (err) => {
+ if (err) throw err;
+ });
+
+ const fileName = `logs/${process.env.LOGGER_FILE_NAME}` || 'logs/server.log';
+
+ Winston.add(Winston.transports.File, {
+ filename: fileName,
+ json: true,
+ options: {
+ flags: 'a+',
+ },
+ });
+ }
+
+ if (loggers.includes('zulip')) {
+ const loggerZulipUsername = process.env.LOGGER_ZULIP_USERNAME;
+ const loggerZulipApikey = process.env.LOGGER_ZULIP_APIKEY;
+ const loggerZulipRealm = process.env.LOGGER_ZULIP_REALM;
+ const loggerZulipTo = process.env.LOGGER_ZULIP_TO || 'logs';
+ const loggerZulipSubject = process.env.LOGGER_ZULIP_SUBJECT || 'wekan';
+
+ Winston.add(Winston.transports.Zulip, {
+ zulipUsername: loggerZulipUsername,
+ zulipApikey: loggerZulipApikey,
+ zulipRealm: loggerZulipRealm,
+ zulipTo: loggerZulipTo,
+ zulipSubject: loggerZulipSubject,
+ });
+ }
+
+ }
+});
+
diff --git a/server/observableChanges.js b/server/observableChanges.js
new file mode 100644
index 00000000..390ae093
--- /dev/null
+++ b/server/observableChanges.js
@@ -0,0 +1,100 @@
+class Message {
+ constructor(userId, type, method, doc, selector, fieldNames, modifier) {
+ this.userId = userId;
+ this.type = type;
+ this.method = method;
+ this.doc = doc;
+ this.selector;
+ this.fieldNames = fieldNames;
+ this.modifier = modifier;
+ }
+
+}
+
+//------------- CARDS --------------------
+Cards.before.update(function (userId, doc, fieldNames, modifier, options) {
+ Winston.log('info', new Message(userId, 'card', 'update', doc, null, fieldNames, modifier));
+});
+
+Cards.before.remove(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'card', 'remove', doc));
+});
+
+Cards.before.insert(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'card', 'insert', doc));
+});
+
+Cards.before.upsert(function (userId, selector, modifier, options) {
+ Winston.log('info', new Message(userId, 'card', 'update', null, selector, null, modifier));
+});
+
+
+//------------- BOARDS --------------------
+Boards.before.update(function (userId, doc, fieldNames, modifier, options) {
+ Winston.log('info', new Message(userId, 'board', 'update', doc, null, fieldNames, modifier));
+});
+
+Boards.before.remove(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'board', 'remove', doc));
+});
+
+Boards.before.insert(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'board', 'insert', doc));
+});
+
+Boards.before.upsert(function (userId, selector, modifier, options) {
+ Winston.log('info', new Message(userId, 'board', 'update', null, selector, null, modifier));
+});
+
+//------------- LISTS --------------------
+Lists.before.update(function (userId, doc, fieldNames, modifier, options) {
+ Winston.log('info', new Message(userId, 'list', 'update', doc, null, fieldNames, modifier));
+});
+
+Lists.before.remove(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'list', 'remove', doc));
+});
+
+Lists.before.insert(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'list', 'insert', doc));
+});
+
+Lists.before.upsert(function (userId, selector, modifier, options) {
+ Winston.log('info', new Message(userId, 'list', 'update', null, selector, null, modifier));
+});
+
+
+//------------- CARD COMMENTS --------------------
+CardComments.before.update(function (userId, doc, fieldNames, modifier, options) {
+ Winston.log('info', new Message(userId, 'card-comments', 'update', doc, null, fieldNames, modifier));
+});
+
+CardComments.before.remove(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'card-comments', 'remove', doc));
+});
+
+CardComments.before.insert(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'card-comments', 'insert', doc));
+});
+
+CardComments.before.upsert(function (userId, selector, modifier, options) {
+ Winston.log('info', new Message(userId, 'card-comments', 'update', null, selector, null, modifier));
+});
+
+
+//------------- USERS --------------------
+Users.before.update(function (userId, doc, fieldNames, modifier, options) {
+ Winston.log('info', new Message(userId, 'user', 'update', doc, null, fieldNames, modifier));
+});
+
+Users.before.remove(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'user', 'remove', doc));
+});
+
+Users.before.insert(function (userId, doc) {
+ Winston.log('info', new Message(userId, 'user', 'insert', doc));
+});
+
+Users.before.upsert(function (userId, selector, modifier, options) {
+ Winston.log('info', new Message(userId, 'user', 'update', null, selector, null, modifier));
+});