diff --git a/app.js b/app.js index fd30123972..fb757a497d 100644 --- a/app.js +++ b/app.js @@ -328,14 +328,9 @@ function resetThemes(callback) { function resetPlugin(pluginId) { var db = require('./src/database'); - db.setRemove('plugins:active', pluginId, function(err, result) { - if (err || result !== 1) { - winston.error('[reset] Could not disable plugin: %s', pluginId); - if (err) { - winston.error('[reset] Encountered error: %s', err.message); - } else { - winston.info('[reset] Perhaps it has already been disabled?'); - } + db.setRemove('plugins:active', pluginId, function(err) { + if (err) { + winston.error('[reset] Could not disable plugin: %s encountered error %s', pluginId, err.message); } else { winston.info('[reset] Plugin `%s` disabled', pluginId); } diff --git a/public/language/en_GB/topic.json b/public/language/en_GB/topic.json index 88b1662e3b..1320e21923 100644 --- a/public/language/en_GB/topic.json +++ b/public/language/en_GB/topic.json @@ -90,6 +90,7 @@ "fork_success": "Successfully forked topic! Click here to go to the forked topic.", "composer.title_placeholder": "Enter your topic title here...", + "composer.handle_placeholder": "Name", "composer.discard": "Discard", "composer.submit": "Submit", "composer.replying_to": "Replying to %1", diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index c762bc68f4..75ab7e224c 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -267,14 +267,21 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) handleUserCreate(); - function onUsersLoaded(users) { - templates.parse('admin/manage/users', 'users', {users: users, requireEmailConfirmation: config.requireEmailConfirmation}, function(html) { - $('#users-container').append($(html)); - selectable.enable('#users-container', '.user-selectable'); - }); - } + $('#load-more-users-btn').on('click', loadMoreUsers); + + $(window).off('scroll').on('scroll', function() { + var bottom = ($(document).height() - $(window).height()) * 0.9; + + if ($(window).scrollTop() > bottom && !loadingMoreUsers) { + loadMoreUsers(); + } + }); + function loadMoreUsers() { + if (active === 'search') { + return; + } var set = 'users:joindate'; if (active === 'sort-posts') { set = 'users:postcount'; @@ -284,7 +291,6 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) set = 'users:banned'; } - loadingMoreUsers = true; socket.emit('user.loadMore', { set: set, @@ -297,15 +303,12 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) }); } - $('#load-more-users-btn').on('click', loadMoreUsers); - - $(window).off('scroll').on('scroll', function() { - var bottom = ($(document).height() - $(window).height()) * 0.9; - - if ($(window).scrollTop() > bottom && !loadingMoreUsers) { - loadMoreUsers(); - } - }); + function onUsersLoaded(users) { + templates.parse('admin/manage/users', 'users', {users: users, requireEmailConfirmation: config.requireEmailConfirmation}, function(html) { + $('#users-container').append($(html)); + selectable.enable('#users-container', '.user-selectable'); + }); + } }; diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js index e9ae8e8b5d..5a80616517 100644 --- a/public/src/modules/composer.js +++ b/public/src/modules/composer.js @@ -154,6 +154,8 @@ define('composer', [ push({ pid: pid, + uid: threadData.uid, + handle: threadData.handle, title: $('
').html(threadData.title).text(), body: threadData.body, modified: false, @@ -213,9 +215,11 @@ define('composer', [ } function createNewComposer(post_uuid) { - var allowTopicsThumbnail = config.allowTopicsThumbnail && composer.posts[post_uuid].isMain && (config.hasImageUploadPlugin || config.allowFileUploads); - var isTopic = composer.posts[post_uuid] ? !!composer.posts[post_uuid].cid : false; - var isMain = composer.posts[post_uuid] ? !!composer.posts[post_uuid].isMain : false; + var allowTopicsThumbnail = config.allowTopicsThumbnail && composer.posts[post_uuid].isMain && (config.hasImageUploadPlugin || config.allowFileUploads), + isTopic = composer.posts[post_uuid] ? !!composer.posts[post_uuid].cid : false, + isMain = composer.posts[post_uuid] ? !!composer.posts[post_uuid].isMain : false, + isEditing = composer.posts[post_uuid] ? !!composer.posts[post_uuid].pid : false, + isGuestPost = composer.posts[post_uuid] ? composer.posts[post_uuid].uid === '0' : null; composer.bsEnvironment = utils.findBootstrapEnvironment(); @@ -224,7 +228,9 @@ define('composer', [ var data = { allowTopicsThumbnail: allowTopicsThumbnail, showTags: isTopic || isMain, - isTopic: isTopic + isTopic: isTopic, + showHandleInput: (app.user.uid === 0 || (isEditing && isGuestPost && app.user.isAdmin)) && config.allowGuestHandles, + handle: composer.posts[post_uuid] ? composer.posts[post_uuid].handle || '' : undefined }; parseAndTranslate(template, data, function(composerTemplate) { @@ -377,6 +383,7 @@ define('composer', [ function post(post_uuid) { var postData = composer.posts[post_uuid], postContainer = $('#cmp-uuid-' + post_uuid), + handleEl = postContainer.find('.handle'), titleEl = postContainer.find('.title'), bodyEl = postContainer.find('textarea'), thumbEl = postContainer.find('input#topic-thumb-url'); @@ -405,6 +412,7 @@ define('composer', [ if (parseInt(postData.cid, 10) > 0) { composerData = { + handle: handleEl ? handleEl.val() : undefined, title: titleEl.val(), content: bodyEl.val(), topic_thumb: thumbEl.val() || '', @@ -423,6 +431,7 @@ define('composer', [ } else if (parseInt(postData.tid, 10) > 0) { composerData = { tid: postData.tid, + handle: handleEl ? handleEl.val() : undefined, content: bodyEl.val(), toPid: postData.toPid }; @@ -432,6 +441,7 @@ define('composer', [ } else if (parseInt(postData.pid, 10) > 0) { composerData = { pid: postData.pid, + handle: handleEl ? handleEl.val() : undefined, content: bodyEl.val(), title: titleEl.val(), topic_thumb: thumbEl.val() || '', diff --git a/src/controllers/admin/users.js b/src/controllers/admin/users.js index f750c6648b..17944b8370 100644 --- a/src/controllers/admin/users.js +++ b/src/controllers/admin/users.js @@ -9,7 +9,7 @@ var usersController = {}; usersController.search = function(req, res, next) { res.render('admin/manage/users', { search_display: '', - loadmore_display: 'none', + loadmore_display: 'hide', users: [] }); }; @@ -52,6 +52,9 @@ function getUsers(set, req, res, next) { usersController.getCSV = function(req, res, next) { user.getUsersCSV(function(err, data) { + if (err) { + return next(err); + } res.attachment('users.csv'); res.setHeader('Content-Type', 'text/csv'); res.end(data); diff --git a/src/controllers/api.js b/src/controllers/api.js index 2648f944b4..52bfd6233f 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -30,6 +30,7 @@ apiController.getConfig = function(req, res, next) { config.maximumSignatureLength = meta.config.maximumSignatureLength; config.useOutgoingLinksPage = parseInt(meta.config.useOutgoingLinksPage, 10) === 1; config.allowGuestSearching = parseInt(meta.config.allowGuestSearching, 10) === 1; + config.allowGuestHandles = parseInt(meta.config.allowGuestHandles, 10) === 1; config.allowFileUploads = parseInt(meta.config.allowFileUploads, 10) === 1; config.allowTopicsThumbnail = parseInt(meta.config.allowTopicsThumbnail, 10) === 1; config.allowAccountDelete = parseInt(meta.config.allowAccountDelete, 10) === 1; diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index c35df47952..6db3b5a4a9 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -182,7 +182,9 @@ module.exports = function(db, module) { var data = {}; field = helpers.fieldToString(field); data[field] = ''; - db.collection('objects').update({_key: key}, {$unset : data}, callback); + db.collection('objects').update({_key: key}, {$unset : data}, function(err, res) { + callback(err); + }); }; module.incrObjectField = function(key, field, callback) { diff --git a/src/database/mongo/sorted.js b/src/database/mongo/sorted.js index 2031155b78..74da932a18 100644 --- a/src/database/mongo/sorted.js +++ b/src/database/mongo/sorted.js @@ -100,6 +100,22 @@ module.exports = function(db, module) { }); }; + module.getSortedSetRange = function(key, start, stop, callback) { + getSortedSetRange(key, start, stop, 1, false, callback); + }; + + module.getSortedSetRevRange = function(key, start, stop, callback) { + getSortedSetRange(key, start, stop, -1, false, callback); + }; + + module.getSortedSetRangeWithScores = function(key, start, stop, callback) { + getSortedSetRange(key, start, stop, 1, true, callback); + }; + + module.getSortedSetRevRangeWithScores = function(key, start, stop, callback) { + getSortedSetRange(key, start, stop, -1, true, callback); + }; + function getSortedSetRange(key, start, stop, sort, withScores, callback) { if (!key) { return callback(); @@ -128,22 +144,6 @@ module.exports = function(db, module) { }); } - module.getSortedSetRange = function(key, start, stop, callback) { - getSortedSetRange(key, start, stop, 1, false, callback); - }; - - module.getSortedSetRevRange = function(key, start, stop, callback) { - getSortedSetRange(key, start, stop, -1, false, callback); - }; - - module.getSortedSetRangeWithScores = function(key, start, stop, callback) { - getSortedSetRange(key, start, stop, 1, true, callback); - }; - - module.getSortedSetRevRangeWithScores = function(key, start, stop, callback) { - getSortedSetRange(key, start, stop, -1, true, callback); - }; - module.getSortedSetRangeByScore = function(key, start, count, min, max, callback) { getSortedSetRangeByScore(key, start, count, min, max, 1, false, callback); }; @@ -153,7 +153,7 @@ module.exports = function(db, module) { }; module.getSortedSetRangeByScoreWithScores = function(key, start, count, min, max, callback) { - getSortedSetRangeByScore(key, start, count, min, max, -1, true, callback); + getSortedSetRangeByScore(key, start, count, min, max, 1, true, callback); }; module.getSortedSetRevRangeByScoreWithScores = function(key, start, count, max, min, callback) { @@ -204,7 +204,14 @@ module.exports = function(db, module) { if (!key) { return callback(); } - db.collection('objects').count({_key: key, score: {$gte: min, $lte: max}}, function(err, count) { + var scoreQuery = {}; + if (min !== '-inf') { + scoreQuery.$gte = min; + } + if (max !== '+inf') { + scoreQuery.$lte = max; + } + db.collection('objects').count({_key: key, score: scoreQuery}, function(err, count) { callback(err, count ? count : 0); }); }; @@ -447,8 +454,8 @@ module.exports = function(db, module) { value = helpers.fieldToString(value); data.score = parseInt(increment, 10); - db.collection('objects').findAndModify({_key: key, value: value}, {}, {$inc: data}, {new:true, upsert:true}, function(err, result) { - callback(err, result ? result[value] : null); + db.collection('objects').findAndModify({_key: key, value: value}, {}, {$inc: data}, {new: true, upsert: true}, function(err, result) { + callback(err, result ? result.score : null); }); }; }; \ No newline at end of file diff --git a/src/database/redis/hash.js b/src/database/redis/hash.js index 02df03c92e..ddc21b893c 100644 --- a/src/database/redis/hash.js +++ b/src/database/redis/hash.js @@ -89,7 +89,9 @@ module.exports = function(redisClient, module) { }; module.deleteObjectField = function(key, field, callback) { - redisClient.hdel(key, field, callback); + redisClient.hdel(key, field, function(err, res) { + callback(err); + }); }; module.incrObjectField = function(key, field, callback) { diff --git a/src/messaging.js b/src/messaging.js index 980dc13e86..339ec7798c 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -78,7 +78,7 @@ var db = require('./database'), if (!messages || !messages[0]) { return next(null, null); } - + messages[0].newSet = isNewSet; messages[0].mid = mid; next(null, messages[0]); @@ -341,7 +341,8 @@ var db = require('./database'), summary: '[[notifications:new_message_from, ' + messageObj.fromUser.username + ']]', message: messageObj, site_title: meta.config.title || 'NodeBB', - url: nconf.get('url') + '/chats/' + utils.slugify(messageObj.fromUser.username) + url: nconf.get('url'), + fromUserslug: utils.slugify(messageObj.fromUser.username) }); } }); diff --git a/src/postTools.js b/src/postTools.js index 0e8d3934ce..692878734a 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -18,21 +18,22 @@ var winston = require('winston'), (function(PostTools) { - PostTools.edit = function(uid, pid, title, content, options, callback) { - options = options || {}; + PostTools.edit = function(data, callback) { + var options = data.options || {}, + title = data.title.trim(); async.waterfall([ function (next) { - privileges.posts.canEdit(pid, uid, next); + privileges.posts.canEdit(data.pid, data.uid, next); }, function(canEdit, next) { if (!canEdit) { return next(new Error('[[error:no-privileges]]')); } - posts.getPostData(pid, next); + posts.getPostData(data.pid, next); }, function(postData, next) { - postData.content = content; + postData.content = data.content; plugins.fireHook('filter:post.save', postData, next); } ], function(err, postData) { @@ -42,15 +43,19 @@ var winston = require('winston'), async.parallel({ post: function(next) { - posts.setPostFields(pid, { + var d = { edited: Date.now(), - editor: uid, + editor: data.uid, content: postData.content - }, next); + }; + if (data.handle) { + d.handle = data.handle; + } + posts.setPostFields(data.pid, d, next); }, topic: function(next) { var tid = postData.tid; - posts.isMain(pid, function(err, isMainPost) { + posts.isMain(data.pid, function(err, isMainPost) { if (err) { return next(err); } @@ -64,11 +69,9 @@ var winston = require('winston'), }); } - title = title.trim(); - var topicData = { tid: tid, - mainPid: pid, + mainPid: data.pid, title: title, slug: tid + '/' + utils.slugify(title) }; @@ -96,7 +99,7 @@ var winston = require('winston'), }); }, postData: function(next) { - PostTools.parsePost(postData, uid, next); + PostTools.parsePost(postData, data.uid, next); } }, function(err, results) { if (err) { diff --git a/src/posts/create.js b/src/posts/create.js index d9040e443e..a235b2f3f0 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -17,7 +17,6 @@ module.exports = function(Posts) { content = data.content, timestamp = data.timestamp || Date.now(); - if (!uid && parseInt(uid, 10) !== 0) { return callback(new Error('[[error:invalid-uid]]')); } @@ -51,6 +50,10 @@ module.exports = function(Posts) { postData.ip = data.ip; } + if (parseInt(uid, 10) === 0 && data.handle) { + postData.handle = data.handle; + } + plugins.fireHook('filter:post.save', postData, next); }, function(postData, next) { diff --git a/src/routes/api.js b/src/routes/api.js index c3d011e1e2..a0a710307b 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -169,12 +169,11 @@ function getTemplatesListing(req, res, next) { return next(err); } - var data = []; - data = results.views.filter(function(value, index, self) { - return self.indexOf(value) === index; - }).map(function(el) { - return el.replace(nconf.get('views_dir') + '/', ''); - }); + var data = results.views.filter(function(value, index, self) { + return value && self.indexOf(value) === index; + }).map(function(el) { + return el && el.replace(nconf.get('views_dir') + '/', ''); + }); data = data.concat(results.extended); diff --git a/src/socket.io/admin/categories.js b/src/socket.io/admin/categories.js index 00992fc4b4..2ba036d7e0 100644 --- a/src/socket.io/admin/categories.js +++ b/src/socket.io/admin/categories.js @@ -36,7 +36,7 @@ Categories.search = function(socket, data, callback) { var username = data.username, cid = data.cid; - user.search(username, 'username', function(err, data) { + user.search({query: username}, function(err, data) { if (err) { return callback(err); } diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index 2184ee83c2..bcb78b223f 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -175,7 +175,7 @@ User.deleteUsers = function(socket, uids, callback) { }; User.search = function(socket, data, callback) { - user.search(data.query, data.type, function(err, searchData) { + user.search({query: data.query, by: data.type, startsWith: false}, function(err, searchData) { if (err) { return callback(err); } diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index a922613ac3..71a44f36bd 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -35,7 +35,7 @@ SocketModules.composer.push = function(socket, pid, callback) { if (err || !canRead) { return callback(err || new Error('[[error:no-privileges]]')); } - posts.getPostFields(pid, ['content', 'tid'], function(err, postData) { + posts.getPostFields(pid, ['content', 'tid', 'uid', 'handle'], function(err, postData) { if(err || (!postData && !postData.content)) { return callback(err || new Error('[[error:invalid-pid]]')); } @@ -61,6 +61,8 @@ SocketModules.composer.push = function(socket, pid, callback) { callback(null, { pid: pid, + uid: postData.uid, + handle: parseInt(meta.config.allowGuestHandles, 10) ? postData.handle : undefined, body: postData.content, title: results.topic.title, topic_thumb: results.topic.thumb, diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 1b7193ccf0..97b8e7ea74 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -257,13 +257,25 @@ SocketPosts.edit = function(socket, data, callback) { return callback(new Error('[[error:content-too-short, ' + meta.config.minimumPostLength + ']]')); } - postTools.edit(socket.uid, data.pid, data.title, data.content, {topic_thumb: data.topic_thumb, tags: data.tags}, function(err, results) { + // uid, pid, title, content, options + postTools.edit({ + uid: socket.uid, + handle: data.handle, + pid: data.pid, + title: data.title, + content: data.content, + options: { + topic_thumb: data.topic_thumb, + tags: data.tags + } + }, function(err, results) { if (err) { return callback(err); } websockets.in('topic_' + results.topic.tid).emit('event:post_edited', { pid: data.pid, + handle: data.handle, title: results.topic.title, isMainPost: results.topic.isMainPost, tags: results.topic.tags, diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index e5222e04a5..fe2c313452 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -27,6 +27,7 @@ SocketTopics.post = function(socket, data, callback) { topics.post({ uid: socket.uid, + handle: data.handle, title: data.title, content: data.content, cid: data.category_id, diff --git a/src/socket.io/user.js b/src/socket.io/user.js index a7ca2d6605..e20ff65bbb 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -66,7 +66,7 @@ SocketUser.search = function(socket, username, callback) { if (!socket.uid) { return callback(new Error('[[error:not-logged-in]]')); } - user.search(username, 'username', callback); + user.search({query: username}, callback); }; // Password Reset diff --git a/src/topics/create.js b/src/topics/create.js index 71a1dddec4..415c3b955a 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -93,6 +93,7 @@ module.exports = function(Topics) { Topics.post = function(data, callback) { var uid = data.uid, + handle = data.handle, title = data.title, content = data.content, cid = data.cid; @@ -134,7 +135,7 @@ module.exports = function(Topics) { Topics.create({uid: uid, title: title, cid: cid, thumb: data.thumb, tags: data.tags}, next); }, function(tid, next) { - Topics.reply({uid:uid, tid:tid, content:content, req: data.req}, next); + Topics.reply({uid:uid, tid:tid, handle: handle, content:content, req: data.req}, next); }, function(postData, next) { async.parallel({ @@ -154,7 +155,7 @@ module.exports = function(Topics) { }); }, topicData: function(next) { - Topics.getTopicsByTids([postData.tid], 0, next); + Topics.getTopicsByTids([postData.tid], uid, next); } }, next); }, @@ -184,6 +185,7 @@ module.exports = function(Topics) { var tid = data.tid, uid = data.uid, toPid = data.toPid, + handle = data.handle, content = data.content, postData; @@ -226,7 +228,7 @@ module.exports = function(Topics) { checkContentLength(content, next); }, function(next) { - posts.create({uid: uid, tid: tid, content: content, toPid: toPid, ip: data.req ? data.req.ip : null}, next); + posts.create({uid: uid, tid: tid, handle: handle, content: content, toPid: toPid, ip: data.req ? data.req.ip : null}, next); }, function(data, next) { postData = data; @@ -258,6 +260,11 @@ module.exports = function(Topics) { postData.user = results.userInfo[0]; postData.topic = results.topicInfo; + // Username override for guests, if enabled + if (parseInt(meta.config.allowGuestHandles, 10) === 1 && parseInt(postData.uid, 10) === 0 && data.handle) { + postData.user.username = data.handle; + } + if (results.settings.followTopicsOnReply) { threadTools.follow(postData.tid, uid); } diff --git a/src/topics/posts.js b/src/topics/posts.js index 0666303630..66fbb6bc4a 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -4,12 +4,14 @@ var async = require('async'), winston = require('winston'), + _ = require('underscore'), db = require('../database'), user = require('../user'), favourites = require('../favourites'), posts = require('../posts'), - privileges = require('../privileges'); + privileges = require('../privileges'), + meta = require('../meta'); module.exports = function(Topics) { @@ -106,29 +108,34 @@ module.exports = function(Topics) { posts.getPostIndices(postData, uid, next); } }, function(err, results) { - if(err) { + if (err) { return callback(err); } - for (var i = 0; i < postData.length; ++i) { - if (postData[i]) { - postData[i].index = results.indices[i]; - postData[i].deleted = parseInt(postData[i].deleted, 10) === 1; - postData[i].user = results.userData[postData[i].uid]; - postData[i].editor = postData[i].editor ? results.editors[postData[i].editor] : null; - postData[i].favourited = results.favourites[i]; - postData[i].upvoted = results.voteData.upvotes[i]; - postData[i].downvoted = results.voteData.downvotes[i]; - postData[i].votes = postData[i].votes || 0; - postData[i].display_moderator_tools = results.privileges[i].editable; - postData[i].display_move_tools = results.privileges[i].move && postData[i].index !== 0; - postData[i].selfPost = parseInt(uid, 10) === parseInt(postData[i].uid, 10); - - if(postData[i].deleted && !results.privileges[i].view_deleted) { - postData[i].content = '[[topic:post_is_deleted]]'; + postData.forEach(function(postObj, i) { + if (postObj) { + postObj.index = results.indices[i]; + postObj.deleted = parseInt(postObj.deleted, 10) === 1; + postObj.user = parseInt(postObj.uid, 10) ? results.userData[postObj.uid] : _.clone(results.userData[postObj.uid]); + postObj.editor = postObj.editor ? results.editors[postObj.editor] : null; + postObj.favourited = results.favourites[i]; + postObj.upvoted = results.voteData.upvotes[i]; + postObj.downvoted = results.voteData.downvotes[i]; + postObj.votes = postObj.votes || 0; + postObj.display_moderator_tools = results.privileges[i].editable; + postObj.display_move_tools = results.privileges[i].move && postObj.index !== 0; + postObj.selfPost = parseInt(uid, 10) === parseInt(postObj.uid, 10); + + if(postObj.deleted && !results.privileges[i].view_deleted) { + postObj.content = '[[topic:post_is_deleted]]'; + } + + // Username override for guests, if enabled + if (parseInt(meta.config.allowGuestHandles, 10) === 1 && parseInt(postObj.uid, 10) === 0 && postObj.handle) { + postObj.user.username = postObj.handle; } } - } + }); callback(null, postData); }); diff --git a/src/user/search.js b/src/user/search.js index 1400c63a67..9bc162d097 100644 --- a/src/user/search.js +++ b/src/user/search.js @@ -6,18 +6,22 @@ var async = require('async'), module.exports = function(User) { - User.search = function(query, type, callback) { + User.search = function(data, callback) { + var query = data.query; + var by = data.by || 'username'; + var startsWith = data.hasOwnProperty('startsWith') ? data.startsWith : true; + if (!query || query.length === 0) { return callback(null, {timing:0, users:[]}); } - if (type === 'ip') { + if (by === 'ip') { return searchByIP(query, callback); } var start = process.hrtime(); var key = 'username:uid'; - if (type === 'email') { + if (by === 'email') { key = 'email:uid'; } @@ -32,7 +36,11 @@ module.exports = function(User) { var uids = []; for(var i=0; iReputation
  • Email
  • User
  • +
  • Guest
  • Post
  • Pagination
  • Tags
  • diff --git a/src/views/admin/settings/guest.tpl b/src/views/admin/settings/guest.tpl new file mode 100644 index 0000000000..a212547164 --- /dev/null +++ b/src/views/admin/settings/guest.tpl @@ -0,0 +1,25 @@ + + +
    +
    Guests
    +
    +

    + These options affect guest users as a whole. Control over which categories a guest can see or post to is handled in + the categories themselves +

    + +
    +
    + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/src/views/emails/notif_chat.tpl b/src/views/emails/notif_chat.tpl index 39bbe1e6cd..a7e6be420d 100644 --- a/src/views/emails/notif_chat.tpl +++ b/src/views/emails/notif_chat.tpl @@ -3,7 +3,7 @@

    {summary}:

    {message.content}
    -[[email:notif.chat.cta]] +[[email:notif.chat.cta]]

    [[email:closing]]
    diff --git a/src/views/emails/notif_chat_plaintext.tpl b/src/views/emails/notif_chat_plaintext.tpl index a7b78ee481..3ecc1f82fc 100644 --- a/src/views/emails/notif_chat_plaintext.tpl +++ b/src/views/emails/notif_chat_plaintext.tpl @@ -4,7 +4,7 @@ {message.content} -[[email:notif.chat.cta]]: {url} +[[email:notif.chat.cta]]: {url}/chats/{fromUserslug} [[email:closing]] diff --git a/tests/database.js b/tests/database.js index 45379fc9b3..9118823d00 100644 --- a/tests/database.js +++ b/tests/database.js @@ -13,199 +13,7 @@ describe('Test database', function() { require('./database/keys'); require('./database/list'); require('./database/sets'); + require('./database/hash'); + require('./database/sorted'); - it('should not throw err', function(done) { - var objectKey = 'testObj'; - - function setObject(callback) { - db.setObject(objectKey, {name:'baris', 'lastname':'usakli', age:3}, function(err, result) { - callback(err, {'setObject':result}); - }); - } - - function getObject(callback) { - db.getObject(objectKey, function(err, data) { - callback(err, {'getObject':data}); - }); - } - - function getObjects(callback) { - db.getObjects(['testing1', objectKey, 'doesntexist', 'user:1'], function(err, data) { - callback(err, {'getObjects':data}); - }); - } - - function setObjectField(callback) { - db.setObjectField(objectKey, 'reputation', 5, function(err, result) { - callback(err, {'setObjectField': result}); - }); - } - - function getObjectField(callback) { - db.getObjectField(objectKey, 'age', function(err, age) { - callback(err, {'getObjectField' : age}); - }); - } - - function getObjectFields(callback) { - db.getObjectFields(objectKey, ['name', 'lastname'], function(err, data) { - callback(err, {'getObjectFields':data}); - }); - } - - function getObjectValues(callback) { - db.getObjectValues(objectKey, function(err, data) { - callback(err, {'getObjectValues':data}); - }); - } - - function isObjectField(callback) { - db.isObjectField(objectKey, 'age', function(err, data) { - callback(err, {'isObjectField':data}); - }); - } - - function deleteObjectField(callback) { - db.deleteObjectField(objectKey, 'reputation', function(err, data) { - callback(err, {'deleteObjectField':data}); - }); - } - - function incrObjectFieldBy(callback) { - db.incrObjectFieldBy(objectKey, 'age', 3, function(err, data) { - callback(err, {'incrObjectFieldBy':data}); - }); - } - - function getObjectKeys(callback) { - db.getObjectKeys(objectKey, function(err, data) { - callback(err, {'getObjectKeys':data}); - }); - } - - var objectTasks = [ - setObject, - getObject, - deleteObjectField, - getObject, - setObjectField, - getObject, - deleteObjectField, - getObject, - getObjectField, - getObjectFields, - getObjectValues, - isObjectField, - incrObjectFieldBy, - getObject, - getObjects, - getObjectKeys - ]; - - async.series(objectTasks, function(err, results) { - assert.equal(err, null, 'error in object methods'); - assert.ok(results); - - done(); - }); - }); - - - it('should not throw err', function(done) { - function sortedSetAdd(callback) { - db.sortedSetAdd('sortedSet3', 12, 5, function(err) { - callback(err); - }); - } - - function sortedSetRemove(callback) { - db.sortedSetRemove('sortedSet3', 12, function(err, data) { - callback(err); - }); - } - - function getSortedSetRange(callback) { - db.getSortedSetRange('sortedSet3', 0, -1, function(err, data) { - callback(err, {'getSortedSetRange': data}); - }); - } - - function getSortedSetRevRange(callback) { - db.getSortedSetRevRange('sortedSet3', 0, -1, function(err, data) { - callback(err, {'getSortedSetRevRange': data}); - }); - } - - function getSortedSetRevRangeByScore(callback) { - db.getSortedSetRevRangeByScore('sortedSet3', 0, 10, Infinity, 100, function(err, data) { - callback(err, {'getSortedSetRevRangeByScore': data}); - }); - } - - function sortedSetCount(callback) { - db.sortedSetCount('sortedSet3', -Infinity, Infinity, function(err, data) { - callback(err, {'sortedSetCount': data}); - }); - } - - function sortedSetScore(callback) { - db.sortedSetScore('users:joindate', 1, function(err, data) { - callback(err, {'sortedSetScore': data}); - }); - } - - function sortedSetsScore(callback) { - db.sortedSetsScore(['users:joindate', 'users:derp', 'users:postcount'], 1, function(err, data) { - callback(err, {'sortedSetsScore': data}); - }); - } - - function isSortedSetMember(callback) { - db.isSortedSetMember('sortedSet3', 5, function(err, data) { - callback(err, {'sortedSetMember': data}); - }); - } - - function getSortedSetUnion(callback) { - db.getSortedSetUnion(['users:joindate', 'users:derp', 'users:postcount'], 0, -1, function(err, data) { - callback(err, {'sortedSetUnion': data}); - }); - } - - function getSortedSetRevUnion(callback) { - db.getSortedSetRevUnion(['users:joindate', 'users:derp', 'users:postcount'], 0, -1, function(err, data) { - callback(err, {'sortedSetUnion': data}); - }); - } - - var sortedSetTasks = [ - sortedSetAdd, - sortedSetAdd, - isSortedSetMember, - getSortedSetRange, - sortedSetAdd, - getSortedSetRange, - getSortedSetRevRange, - sortedSetRemove, - getSortedSetRange, - sortedSetCount, - sortedSetScore, - sortedSetsScore, - getSortedSetRevRangeByScore, - getSortedSetUnion, - getSortedSetRevUnion - ]; - - async.series(sortedSetTasks, function(err, results) { - assert.equal(err, null, 'error in sorted set methods'); - assert.ok(results); - - done(); - }); - - }); - - after(function() { - db.flushdb(); - }); }); diff --git a/tests/database/hash.js b/tests/database/hash.js new file mode 100644 index 0000000000..2086dc1af7 --- /dev/null +++ b/tests/database/hash.js @@ -0,0 +1,302 @@ +'use strict'; + +var async = require('async'), + assert = require('assert'), + db = require('../mocks/databasemock'); + +describe('Hash methods', function() { + var testData = { + name: 'baris', + age: 99 + }; + + describe('setObject()', function() { + it('should create a object', function(done) { + db.setObject('testObject1', testData, function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + }); + + describe('setObjectField()', function() { + it('should add a new field to an object', function(done) { + db.setObjectField('testObject1', 'lastname', 'usakli', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + + it('should create a new object with field', function(done) { + db.setObjectField('testObject2', 'name', 'ginger', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + }); + + describe('getObject()', function() { + it('should return falsy if object does not exist', function(done) { + db.getObject('doesnotexist', function(err, data) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!data, false); + done(); + }); + }); + + it('should retrieve an object', function(done) { + db.getObject('testObject1', function(err, data) { + assert.equal(err, null); + assert.equal(data.name, testData.name); + assert.equal(data.age, testData.age); + assert.equal(data.lastname, 'usakli'); + done(); + }); + }); + }); + + describe('getObjects()', function() { + it('should return 3 objects with correct data', function(done) { + db.getObjects(['testObject1', 'testObject2', 'doesnotexist'], function(err, objects) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(objects) && objects.length === 3, true); + assert.equal(objects[0].name, 'baris'); + assert.equal(objects[1].name, 'ginger'); + assert.equal(!!objects[2], false); + done(); + }); + }); + }); + + describe('getObjectField()', function() { + it('should return falsy if object does not exist', function(done) { + db.getObjectField('doesnotexist', 'fieldName', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!value, false); + done(); + }); + }); + + it('should return falsy if field does not exist', function(done) { + db.getObjectField('testObject1', 'fieldName', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!value, false); + done(); + }); + }); + + it('should get an objects field', function(done) { + db.getObjectField('testObject1', 'lastname', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, 'usakli'); + done(); + }); + }); + }); + + describe('getObjectFields()', function() { + it('should return an object with falsy values', function(done) { + db.getObjectFields('doesnotexist', ['field1', 'field2'], function(err, object) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(typeof object, 'object'); + assert.equal(!!object.field1, false); + assert.equal(!!object.field2, false); + done(); + }); + }); + + it('should return an object with correct fields', function(done) { + db.getObjectFields('testObject1', ['lastname', 'age', 'field1'], function(err, object) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(typeof object, 'object'); + assert.equal(object.lastname, 'usakli'); + assert.equal(object.age, 99); + assert.equal(!!object.field1, false); + done(); + }); + }); + }); + + describe('getObjectsFields()', function() { + it('should return an array of objects with correct values', function(done) { + db.getObjectsFields(['testObject1', 'testObject2', 'doesnotexist'], ['name', 'age'], function(err, objects) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(objects), true); + assert.equal(objects.length, 3); + assert.equal(objects[0].name, 'baris'); + assert.equal(objects[0].age, 99); + assert.equal(objects[1].name, 'ginger'); + assert.equal(!!objects[2].name, false); + done(); + }); + }); + }); + + describe('getObjectKeys()', function() { + it('should return an empty array for a object that does not exist', function(done) { + db.getObjectKeys('doesnotexist', function(err, keys) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(keys) && keys.length === 0, true); + done(); + }); + }); + + it('should return an array of keys for the object\'s fields', function(done) { + db.getObjectKeys('testObject1', function(err, keys) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(keys) && keys.length === 3, true); + keys.forEach(function(key) { + assert.notEqual(['name', 'lastname', 'age'].indexOf(key), -1); + }); + + done(); + }); + }); + }); + + describe('getObjectValues()', function() { + it('should return an empty array for a object that does not exist', function(done) { + db.getObjectValues('doesnotexist', function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(values) && values.length === 0, true); + done(); + }); + }); + + it('should return an array of values for the object\'s fields', function(done) { + db.getObjectValues('testObject1', function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(Array.isArray(values) && values.length === 3, true); + values.forEach(function(value) { + assert.notEqual(['baris', 'usakli', 99].indexOf(value), -1); + }); + + done(); + }); + }); + }); + + describe('isObjectField()', function() { + it('should return false if object does not exist', function(done) { + db.isObjectField('doesnotexist', 'field1', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, false); + done(); + }); + }); + + it('should return false if field does not exist', function(done) { + db.isObjectField('testObject1', 'field1', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, false); + done(); + }); + }); + + it('should return true if field exists', function(done) { + db.isObjectField('testObject1', 'lastname', function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, true); + done(); + }); + }); + }); + + describe('deleteObjectField()', function() { + it('should delete an objects field', function(done) { + db.deleteObjectField('testObject1', 'lastname', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + db.isObjectField('testObject1', 'lastname', function(err, isField) { + assert.equal(err, null); + assert.equal(isField, false); + done(); + }); + }); + }); + }); + + describe('incrObjectField()', function() { + it('should set an objects field to 1 if object does not exist', function(done) { + db.incrObjectField('testObject3', 'field1', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 1); + done(); + }); + }); + + it('should increment an object fields by 1 and return it', function(done) { + db.incrObjectField('testObject1', 'age', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 100); + done(); + }); + }); + }); + + describe('decrObjectField()', function() { + it('should set an objects field to -1 if object does not exist', function(done) { + db.decrObjectField('testObject4', 'field1', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, -1); + done(); + }); + }); + + it('should decrement an object fields by 1 and return it', function(done) { + db.decrObjectField('testObject1', 'age', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 99); + done(); + }); + }); + }); + + describe('incrObjectFieldBy()', function() { + it('should set an objects field to 5 if object does not exist', function(done) { + db.incrObjectFieldBy('testObject5', 'field1', 5, function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 5); + done(); + }); + }); + + it('should increment an object fields by passed in value and return it', function(done) { + db.incrObjectFieldBy('testObject1', 'age', 11, function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 110); + done(); + }); + }); + }); + + + + after(function() { + db.flushdb(); + }); +}); diff --git a/tests/database/sorted.js b/tests/database/sorted.js new file mode 100644 index 0000000000..a675c709ec --- /dev/null +++ b/tests/database/sorted.js @@ -0,0 +1,472 @@ +'use strict'; + +var async = require('async'), + assert = require('assert'), + db = require('../mocks/databasemock'); + +describe('Sorted Set methods', function() { + + describe('sortedSetAdd()', function() { + it('should add an element to a sorted set', function(done) { + db.sortedSetAdd('sorted1', 1, 'value1', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + + it('should add two elements to a sorted set', function(done) { + db.sortedSetAdd('sorted2', [1, 2], ['value1', 'value2'], function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + + it('should add four elements to a sorted set', function(done) { + db.sortedSetAdd('sorted3', [2, 3, 4, 5], ['value2', 'value3', 'value4', 'value5'], function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + }); + + describe('sortedSetsAdd()', function() { + it('should add an element to two sorted sets', function(done) { + db.sortedSetsAdd(['sorted1', 'sorted2'], 3, 'value3', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + done(); + }); + }); + }); + + describe('getSortedSetRange()', function() { + it('should return the lowest scored element', function(done) { + db.getSortedSetRange('sorted2', 0, 0, function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, 'value1'); + done(); + }); + }); + + it('should return elements sorted by score lowest to highest', function(done) { + db.getSortedSetRange('sorted2', 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value1', 'value2', 'value3']); + done(); + }); + }); + }); + + describe('getSortedSetRevRange()', function() { + it('should return the highest scored element', function(done) { + db.getSortedSetRevRange('sorted2', 0, 0, function(err, value) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(value, 'value3'); + done(); + }); + }); + + it('should return elements sorted by score highest to lowest', function(done) { + db.getSortedSetRevRange('sorted2', 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value3', 'value2', 'value1']); + done(); + }); + }); + }); + + describe('getSortedSetRangeWithScores()', function() { + it('should return array of elements sorted by score lowest to highest with scores', function(done) { + db.getSortedSetRangeWithScores('sorted2', 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, [{value: 'value1', score: 1}, {value: 'value2', score: 2}, {value: 'value3', score: 3}]); + done(); + }); + }); + }); + + describe('getSortedSetRevRangeWithScores()', function() { + it('should return array of elements sorted by score highest to lowest with scores', function(done) { + db.getSortedSetRevRangeWithScores('sorted2', 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, [{value: 'value3', score: 3}, {value: 'value2', score: 2}, {value: 'value1', score: 1}]); + done(); + }); + }); + }); + + + describe('getSortedSetRangeByScore()', function() { + it('should get count elements with score between min max sorted by score lowest to highest', function(done) { + db.getSortedSetRangeByScore('sorted2', 0, -1, '-inf', 2, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value1', 'value2']); + done(); + }); + }); + }); + + describe('getSortedSetRevRangeByScore()', function() { + it('should get count elements with score between max min sorted by score highest to lowest', function(done) { + db.getSortedSetRevRangeByScore('sorted2', 0, -1, '+inf', 2, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value3', 'value2']); + done(); + }); + }); + }); + + + describe('getSortedSetRangeByScoreWithScores()', function() { + it('should get count elements with score between min max sorted by score lowest to highest with scores', function(done) { + db.getSortedSetRangeByScoreWithScores('sorted2', 0, -1, '-inf', 2, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, [{value: 'value1', score: 1}, {value: 'value2', score: 2}]); + done(); + }); + }); + }); + + describe('getSortedSetRevRangeByScoreWithScores()', function() { + it('should get count elements with score between max min sorted by score highest to lowest', function(done) { + db.getSortedSetRevRangeByScoreWithScores('sorted2', 0, -1, '+inf', 2, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, [{value: 'value3', score: 3}, {value: 'value2', score: 2}]); + done(); + }); + }); + }); + + describe('sortedSetCount()', function() { + it('should return 0 for a sorted set that does not exist', function(done) { + db.sortedSetCount('doesnotexist', 0, 10, function(err, count) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(count, 0); + done(); + }); + }); + + it('should return number of elements between scores min max inclusive', function(done) { + db.sortedSetCount('sorted2', '-inf', 2, function(err, count) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(count, 2); + done(); + }); + }); + }); + + describe('sortedSetCard()', function() { + it('should return 0 for a sorted set that does not exist', function(done) { + db.sortedSetCard('doesnotexist', function(err, count) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(count, 0); + done(); + }); + }); + + it('should return number of elements in a sorted set', function(done) { + db.sortedSetCard('sorted2', function(err, count) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(count, 3); + done(); + }); + }); + }); + + describe('sortedSetsCard()', function() { + it('should return the number of elements in sorted sets', function(done) { + db.sortedSetsCard(['sorted1', 'sorted2', 'doesnotexist'], function(err, counts) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(counts, [2, 3, 0]); + done(); + }); + }); + }); + + describe('sortedSetRank()', function() { + it('should return falsy if sorted set doesnot exist', function(done) { + db.sortedSetRank('doesnotexist', 'value1', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!rank, false); + done(); + }); + }); + + it('should return falsy if element isnt in sorted set', function(done) { + db.sortedSetRank('sorted2', 'value5', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!rank, false); + done(); + }); + }); + + it('should return the rank of the element in the sorted set sorted by lowest to highest score', function(done) { + db.sortedSetRank('sorted2', 'value1', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(rank, 0); + done(); + }); + }); + }); + + describe('sortedSetRevRank()', function() { + it('should return falsy if sorted set doesnot exist', function(done) { + db.sortedSetRevRank('doesnotexist', 'value1', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!rank, false); + done(); + }); + }); + + it('should return falsy if element isnt in sorted set', function(done) { + db.sortedSetRevRank('sorted2', 'value5', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!rank, false); + done(); + }); + }); + + it('should return the rank of the element in the sorted set sorted by highest to lowest score', function(done) { + db.sortedSetRevRank('sorted2', 'value1', function(err, rank) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(rank, 2); + done(); + }); + }); + }); + + describe('sortedSetsRanks()', function() { + it('should return the ranks of values in sorted sets', function(done) { + db.sortedSetsRanks(['sorted1', 'sorted2'], ['value1', 'value2'], function(err, ranks) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(ranks, [0, 1]); + done(); + }); + }); + }); + + describe('sortedSetRanks()', function() { + it('should return the ranks of values in a sorted set', function(done) { + db.sortedSetRanks('sorted2', ['value2', 'value1', 'value3', 'value4'], function(err, ranks) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(ranks, [1, 0, 2, null]); + done(); + }); + }); + }); + + describe('sortedSetScore()', function() { + it('should return falsy if sorted set does not exist', function(done) { + db.sortedSetScore('doesnotexist', 'value1', function(err, score) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!score, false); + done(); + }); + }); + + it('should return falsy if element is not in sorted set', function(done) { + db.sortedSetScore('sorted2', 'value5', function(err, score) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(!!score, false); + done(); + }); + }); + + it('should return the score of an element', function(done) { + db.sortedSetScore('sorted2', 'value2', function(err, score) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(score, 2); + done(); + }); + }); + }); + + describe('sortedSetsScore()', function() { + it('should return the scores of value in sorted sets', function(done) { + db.sortedSetsScore(['sorted1', 'sorted2', 'doesnotexist'], 'value2', function(err, scores) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(scores, [null, 2, null]); + done(); + }); + }); + }); + + describe('sortedSetScores()', function() { + it('should return the scores of value in sorted sets', function(done) { + db.sortedSetScores('sorted2', ['value2', 'value1', 'doesnotexist'], function(err, scores) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(scores, [2, 1, null]); + done(); + }); + }); + }); + + describe('isSortedSetMember()', function() { + it('should return false if sorted set does not exist', function(done) { + db.isSortedSetMember('doesnotexist', 'value1', function(err, isMember) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(isMember, false); + done(); + }); + }); + + it('should return false if element is not in sorted set', function(done) { + db.isSortedSetMember('sorted2', 'value5', function(err, isMember) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(isMember, false); + done(); + }); + }); + + it('should return true if element is in sorted set', function(done) { + db.isSortedSetMember('sorted2', 'value2', function(err, isMember) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(isMember, true); + done(); + }); + }); + }); + + describe('isSortedSetMembers()', function() { + it('should return an array of booleans indicating membership', function(done) { + db.isSortedSetMembers('sorted2', ['value1', 'value2', 'value5'], function(err, isMembers) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(isMembers, [true, true, false]); + done(); + }); + }); + }); + + describe('getSortedSetUnion()', function() { + it('should return an array of values from both sorted sets sorted by scores lowest to highest', function(done) { + db.getSortedSetUnion(['sorted1', 'sorted3'], 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value1', 'value2', 'value4', 'value5', 'value3']); + done(); + }); + }); + }); + + describe('getSortedSetRevUnion()', function() { + it('should return an array of values from both sorted sets sorted by scores highest to lowest', function(done) { + db.getSortedSetRevUnion(['sorted1', 'sorted3'], 0, -1, function(err, values) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.deepEqual(values, ['value3', 'value5', 'value4', 'value2', 'value1']); + done(); + }); + }); + }); + + describe('sortedSetIncrBy()', function() { + it('should create a sorted set with a field set to 1', function(done) { + db.sortedSetIncrBy('sortedIncr', 1, 'field1', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 1); + db.sortedSetScore('sortedIncr', 'field1', function(err, score) { + assert.equal(err, null); + assert.equal(score, 1); + done(); + }); + }); + }); + + it('should increment a field of a sorted set by 5', function(done) { + db.sortedSetIncrBy('sortedIncr', 5, 'field1', function(err, newValue) { + assert.equal(err, null); + assert.equal(arguments.length, 2); + assert.equal(newValue, 6); + db.sortedSetScore('sortedIncr', 'field1', function(err, score) { + assert.equal(err, null); + assert.equal(score, 6); + done(); + }); + }); + }); + }); + + + describe('sortedSetRemove()', function() { + it('should remove an element from a sorted set', function(done) { + db.sortedSetRemove('sorted2', 'value2', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + db.isSortedSetMember('sorted2', 'value2', function(err, isMember) { + assert.equal(err, null); + assert.equal(isMember, false); + done(); + }); + }); + }); + }); + + describe('sortedSetsRemove()', function() { + it('should remove element from multiple sorted sets', function(done) { + db.sortedSetsRemove(['sorted1', 'sorted2'], 'value1', function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + db.sortedSetsScore(['sorted1', 'sorted2'], 'value1', function(err, scores) { + assert.equal(err, null); + assert.deepEqual(scores, [null, null]); + done(); + }); + }); + }); + }); + + describe('sortedSetsRemoveRangeByScore()', function() { + it('should remove elements with scores between min max inclusive', function(done) { + db.sortedSetsRemoveRangeByScore(['sorted3'], 4, 5, function(err) { + assert.equal(err, null); + assert.equal(arguments.length, 1); + db.getSortedSetRange('sorted3', 0, -1, function(err, values) { + assert.equal(err, null); + assert.deepEqual(values, ['value2', 'value3']); + done(); + }); + }); + }); + }); + + + after(function() { + db.flushdb(); + }); +}); diff --git a/tests/user.js b/tests/user.js index 2f992215f7..9e53b4c4c5 100644 --- a/tests/user.js +++ b/tests/user.js @@ -157,6 +157,17 @@ describe('User', function() { }); }); + describe('.search()', function() { + it('should return an object containing an array of matching users', function(done) { + User.search({query: 'john'}, function(err, searchData) { + assert.ifError(err); + assert.equal(Array.isArray(searchData.users) && searchData.users.length > 0, true); + assert.equal(searchData.users[0].username, 'John Smith'); + done(); + }); + }); + }); + after(function() { db.flushdb(); });