diff options
author | Liming Xie <liming.xie@gmail.com> | 2016-01-05 23:26:02 +0800 |
---|---|---|
committer | Liming Xie <liming.xie@gmail.com> | 2016-01-05 23:26:02 +0800 |
commit | 9bbdacc79a89667e0d6f1ed30c415e5350ad468b (patch) | |
tree | fc6d9918dcd77699295ecb5bdbaf59f9d7c2f479 /server/notifications | |
parent | 9ef8ebaf09e52d7133ebe08ab1354ef663ee948b (diff) | |
download | wekan-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.js | 35 | ||||
-rw-r--r-- | server/notifications/notifications.js | 48 | ||||
-rw-r--r-- | server/notifications/profile.js | 9 | ||||
-rw-r--r-- | server/notifications/watch.js | 36 |
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; + }, +}); |