Files
NodeBB/src/user.js

1027 lines
25 KiB
JavaScript
Raw Normal View History

var bcrypt = require('bcrypt'),
2013-08-29 13:40:04 -04:00
async = require('async'),
emailjs = require('emailjs'),
nconf = require('nconf'),
winston = require('winston'),
userSearch = require('reds').createSearch('nodebbusersearch'),
2013-11-22 14:08:02 -05:00
check = require('validator').check,
sanitize = require('validator').sanitize,
utils = require('./../public/src/utils'),
RDB = require('./redis'),
meta = require('./meta'),
emailjsServer = emailjs.server.connect(meta.config['email:smtp:host'] || '127.0.0.1'),
Groups = require('./groups'),
notifications = require('./notifications'),
topics = require('./topics');
2013-11-22 14:08:02 -05:00
(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) {
2013-11-07 12:17:23 -05:00
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) {
2013-07-24 12:04:32 -04:00
if (email !== undefined) {
2013-09-11 15:24:01 -04:00
User.isEmailAvailable(email, function(err, available) {
if (err) {
2013-09-11 15:24:01 -04:00
return next(err);
}
2013-07-24 12:04:32 -04:00
next(!available ? new Error('Email taken!') : null);
});
} else {
next();
}
}
], function(err, results) {
if (err) {
return callback(err, null);
}
RDB.incr('global:next_user_id', function(err, uid) {
RDB.handle(err);
var gravatar = User.createGravatarURLFromEmail(email);
var timestamp = Date.now();
2013-08-23 13:14:36 -04:00
2013-09-17 13:09:37 -04:00
RDB.hmset('user:' + uid, {
'uid': uid,
2013-09-17 13:09:37 -04:00
'username': username,
'userslug': userslug,
'fullname': '',
2013-09-17 13:09:37 -04:00
'location': '',
'birthday': '',
'website': '',
'email': email || '',
'signature': '',
'joindate': timestamp,
'picture': gravatar,
2013-09-17 13:09:37 -04:00
'gravatarpicture': gravatar,
'uploadedpicture': '',
'profileviews': 0,
'reputation': 0,
'postcount': 0,
'lastposttime': 0,
'banned': 0,
2013-07-25 14:30:12 -04:00
'showemail': 0
});
2013-08-23 13:14:36 -04:00
2013-09-14 22:28:50 -04:00
RDB.hset('username:uid', username, uid);
RDB.hset('userslug:uid', userslug, uid);
2013-07-24 12:04:32 -04:00
if (email !== undefined) {
2013-09-14 22:28:50 -04:00
RDB.hset('email:uid', email, uid);
2013-07-24 12:04:32 -04:00
User.sendConfirmationEmail(email);
}
RDB.incr('usercount');
RDB.zadd('users:joindate', timestamp, uid);
RDB.zadd('users:postcount', 0, uid);
RDB.zadd('users:reputation', 0, uid);
2013-08-23 13:14:36 -04:00
2013-08-29 13:40:04 -04:00
userSearch.index(username, uid);
2013-07-24 12:04:32 -04:00
if (password !== undefined) {
User.hashPassword(password, function(err, hash) {
User.setUserField(uid, 'password', hash);
callback(null, uid);
});
} else {
callback(null, uid);
}
});
});
};
2013-08-23 13:14:36 -04:00
User.ban = function(uid, callback) {
User.setUserField(uid, 'banned', 1, callback);
};
User.unban = function(uid, callback) {
2013-08-23 13:14:36 -04:00
User.setUserField(uid, 'banned', 0, callback);
};
2013-08-23 13:14:36 -04:00
User.getUserField = function(uid, field, callback) {
2013-08-23 14:57:33 -04:00
RDB.hget('user:' + uid, field, callback);
};
2013-07-05 16:37:45 -05:00
User.getUserFields = function(uid, fields, callback) {
2013-08-23 13:14:36 -04:00
RDB.hmgetObject('user:' + uid, fields, callback);
};
User.getMultipleUserFields = function(uids, fields, callback) {
2013-09-17 13:09:37 -04:00
if (uids.length === 0) {
2013-09-11 12:49:54 -04:00
return callback(null, []);
}
2013-07-05 19:08:59 -04:00
var returnData = [];
var uuids = uids.filter(function(value, index, self) {
return self.indexOf(value) === index;
});
2013-09-11 12:49:54 -04:00
function iterator(uid, next) {
2013-08-23 13:14:36 -04:00
User.getUserFields(uid, fields, function(err, userData) {
if (err) {
2013-09-11 12:49:54 -04:00
return next(err);
}
2013-07-05 19:08:59 -04:00
returnData.push(userData);
2013-09-11 12:49:54 -04:00
next(null);
2013-07-05 19:08:59 -04:00
});
}
2013-07-05 19:08:59 -04:00
2013-07-19 15:26:29 -04:00
async.eachSeries(uuids, iterator, function(err) {
2013-09-11 12:49:54 -04:00
callback(err, returnData);
2013-07-05 19:08:59 -04:00
});
};
User.getUserData = function(uid, callback) {
2013-07-02 16:24:13 -04:00
RDB.hgetall('user:' + uid, function(err, data) {
2013-09-11 13:02:55 -04:00
if (data && data.password) {
delete data.password;
}
2013-09-11 13:02:55 -04:00
callback(err, data);
});
};
User.filterBannedUsers = function(users) {
return users.filter(function(user) {
return (!user.banned || user.banned === '0');
});
};
User.updateProfile = function(uid, data, callback) {
2013-05-17 14:48:05 -04:00
var fields = ['email', 'fullname', 'website', 'location', 'birthday', 'signature'];
2013-09-17 13:09:37 -04:00
var returnData = {
success: false
};
2013-07-08 14:14:18 -04:00
function isSignatureValid(next) {
if (data.signature !== undefined && data.signature.length > 150) {
2013-09-17 13:09:37 -04:00
next({
error: 'Signature can\'t be longer than 150 characters!'
}, false);
2013-07-08 14:14:18 -04:00
} else {
2013-08-23 13:14:36 -04:00
next(null, true);
}
2013-07-08 14:14:18 -04:00
}
2013-08-23 13:14:36 -04:00
2013-07-08 14:14:18 -04:00
function isEmailAvailable(next) {
if (!data.email) {
return next(null, true);
2013-07-08 14:14:18 -04:00
}
2013-08-26 11:58:52 -04:00
User.getUserField(uid, 'email', function(err, email) {
if (email !== data.email) {
User.isEmailAvailable(data.email, function(err, available) {
if (err) {
2013-09-11 15:24:01 -04:00
return next(err, null);
}
2013-09-17 13:09:37 -04:00
if (!available) {
next({
error: 'Email not available!'
}, false);
} else {
2013-08-23 13:14:36 -04:00
next(null, true);
}
});
} else {
2013-08-23 13:14:36 -04:00
next(null, true);
}
});
2013-05-21 13:44:53 -04:00
}
2013-08-23 13:14:36 -04:00
2013-07-08 14:14:18 -04:00
async.series([isSignatureValid, isEmailAvailable], function(err, results) {
2013-09-17 13:09:37 -04:00
if (err) {
2013-09-11 13:53:42 -04:00
callback(err, returnData);
2013-07-08 14:14:18 -04:00
} else {
async.each(fields, updateField, function(err) {
2013-09-17 13:09:37 -04:00
if (err) {
2013-09-11 13:53:42 -04:00
callback(err, returnData);
} else {
returnData.success = true;
2013-09-11 13:53:42 -04:00
callback(null, returnData);
}
});
2013-07-08 14:14:18 -04:00
}
});
2013-09-11 13:53:42 -04:00
function updateField(field, next) {
if (data[field] !== undefined && typeof data[field] === 'string') {
2013-11-22 14:08:02 -05:00
data[field] = data[field].trim();
data[field] = sanitize(data[field]).escape();
2013-09-17 13:09:37 -04:00
if (field === 'email') {
var gravatarpicture = User.createGravatarURLFromEmail(data[field]);
User.setUserField(uid, 'gravatarpicture', gravatarpicture);
2013-08-23 13:14:36 -04:00
User.getUserFields(uid, ['email', 'picture', 'uploadedpicture'], function(err, userData) {
if (err) {
2013-09-11 13:53:42 -04:00
return next(err);
}
2013-08-23 13:14:36 -04:00
RDB.hdel('email:uid', userData.email);
RDB.hset('email:uid', data.email, uid);
User.setUserField(uid, field, data[field]);
2013-09-17 13:09:37 -04:00
if (userData.picture !== userData.uploadedpicture) {
returnData.picture = gravatarpicture;
User.setUserField(uid, 'picture', gravatarpicture);
}
returnData.gravatarpicture = gravatarpicture;
2013-09-11 13:53:42 -04:00
next(null);
});
return;
2013-09-17 13:09:37 -04:00
} else if (field === 'signature') {
data[field] = utils.strip_tags(data[field]);
2013-11-22 14:08:02 -05:00
} else if (field === 'website') {
if(data[field].substr(0, 7) !== 'http://' && data[field].substr(0, 8) !== 'https://') {
data[field] = 'http://' + data[field];
}
2013-08-23 13:14:36 -04:00
}
2013-07-05 16:37:45 -05:00
2013-08-23 13:14:36 -04:00
User.setUserField(uid, field, data[field]);
2013-07-08 14:14:18 -04:00
2013-09-11 13:53:42 -04:00
next(null);
} else {
2013-09-11 13:53:42 -04:00
next(null);
}
}
};
2013-07-05 16:37:45 -05:00
2013-07-08 14:14:18 -04:00
User.isEmailAvailable = function(email, callback) {
2013-09-14 22:28:50 -04:00
RDB.hexists('email:uid', email, function(err, exists) {
callback(err, !exists);
2013-07-08 14:14:18 -04:00
});
};
User.changePassword = function(uid, data, callback) {
2013-09-17 13:09:37 -04:00
if (!utils.isPasswordValid(data.newPassword)) {
return callback({
error: 'Invalid password!'
});
2013-07-08 12:10:21 -04:00
}
2013-08-26 11:58:52 -04:00
User.getUserField(uid, 'password', function(err, user_password) {
2013-07-08 12:10:21 -04:00
bcrypt.compare(data.currentPassword, user_password, function(err, res) {
2013-09-17 13:09:37 -04:00
if (err) {
2013-09-11 15:24:01 -04:00
return callback(err);
2013-07-08 12:10:21 -04:00
}
if (res) {
User.hashPassword(data.newPassword, function(err, hash) {
2013-07-08 12:10:21 -04:00
User.setUserField(uid, 'password', hash);
2013-09-11 15:24:01 -04:00
callback(null);
2013-07-08 12:10:21 -04:00
});
} else {
2013-09-17 13:09:37 -04:00
callback({
error: 'Your current password is not correct!'
});
2013-07-08 12:10:21 -04:00
}
});
});
};
2013-07-08 12:10:21 -04:00
User.setUserField = function(uid, field, value, callback) {
RDB.hset('user:' + uid, field, value, callback);
};
2013-11-27 15:02:09 -05:00
User.setUserFields = function(uid, data, callback) {
RDB.hmset('user:' + uid, data, callback);
};
2013-07-24 13:12:56 -04:00
User.incrementUserFieldBy = function(uid, field, value, callback) {
RDB.hincrby('user:' + uid, field, value, callback);
};
User.decrementUserFieldBy = function(uid, field, value, callback) {
RDB.hincrby('user:' + uid, field, -value, callback);
};
User.getUsers = function(set, start, stop, callback) {
var data = [];
2013-08-23 13:14:36 -04:00
RDB.zrevrange(set, start, stop, function(err, uids) {
2013-09-17 13:09:37 -04:00
if (err) {
return callback(err, null);
}
2013-08-23 13:14:36 -04:00
2013-09-30 16:24:00 -04:00
function iterator(uid, callback) {
User.getUserData(uid, function(err, userData) {
User.isAdministrator(uid, function(err, isAdmin) {
2013-09-30 16:24:00 -04:00
if (userData) {
userData.administrator = isAdmin?"1":"0";
data.push(userData);
}
callback(null);
});
});
2013-09-30 16:24:00 -04:00
}
async.eachSeries(uids, iterator, function(err) {
callback(err, data);
});
});
2013-09-30 16:24:00 -04:00
};
User.createGravatarURLFromEmail = function(email) {
2013-07-12 16:23:35 -04:00
var options = {
size: '128',
default: 'identicon',
rating: 'pg'
},
https = nconf.get('https');
2013-08-23 13:14:36 -04:00
if (!email) {
2013-07-12 16:23:35 -04:00
email = '';
options.forcedefault = 'y';
}
2013-07-12 16:23:35 -04:00
return require('gravatar').url(email, options, https);
};
User.hashPassword = function(password, callback) {
2013-09-17 13:09:37 -04:00
if (!password) {
callback(password);
return;
}
2013-08-23 13:14:36 -04:00
bcrypt.genSalt(nconf.get('bcrypt_rounds'), function(err, salt) {
bcrypt.hash(password, salt, callback);
});
};
2013-08-29 13:40:04 -04:00
User.reIndexAll = function(callback) {
User.getUsers('users:joindate', 0, -1, function(err, usersData) {
2013-09-17 13:09:37 -04:00
if (err) {
2013-08-29 13:40:04 -04:00
return callback(err, null);
}
function reIndexUser(uid, username) {
2013-09-17 13:09:37 -04:00
userSearch.remove(uid, function() {
2013-08-29 13:40:04 -04:00
userSearch.index(username, uid);
});
2013-08-29 13:40:04 -04:00
}
2013-09-17 13:09:37 -04:00
for (var i = 0; i < usersData.length; ++i) {
2013-08-29 13:40:04 -04:00
reIndexUser(usersData[i].uid, usersData[i].username);
}
callback(null, 1);
});
};
2013-08-29 13:40:04 -04:00
2013-06-26 12:32:30 -04:00
User.search = function(username, callback) {
2013-09-17 13:09:37 -04:00
if (!username) {
callback([]);
return;
}
userSearch.query(username).type('or').end(function(err, uids) {
2013-09-17 13:09:37 -04:00
if (err) {
2013-06-26 12:32:30 -04:00
console.log(err);
2013-08-29 13:40:04 -04:00
return;
}
2013-09-17 13:09:37 -04:00
if (uids && uids.length) {
2013-08-29 13:40:04 -04:00
User.getDataForUsers(uids, function(userdata) {
callback(userdata);
});
} else {
callback([]);
}
2013-06-26 12:32:30 -04:00
});
};
2013-06-26 12:32:30 -04:00
2013-07-02 16:24:13 -04:00
User.onNewPostMade = function(uid, tid, pid, timestamp) {
2013-07-10 11:51:15 -04:00
User.addPostIdToUser(uid, pid);
User.incrementUserFieldBy(uid, 'postcount', 1, function(err, newpostcount) {
RDB.zadd('users:postcount', newpostcount, uid);
});
2013-08-23 13:14:36 -04:00
2013-07-02 16:24:13 -04:00
User.setUserField(uid, 'lastposttime', timestamp);
User.sendPostNotificationToFollowers(uid, tid, pid);
};
2013-07-02 16:24:13 -04:00
User.addPostIdToUser = function(uid, pid) {
RDB.lpush('uid:' + uid + ':posts', pid);
};
2013-07-02 16:24:13 -04:00
User.addTopicIdToUser = function(uid, tid) {
RDB.lpush('uid:' + uid + ':topics', tid);
};
2013-07-02 16:24:13 -04:00
User.getPostIds = function(uid, start, end, callback) {
RDB.lrange('uid:' + uid + ':posts', start, end, function(err, pids) {
2013-09-17 13:09:37 -04:00
if (!err) {
if (pids && pids.length) {
2013-07-02 16:24:13 -04:00
callback(pids);
} else {
2013-07-02 16:24:13 -04:00
callback([]);
}
} else {
2013-07-02 16:24:13 -04:00
console.log(err);
callback([]);
}
});
};
2013-07-02 16:24:13 -04:00
2013-09-17 13:09:37 -04:00
User.sendConfirmationEmail = function(email) {
2013-09-26 13:56:36 -04:00
if (meta.config['email:smtp:host'] && meta.config['email:smtp:port'] && meta.config['email:from']) {
2013-05-28 11:50:09 -04:00
var confirm_code = utils.generateUUID(),
2013-08-23 13:14:36 -04:00
confirm_link = nconf.get('url') + 'confirm/' + confirm_code,
2013-09-17 13:09:37 -04:00
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
});
2013-05-28 11:50:09 -04:00
// Email confirmation code
2013-09-17 13:09:37 -04:00
var expiry_time = 60 * 60 * 2, // Expire after 2 hours
2013-05-28 11:50:09 -04:00
email_key = 'email:' + email + ':confirm',
confirm_key = 'confirm:' + confirm_code + ':email';
RDB.set(email_key, confirm_code);
RDB.expire(email_key, expiry_time);
RDB.set(confirm_key, email);
RDB.expire(confirm_key, expiry_time);
2013-08-23 13:14:36 -04:00
// Send intro email w/ confirm code
2013-05-28 11:50:09 -04:00
var message = emailjs.message.create({
text: confirm_email_plaintext,
2013-09-26 13:31:38 -04:00
from: meta.config['email:from'] || 'localhost@example.org',
2013-05-28 11:50:09 -04:00
to: email,
subject: '[NodeBB] Registration Email Verification',
2013-09-17 13:09:37 -04:00
attachment: [{
data: confirm_email,
alternative: true
}]
2013-05-28 11:50:09 -04:00
});
2013-07-05 16:37:45 -05:00
2013-05-28 11:50:09 -04:00
emailjsServer.send(message, function(err, success) {
if (err) {
2013-05-28 11:50:09 -04:00
console.log(err);
}
2013-05-28 11:50:09 -04:00
});
}
};
User.follow = function(uid, followid, callback) {
RDB.sadd('following:' + uid, followid, function(err, data) {
2013-09-17 13:09:37 -04:00
if (!err) {
RDB.sadd('followers:' + followid, uid, function(err, data) {
2013-09-17 13:09:37 -04:00
if (!err) {
callback(true);
} else {
console.log(err);
callback(false);
}
});
} else {
console.log(err);
callback(false);
}
});
};
User.unfollow = function(uid, unfollowid, callback) {
2013-09-17 13:09:37 -04:00
RDB.srem('following:' + uid, unfollowid, function(err, data) {
if (!err) {
RDB.srem('followers:' + unfollowid, uid, function(err, data) {
callback(data);
});
} else {
console.log(err);
}
});
};
User.getFollowing = function(uid, callback) {
RDB.smembers('following:' + uid, function(err, userIds) {
2013-09-17 13:09:37 -04:00
if (!err) {
User.getDataForUsers(userIds, callback);
} else {
console.log(err);
}
});
};
User.getFollowers = function(uid, callback) {
RDB.smembers('followers:' + uid, function(err, userIds) {
2013-09-17 13:09:37 -04:00
if (!err) {
User.getDataForUsers(userIds, callback);
} else {
2013-07-05 16:37:45 -05:00
console.log(err);
}
2013-05-15 11:46:41 -04:00
});
};
2013-07-05 16:37:45 -05:00
User.getFollowingCount = function(uid, callback) {
RDB.smembers('following:' + uid, function(err, userIds) {
2013-10-11 14:29:34 -04:00
if (err) {
2013-07-05 16:37:45 -05:00
console.log(err);
2013-10-11 14:29:34 -04:00
} else {
userIds = userIds.filter(function(value) {
return value !== '0';
2013-10-11 14:29:34 -04:00
});
callback(userIds.length);
}
});
};
2013-07-05 16:37:45 -05:00
User.getFollowerCount = function(uid, callback) {
RDB.smembers('followers:' + uid, function(err, userIds) {
2013-10-11 14:29:34 -04:00
if(err) {
console.log(err);
2013-10-11 14:29:34 -04:00
} else {
userIds = userIds.filter(function(value) {
return value !== '0';
2013-10-11 14:29:34 -04:00
});
callback(userIds.length);
}
});
};
2013-07-05 16:37:45 -05:00
2013-07-05 19:08:59 -04:00
User.getDataForUsers = function(uids, callback) {
var returnData = [];
2013-09-17 13:09:37 -04:00
if (!uids || !Array.isArray(uids) || uids.length === 0) {
callback(returnData);
return;
}
2013-07-05 19:11:27 -04:00
function iterator(uid, callback) {
if(uid === "0") {
2013-10-11 14:29:34 -04:00
return callback(null);
}
2013-10-11 14:29:34 -04:00
2013-09-11 13:02:55 -04:00
User.getUserData(uid, function(err, userData) {
returnData.push(userData);
2013-07-05 19:08:59 -04:00
callback(null);
2013-07-05 16:37:45 -05:00
});
}
2013-08-23 13:14:36 -04:00
2013-07-19 15:26:29 -04:00
async.eachSeries(uids, iterator, function(err) {
2013-07-05 19:08:59 -04:00
callback(returnData);
});
};
2013-07-05 16:37:45 -05:00
User.sendPostNotificationToFollowers = function(uid, tid, pid) {
2013-08-26 11:58:52 -04:00
User.getUserField(uid, 'username', function(err, username) {
RDB.smembers('followers:' + uid, function(err, followers) {
topics.getTopicField(tid, 'slug', function(err, slug) {
2013-08-27 13:04:18 -04:00
var message = '<strong>' + username + '</strong> made a new post';
2013-06-18 10:47:43 -04:00
notifications.create(message, nconf.get('relative_path') + '/topic/' + slug + '#' + pid, 'topic:' + tid, function(nid) {
2013-09-17 13:09:37 -04:00
notifications.push(nid, followers);
2013-06-18 10:47:43 -04:00
});
});
});
});
};
2013-05-15 11:46:41 -04:00
User.isFollowing = function(uid, theirid, callback) {
RDB.sismember('following:' + uid, theirid, function(err, data) {
2013-09-17 13:09:37 -04:00
if (!err) {
callback(data === 1);
} else {
console.log(err);
}
});
};
User.exists = function(userslug, callback) {
2013-10-30 18:31:36 -04:00
User.getUidByUserslug(userslug, function(err, exists) {
2013-09-17 13:09:37 -04:00
callback( !! exists);
});
};
2013-07-05 16:37:45 -05:00
User.count = function(socket) {
RDB.get('usercount', function(err, count) {
RDB.handle(err);
2013-09-17 13:09:37 -04:00
socket.emit('user.count', {
count: count ? count : 0
});
});
};
2013-07-05 16:37:45 -05:00
2013-10-30 18:31:36 -04:00
User.getUidByUsername = function(username, callback) {
2013-09-15 10:11:29 -04:00
RDB.hget('username:uid', username, callback);
};
2013-10-30 18:31:36 -04:00
User.getUidByUserslug = function(userslug, callback) {
RDB.hget('userslug:uid', userslug, callback);
};
2013-06-24 14:33:53 -04:00
2013-10-30 18:31:36 -04:00
User.getUsernamesByUids = function(uids, callback) {
var usernames = [];
if (!Array.isArray(uids)) {
return callback([]);
}
2013-07-05 16:37:45 -05:00
2013-07-05 19:11:27 -04:00
function iterator(uid, callback) {
2013-08-26 11:58:52 -04:00
User.getUserField(uid, 'username', function(err, username) {
usernames.push(username);
2013-07-05 19:11:27 -04:00
callback(null);
});
}
2013-07-05 19:11:27 -04:00
2013-07-19 15:26:29 -04:00
async.eachSeries(uids, iterator, function(err) {
2013-08-23 13:14:36 -04:00
callback(usernames);
2013-07-05 19:11:27 -04:00
});
};
2013-06-24 14:33:53 -04:00
2013-10-30 18:31:36 -04:00
User.getUserSlugsByUids = function(uids, callback) {
2013-06-24 14:33:53 -04:00
var userslugs = [];
if (!Array.isArray(uids)) {
return callback([]);
}
2013-07-05 16:37:45 -05:00
2013-07-05 18:19:55 -04:00
function iterator(uid, callback) {
2013-08-26 11:58:52 -04:00
User.getUserField(uid, 'userslug', function(err, userslug) {
2013-07-05 18:19:55 -04:00
userslugs.push(userslug);
callback(null);
});
}
2013-07-19 15:26:29 -04:00
async.eachSeries(uids, iterator, function(err) {
2013-08-23 13:14:36 -04:00
callback(userslugs);
2013-07-05 18:19:55 -04:00
});
};
2013-10-30 18:31:36 -04:00
User.getUidByEmail = function(email, callback) {
2013-09-14 22:28:50 -04:00
RDB.hget('email:uid', email, function(err, data) {
if (err) {
RDB.handle(err);
}
callback(data);
});
};
2013-10-30 18:31:36 -04:00
User.getUidByTwitterId = function(twid, callback) {
RDB.hget('twid:uid', twid, function(err, uid) {
if (err) {
RDB.handle(err);
}
callback(uid);
});
};
2013-10-30 18:31:36 -04:00
User.getUidByGoogleId = function(gplusid, callback) {
RDB.hget('gplusid:uid', gplusid, function(err, uid) {
if (err) {
RDB.handle(err);
}
callback(uid);
2013-07-05 16:37:45 -05:00
});
};
2013-10-30 18:31:36 -04:00
User.getUidByFbid = function(fbid, callback) {
RDB.hget('fbid:uid', fbid, function(err, uid) {
if (err) {
RDB.handle(err);
}
callback(uid);
2013-07-05 16:37:45 -05:00
});
};
2013-05-16 12:49:39 -04:00
User.isModerator = function(uid, cid, callback) {
RDB.sismember('cid:' + cid + ':moderators', uid, function(err, exists) {
2013-07-20 15:23:04 -04:00
RDB.handle(err);
callback(err, !! exists);
});
};
User.isAdministrator = function(uid, callback) {
2013-09-07 15:49:23 -04:00
Groups.getGidFromName('Administrators', function(err, gid) {
Groups.isMember(uid, gid, function(err, isAdmin) {
callback(err, !! isAdmin);
2013-09-07 15:49:23 -04:00
});
2013-06-20 14:45:38 -04:00
});
};
2013-06-20 14:45:38 -04:00
User.reset = {
validate: function(socket, code, callback) {
2013-08-23 13:14:36 -04:00
if (typeof callback !== 'function') {
callback = null;
}
2013-09-19 12:29:25 -04:00
RDB.hget('reset:uid', code, function(err, uid) {
if (err) {
RDB.handle(err);
}
if (uid !== null) {
2013-09-19 12:29:25 -04:00
RDB.hget('reset:expiry', code, function(err, expiry) {
if (err) {
RDB.handle(err);
}
2013-09-17 13:09:37 -04:00
if (expiry >= +Date.now() / 1000 | 0) {
if (!callback) {
2013-09-17 13:09:37 -04:00
socket.emit('user:reset.valid', {
valid: true
});
} else {
callback(true);
}
} else {
// Expired, delete from db
2013-09-19 12:29:25 -04:00
RDB.hdel('reset:uid', code);
RDB.hdel('reset:expiry', code);
if (!callback) {
2013-09-17 13:09:37 -04:00
socket.emit('user:reset.valid', {
valid: false
});
} else {
callback(false);
}
}
});
} else {
if (!callback) {
2013-09-17 13:09:37 -04:00
socket.emit('user:reset.valid', {
valid: false
});
} else {
callback(false);
}
}
});
},
send: function(socket, email) {
2013-10-30 18:31:36 -04:00
User.getUidByEmail(email, function(uid) {
if (uid !== null) {
// Generate a new reset code
var reset_code = utils.generateUUID();
2013-09-19 12:29:25 -04:00
RDB.hset('reset:uid', reset_code, uid);
RDB.hset('reset:expiry', reset_code, (60 * 60) + new Date() / 1000 | 0); // Active for one hour
2013-08-19 13:31:04 -04:00
var reset_link = nconf.get('url') + 'reset/' + reset_code,
2013-09-17 13:09:37 -04:00
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,
2013-09-26 13:31:38 -04:00
from: meta.config['email:from'] ? meta.config['email:from'] : 'localhost@example.org',
to: email,
subject: 'Password Reset Requested',
2013-09-17 13:09:37 -04:00
attachment: [{
data: reset_email,
alternative: true
}]
});
2013-07-05 16:37:45 -05:00
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"
});
2013-09-26 12:29:35 -04:00
winston.err(err);
}
});
} else {
socket.emit('user.send_reset', {
status: "error",
message: "invalid-email",
email: email
});
}
});
},
commit: function(socket, code, password) {
2013-08-19 13:31:04 -04:00
this.validate(socket, code, function(validated) {
if (validated) {
2013-09-19 12:29:25 -04:00
RDB.hget('reset:uid', code, function(err, uid) {
if (err) {
RDB.handle(err);
}
User.hashPassword(password, function(err, hash) {
2013-08-19 13:31:04 -04:00
User.setUserField(uid, 'password', hash);
});
2013-09-19 12:29:25 -04:00
RDB.hdel('reset:uid', code);
RDB.hdel('reset:expiry', code);
2013-09-17 13:09:37 -04:00
socket.emit('user:reset.commit', {
status: 'ok'
});
});
}
});
}
};
User.email = {
exists: function(socket, email, callback) {
2013-10-30 18:31:36 -04:00
User.getUidByEmail(email, function(exists) {
2013-09-17 13:09:37 -04:00
exists = !! exists;
if (typeof callback !== 'function') {
2013-09-17 13:09:37 -04:00
socket.emit('user.email.exists', {
exists: exists
});
} else {
callback(exists);
}
});
},
confirm: function(code, callback) {
RDB.get('confirm:' + code + ':email', function(err, email) {
if (err) {
RDB.handle(err);
}
if (email !== null) {
RDB.set('email:' + email + ':confirm', true);
RDB.del('confirm:' + code + ':email');
2013-09-17 13:09:37 -04:00
callback({
status: 'ok'
});
} else {
2013-09-17 13:09:37 -04:00
callback({
status: 'not_ok'
});
}
});
}
};
2013-05-22 21:43:01 -04:00
User.notifications = {
get: function(uid, callback) {
2013-09-17 13:09:37 -04:00
var maxNotifs = 15;
2013-05-22 21:43:01 -04:00
async.parallel({
unread: function(next) {
RDB.zrevrange('uid:' + uid + ':notifications:unread', 0, 10, function(err, nids) {
// @todo handle err
2013-05-22 21:43:01 -04:00
var unread = [];
// Cap the number of notifications returned
if (nids.length > maxNotifs) {
nids.length = maxNotifs;
}
2013-05-22 21:43:01 -04:00
if (nids && nids.length > 0) {
async.eachSeries(nids, function(nid, next) {
notifications.get(nid, uid, function(notif_data) {
2013-10-28 14:53:41 -04:00
// If the notification could not be found, silently drop it
if (notif_data) {
unread.push(notif_data);
} else {
RDB.zrem('uid:' + uid + ':notifications:unread', nid);
}
2013-05-22 21:43:01 -04:00
next();
});
}, function(err) {
next(null, unread);
});
} else {
next(null, unread);
}
2013-05-22 21:43:01 -04:00
});
},
read: function(next) {
RDB.zrevrange('uid:' + uid + ':notifications:read', 0, 10, function(err, nids) {
// @todo handle err
2013-05-22 21:43:01 -04:00
var read = [];
// Cap the number of notifications returned
if (nids.length > maxNotifs) {
nids.length = maxNotifs;
}
2013-05-22 21:43:01 -04:00
if (nids && nids.length > 0) {
async.eachSeries(nids, function(nid, next) {
notifications.get(nid, uid, function(notif_data) {
2013-10-28 14:53:41 -04:00
// If the notification could not be found, silently drop it
if (notif_data) {
read.push(notif_data);
} else {
RDB.zrem('uid:' + uid + ':notifications:read', nid);
}
2013-05-22 21:43:01 -04:00
next();
});
}, function(err) {
next(null, read);
});
} else {
next(null, read);
}
2013-05-22 21:43:01 -04:00
});
}
}, 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;
}
2013-05-22 21:43:01 -04:00
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));
}
RDB.multi()
.zrevrangebyscore('uid:' + uid + ':notifications:read', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit)
.zrevrangebyscore('uid:' + uid + ':notifications:unread', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit)
.exec(function(err, results) {
// Merge the read and unread notifications
var nids = results[0].concat(results[1]);
async.map(nids, function(nid, next) {
notifications.get(nid, uid, function(notif_data) {
next(null, notif_data);
});
}, function(err, notifs) {
2013-11-18 15:09:34 -05:00
notifs = notifs.filter(function(notif) {
2013-11-18 15:56:12 -05:00
return notif !== null;
2013-11-18 15:09:34 -05:00
}).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) {
RDB.zcount('uid:' + uid + ':notifications:unread', 0, 10, callback);
},
getUnreadByUniqueId: function(uid, uniqueId, callback) {
RDB.zrange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) {
async.filter(nids, function(nid, next) {
notifications.get(nid, uid, function(notifObj) {
if (notifObj.uniqueId === uniqueId) {
next(true);
} else {
next(false);
}
});
}, function(nids) {
callback(null, nids);
});
});
2013-05-22 21:43:01 -04:00
}
};
2013-09-17 13:09:37 -04:00
}(exports));