From ef97f627f4c13f2d31a8d1407f3dcb85837f70ec Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 17 Mar 2014 17:25:22 -0400 Subject: [PATCH 1/5] closes #1237 --- public/src/ajaxify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 79d1482860..c90461dcb1 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -198,7 +198,7 @@ var ajaxify = {}; function checkCallback() { numLocations--; - if (numLocations <= 0 && callback) { + if (numLocations < 0 && callback) { callback(); } } From 8efc33d6a1ed06480fcebf6a859d4feef0bfcd24 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 17 Mar 2014 17:27:42 -0400 Subject: [PATCH 2/5] closes #976 --- src/categories.js | 16 +++++++++++----- src/posts.js | 5 ++--- src/topics.js | 13 ++++++++----- src/user.js | 13 ++++++++----- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/categories.js b/src/categories.js index e75ea055af..46673d0873 100644 --- a/src/categories.js +++ b/src/categories.js @@ -10,6 +10,7 @@ var db = require('./database'), plugins = require('./plugins'), CategoryTools = require('./categoryTools'), meta = require('./meta'), + emitter = require('./emitter'), async = require('async'), winston = require('winston'), @@ -299,19 +300,24 @@ var db = require('./database'), }); }; - Categories.onNewPostMade = function(uid, tid, pid, timestamp) { - topics.getTopicFields(tid, ['cid', 'pinned'], function(err, topicData) { + Categories.onNewPostMade = function(postData) { + topics.getTopicFields(postData.tid, ['cid', 'pinned'], function(err, topicData) { + if (err) { + winston.error(err.message); + } var cid = topicData.cid; - db.sortedSetAdd('categories:recent_posts:cid:' + cid, timestamp, pid); + db.sortedSetAdd('categories:recent_posts:cid:' + cid, postData.timestamp, postData.pid); if(parseInt(topicData.pinned, 10) === 0) { - db.sortedSetAdd('categories:' + cid + ':tid', timestamp, tid); + db.sortedSetAdd('categories:' + cid + ':tid', postData.timestamp, postData.tid); } - Categories.addActiveUser(cid, uid, timestamp); + Categories.addActiveUser(cid, postData.uid, postData.timestamp); }); }; + emitter.on('newpost', Categories.onNewPostMade); + }(exports)); \ No newline at end of file diff --git a/src/posts.js b/src/posts.js index dff6af855d..ce28617cda 100644 --- a/src/posts.js +++ b/src/posts.js @@ -9,6 +9,7 @@ var db = require('./database'), categories = require('./categories'), plugins = require('./plugins'), meta = require('./meta'), + emitter = require('./emitter'), async = require('async'), path = require('path'), @@ -68,9 +69,7 @@ var db = require('./database'), db.incrObjectField('global', 'postCount'); - topics.onNewPostMade(tid, postData.pid, timestamp); - categories.onNewPostMade(uid, tid, postData.pid, timestamp); - user.onNewPostMade(uid, tid, postData.pid, timestamp); + emitter.emit('newpost', postData); plugins.fireHook('filter:post.get', postData, next); }, diff --git a/src/topics.js b/src/topics.js index 032e3ce440..f34a6dde94 100644 --- a/src/topics.js +++ b/src/topics.js @@ -21,7 +21,8 @@ var async = require('async'), notifications = require('./notifications'), favourites = require('./favourites'), meta = require('./meta'), - Plugins = require('./plugins'); + Plugins = require('./plugins'), + emitter = require('./emitter'); (function(Topics) { @@ -1015,12 +1016,14 @@ var async = require('async'), Topics.setTopicField(tid, 'lastposttime', timestamp); }; - Topics.onNewPostMade = function(tid, pid, timestamp, callback) { - Topics.increasePostCount(tid); - Topics.updateTimestamp(tid, timestamp); - Topics.addPostToTopic(tid, pid, timestamp, callback); + Topics.onNewPostMade = function(postData) { + Topics.increasePostCount(postData.tid); + Topics.updateTimestamp(postData.tid, postData.timestamp); + Topics.addPostToTopic(postData.tid, postData.pid, postData.timestamp); }; + emitter.on('newpost', Topics.onNewPostMade); + Topics.addPostToTopic = function(tid, pid, timestamp, callback) { db.sortedSetAdd('tid:' + tid + ':posts', timestamp, pid, callback); }; diff --git a/src/user.js b/src/user.js index 89e88f490b..d0e32f4486 100644 --- a/src/user.js +++ b/src/user.js @@ -15,6 +15,7 @@ var bcrypt = require('bcryptjs'), groups = require('./groups'), topics = require('./topics'), events = require('./events'), + emitter = require('./emitter'), Emailer = require('./emailer'); (function(User) { @@ -339,16 +340,18 @@ var bcrypt = require('bcryptjs'), }); }; - User.onNewPostMade = function(uid, tid, pid, timestamp) { - User.addPostIdToUser(uid, pid, timestamp); + User.onNewPostMade = function(postData) { + User.addPostIdToUser(postData.uid, postData.pid, postData.timestamp); - User.incrementUserFieldBy(uid, 'postcount', 1, function(err, newpostcount) { - db.sortedSetAdd('users:postcount', newpostcount, uid); + User.incrementUserFieldBy(postData.uid, 'postcount', 1, function(err, newpostcount) { + db.sortedSetAdd('users:postcount', newpostcount, postData.uid); }); - User.setUserField(uid, 'lastposttime', timestamp); + User.setUserField(postData.uid, 'lastposttime', postData.timestamp); }; + emitter.on('newpost', User.onNewPostMade); + User.addPostIdToUser = function(uid, pid, timestamp) { db.sortedSetAdd('uid:' + uid + ':posts', timestamp, pid); }; From f83b7ee9720c92f1dfa0b5883c60812ca5c9736c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 17 Mar 2014 17:38:47 -0400 Subject: [PATCH 3/5] renamed event --- src/categories.js | 2 +- src/posts.js | 2 +- src/topics.js | 2 +- src/user.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/categories.js b/src/categories.js index 46673d0873..368f293794 100644 --- a/src/categories.js +++ b/src/categories.js @@ -318,6 +318,6 @@ var db = require('./database'), }); }; - emitter.on('newpost', Categories.onNewPostMade); + emitter.on('event:newpost', Categories.onNewPostMade); }(exports)); \ No newline at end of file diff --git a/src/posts.js b/src/posts.js index ce28617cda..63572d5104 100644 --- a/src/posts.js +++ b/src/posts.js @@ -69,7 +69,7 @@ var db = require('./database'), db.incrObjectField('global', 'postCount'); - emitter.emit('newpost', postData); + emitter.emit('event:newpost', postData); plugins.fireHook('filter:post.get', postData, next); }, diff --git a/src/topics.js b/src/topics.js index f34a6dde94..58399d7344 100644 --- a/src/topics.js +++ b/src/topics.js @@ -1022,7 +1022,7 @@ var async = require('async'), Topics.addPostToTopic(postData.tid, postData.pid, postData.timestamp); }; - emitter.on('newpost', Topics.onNewPostMade); + emitter.on('event:newpost', Topics.onNewPostMade); Topics.addPostToTopic = function(tid, pid, timestamp, callback) { db.sortedSetAdd('tid:' + tid + ':posts', timestamp, pid, callback); diff --git a/src/user.js b/src/user.js index d0e32f4486..0d1c25c064 100644 --- a/src/user.js +++ b/src/user.js @@ -350,7 +350,7 @@ var bcrypt = require('bcryptjs'), User.setUserField(postData.uid, 'lastposttime', postData.timestamp); }; - emitter.on('newpost', User.onNewPostMade); + emitter.on('event:newpost', User.onNewPostMade); User.addPostIdToUser = function(uid, pid, timestamp) { db.sortedSetAdd('uid:' + uid + ':posts', timestamp, pid); From 3f238215800be8f2247c20fd7a3c5b13f0244afc Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 17 Mar 2014 21:23:30 -0400 Subject: [PATCH 4/5] moved topic moving and thread tools out of topic.js --- public/src/forum/topic.js | 145 +------------------------- public/src/forum/topic/move.js | 100 ++++++++++++++++++ public/src/forum/topic/threadTools.js | 53 ++++++++++ 3 files changed, 158 insertions(+), 140 deletions(-) create mode 100644 public/src/forum/topic/move.js create mode 100644 public/src/forum/topic/threadTools.js diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index 6084daffaf..0369452758 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -3,7 +3,7 @@ /* globals define, app, templates, translator, socket, bootbox, config, ajaxify, RELATIVE_PATH */ -define(['composer', 'forum/pagination', 'forum/topic/fork'], function(composer, pagination, fork) { +define(['composer', 'forum/pagination', 'forum/topic/threadTools'], function(composer, pagination, threadTools) { var Topic = {}, infiniteLoaderActive = false, scrollingToPost = false, @@ -24,8 +24,7 @@ define(['composer', 'forum/pagination', 'forum/topic/fork'], function(composer, }); Topic.init = function() { - var expose_tools = templates.get('expose_tools'), - tid = templates.get('topic_id'), + var tid = templates.get('topic_id'), thread_state = { locked: templates.get('locked'), deleted: templates.get('deleted'), @@ -68,145 +67,12 @@ define(['composer', 'forum/pagination', 'forum/topic/fork'], function(composer, set_pinned_state(true); } - if (expose_tools === '1') { - var moveThreadModal = $('#move_thread_modal'); - $('.thread-tools').removeClass('hide'); - - // Add events to the thread tools - $('.delete_thread').on('click', function(e) { - if (thread_state.deleted !== '1') { - bootbox.confirm('Are you sure you want to delete this thread?', function(confirm) { - if (confirm) { - socket.emit('topics.delete', tid); - } - }); - } else { - bootbox.confirm('Are you sure you want to restore this thread?', function(confirm) { - if (confirm) { - socket.emit('topics.restore', tid); - } - }); - } - return false; - }); - - $('.lock_thread').on('click', function(e) { - if (thread_state.locked !== '1') { - socket.emit('topics.lock', tid); - } else { - socket.emit('topics.unlock', tid); - } - return false; - }); - - $('.pin_thread').on('click', function(e) { - if (thread_state.pinned !== '1') { - socket.emit('topics.pin', tid); - } else { - socket.emit('topics.unpin', tid); - } - return false; - }); - - $('.move_thread').on('click', function(e) { - moveThreadModal.modal('show'); - return false; - }); - - $('.markAsUnreadForAll').on('click', function() { - var btn = $(this); - socket.emit('topics.markAsUnreadForAll', tid, function(err) { - if(err) { - return app.alertError(err.message); - } - app.alertSuccess('[[topic:markAsUnreadForAll.success]]'); - btn.parents('.thread-tools.open').find('.dropdown-toggle').trigger('click'); - }); - return false; - }); - - moveThreadModal.on('shown.bs.modal', function() { - - var loadingEl = $('#categories-loading'); - if (loadingEl.length) { - socket.emit('categories.get', function(err, data) { - - // Render categories - var categoryEl, - numCategories = data.categories.length, - modalBody = moveThreadModal.find('.modal-body'), - categoriesEl = modalBody.find('ul').eq(0).addClass('categories-list'), - confirmDiv = $('#move-confirm'), - confirmCat = confirmDiv.find('span').eq(0), - commitEl = $('#move_thread_commit'), - cancelEl = $('#move_thread_cancel'), - x, info, targetCid, targetCatLabel; - - for (x = 0; x < numCategories; x++) { - info = data.categories[x]; - categoryEl = $('
  • '); - categoryEl.css({background: info.bgColor, color: info.color || '#fff'}) - .addClass(info.disabled === '1' ? ' disabled' : '') - .attr('data-cid', info.cid) - .html(' ' + info.name); - - categoriesEl.append(categoryEl); - } - loadingEl.remove(); - - categoriesEl.on('click', 'li[data-cid]', function(e) { - var el = $(this); - if (el.is('li')) { - confirmCat.html(el.html()); - confirmDiv.css({display: 'block'}); - targetCid = el.attr('data-cid'); - targetCatLabel = el.html(); - commitEl.prop('disabled', false); - } - }); - - commitEl.on('click', function() { - if (!commitEl.prop('disabled') && targetCid) { - commitEl.prop('disabled', true); - cancelEl.fadeOut(250); - moveThreadModal.find('.modal-header button').fadeOut(250); - commitEl.html('Moving '); - - socket.emit('topics.move', { - tid: tid, - cid: targetCid - }, function(err) { - moveThreadModal.modal('hide'); - if(err) { - return app.alert({ - 'alert_id': 'thread_move', - type: 'danger', - title: 'Unable to Move Topic', - message: 'This topic could not be moved to ' + targetCatLabel + '.
    Please try again later', - timeout: 5000 - }); - } - - app.alert({ - 'alert_id': 'thread_move', - type: 'success', - title: 'Topic Successfully Moved', - message: 'This topic has been successfully moved to ' + targetCatLabel, - timeout: 5000 - }); - }); - } - }); - }); - } - }); - - fork.init(); + if (templates.get('expose_tools') === '1') { + threadTools.init(tid, thread_state); } fixDeleteStateForPosts(); - socket.emit('topics.followCheck', tid, function(err, state) { set_follow_state(state, false); }); @@ -860,10 +726,9 @@ define(['composer', 'forum/pagination', 'forum/topic/fork'], function(composer, $('.lock_thread').html(translated); }); - $('.topic-main-buttons .post_reply').attr('disabled', locked).html(locked ? 'Locked ' : 'Reply'); - $('#post-container .post_reply').html(locked ? 'Locked ' : 'Reply '); $('#post-container').find('.quote, .edit, .delete').toggleClass('none', locked); + $('.topic-main-buttons .post_reply').attr('disabled', locked).html(locked ? 'Locked ' : 'Reply'); if (alert) { app.alert({ diff --git a/public/src/forum/topic/move.js b/public/src/forum/topic/move.js new file mode 100644 index 0000000000..2f92d44ad9 --- /dev/null +++ b/public/src/forum/topic/move.js @@ -0,0 +1,100 @@ +'use strict'; + +/* globals define, app, socket */ + +define(function() { + + var Move = {}; + + Move.init = function(tid) { + var modal = $('#move_thread_modal'), + targetCid, + targetCategoryLabel; + + $('.move_thread').on('click', function(e) { + modal.modal('show'); + return false; + }); + + modal.on('shown.bs.modal', onMoveModalShown); + + function onMoveModalShown() { + var loadingEl = $('#categories-loading'); + if (!loadingEl.length) { + return; + } + + socket.emit('categories.get', onCategoriesLoaded); + } + + function onCategoriesLoaded(err, data) { + if (err) { + return app.alertError(err.message); + } + + renderCategories(data.categories); + + modal.find('.category-list').on('click', 'li[data-cid]', function(e) { + selectCategory($(this)); + }); + + $('#move_thread_commit').on('click', onCommitClicked); + } + + function selectCategory(category) { + modal.find('#confirm-category-name').html(category.html()); + $('#move-confirm').css({display: 'block'}); + + targetCid = category.attr('data-cid'); + targetCategoryLabel = category.html(); + $('#move_thread_commit').prop('disabled', false); + } + + function onCommitClicked() { + var commitEl = $('#move_thread_commit'), + cancelEl = $('#move_thread_cancel'); + + if (!commitEl.prop('disabled') && targetCid) { + commitEl.prop('disabled', true); + cancelEl.fadeOut(250); + modal.find('.modal-header button').fadeOut(250); + commitEl.html('Moving '); + + moveTopic(); + } + } + + function moveTopic() { + socket.emit('topics.move', { + tid: tid, + cid: targetCid + }, function(err) { + modal.modal('hide'); + if(err) { + return app.alertError('This topic could not be moved to ' + targetCategoryLabel + '.
    Please try again later'); + } + + app.alertSuccess('This topic has been successfully moved to ' + targetCategoryLabel); + }); + } + + function renderCategories(categories) { + var categoriesEl = modal.find('.category-list'), + info; + + for (var x = 0; x < categories.length; ++x) { + info = categories[x]; + $('
  • ') + .css({background: info.bgColor, color: info.color || '#fff'}) + .addClass(info.disabled === '1' ? ' disabled' : '') + .attr('data-cid', info.cid) + .html(' ' + info.name) + .appendTo(categoriesEl); + } + + $('#categories-loading').remove(); + } + }; + + return Move; +}); \ No newline at end of file diff --git a/public/src/forum/topic/threadTools.js b/public/src/forum/topic/threadTools.js new file mode 100644 index 0000000000..75ba2f4e0e --- /dev/null +++ b/public/src/forum/topic/threadTools.js @@ -0,0 +1,53 @@ +'use strict'; + +/* globals define, app, translator, socket, bootbox */ + +define(['forum/topic/fork', 'forum/topic/move'], function(fork, move) { + + var ThreadTools = {}; + + ThreadTools.init = function(tid, threadState) { + + $('.thread-tools').removeClass('hide'); + + $('.delete_thread').on('click', function(e) { + var command = threadState.deleted !== '1' ? 'delete' : 'restore'; + + bootbox.confirm('Are you sure you want to ' + command + ' this thread?', function(confirm) { + if (confirm) { + socket.emit('topics.' + command, tid); + } + }); + return false; + }); + + $('.lock_thread').on('click', function(e) { + socket.emit(threadState.locked !== '1' ? 'topics.lock' : 'topics.unlock', tid); + return false; + }); + + $('.pin_thread').on('click', function(e) { + socket.emit(threadState.pinned !== '1' ? 'topics.pin' : 'topics.unpin', tid); + return false; + }); + + $('.markAsUnreadForAll').on('click', function() { + var btn = $(this); + socket.emit('topics.markAsUnreadForAll', tid, function(err) { + if(err) { + return app.alertError(err.message); + } + app.alertSuccess('[[topic:markAsUnreadForAll.success]]'); + btn.parents('.thread-tools.open').find('.dropdown-toggle').trigger('click'); + }); + return false; + }); + + move.init(tid); + + fork.init(); + }; + + + return ThreadTools; +}); \ No newline at end of file From e67e9aee42a648975a42b5790fdf3eef73f1383b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 17 Mar 2014 21:47:37 -0400 Subject: [PATCH 5/5] moved user creation to new file --- public/src/utils.js | 9 ++- src/user.js | 133 +------------------------------------------ src/user/create.js | 134 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 136 deletions(-) create mode 100644 src/user/create.js diff --git a/public/src/utils.js b/public/src/utils.js index 44d348b096..34a4224762 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -126,18 +126,17 @@ return str; }, - // from http://stackoverflow.com/questions/46155/validate-email-address-in-javascript + isEmailValid: function(email) { - // var re = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/; - return email.indexOf('@') !== -1; + return typeof email === 'string' && email.length && email.indexOf('@') !== -1; }, isUserNameValid: function(name) { - return (name && name !== "" && (/^['"\s\-.*0-9\u00BF-\u1FFF\u2C00-\uD7FF\w]+$/.test(name))); + return (name && name !== '' && (/^['"\s\-.*0-9\u00BF-\u1FFF\u2C00-\uD7FF\w]+$/.test(name))); }, isPasswordValid: function(password) { - return password && password.indexOf(' ') === -1; + return typeof password === 'string' && password.length && password.indexOf(' ') === -1; }, isNumber: function(n) { diff --git a/src/user.js b/src/user.js index 0d1c25c064..32bba3d6ce 100644 --- a/src/user.js +++ b/src/user.js @@ -24,6 +24,7 @@ var bcrypt = require('bcryptjs'), User.notifications = require('./user/notifications'); User.reset = require('./user/reset'); + require('./user/create')(User); require('./user/follow')(User); require('./user/profile')(User); require('./user/admin')(User); @@ -31,136 +32,6 @@ var bcrypt = require('bcryptjs'), require('./user/settings')(User); require('./user/search')(User); - User.create = function(userData, callback) { - userData = userData || {}; - userData.userslug = utils.slugify(userData.username); - - userData.username = userData.username.trim(); - if (userData.email !== undefined) { - userData.email = userData.email.trim(); - userData.email = validator.escape(userData.email); - } - - async.parallel([ - function(next) { - if (userData.email) { - next(!utils.isEmailValid(userData.email) ? new Error('Invalid Email!') : null); - } else { - next(); - } - }, - function(next) { - next((!utils.isUserNameValid(userData.username) || !userData.userslug) ? new Error('Invalid Username!') : null); - }, - function(next) { - if (userData.password) { - next(!utils.isPasswordValid(userData.password) ? new Error('Invalid Password!') : null); - } else { - next(); - } - }, - function(next) { - User.exists(userData.userslug, function(err, exists) { - if (err) { - return next(err); - } - next(exists ? new Error('Username taken!') : null); - }); - }, - function(next) { - if (userData.email) { - User.email.available(userData.email, function(err, available) { - if (err) { - return next(err); - } - next(!available ? new Error('Email taken!') : null); - }); - } else { - next(); - } - }, - function(next) { - plugins.fireHook('filter:user.create', userData, function(err, filteredUserData){ - next(err, utils.merge(userData, filteredUserData)); - }); - } - ], function(err, results) { - if (err) { - return callback(err); - } - userData = results[results.length - 1]; - - db.incrObjectField('global', 'nextUid', function(err, uid) { - if(err) { - return callback(err); - } - - var gravatar = User.createGravatarURLFromEmail(userData.email); - var timestamp = Date.now(); - var password = userData.password; - - userData = { - 'uid': uid, - 'username': userData.username, - 'userslug': userData.userslug, - 'fullname': '', - 'location': '', - 'birthday': '', - 'website': '', - 'email': userData.email || '', - 'signature': '', - 'joindate': timestamp, - 'picture': gravatar, - 'gravatarpicture': gravatar, - 'uploadedpicture': '', - 'profileviews': 0, - 'reputation': 0, - 'postcount': 0, - 'lastposttime': 0, - 'banned': 0, - 'status': 'online' - }; - - db.setObject('user:' + uid, userData, function(err) { - - if(err) { - return callback(err); - } - db.setObjectField('username:uid', userData.username, uid); - db.setObjectField('userslug:uid', userData.userslug, uid); - - if (userData.email !== undefined) { - db.setObjectField('email:uid', userData.email, uid); - if (parseInt(uid, 10) !== 1) { - User.email.verify(uid, userData.email); - } - } - - plugins.fireHook('action:user.create', userData); - db.incrObjectField('global', 'userCount'); - - db.sortedSetAdd('users:joindate', timestamp, uid); - db.sortedSetAdd('users:postcount', 0, uid); - db.sortedSetAdd('users:reputation', 0, uid); - - groups.joinByGroupName('registered-users', uid); - - if (password) { - User.hashPassword(password, function(err, hash) { - if(err) { - return callback(err); - } - - User.setUserField(uid, 'password', hash); - callback(null, uid); - }); - } else { - callback(null, uid); - } - }); - }); - }); - }; User.getUserField = function(uid, field, callback) { db.getObjectField('user:' + uid, field, callback); @@ -329,7 +200,7 @@ var bcrypt = require('bcryptjs'), User.hashPassword = function(password, callback) { if (!password) { - return callback(password); + return callback(null, password); } bcrypt.genSalt(nconf.get('bcrypt_rounds'), function(err, salt) { diff --git a/src/user/create.js b/src/user/create.js new file mode 100644 index 0000000000..6e39ae047a --- /dev/null +++ b/src/user/create.js @@ -0,0 +1,134 @@ +'use strict'; + +var async = require('async'), + db = require('./../database'), + utils = require('./../../public/src/utils'), + validator = require('validator'), + plugins = require('./../plugins'), + groups = require('./../groups'); + +module.exports = function(User) { + + User.create = function(userData, callback) { + userData = userData || {}; + userData.userslug = utils.slugify(userData.username); + + userData.username = userData.username.trim(); + if (userData.email !== undefined) { + userData.email = userData.email.trim(); + userData.email = validator.escape(userData.email); + } + + async.parallel([ + function(next) { + next(!utils.isEmailValid(userData.email) ? new Error('Invalid Email!') : null); + }, + function(next) { + next((!utils.isUserNameValid(userData.username) || !userData.userslug) ? new Error('Invalid Username!') : null); + }, + function(next) { + next(!utils.isPasswordValid(userData.password) ? new Error('Invalid Password!') : null); + }, + function(next) { + User.exists(userData.userslug, function(err, exists) { + if (err) { + return next(err); + } + next(exists ? new Error('Username taken!') : null); + }); + }, + function(next) { + if (userData.email) { + User.email.available(userData.email, function(err, available) { + if (err) { + return next(err); + } + next(!available ? new Error('Email taken!') : null); + }); + } else { + next(); + } + }, + function(next) { + plugins.fireHook('filter:user.create', userData, function(err, filteredUserData){ + next(err, utils.merge(userData, filteredUserData)); + }); + } + ], function(err, results) { + if (err) { + return callback(err); + } + userData = results[results.length - 1]; + + db.incrObjectField('global', 'nextUid', function(err, uid) { + if(err) { + return callback(err); + } + + var gravatar = User.createGravatarURLFromEmail(userData.email); + var timestamp = Date.now(); + var password = userData.password; + + userData = { + 'uid': uid, + 'username': userData.username, + 'userslug': userData.userslug, + 'fullname': '', + 'location': '', + 'birthday': '', + 'website': '', + 'email': userData.email || '', + 'signature': '', + 'joindate': timestamp, + 'picture': gravatar, + 'gravatarpicture': gravatar, + 'uploadedpicture': '', + 'profileviews': 0, + 'reputation': 0, + 'postcount': 0, + 'lastposttime': 0, + 'banned': 0, + 'status': 'online' + }; + + db.setObject('user:' + uid, userData, function(err) { + + if(err) { + return callback(err); + } + db.setObjectField('username:uid', userData.username, uid); + db.setObjectField('userslug:uid', userData.userslug, uid); + + if (userData.email !== undefined) { + db.setObjectField('email:uid', userData.email, uid); + if (parseInt(uid, 10) !== 1) { + User.email.verify(uid, userData.email); + } + } + + plugins.fireHook('action:user.create', userData); + db.incrObjectField('global', 'userCount'); + + db.sortedSetAdd('users:joindate', timestamp, uid); + db.sortedSetAdd('users:postcount', 0, uid); + db.sortedSetAdd('users:reputation', 0, uid); + + groups.joinByGroupName('registered-users', uid); + + if (password) { + User.hashPassword(password, function(err, hash) { + if(err) { + return callback(err); + } + + User.setUserField(uid, 'password', hash); + callback(null, uid); + }); + } else { + callback(null, uid); + } + }); + }); + }); + }; +}; \ No newline at end of file