summaryrefslogtreecommitdiffstats
path: root/server/notifications
diff options
context:
space:
mode:
authorLiming Xie <liming.xie@gmail.com>2016-01-05 23:26:02 +0800
committerLiming Xie <liming.xie@gmail.com>2016-01-05 23:26:02 +0800
commit9bbdacc79a89667e0d6f1ed30c415e5350ad468b (patch)
treefc6d9918dcd77699295ecb5bdbaf59f9d7c2f479 /server/notifications
parent9ef8ebaf09e52d7133ebe08ab1354ef663ee948b (diff)
downloadwekan-9bbdacc79a89667e0d6f1ed30c415e5350ad468b.tar.gz
wekan-9bbdacc79a89667e0d6f1ed30c415e5350ad468b.tar.bz2
wekan-9bbdacc79a89667e0d6f1ed30c415e5350ad468b.zip
Add notification, allow watch boards / lists / cards
Diffstat (limited to 'server/notifications')
-rw-r--r--server/notifications/email.js35
-rw-r--r--server/notifications/notifications.js48
-rw-r--r--server/notifications/profile.js9
-rw-r--r--server/notifications/watch.js36
4 files changed, 128 insertions, 0 deletions
diff --git a/server/notifications/email.js b/server/notifications/email.js
new file mode 100644
index 00000000..40968329
--- /dev/null
+++ b/server/notifications/email.js
@@ -0,0 +1,35 @@
+// cache the email text in a queue, and send them in a batch
+Meteor.startup(() => {
+ Notifications.subscribe('cachedEmail', (user, title, description, params) => {
+ // add quote to make titles easier to read in email text
+ const quoteParams = _.clone(params);
+ ['card', 'list', 'oldList', 'board', 'comment'].forEach((key) => {
+ if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`;
+ });
+
+ const text = `${params.user} ${TAPi18n.__(description, quoteParams, user.getLanguage())}\n${params.url}`;
+ user.addEmailCache(text);
+
+ const userId = user._id;
+ Meteor.setTimeout(() => {
+ const user = Users.findOne(userId);
+
+ const emailCache = user.getEmailCache();
+ if (emailCache.length === 0) return;
+
+ const text = emailCache.join('\n\n');
+ user.clearEmailCache();
+
+ try {
+ Email.send({
+ to: user.emails[0].address,
+ from: Accounts.emailTemplates.from,
+ subject : TAPi18n.__('act-activity-notify', {}, user.getLanguage()),
+ text,
+ });
+ } catch (e) {
+ return;
+ }
+ }, 30000, user._id);
+ });
+});
diff --git a/server/notifications/notifications.js b/server/notifications/notifications.js
new file mode 100644
index 00000000..bc5557e1
--- /dev/null
+++ b/server/notifications/notifications.js
@@ -0,0 +1,48 @@
+// a map of notification service, like email, web, IM, qq, etc.
+
+// serviceName -> callback(user, title, description, params)
+// expected arguments to callback:
+// - user: Meteor user object
+// - title: String, TAPi18n key
+// - description, String, TAPi18n key
+// - params: Object, values extracted from context, to used for above two TAPi18n keys
+// see example call to Notifications.notify() in models/activities.js
+const notifyServices = {};
+
+Notifications = {
+ subscribe: (serviceName, callback) => {
+ notifyServices[serviceName] = callback;
+ },
+
+ unsubscribe: (serviceName) => {
+ if (typeof notifyServices[serviceName] === 'function')
+ delete notifyServices[serviceName];
+ },
+
+ // filter recipients according to user settings for notification
+ getUsers: (participants, watchers) => {
+ const userMap = {};
+ participants.forEach((userId) => {
+ if (userMap[userId]) return;
+ const user = Users.findOne(userId);
+ if (user && user.hasTag('notify-participate')) {
+ userMap[userId] = user;
+ }
+ });
+ watchers.forEach((userId) => {
+ if (userMap[userId]) return;
+ const user = Users.findOne(userId);
+ if (user && user.hasTag('notify-watch')) {
+ userMap[userId] = user;
+ }
+ });
+ return _.map(userMap, (v) => v);
+ },
+
+ notify: (user, title, description, params) => {
+ for(const k in notifyServices) {
+ const notifyImpl = notifyServices[k];
+ if (notifyImpl && typeof notifyImpl === 'function') notifyImpl(user, title, description, params);
+ }
+ },
+};
diff --git a/server/notifications/profile.js b/server/notifications/profile.js
new file mode 100644
index 00000000..6d9c7018
--- /dev/null
+++ b/server/notifications/profile.js
@@ -0,0 +1,9 @@
+Meteor.startup(() => {
+ // XXX: add activity id to profile.notifications,
+ // it can be displayed and rendered on web or mobile UI
+ // will uncomment the following code once UI implemented
+ //
+ // Notifications.subscribe('profile', (user, title, description, params) => {
+ // user.addNotification(params.activityId);
+ // });
+});
diff --git a/server/notifications/watch.js b/server/notifications/watch.js
new file mode 100644
index 00000000..253e15ba
--- /dev/null
+++ b/server/notifications/watch.js
@@ -0,0 +1,36 @@
+Meteor.methods({
+ watch(watchableType, id, level) {
+ check(watchableType, String);
+ check(id, String);
+ check(level, Match.OneOf(String, null));
+
+ const userId = Meteor.userId();
+
+ let watchableObj = null;
+ let board = null;
+ if (watchableType === 'board') {
+ watchableObj = Boards.findOne(id);
+ if (!watchableObj) throw new Meteor.Error('error-board-doesNotExist');
+ board = watchableObj;
+
+ } else if (watchableType === 'list') {
+ watchableObj = Lists.findOne(id);
+ if (!watchableObj) throw new Meteor.Error('error-list-doesNotExist');
+ board = watchableObj.board();
+
+ } else if (watchableType === 'card') {
+ watchableObj = Cards.findOne(id);
+ if (!watchableObj) throw new Meteor.Error('error-card-doesNotExist');
+ board = watchableObj.board();
+
+ } else {
+ throw new Meteor.Error('error-json-schema');
+ }
+
+ if ((board.permission === 'private') && !board.hasMember(userId))
+ throw new Meteor.Error('error-board-notAMember');
+
+ watchableObj.setWatcher(userId, level);
+ return true;
+ },
+});