mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 19:46:01 +01:00
Notification delivery (#6072)
* ability for users to choose how they receive notifications add type field to more notifications, the type field is used to determine what to do based on user setting(none,notification,email,notificationemail) * change var name to types * cleanup * add event types for privileged users * remove unused language keys * fix uids check * changed if statements * upgrade script to preserver old settings
This commit is contained in:
committed by
GitHub
parent
e68e5122e2
commit
dd176dd5f2
@@ -10,6 +10,7 @@
|
|||||||
"continue_to": "Continue to %1",
|
"continue_to": "Continue to %1",
|
||||||
"return_to": "Return to %1",
|
"return_to": "Return to %1",
|
||||||
"new_notification": "New Notification",
|
"new_notification": "New Notification",
|
||||||
|
"new_notification_from": "You have a new Notification from %1",
|
||||||
"you_have_unread_notifications": "You have unread notifications.",
|
"you_have_unread_notifications": "You have unread notifications.",
|
||||||
|
|
||||||
"all": "All",
|
"all": "All",
|
||||||
@@ -50,5 +51,20 @@
|
|||||||
"email-confirmed": "Email Confirmed",
|
"email-confirmed": "Email Confirmed",
|
||||||
"email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.",
|
"email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.",
|
||||||
"email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.",
|
"email-confirm-error-message": "There was a problem validating your email address. Perhaps the code was invalid or has expired.",
|
||||||
"email-confirm-sent": "Confirmation email sent."
|
"email-confirm-sent": "Confirmation email sent.",
|
||||||
|
|
||||||
|
"none": "None",
|
||||||
|
"notification_only": "Notification Only",
|
||||||
|
"email_only": "Email Only",
|
||||||
|
"notification_and_email": "Notification & Email",
|
||||||
|
"notificationType_upvote": "When someone upvotes your post",
|
||||||
|
"notificationType_new-topic": "When someone you follow posts a topic",
|
||||||
|
"notificationType_new-reply": "When a new reply is posted in a topic you are watching",
|
||||||
|
"notificationType_follow": "When someone starts following you",
|
||||||
|
"notificationType_new-chat": "When you receive a chat message",
|
||||||
|
"notificationType_group-invite": "When you receive a group invite",
|
||||||
|
"notificationType_new-register": "When someone gets added to registration queue",
|
||||||
|
"notificationType_post-queue": "When a new post is queued",
|
||||||
|
"notificationType_new-post-flag": "When a post is flagged",
|
||||||
|
"notificationType_new-user-flag": "When a user is flagged"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,8 +84,6 @@
|
|||||||
"digest_daily": "Daily",
|
"digest_daily": "Daily",
|
||||||
"digest_weekly": "Weekly",
|
"digest_weekly": "Weekly",
|
||||||
"digest_monthly": "Monthly",
|
"digest_monthly": "Monthly",
|
||||||
"send_chat_notifications": "Send an email if a new chat message arrives and I am not online",
|
|
||||||
"send_post_notifications": "Send an email when replies are made to topics I am subscribed to",
|
|
||||||
"settings-require-reload": "Some setting changes require a reload. Click here to reload the page.",
|
"settings-require-reload": "Some setting changes require a reload. Click here to reload the page.",
|
||||||
|
|
||||||
"has_no_follower": "This user doesn't have any followers :(",
|
"has_no_follower": "This user doesn't have any followers :(",
|
||||||
|
|||||||
@@ -84,13 +84,19 @@ settingsController.get = function (req, res, callback) {
|
|||||||
plugins.fireHook('filter:user.customSettings', { settings: results.settings, customSettings: [], uid: req.uid }, next);
|
plugins.fireHook('filter:user.customSettings', { settings: results.settings, customSettings: [], uid: req.uid }, next);
|
||||||
},
|
},
|
||||||
function (data, next) {
|
function (data, next) {
|
||||||
getHomePageRoutes(userData, function (err, routes) {
|
|
||||||
userData.homePageRoutes = routes;
|
|
||||||
next(err, data);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function (data) {
|
|
||||||
userData.customSettings = data.customSettings;
|
userData.customSettings = data.customSettings;
|
||||||
|
async.parallel({
|
||||||
|
notificationSettings: function (next) {
|
||||||
|
getNotificationSettings(userData, next);
|
||||||
|
},
|
||||||
|
routes: function (next) {
|
||||||
|
getHomePageRoutes(userData, next);
|
||||||
|
},
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results) {
|
||||||
|
userData.homePageRoutes = results.routes;
|
||||||
|
userData.notificationSettings = results.notificationSettings;
|
||||||
userData.disableEmailSubscriptions = parseInt(meta.config.disableEmailSubscriptions, 10) === 1;
|
userData.disableEmailSubscriptions = parseInt(meta.config.disableEmailSubscriptions, 10) === 1;
|
||||||
|
|
||||||
userData.dailyDigestFreqOptions = [
|
userData.dailyDigestFreqOptions = [
|
||||||
@@ -149,6 +155,56 @@ settingsController.get = function (req, res, callback) {
|
|||||||
], callback);
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getNotificationSettings(userData, callback) {
|
||||||
|
var types = [
|
||||||
|
'notificationType_upvote',
|
||||||
|
'notificationType_new-topic',
|
||||||
|
'notificationType_new-reply',
|
||||||
|
'notificationType_follow',
|
||||||
|
'notificationType_new-chat',
|
||||||
|
'notificationType_group-invite',
|
||||||
|
];
|
||||||
|
|
||||||
|
var privilegedTypes = [];
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
user.getPrivileges(userData.uid, next);
|
||||||
|
},
|
||||||
|
function (privileges, next) {
|
||||||
|
if (privileges.isAdmin) {
|
||||||
|
privilegedTypes.push('notificationType_new-register');
|
||||||
|
}
|
||||||
|
if (privileges.isAdmin || privileges.isGlobalMod || privileges.isModeratorOfAnyCategory) {
|
||||||
|
privilegedTypes.push('notificationType_post-queue', 'notificationType_new-post-flag');
|
||||||
|
}
|
||||||
|
if (privileges.isAdmin || privileges.isGlobalMod) {
|
||||||
|
privilegedTypes.push('notificationType_new-user-flag');
|
||||||
|
}
|
||||||
|
plugins.fireHook('filter:user.notificationTypes', {
|
||||||
|
userData: userData,
|
||||||
|
types: types,
|
||||||
|
privilegedTypes: privilegedTypes,
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
function modifyType(type) {
|
||||||
|
var setting = userData.settings[type] || 'notification';
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: type,
|
||||||
|
label: '[[notifications:' + type + ']]',
|
||||||
|
none: setting === 'none',
|
||||||
|
notification: setting === 'notification',
|
||||||
|
email: setting === 'email',
|
||||||
|
notificationemail: setting === 'notificationemail',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var notificationSettings = results.types.map(modifyType).concat(results.privilegedTypes.map(modifyType));
|
||||||
|
next(null, notificationSettings);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
function getHomePageRoutes(userData, callback) {
|
function getHomePageRoutes(userData, callback) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
|
|||||||
@@ -696,6 +696,7 @@ Flags.notify = function (flagObj, uid, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifications.create({
|
notifications.create({
|
||||||
|
type: 'new-user-flag',
|
||||||
bodyShort: '[[notifications:user_flagged_user, ' + flagObj.reporter.username + ', ' + flagObj.target.username + ']]',
|
bodyShort: '[[notifications:user_flagged_user, ' + flagObj.reporter.username + ', ' + flagObj.target.username + ']]',
|
||||||
bodyLong: flagObj.description,
|
bodyLong: flagObj.description,
|
||||||
path: '/uid/' + flagObj.targetId,
|
path: '/uid/' + flagObj.targetId,
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ module.exports = function (Groups) {
|
|||||||
async.waterfall([
|
async.waterfall([
|
||||||
async.apply(inviteOrRequestMembership, groupName, uid, 'invite'),
|
async.apply(inviteOrRequestMembership, groupName, uid, 'invite'),
|
||||||
async.apply(notifications.create, {
|
async.apply(notifications.create, {
|
||||||
|
type: 'group-invite',
|
||||||
bodyShort: '[[groups:invited.notification_title, ' + groupName + ']]',
|
bodyShort: '[[groups:invited.notification_title, ' + groupName + ']]',
|
||||||
bodyLong: '',
|
bodyLong: '',
|
||||||
nid: 'group:' + groupName + ':uid:' + uid + ':invite',
|
nid: 'group:' + groupName + ':uid:' + uid + ':invite',
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var winston = require('winston');
|
|
||||||
|
|
||||||
var user = require('../user');
|
var user = require('../user');
|
||||||
var emailer = require('../emailer');
|
|
||||||
var notifications = require('../notifications');
|
var notifications = require('../notifications');
|
||||||
var meta = require('../meta');
|
|
||||||
var sockets = require('../socket.io');
|
var sockets = require('../socket.io');
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
|
|
||||||
@@ -92,46 +89,6 @@ module.exports = function (Messaging) {
|
|||||||
if (notification) {
|
if (notification) {
|
||||||
notifications.push(notification, uids);
|
notifications.push(notification, uids);
|
||||||
}
|
}
|
||||||
sendNotificationEmails(uids, messageObj);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendNotificationEmails(uids, messageObj) {
|
|
||||||
if (parseInt(meta.config.disableEmailSubscriptions, 10) === 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
async.parallel({
|
|
||||||
userData: function (next) {
|
|
||||||
user.getUsersFields(uids, ['uid', 'username', 'userslug'], next);
|
|
||||||
},
|
|
||||||
userSettings: function (next) {
|
|
||||||
user.getMultipleUserSettings(uids, next);
|
|
||||||
},
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
|
|
||||||
function (results, next) {
|
|
||||||
results.userData = results.userData.filter(function (userData, index) {
|
|
||||||
return userData && results.userSettings[index] && results.userSettings[index].sendChatNotifications;
|
|
||||||
});
|
|
||||||
async.each(results.userData, function (userData, next) {
|
|
||||||
emailer.send('notif_chat', userData.uid, {
|
|
||||||
subject: '[[email:notif.chat.subject, ' + messageObj.fromUser.username + ']]',
|
|
||||||
summary: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]',
|
|
||||||
message: messageObj,
|
|
||||||
roomId: messageObj.roomId,
|
|
||||||
username: userData.username,
|
|
||||||
userslug: userData.userslug,
|
|
||||||
}, next);
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
], function (err) {
|
|
||||||
if (err) {
|
|
||||||
return winston.error(err);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ var meta = require('./meta');
|
|||||||
var batch = require('./batch');
|
var batch = require('./batch');
|
||||||
var plugins = require('./plugins');
|
var plugins = require('./plugins');
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
|
var emailer = require('./emailer');
|
||||||
|
|
||||||
var Notifications = module.exports;
|
var Notifications = module.exports;
|
||||||
|
|
||||||
@@ -178,22 +179,15 @@ Notifications.push = function (notification, uids, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function pushToUids(uids, notification, callback) {
|
function pushToUids(uids, notification, callback) {
|
||||||
|
function sendNotification(uids, callback) {
|
||||||
|
if (!uids.length) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
var oneWeekAgo = Date.now() - 604800000;
|
var oneWeekAgo = Date.now() - 604800000;
|
||||||
var unreadKeys = [];
|
var unreadKeys = [];
|
||||||
var readKeys = [];
|
var readKeys = [];
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
plugins.fireHook('filter:notification.push', { notification: notification, uids: uids }, next);
|
|
||||||
},
|
|
||||||
function (data, next) {
|
|
||||||
if (!data || !data.notification || !data.uids || !data.uids.length) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
uids = data.uids;
|
|
||||||
notification = data.notification;
|
|
||||||
|
|
||||||
uids.forEach(function (uid) {
|
uids.forEach(function (uid) {
|
||||||
unreadKeys.push('uid:' + uid + ':notifications:unread');
|
unreadKeys.push('uid:' + uid + ':notifications:unread');
|
||||||
readKeys.push('uid:' + uid + ':notifications:read');
|
readKeys.push('uid:' + uid + ':notifications:read');
|
||||||
@@ -217,8 +211,81 @@ function pushToUids(uids, notification, callback) {
|
|||||||
websockets.in('uid_' + uid).emit('event:new_notification', notification);
|
websockets.in('uid_' + uid).emit('event:new_notification', notification);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
plugins.fireHook('action:notification.pushed', { notification: notification, uids: uids });
|
function sendEmail(uids, callback) {
|
||||||
|
async.eachLimit(uids, 3, function (uid, next) {
|
||||||
|
emailer.send('notification', uid, {
|
||||||
|
path: notification.path,
|
||||||
|
subject: '[' + (meta.config.title || 'NodeBB') + '] ' + notification.bodyShort,
|
||||||
|
intro: '[[notifications:new_notification_from, ' + meta.config.title + ']]',
|
||||||
|
body: notification.bodyLong || notification.bodyShort,
|
||||||
|
showUnsubscribe: true,
|
||||||
|
}, next);
|
||||||
|
}, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUidsBySettings(uids, callback) {
|
||||||
|
var uidsToNotify = [];
|
||||||
|
var uidsToEmail = [];
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
User.getMultipleUserSettings(uids, next);
|
||||||
|
},
|
||||||
|
function (usersSettings, next) {
|
||||||
|
usersSettings.forEach(function (userSettings) {
|
||||||
|
var setting = userSettings['notificationType_' + notification.type] || 'notification';
|
||||||
|
|
||||||
|
if (setting === 'notification' || setting === 'notificationemail') {
|
||||||
|
uidsToNotify.push(userSettings.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setting === 'email' || setting === 'notificationemail') {
|
||||||
|
uidsToEmail.push(userSettings.uid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
next(null, { uidsToNotify: uidsToNotify, uidsToEmail: uidsToEmail });
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
plugins.fireHook('filter:notification.push', { notification: notification, uids: uids }, next);
|
||||||
|
},
|
||||||
|
function (data, next) {
|
||||||
|
if (!data || !data.notification || !data.uids || !data.uids.length) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
notification = data.notification;
|
||||||
|
if (notification.type) {
|
||||||
|
getUidsBySettings(data.uids, next);
|
||||||
|
} else {
|
||||||
|
next(null, { uidsToNotify: data.uids, uidsToEmail: [] });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
async.parallel([
|
||||||
|
function (next) {
|
||||||
|
sendNotification(results.uidsToNotify, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
sendEmail(results.uidsToEmail, next);
|
||||||
|
},
|
||||||
|
], function (err) {
|
||||||
|
next(err, results);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
plugins.fireHook('action:notification.pushed', {
|
||||||
|
notification: notification,
|
||||||
|
uids: results.uidsToNotify,
|
||||||
|
uidsNotified: results.uidsToNotify,
|
||||||
|
uidsEmailed: results.uidsToEmail,
|
||||||
|
});
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
|
|||||||
@@ -53,17 +53,25 @@ module.exports = function (Posts) {
|
|||||||
user.setUserField(data.uid, 'lastqueuetime', Date.now(), next);
|
user.setUserField(data.uid, 'lastqueuetime', Date.now(), next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
|
async.parallel({
|
||||||
|
notification: function (next) {
|
||||||
notifications.create({
|
notifications.create({
|
||||||
nid: 'post-queued-' + id,
|
type: 'post-queue',
|
||||||
|
nid: 'post-queue-' + id,
|
||||||
mergeId: 'post-queue',
|
mergeId: 'post-queue',
|
||||||
bodyShort: '[[notifications:post_awaiting_review]]',
|
bodyShort: '[[notifications:post_awaiting_review]]',
|
||||||
bodyLong: data.content,
|
bodyLong: data.content,
|
||||||
path: '/post-queue',
|
path: '/post-queue',
|
||||||
}, next);
|
}, next);
|
||||||
},
|
},
|
||||||
function (notification, next) {
|
cid: function (next) {
|
||||||
if (notification) {
|
getCid(type, data, next);
|
||||||
notifications.pushGroups(notification, ['administrators', 'Global Moderators'], next);
|
},
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
if (results.notification) {
|
||||||
|
notifications.pushGroups(results.notification, ['administrators', 'Global Moderators', 'cid:' + results.cid + ':privileges:moderate'], next);
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
@@ -79,20 +87,26 @@ module.exports = function (Posts) {
|
|||||||
], callback);
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getCid(type, data, callback) {
|
||||||
|
if (type === 'topic') {
|
||||||
|
return setImmediate(callback, null, data.cid);
|
||||||
|
} else if (type === 'reply') {
|
||||||
|
topics.getTopicField(data.tid, 'cid', callback);
|
||||||
|
} else {
|
||||||
|
return setImmediate(callback, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function canPost(type, data, callback) {
|
function canPost(type, data, callback) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
if (type === 'topic') {
|
getCid(type, data, next);
|
||||||
next(null, data.cid);
|
|
||||||
} else if (type === 'reply') {
|
|
||||||
topics.getTopicField(data.tid, 'cid', next);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
function (cid, next) {
|
function (cid, next) {
|
||||||
async.parallel({
|
async.parallel({
|
||||||
canPost: function (next) {
|
canPost: function (next) {
|
||||||
if (type === 'topic') {
|
if (type === 'topic') {
|
||||||
privileges.categories.can('topics:create', data.cid, data.uid, next);
|
privileges.categories.can('topics:create', cid, data.uid, next);
|
||||||
} else if (type === 'reply') {
|
} else if (type === 'reply') {
|
||||||
privileges.categories.can('topics:reply', cid, data.uid, next);
|
privileges.categories.can('topics:reply', cid, data.uid, next);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var winston = require('winston');
|
|
||||||
|
|
||||||
var db = require('../database');
|
var db = require('../database');
|
||||||
var user = require('../user');
|
|
||||||
var posts = require('../posts');
|
var posts = require('../posts');
|
||||||
var notifications = require('../notifications');
|
var notifications = require('../notifications');
|
||||||
var privileges = require('../privileges');
|
var privileges = require('../privileges');
|
||||||
var meta = require('../meta');
|
|
||||||
var emailer = require('../emailer');
|
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
var utils = require('../utils');
|
var utils = require('../utils');
|
||||||
|
|
||||||
@@ -239,36 +235,6 @@ module.exports = function (Topics) {
|
|||||||
notifications.push(notification, followers);
|
notifications.push(notification, followers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parseInt(meta.config.disableEmailSubscriptions, 10) === 1) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
async.eachLimit(followers, 3, function (toUid, next) {
|
|
||||||
async.parallel({
|
|
||||||
userData: async.apply(user.getUserFields, toUid, ['username', 'userslug']),
|
|
||||||
userSettings: async.apply(user.getSettings, toUid),
|
|
||||||
}, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.userSettings.sendPostNotifications) {
|
|
||||||
emailer.send('notif_post', toUid, {
|
|
||||||
pid: postData.pid,
|
|
||||||
subject: '[' + (meta.config.title || 'NodeBB') + '] ' + title,
|
|
||||||
intro: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + titleEscaped + ']]',
|
|
||||||
postBody: postData.content.replace(/"\/\//g, '"https://'),
|
|
||||||
username: data.userData.username,
|
|
||||||
userslug: data.userData.userslug,
|
|
||||||
topicSlug: postData.topic.slug,
|
|
||||||
showUnsubscribe: true,
|
|
||||||
}, next);
|
|
||||||
} else {
|
|
||||||
winston.debug('[topics.notifyFollowers] uid ' + toUid + ' does not have post notifications enabled, skipping.');
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
|
|||||||
48
src/upgrades/1.7.1/notification-settings.js
Normal file
48
src/upgrades/1.7.1/notification-settings.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
|
var batch = require('../../batch');
|
||||||
|
var db = require('../../database');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'Convert old notification digest settings',
|
||||||
|
timestamp: Date.UTC(2017, 10, 15),
|
||||||
|
method: function (callback) {
|
||||||
|
var progress = this.progress;
|
||||||
|
|
||||||
|
batch.processSortedSet('users:joindate', function (uids, next) {
|
||||||
|
async.eachLimit(uids, 500, function (uid, next) {
|
||||||
|
progress.incr();
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.getObjectFields('user:' + uid + ':settings', ['sendChatNotifications', 'sendPostNotifications'], next);
|
||||||
|
},
|
||||||
|
function (userSettings, _next) {
|
||||||
|
if (!userSettings) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
var tasks = [];
|
||||||
|
if (parseInt(userSettings.sendChatNotifications, 10) === 1) {
|
||||||
|
tasks.push(async.apply(db.setObjectField, 'user:' + uid + ':settings', 'notificationType_new-chat', 'notificationemail'));
|
||||||
|
}
|
||||||
|
if (parseInt(userSettings.sendPostNotifications, 10) === 1) {
|
||||||
|
tasks.push(async.apply(db.setObjectField, 'user:' + uid + ':settings', 'notificationType_new-reply', 'notificationemail'));
|
||||||
|
}
|
||||||
|
if (!tasks.length) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
async.series(tasks, function (err) {
|
||||||
|
_next(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
db.deleteObjectFields('user:' + uid + ':settings', ['sendChatNotifications', 'sendPostNotifications'], next);
|
||||||
|
},
|
||||||
|
], next);
|
||||||
|
}, next);
|
||||||
|
}, {
|
||||||
|
progress: progress,
|
||||||
|
}, callback);
|
||||||
|
},
|
||||||
|
};
|
||||||
16
src/user.js
16
src/user.js
@@ -208,13 +208,17 @@ User.isGlobalModerator = function (uid, callback) {
|
|||||||
privileges.users.isGlobalModerator(uid, callback);
|
privileges.users.isGlobalModerator(uid, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
User.getPrivileges = function (uid, callback) {
|
||||||
|
async.parallel({
|
||||||
|
isAdmin: async.apply(User.isAdministrator, uid),
|
||||||
|
isGlobalModerator: async.apply(User.isGlobalModerator, uid),
|
||||||
|
isModeratorOfAnyCategory: async.apply(User.isModeratorOfAnyCategory, uid),
|
||||||
|
}, callback);
|
||||||
|
};
|
||||||
|
|
||||||
User.isPrivileged = function (uid, callback) {
|
User.isPrivileged = function (uid, callback) {
|
||||||
async.parallel([
|
User.getPrivileges(uid, function (err, results) {
|
||||||
async.apply(User.isAdministrator, uid),
|
callback(err, results ? (results.isAdmin || results.isGlobalModerator || results.isModeratorOfAnyCategory) : false);
|
||||||
async.apply(User.isGlobalModerator, uid),
|
|
||||||
async.apply(User.isModeratorOfAnyCategory, uid),
|
|
||||||
], function (err, results) {
|
|
||||||
callback(err, results ? results.some(Boolean) : false);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ module.exports = function (User) {
|
|||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
notifications.create({
|
notifications.create({
|
||||||
|
type: 'new-register',
|
||||||
bodyShort: '[[notifications:new_register, ' + username + ']]',
|
bodyShort: '[[notifications:new_register, ' + username + ']]',
|
||||||
nid: 'new_register:' + username,
|
nid: 'new_register:' + username,
|
||||||
path: '/admin/manage/registration',
|
path: '/admin/manage/registration',
|
||||||
|
|||||||
@@ -131,6 +131,12 @@ module.exports = function (User) {
|
|||||||
notificationSound: data.notificationSound,
|
notificationSound: data.notificationSound,
|
||||||
incomingChatSound: data.incomingChatSound,
|
incomingChatSound: data.incomingChatSound,
|
||||||
outgoingChatSound: data.outgoingChatSound,
|
outgoingChatSound: data.outgoingChatSound,
|
||||||
|
notificationType_upvote: data.notificationType_upvote,
|
||||||
|
'notificationType_new-topic': data['notificationType_new-topic'],
|
||||||
|
'notificationType_new-reply': data['notificationType_new-reply'],
|
||||||
|
notificationType_follow: data.notificationType_follow,
|
||||||
|
'notificationType_new-chat': data['notificationType_new-chat'],
|
||||||
|
'notificationType_group-invite': data['notificationType_group-invite'],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data.bootswatchSkin) {
|
if (data.bootswatchSkin) {
|
||||||
|
|||||||
57
src/views/emails/notification.tpl
Normal file
57
src/views/emails/notification.tpl
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<!-- IMPORT emails/partials/header.tpl -->
|
||||||
|
|
||||||
|
<!-- Email Body : BEGIN -->
|
||||||
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 600px;">
|
||||||
|
|
||||||
|
<!-- Hero Image, Flush : BEGIN -->
|
||||||
|
<tr>
|
||||||
|
<td bgcolor="#efeff0" style="text-align: center; background-image: url({url}/assets/images/emails/triangularbackground.png); background-size: cover; background-repeat: no-repeat;">
|
||||||
|
<img src="{url}/assets/images/emails/unreadpost.png" width="300" height="300" border="0" align="center" style="width: 300px; height: 300px; max-width: 300px; height: auto; font-family: sans-serif; font-size: 15px; line-height: 20px; color: #555555;" class="g-img">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- Hero Image, Flush : END -->
|
||||||
|
|
||||||
|
<!-- 1 Column Text + Button : BEGIN -->
|
||||||
|
<tr>
|
||||||
|
<td bgcolor="#efeff0">
|
||||||
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 40px 40px 0px 40px; font-family: sans-serif; font-size: 15px; line-height: 20px; color: #555555;">
|
||||||
|
<p style="margin: 0 0 20px 0;">{intro}</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 0px 60px 40px 60px; font-family: sans-serif; font-size: 15px; line-height: 20px; color: #555555;">
|
||||||
|
{body}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 0 40px; font-family: sans-serif; font-size: 15px; line-height: 20px; color: #555555;">
|
||||||
|
<!-- Button : BEGIN -->
|
||||||
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" style="margin: auto;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
|
||||||
|
<a href="{url}{path}" style="background: #222222; border: 15px solid #222222; font-family: sans-serif; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
|
||||||
|
<span style="color:#ffffff;" class="button-link"> [[email:notif.cta]] </span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<!-- Button : END -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 40px; font-family: sans-serif; font-size: 15px; line-height: 20px; color: #555555;">
|
||||||
|
<h2 style="margin: 0 0 10px 0; font-family: sans-serif; font-size: 18px; line-height: 21px; color: #333333; font-weight: bold;">[[email:closing]]</h2>
|
||||||
|
<p style="margin: 0;">{site_title}</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- 1 Column Text + Button : END -->
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<!-- Email Body : END -->
|
||||||
|
|
||||||
|
<!-- IMPORT emails/partials/footer.tpl -->
|
||||||
Reference in New Issue
Block a user