Files
NodeBB/src/user/profile.js

349 lines
9.6 KiB
JavaScript
Raw Normal View History

'use strict';
2016-01-10 10:26:47 +02:00
var async = require('async');
var utils = require('../utils');
2016-01-10 10:26:47 +02:00
var meta = require('../meta');
var db = require('../database');
2016-06-29 22:58:05 +03:00
var groups = require('../groups');
2016-01-10 10:26:47 +02:00
var plugins = require('../plugins');
module.exports = function (User) {
User.updateProfile = function (uid, data, callback) {
var fields = ['username', 'email', 'fullname', 'website', 'location',
2016-12-14 21:58:14 +03:00
'groupTitle', 'birthday', 'signature', 'aboutme'];
2017-01-11 14:40:52 +03:00
var updateUid = data.uid;
var oldData;
async.waterfall([
function (next) {
2017-02-18 12:30:49 -07:00
plugins.fireHook('filter:user.updateProfile', { uid: uid, data: data, fields: fields }, next);
},
function (data, next) {
fields = data.fields;
data = data.data;
2018-01-12 17:29:47 -05:00
validateData(uid, data, next);
},
function (next) {
2017-01-11 14:40:52 +03:00
User.getUserFields(updateUid, fields, next);
},
function (_oldData, next) {
oldData = _oldData;
async.each(fields, function (field, next) {
if (!(data[field] !== undefined && typeof data[field] === 'string')) {
return next();
2014-10-15 16:26:40 -07:00
}
data[field] = data[field].trim();
if (field === 'email') {
2017-01-11 14:40:52 +03:00
return updateEmail(updateUid, data.email, next);
} else if (field === 'username') {
2017-01-11 14:40:52 +03:00
return updateUsername(updateUid, data.username, next);
} else if (field === 'fullname') {
2017-01-11 14:40:52 +03:00
return updateFullname(updateUid, data.fullname, next);
} else if (field === 'signature') {
2017-10-13 21:02:41 -06:00
data[field] = utils.stripHTMLTags(data[field]);
2016-02-23 15:35:16 +02:00
}
2017-01-11 14:40:52 +03:00
User.setUserField(updateUid, field, data[field], next);
}, next);
},
function (next) {
2017-02-24 12:46:40 -05:00
plugins.fireHook('action:user.updateProfile', { uid: uid, data: data, fields: fields, oldData: oldData });
2017-01-11 14:40:52 +03:00
User.getUserFields(updateUid, ['email', 'username', 'userslug', 'picture', 'icon:text', 'icon:bgColor'], next);
2017-02-17 19:31:21 -07:00
},
], callback);
};
2018-01-12 17:29:47 -05:00
function validateData(callerUid, data, callback) {
async.series([
async.apply(isEmailAvailable, data, data.uid),
async.apply(isUsernameAvailable, data, data.uid),
async.apply(isGroupTitleValid, data),
async.apply(isWebsiteValid, callerUid, data),
async.apply(isAboutMeValid, callerUid, data),
async.apply(isSignatureValid, callerUid, data),
], function (err) {
callback(err);
});
}
function isEmailAvailable(data, uid, callback) {
if (!data.email) {
return callback();
}
if (!utils.isEmailValid(data.email)) {
return callback(new Error('[[error:invalid-email]]'));
}
async.waterfall([
function (next) {
User.getUserField(uid, 'email', next);
},
function (email, next) {
if (email === data.email) {
return callback();
2016-06-29 22:58:05 +03:00
}
User.email.available(data.email, next);
},
function (available, next) {
next(!available ? new Error('[[error:email-taken]]') : null);
2017-02-17 19:31:21 -07:00
},
], callback);
}
function isUsernameAvailable(data, uid, callback) {
if (!data.username) {
return callback();
}
data.username = data.username.trim();
async.waterfall([
function (next) {
User.getUserFields(uid, ['username', 'userslug'], next);
},
function (userData, next) {
var userslug = utils.slugify(data.username);
if (data.username.length < meta.config.minimumUsernameLength) {
return next(new Error('[[error:username-too-short]]'));
}
if (data.username.length > meta.config.maximumUsernameLength) {
return next(new Error('[[error:username-too-long]]'));
2014-10-15 16:26:40 -07:00
}
if (!utils.isUserNameValid(data.username) || !userslug) {
return next(new Error('[[error:invalid-username]]'));
}
if (userslug === userData.userslug) {
return callback();
}
User.existsBySlug(userslug, next);
},
function (exists, next) {
next(exists ? new Error('[[error:username-taken]]') : null);
2017-02-17 19:31:21 -07:00
},
], callback);
}
function isGroupTitleValid(data, callback) {
if (data.groupTitle === 'registered-users' || groups.isPrivilegeGroup(data.groupTitle)) {
callback(new Error('[[error:invalid-group-title]]'));
} else {
callback();
}
}
2018-01-12 17:29:47 -05:00
function isWebsiteValid(callerUid, data, callback) {
if (!data.website) {
return setImmediate(callback);
}
checkMinReputation(callerUid, data.uid, 'min:rep:website', callback);
}
function isAboutMeValid(callerUid, data, callback) {
if (!data.aboutme) {
return setImmediate(callback);
}
if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) {
return callback(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]'));
}
checkMinReputation(callerUid, data.uid, 'min:rep:aboutme', callback);
}
function isSignatureValid(callerUid, data, callback) {
if (!data.signature) {
return setImmediate(callback);
}
if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) {
return callback(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]'));
}
checkMinReputation(callerUid, data.uid, 'min:rep:signature', callback);
}
function checkMinReputation(callerUid, uid, setting, callback) {
var isSelf = parseInt(callerUid, 10) === parseInt(uid, 10);
if (!isSelf) {
return setImmediate(callback);
}
async.waterfall([
function (next) {
User.getUserField(uid, 'reputation', next);
},
function (reputation, next) {
if (parseInt(reputation, 10) < (parseInt(meta.config[setting], 10) || 0)) {
return next(new Error('[[error:not-enough-reputation-' + setting.replace(/:/g, '-') + ']]'));
}
next();
},
], callback);
}
function updateEmail(uid, newEmail, callback) {
2016-12-14 21:58:14 +03:00
async.waterfall([
function (next) {
User.getUserField(uid, 'email', next);
},
function (oldEmail, next) {
oldEmail = oldEmail || '';
2014-10-01 00:29:47 -04:00
2016-12-14 21:58:14 +03:00
if (oldEmail === newEmail) {
return callback();
}
2016-12-14 21:58:14 +03:00
async.series([
async.apply(db.sortedSetRemove, 'email:uid', oldEmail.toLowerCase()),
2017-02-17 19:31:21 -07:00
async.apply(db.sortedSetRemove, 'email:sorted', oldEmail.toLowerCase() + ':' + uid),
2016-12-14 21:58:14 +03:00
], function (err) {
next(err);
});
},
function (next) {
async.parallel([
function (next) {
db.sortedSetAdd('email:uid', uid, newEmail.toLowerCase(), next);
},
function (next) {
db.sortedSetAdd('email:sorted', 0, newEmail.toLowerCase() + ':' + uid, next);
2015-05-19 23:04:28 -04:00
},
2016-12-14 21:58:14 +03:00
function (next) {
db.sortedSetAdd('user:' + uid + ':emails', Date.now(), newEmail + ':' + Date.now(), next);
},
function (next) {
User.setUserField(uid, 'email', newEmail, next);
},
function (next) {
2014-10-21 14:17:30 -04:00
if (parseInt(meta.config.requireEmailConfirmation, 10) === 1 && newEmail) {
User.email.sendValidationEmail(uid, {
email: newEmail,
});
}
User.setUserField(uid, 'email:confirmed', 0, next);
2016-01-20 16:12:57 +02:00
},
function (next) {
db.sortedSetAdd('users:notvalidated', Date.now(), uid, next);
2017-02-17 19:31:21 -07:00
},
2017-08-16 16:47:52 -04:00
function (next) {
User.reset.cleanByUid(uid, next);
},
2016-12-14 21:58:14 +03:00
], function (err) {
next(err);
});
2017-02-17 19:31:21 -07:00
},
2016-12-14 21:58:14 +03:00
], callback);
}
function updateUsername(uid, newUsername, callback) {
if (!newUsername) {
2017-11-27 12:52:08 -05:00
return setImmediate(callback);
}
2015-02-01 19:11:58 -05:00
2017-05-13 21:45:50 -04:00
async.waterfall([
function (next) {
User.getUserFields(uid, ['username', 'userslug'], next);
},
function (userData, next) {
2017-11-27 12:52:08 -05:00
if (userData.username === newUsername) {
return callback();
}
2017-05-13 21:45:50 -04:00
async.parallel([
function (next) {
updateUidMapping('username', uid, newUsername, userData.username, next);
},
function (next) {
var newUserslug = utils.slugify(newUsername);
updateUidMapping('userslug', uid, newUserslug, userData.userslug, next);
},
function (next) {
var now = Date.now();
async.series([
async.apply(db.sortedSetRemove, 'username:sorted', userData.username.toLowerCase() + ':' + uid),
async.apply(db.sortedSetAdd, 'username:sorted', 0, newUsername.toLowerCase() + ':' + uid),
async.apply(db.sortedSetAdd, 'user:' + uid + ':usernames', now, newUsername + ':' + now),
], next);
},
], next);
},
], function (err) {
callback(err);
});
}
function updateUidMapping(field, uid, value, oldValue, callback) {
if (value === oldValue) {
return callback();
}
async.series([
function (next) {
db.sortedSetRemove(field + ':uid', oldValue, next);
2015-01-08 15:49:00 -05:00
},
function (next) {
User.setUserField(uid, field, value, next);
2015-01-08 15:49:00 -05:00
},
function (next) {
if (value) {
db.sortedSetAdd(field + ':uid', uid, value, next);
2015-01-08 15:49:00 -05:00
} else {
next();
}
2017-02-17 19:31:21 -07:00
},
2015-01-08 15:49:00 -05:00
], callback);
}
function updateFullname(uid, newFullname, callback) {
async.waterfall([
function (next) {
User.getUserField(uid, 'fullname', next);
},
function (fullname, next) {
updateUidMapping('fullname', uid, newFullname, fullname, next);
2017-02-17 19:31:21 -07:00
},
], callback);
}
User.changePassword = function (uid, data, callback) {
if (!uid || !data || !data.uid) {
2014-04-09 22:26:23 -04:00
return callback(new Error('[[error:invalid-uid]]'));
2014-03-12 21:57:25 -04:00
}
2016-01-10 10:26:47 +02:00
async.waterfall([
function (next) {
User.isPasswordValid(data.newPassword, next);
},
function (next) {
if (parseInt(uid, 10) !== parseInt(data.uid, 10)) {
User.isAdministrator(uid, next);
} else {
User.isPasswordCorrect(uid, data.currentPassword, next);
}
},
function (isAdminOrPasswordMatch, next) {
if (!isAdminOrPasswordMatch) {
return next(new Error('[[error:change_password_error_wrong_current]]'));
2014-03-12 21:57:25 -04:00
}
2016-01-10 10:26:47 +02:00
User.hashPassword(data.newPassword, next);
},
function (hashedPassword, next) {
2015-04-01 17:26:22 -04:00
async.parallel([
2017-06-20 16:12:55 -04:00
async.apply(User.setUserFields, data.uid, {
password: hashedPassword,
rss_token: utils.generateUUID(),
}),
2017-02-17 19:31:21 -07:00
async.apply(User.reset.updateExpiry, data.uid),
2017-03-15 12:19:09 +03:00
async.apply(User.auth.revokeAllSessions, data.uid),
], function (err) {
2016-01-10 10:26:47 +02:00
next(err);
});
2017-02-17 19:31:21 -07:00
},
2016-01-10 10:26:47 +02:00
], callback);
2014-03-12 21:57:25 -04:00
};
2014-04-10 20:31:57 +01:00
};