You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodebb/src/user.js

1065 lines
26 KiB
JavaScript

var bcrypt = require('bcrypt'),
async = require('async'),
emailjs = require('emailjs'),
nconf = require('nconf'),
winston = require('winston'),
gravatar = require('gravatar'),
11 years ago
check = require('validator').check,
sanitize = require('validator').sanitize,
11 years ago
utils = require('./../public/src/utils'),
plugins = require('./plugins'),
11 years ago
db = require('./database'),
meta = require('./meta'),
emailjsServer = emailjs.server.connect(meta.config['email:smtp:host'] || '127.0.0.1'),
11 years ago
groups = require('./groups'),
notifications = require('./notifications'),
topics = require('./topics'),
11 years ago
events = require('./events'),
websockets = require('./websockets');
11 years ago
(function(User) {
'use strict';
User.create = function(username, password, email, callback) {
var userslug = utils.slugify(username);
username = username.trim();
if (email !== undefined) {
email = email.trim();
}
async.parallel([
function(next) {
if (email !== undefined) {
next(!utils.isEmailValid(email) ? new Error('Invalid Email!') : null);
} else {
next();
}
},
function(next) {
next((!utils.isUserNameValid(username) || !userslug) ? new Error('Invalid Username!') : null);
},
function(next) {
if (password !== undefined) {
next(!utils.isPasswordValid(password) ? new Error('Invalid Password!') : null);
} else {
next();
}
},
function(next) {
User.exists(userslug, function(exists) {
next(exists ? new Error('Username taken!') : null);
});
},
function(next) {
12 years ago
if (email !== undefined) {
12 years ago
User.isEmailAvailable(email, function(err, available) {
if (err) {
12 years ago
return next(err);
}
12 years ago
next(!available ? new Error('Email taken!') : null);
});
} else {
next();
}
}
], function(err, results) {
if (err) {
11 years ago
return callback(err);
}
11 years ago
db.incrObjectField('global', 'nextUid', function(err, uid) {
11 years ago
if(err) {
return callback(err);
}
var gravatar = User.createGravatarURLFromEmail(email);
var timestamp = Date.now();
11 years ago
db.setObject('user:' + uid, {
'uid': uid,
'username': username,
'userslug': userslug,
'fullname': '',
'location': '',
'birthday': '',
'website': '',
'email': email || '',
'signature': '',
'joindate': timestamp,
'picture': gravatar,
'gravatarpicture': gravatar,
'uploadedpicture': '',
'profileviews': 0,
'reputation': 0,
'postcount': 0,
'lastposttime': 0,
'banned': 0,
12 years ago
'showemail': 0
});
11 years ago
db.setObjectField('username:uid', username, uid);
db.setObjectField('userslug:uid', userslug, uid);
12 years ago
if (email !== undefined) {
11 years ago
db.setObjectField('email:uid', email, uid);
11 years ago
User.sendConfirmationEmail(email);
12 years ago
}
plugins.fireHook('action:user.create', {uid: uid, username: username, email: email, picture: gravatar, timestamp: timestamp});
db.incrObjectField('global', 'userCount');
11 years ago
db.sortedSetAdd('users:joindate', timestamp, uid);
db.sortedSetAdd('users:postcount', 0, uid);
db.sortedSetAdd('users:reputation', 0, uid);
db.searchIndex('user', username, uid);
12 years ago
if (password !== undefined) {
User.hashPassword(password, function(err, hash) {
User.setUserField(uid, 'password', hash);
callback(null, uid);
});
} else {
callback(null, uid);
}
});
});
};
User.ban = function(uid, callback) {
User.setUserField(uid, 'banned', 1, callback);
};
User.unban = function(uid, callback) {
User.setUserField(uid, 'banned', 0, callback);
};
User.getUserField = function(uid, field, callback) {
11 years ago
db.getObjectField('user:' + uid, field, callback);
};
User.getUserFields = function(uid, fields, callback) {
11 years ago
db.getObjectFields('user:' + uid, fields, callback);
};
User.getMultipleUserFields = function(uids, fields, callback) {
if (uids.length === 0) {
return callback(null, []);
}
var returnData = [];
var uuids = uids.filter(function(value, index, self) {
return self.indexOf(value) === index;
});
function iterator(uid, next) {
User.getUserFields(uid, fields, function(err, userData) {
if (err) {
return next(err);
}
returnData.push(userData);
next(null);
});
}
async.eachSeries(uuids, iterator, function(err) {
callback(err, returnData);
});
};
User.getUserData = function(uid, callback) {
11 years ago
db.getObject('user:' + uid, function(err, data) {
if(err) {
return callback(err);
}
if (data && data.password) {
delete data.password;
}
callback(err, data);
});
};
User.updateProfile = function(uid, data, callback) {
12 years ago
var fields = ['email', 'fullname', 'website', 'location', 'birthday', 'signature'];
var returnData = {
success: false
};
12 years ago
function isSignatureValid(next) {
11 years ago
if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) {
next({
11 years ago
error: 'Signature can\'t be longer than ' + meta.config.maximumSignatureLength + ' characters!'
}, false);
12 years ago
} else {
next(null, true);
}
12 years ago
}
12 years ago
function isEmailAvailable(next) {
if (!data.email) {
return next(null, true);
12 years ago
}
12 years ago
User.getUserField(uid, 'email', function(err, email) {
if (email !== data.email) {
User.isEmailAvailable(data.email, function(err, available) {
if (err) {
12 years ago
return next(err, null);
}
if (!available) {
next({
error: 'Email not available!'
}, false);
} else {
next(null, true);
}
});
} else {
next(null, true);
}
});
12 years ago
}
12 years ago
async.series([isSignatureValid, isEmailAvailable], function(err, results) {
if (err) {
callback(err, returnData);
12 years ago
} else {
async.each(fields, updateField, function(err) {
if (err) {
callback(err, returnData);
} else {
returnData.success = true;
callback(null, returnData);
}
});
12 years ago
}
});
function updateField(field, next) {
if (data[field] !== undefined && typeof data[field] === 'string') {
11 years ago
data[field] = data[field].trim();
data[field] = sanitize(data[field]).escape();
if (field === 'email') {
var gravatarpicture = User.createGravatarURLFromEmail(data[field]);
User.setUserField(uid, 'gravatarpicture', gravatarpicture);
User.getUserFields(uid, ['email', 'picture', 'uploadedpicture'], function(err, userData) {
if (err) {
return next(err);
}
11 years ago
db.deleteObjectField('email:uid', userData.email);
db.setObjectField('email:uid', data.email, uid);
User.setUserField(uid, field, data[field]);
if (userData.picture !== userData.uploadedpicture) {
returnData.picture = gravatarpicture;
User.setUserField(uid, 'picture', gravatarpicture);
}
returnData.gravatarpicture = gravatarpicture;
11 years ago
events.logEmailChange(uid, userData.email, data.email);
next(null);
});
return;
} else if (field === 'signature') {
data[field] = utils.strip_tags(data[field]);
11 years ago
} else if (field === 'website') {
if(data[field].substr(0, 7) !== 'http://' && data[field].substr(0, 8) !== 'https://') {
data[field] = 'http://' + data[field];
}
}
User.setUserField(uid, field, data[field]);
12 years ago
next(null);
} else {
next(null);
}
}
};
12 years ago
User.isEmailAvailable = function(email, callback) {
11 years ago
db.isObjectField('email:uid', email, function(err, exists) {
callback(err, !exists);
12 years ago
});
};
User.changePassword = function(uid, data, callback) {
if (!utils.isPasswordValid(data.newPassword)) {
return callback({
error: 'Invalid password!'
});
}
12 years ago
User.getUserField(uid, 'password', function(err, user_password) {
bcrypt.compare(data.currentPassword, user_password, function(err, res) {
if (err) {
12 years ago
return callback(err);
}
if (res) {
User.hashPassword(data.newPassword, function(err, hash) {
User.setUserField(uid, 'password', hash);
11 years ago
events.logPasswordChange(uid);
12 years ago
callback(null);
});
} else {
callback({
error: 'Your current password is not correct!'
});
}
});
});
};
User.setUserField = function(uid, field, value, callback) {
11 years ago
db.setObjectField('user:' + uid, field, value, callback);
};
11 years ago
User.setUserFields = function(uid, data, callback) {
11 years ago
db.setObject('user:' + uid, data, callback);
};
12 years ago
User.incrementUserFieldBy = function(uid, field, value, callback) {
11 years ago
db.incrObjectFieldBy('user:' + uid, field, value, callback);
};
User.decrementUserFieldBy = function(uid, field, value, callback) {
11 years ago
db.incrObjectFieldBy('user:' + uid, field, -value, callback);
};
User.getUsers = function(set, start, stop, callback) {
var data = [];
11 years ago
db.getSortedSetRevRange(set, start, stop, function(err, uids) {
if (err) {
return callback(err, null);
}
function iterator(uid, callback) {
User.getUserData(uid, function(err, userData) {
User.isAdministrator(uid, function(err, isAdmin) {
if (userData) {
userData.administrator = isAdmin?"1":"0";
data.push(userData);
}
callback(null);
});
});
}
async.eachSeries(uids, iterator, function(err) {
callback(err, data);
});
});
};
User.createGravatarURLFromEmail = function(email) {
12 years ago
var options = {
size: '128',
default: 'identicon',
rating: 'pg'
},
https = nconf.get('https');
if (!email) {
12 years ago
email = '';
options.forcedefault = 'y';
}
12 years ago
return gravatar.url(email, options, https);
};
User.hashPassword = function(password, callback) {
if (!password) {
callback(password);
return;
}
bcrypt.genSalt(nconf.get('bcrypt_rounds'), function(err, salt) {
bcrypt.hash(password, salt, callback);
});
};
User.reIndexAll = function(callback) {
User.getUsers('users:joindate', 0, -1, function(err, usersData) {
if (err) {
return callback(err, null);
}
function reIndexUser(uid, username) {
db.searchRemove('user', uid, function() {
db.searchIndex('user', username, uid);
});
}
for (var i = 0; i < usersData.length; ++i) {
reIndexUser(usersData[i].uid, usersData[i].username);
}
callback(null, 1);
});
};
User.search = function(username, callback) {
if (!username) {
callback([]);
return;
}
11 years ago
db.search('user', username, function(err, uids) {
if (err) {
console.log(err);
return;
}
if (uids && uids.length) {
User.getDataForUsers(uids, function(userdata) {
callback(userdata);
});
} else {
callback([]);
}
});
};
User.onNewPostMade = function(uid, tid, pid, timestamp) {
User.addPostIdToUser(uid, pid);
User.incrementUserFieldBy(uid, 'postcount', 1, function(err, newpostcount) {
11 years ago
db.sortedSetAdd('users:postcount', newpostcount, uid);
});
User.setUserField(uid, 'lastposttime', timestamp);
User.sendPostNotificationToFollowers(uid, tid, pid);
};
User.addPostIdToUser = function(uid, pid) {
11 years ago
db.listPrepend('uid:' + uid + ':posts', pid);
};
User.addTopicIdToUser = function(uid, tid) {
11 years ago
db.listPrepend('uid:' + uid + ':topics', tid);
};
11 years ago
User.getPostIds = function(uid, start, stop, callback) {
db.getListRange('uid:' + uid + ':posts', start, stop, function(err, pids) {
if(err) {
return callback(err);
}
if (pids && pids.length) {
callback(null, pids);
} else {
callback(null, []);
}
});
};
User.follow = function(uid, followid, callback) {
11 years ago
db.setAdd('following:' + uid, followid, function(err, data) {
if (!err) {
11 years ago
db.setAdd('followers:' + followid, uid, function(err, data) {
if (!err) {
callback(true);
} else {
console.log(err);
callback(false);
}
});
} else {
console.log(err);
callback(false);
}
});
};
User.unfollow = function(uid, unfollowid, callback) {
11 years ago
db.setRemove('following:' + uid, unfollowid, function(err, data) {
if (!err) {
11 years ago
db.setRemove('followers:' + unfollowid, uid, function(err, data) {
callback(data);
});
} else {
console.log(err);
}
});
};
User.getFollowing = function(uid, callback) {
11 years ago
db.getSetMembers('following:' + uid, function(err, userIds) {
if (!err) {
User.getDataForUsers(userIds, callback);
} else {
console.log(err);
}
});
};
User.getFollowers = function(uid, callback) {
11 years ago
db.getSetMembers('followers:' + uid, function(err, userIds) {
if (!err) {
User.getDataForUsers(userIds, callback);
} else {
console.log(err);
}
});
};
User.getFollowingCount = function(uid, callback) {
11 years ago
db.getSetMembers('following:' + uid, function(err, userIds) {
if (err) {
console.log(err);
} else {
userIds = userIds.filter(function(value) {
return parseInt(value, 10) !== 0;
});
callback(userIds.length);
}
});
};
User.getFollowerCount = function(uid, callback) {
11 years ago
db.getSetMembers('followers:' + uid, function(err, userIds) {
if(err) {
console.log(err);
} else {
userIds = userIds.filter(function(value) {
return parseInt(value, 10) !== 0;
});
callback(userIds.length);
}
});
};
User.getDataForUsers = function(uids, callback) {
var returnData = [];
if (!uids || !Array.isArray(uids) || uids.length === 0) {
callback(returnData);
return;
}
function iterator(uid, callback) {
11 years ago
if(parseInt(uid, 10) === 0) {
return callback(null);
}
User.getUserData(uid, function(err, userData) {
returnData.push(userData);
callback(null);
});
}
async.eachSeries(uids, iterator, function(err) {
callback(returnData);
});
};
User.sendPostNotificationToFollowers = function(uid, tid, pid) {
12 years ago
User.getUserField(uid, 'username', function(err, username) {
11 years ago
db.getSetMembers('followers:' + uid, function(err, followers) {
topics.getTopicField(tid, 'slug', function(err, slug) {
12 years ago
var message = '<strong>' + username + '</strong> made a new post';
notifications.create(message, nconf.get('relative_path') + '/topic/' + slug + '#' + pid, 'topic:' + tid, function(nid) {
notifications.push(nid, followers);
});
});
});
});
};
User.isFollowing = function(uid, theirid, callback) {
db.isSetMember('following:' + uid, theirid, function(err, isMember) {
if (!err) {
callback(isMember);
} else {
console.log(err);
}
});
};
User.exists = function(userslug, callback) {
User.getUidByUserslug(userslug, function(err, exists) {
callback( !! exists);
});
};
User.count = function(socket) {
db.getObjectField('global', 'userCount', function(err, count) {
11 years ago
if(err) {
return;
}
socket.emit('user.count', {
count: count ? count : 0
});
});
};
User.getUidByUsername = function(username, callback) {
11 years ago
db.getObjectField('username:uid', username, callback);
};
User.getUidByUserslug = function(userslug, callback) {
11 years ago
db.getObjectField('userslug:uid', userslug, callback);
};
User.getUsernamesByUids = function(uids, callback) {
var usernames = [];
if (!Array.isArray(uids)) {
return callback([]);
}
function iterator(uid, callback) {
12 years ago
User.getUserField(uid, 'username', function(err, username) {
usernames.push(username);
callback(null);
});
}
async.eachSeries(uids, iterator, function(err) {
callback(usernames);
});
};
User.getUserSlugsByUids = function(uids, callback) {
var userslugs = [];
if (!Array.isArray(uids)) {
return callback([]);
}
function iterator(uid, callback) {
12 years ago
User.getUserField(uid, 'userslug', function(err, userslug) {
userslugs.push(userslug);
callback(null);
});
}
async.eachSeries(uids, iterator, function(err) {
callback(userslugs);
});
};
User.getUidByEmail = function(email, callback) {
11 years ago
db.getObjectField('email:uid', email, function(err, data) {
if (err) {
11 years ago
return callback(err);
}
11 years ago
callback(null, data);
});
};
User.getUidByTwitterId = function(twid, callback) {
11 years ago
db.getObjectField('twid:uid', twid, function(err, uid) {
if (err) {
11 years ago
return callback(err);
}
11 years ago
callback(null, uid);
});
};
User.getUidByGoogleId = function(gplusid, callback) {
11 years ago
db.getObjectField('gplusid:uid', gplusid, function(err, uid) {
if (err) {
11 years ago
return callback(err);
}
11 years ago
callback(null, uid);
});
};
User.getUidByFbid = function(fbid, callback) {
11 years ago
db.getObjectField('fbid:uid', fbid, function(err, uid) {
if (err) {
11 years ago
return callback(err);
}
11 years ago
callback(null, uid);
});
};
User.isModerator = function(uid, cid, callback) {
11 years ago
db.isSetMember('cid:' + cid + ':moderators', uid, function(err, exists) {
if(err) {
return calback(err);
}
callback(err, exists);
});
};
User.isAdministrator = function(uid, callback) {
11 years ago
groups.getGidFromName('Administrators', function(err, gid) {
groups.isMember(uid, gid, function(err, isAdmin) {
callback(err, isAdmin);
});
});
};
User.reset = {
validate: function(socket, code, callback) {
if (typeof callback !== 'function') {
callback = null;
}
11 years ago
db.getObjectField('reset:uid', code, function(err, uid) {
if (err) {
11 years ago
return callback(false);
}
if (uid !== null) {
11 years ago
db.getObjectField('reset:expiry', code, function(err, expiry) {
if (err) {
11 years ago
return callback(false);
}
if (expiry >= +Date.now() / 1000 | 0) {
if (!callback) {
socket.emit('user:reset.valid', {
valid: true
});
} else {
callback(true);
}
} else {
// Expired, delete from db
11 years ago
db.deleteObjectField('reset:uid', code);
db.deleteObjectField('reset:expiry', code);
if (!callback) {
socket.emit('user:reset.valid', {
valid: false
});
} else {
callback(false);
}
}
});
} else {
if (!callback) {
socket.emit('user:reset.valid', {
valid: false
});
} else {
callback(false);
}
}
});
},
send: function(socket, email) {
11 years ago
User.getUidByEmail(email, function(err, uid) {
if (uid !== null) {
// Generate a new reset code
var reset_code = utils.generateUUID();
11 years ago
db.setObjectField('reset:uid', reset_code, uid);
db.setobjectField('reset:expiry', reset_code, (60 * 60) + new Date() / 1000 | 0); // Active for one hour
12 years ago
var reset_link = nconf.get('url') + 'reset/' + reset_code,
reset_email = global.templates['emails/reset'].parse({
'RESET_LINK': reset_link
}),
reset_email_plaintext = global.templates['emails/reset_plaintext'].parse({
'RESET_LINK': reset_link
});
var message = emailjs.message.create({
text: reset_email_plaintext,
from: meta.config['email:from'] ? meta.config['email:from'] : '[email protected]',
to: email,
subject: 'Password Reset Requested',
attachment: [{
data: reset_email,
alternative: true
}]
});
emailjsServer.send(message, function(err, success) {
if (err === null) {
socket.emit('user.send_reset', {
status: "ok",
message: "code-sent",
email: email
});
} else {
socket.emit('user.send_reset', {
status: "error",
message: "send-failed"
});
winston.err(err);
}
});
} else {
socket.emit('user.send_reset', {
status: "error",
message: "invalid-email",
email: email
});
}
});
},
commit: function(socket, code, password) {
12 years ago
this.validate(socket, code, function(validated) {
if (validated) {
11 years ago
db.getObjectField('reset:uid', code, function(err, uid) {
if (err) {
11 years ago
return;
}
User.hashPassword(password, function(err, hash) {
12 years ago
User.setUserField(uid, 'password', hash);
11 years ago
events.logPasswordReset(uid);
12 years ago
});
11 years ago
db.deleteObjectField('reset:uid', code);
db.deleteObjectField('reset:expiry', code);
socket.emit('user:reset.commit', {
status: 'ok'
});
});
}
});
}
};
11 years ago
User.sendConfirmationEmail = function(email) {
if (meta.config['email:smtp:host'] && meta.config['email:smtp:port'] && meta.config['email:from']) {
var confirm_code = utils.generateUUID(),
confirm_link = nconf.get('url') + 'confirm/' + confirm_code,
confirm_email = global.templates['emails/header'] + global.templates['emails/email_confirm'].parse({
'CONFIRM_LINK': confirm_link
}) + global.templates['emails/footer'],
confirm_email_plaintext = global.templates['emails/email_confirm_plaintext'].parse({
'CONFIRM_LINK': confirm_link
});
// Email confirmation code
var expiry_time = Date.now() / 1000 + 60 * 60 * 2;
db.setObjectField('email:confirm', email, confirm_code);
db.setObjectField('confirm:email', confirm_code, email);
db.setObjectField('confirm:email', confirm_code + ':expire', expiry_time);
// Send intro email w/ confirm code
var message = emailjs.message.create({
text: confirm_email_plaintext,
from: meta.config['email:from'] || '[email protected]',
to: email,
subject: '[NodeBB] Registration Email Verification',
attachment: [{
data: confirm_email,
alternative: true
}]
});
emailjsServer.send(message, function(err, success) {
if (err) {
console.log(err);
}
});
}
};
User.pushNotifCount = function(uid) {
User.notifications.getUnreadCount(uid, function(err, count) {
if (!err) {
websockets.in('uid_' + uid).emit('event:notifications.updateCount', count);
} else {
winston.warn('[User.pushNotifCount] Count not retrieve unread notifications count to push to uid ' + uid + '\'s client(s)');
}
});
};
User.email = {
exists: function(socket, email, callback) {
11 years ago
User.getUidByEmail(email, function(err, exists) {
exists = !! exists;
if (typeof callback !== 'function') {
socket.emit('user.email.exists', {
exists: exists
});
} else {
callback(exists);
}
});
},
confirm: function(code, callback) {
11 years ago
db.getObjectFields('confirm:email', [code, code + ':expire'], function(err, data) {
if (err) {
11 years ago
return callback({
status:'error'
});
}
var email = data.email;
var expiry = data[code + ':expire'];
if (parseInt(expiry, 10) >= Date.now() / 1000) {
db.deleteObjectField('confirm:email', code);
db.deleteObjectField('confirm:email', code + ':expire');
return callback({
status: 'expired'
});
}
if (email !== null) {
11 years ago
db.setObjectField('email:confirm', email, true);
db.deleteObjectField('confirm:email', code);
db.deleteObjectField('confirm:email', code + ':expire');
callback({
status: 'ok'
});
} else {
callback({
status: 'not_ok'
});
}
});
}
};
User.notifications = {
get: function(uid, callback) {
var maxNotifs = 15;
async.parallel({
unread: function(next) {
11 years ago
db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, 10, function(err, nids) {
// @todo handle err
var unread = [];
// Cap the number of notifications returned
if (nids.length > maxNotifs) {
nids.length = maxNotifs;
}
if (nids && nids.length > 0) {
async.eachSeries(nids, function(nid, next) {
notifications.get(nid, uid, function(notif_data) {
// If the notification could not be found, silently drop it
if (notif_data) {
unread.push(notif_data);
} else {
11 years ago
db.sortedSetRemove('uid:' + uid + ':notifications:unread', nid);
}
next();
});
}, function(err) {
next(null, unread);
});
} else {
next(null, unread);
}
});
},
read: function(next) {
11 years ago
db.getSortedSetRevRange('uid:' + uid + ':notifications:read', 0, 10, function(err, nids) {
// @todo handle err
var read = [];
// Cap the number of notifications returned
if (nids.length > maxNotifs) {
nids.length = maxNotifs;
}
if (nids && nids.length > 0) {
async.eachSeries(nids, function(nid, next) {
notifications.get(nid, uid, function(notif_data) {
// If the notification could not be found, silently drop it
if (notif_data) {
read.push(notif_data);
} else {
11 years ago
db.sortedSetRemove('uid:' + uid + ':notifications:read', nid);
}
next();
});
}, function(err) {
next(null, read);
});
} else {
next(null, read);
}
});
}
}, function(err, notifications) {
// Limit the number of notifications to `maxNotifs`, prioritising unread notifications
if (notifications.read.length + notifications.unread.length > maxNotifs) {
notifications.read.length = maxNotifs - notifications.unread.length;
}
callback(notifications);
});
},
getAll: function(uid, limit, before, callback) {
var now = new Date();
if (!limit || parseInt(limit,10) <= 0) {
limit = 25;
}
if (before) {
before = new Date(parseInt(before, 10));
}
var args1 = ['uid:' + uid + ':notifications:read', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit];
var args2 = ['uid:' + uid + ':notifications:unread', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit];
db.getSortedSetRevRangeByScore(args1, function(err, results1) {
db.getSortedSetRevRangeByScore(args2, function(err, results2) {
var nids = results1.concat(results2);
async.map(nids, function(nid, next) {
notifications.get(nid, uid, function(notif_data) {
next(null, notif_data);
});
}, function(err, notifs) {
notifs = notifs.filter(function(notif) {
return notif !== null;
}).sort(function(a, b) {
return parseInt(b.datetime, 10) - parseInt(a.datetime, 10);
}).map(function(notif) {
notif.datetimeISO = new Date(parseInt(notif.datetime, 10)).toISOString();
notif.readClass = !notif.read ? 'unread' : '';
return notif;
});
callback(err, notifs);
});
});
});
},
getUnreadCount: function(uid, callback) {
db.sortedSetCount('uid:' + uid + ':notifications:unread', -Infinity, Infinity, callback);
},
getUnreadByUniqueId: function(uid, uniqueId, callback) {
11 years ago
db.getSortedSetRange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) {
async.filter(nids, function(nid, next) {
notifications.get(nid, uid, function(notifObj) {
if(!notifObj) {
next(false);
}
if (notifObj.uniqueId === uniqueId) {
next(true);
} else {
next(false);
}
});
}, function(nids) {
callback(null, nids);
});
});
}
};
}(exports));