diff --git a/public/language/en_GB/topic.json b/public/language/en_GB/topic.json index a37a4a791f..e1d1651e0d 100644 --- a/public/language/en_GB/topic.json +++ b/public/language/en_GB/topic.json @@ -41,6 +41,11 @@ "favourites.not_logged_in.message": "Please log in in order to favourite this post", "favourites.has_no_favourites": "You don't have any favourites, favourite some posts to see them here!", + "vote.not_logged_in.title": "Not Logged In", + "vote.not_logged_in.message": "Please log in in order to vote", + "vote.cant_vote_self.title": "Invalid Vote", + "vote.cant_vote_self.message": "You cannot vote for your own post", + "loading_more_posts": "Loading More Posts", "move_topic": "Move Topic", "move_post": "Move Post", diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index a532693cc8..3d1fe48075 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -442,6 +442,46 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { return false; }); + $('#post-container').on('click', '.upvote', function() { + var post = $(this).parents('.post-row'), + pid = post.attr('data-pid'), + upvoted = post.find('.upvoted').length; + + if (upvoted) { + socket.emit('posts.unvote', { + pid: pid, + room_id: app.currentRoom + }); + } else { + socket.emit('posts.upvote', { + pid: pid, + room_id: app.currentRoom + }); + } + + return false; + }); + + $('#post-container').on('click', '.downvote', function() { + var post = $(this).parents('.post-row'), + pid = post.attr('data-pid'), + downvoted = post.find('.downvoted').length; + + if (downvoted) { + socket.emit('posts.unvote', { + pid: pid, + room_id: app.currentRoom + }); + } else { + socket.emit('posts.downvote', { + pid: pid, + room_id: app.currentRoom + }); + } + + return false; + }); + $('#post-container').on('click', '.flag', function() { bootbox.confirm('Are you sure you want to flag this post?', function(confirm) { if (confirm) { @@ -572,11 +612,11 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { }); ajaxify.register_events([ - 'event:rep_up', 'event:rep_down', 'event:new_post', 'get_users_in_room', + 'event:rep_up', 'event:rep_down', 'event:favourited', 'event:unfavourited', 'event:new_post', 'get_users_in_room', 'event:topic_deleted', 'event:topic_restored', 'event:topic:locked', 'event:topic_unlocked', 'event:topic_pinned', 'event:topic_unpinned', 'event:topic_moved', 'event:post_edited', 'event:post_deleted', 'event:post_restored', - 'posts.favourite', 'user.isOnline' + 'posts.favourite', 'user.isOnline', 'posts.upvote', 'posts.downvote' ]); socket.on('get_users_in_room', function(data) { @@ -674,6 +714,14 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { adjust_rep(-1, data.pid, data.uid); }); + socket.on('event:favourited', function(data) { + adjust_favourites(1, data.pid, data.uid); + }); + + socket.on('event:unfavourited', function(data) { + adjust_favourites(-1, data.pid, data.uid); + }); + socket.on('event:new_post', function(data) { if(config.usePagination) { onNewPostPagination(data); @@ -759,6 +807,35 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { }); }); + socket.on('posts.upvote', function(data) { + if (data && data.pid) { + var post = $('li[data-pid="' + data.pid + '"]'), + upvote = post.find('.upvote'); + + upvote.addClass('btn-primary upvoted'); + } + }); + + socket.on('posts.downvote', function(data) { + if (data && data.pid) { + var post = $('li[data-pid="' + data.pid + '"]'), + downvote = post.find('.downvote'); + + downvote.addClass('btn-primary downvoted'); + } + }); + + socket.on('posts.unvote', function(data) { + if (data && data.pid) { + var post = $('li[data-pid="' + data.pid + '"]'), + upvote = post.find('.upvote'), + downvote = post.find('.downvote'); + + upvote.removeClass('btn-primary upvoted'); + downvote.removeClass('btn-primary downvoted'); + } + }); + socket.on('posts.favourite', function(data) { if (data && data.pid) { var favBtn = $('li[data-pid="' + data.pid + '"] .favourite'); @@ -803,17 +880,25 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { }); function adjust_rep(value, pid, uid) { - var post_rep = jQuery('.post_rep_' + pid), - user_rep = jQuery('.user_rep_' + uid); + var votes = $('li[data-pid="' + pid + '"] .votes'), + reputationElements = $('.reputation[data-uid="' + uid + '"]'), + currentVotes = parseInt(votes.attr('data-votes'), 10), + reputation = parseInt(reputationElements.attr('data-reputation'), 10); + + currentVotes += value; + reputation += value; + + votes.html(currentVotes).attr('data-votes', currentVotes); + reputationElements.html(reputation).attr('data-reputation', reputation); + } - var ptotal = parseInt(post_rep.html(), 10), - utotal = parseInt(user_rep.html(), 10); + function adjust_favourites(value, pid, uid) { + var favourites = $('li[data-pid="' + pid + '"] .favouriteCount'), + currentFavourites = parseInt(favourites.attr('data-favourites'), 10); - ptotal += value; - utotal += value; + currentFavourites += value; - post_rep.html(ptotal + ' '); - user_rep.html(utotal + ' '); + favourites.html(currentFavourites).attr('data-favourites', currentFavourites); } function set_locked_state(locked, alert) { diff --git a/public/src/forum/users.js b/public/src/forum/users.js index f4e9ff988d..3021ad36dc 100644 --- a/public/src/forum/users.js +++ b/public/src/forum/users.js @@ -81,19 +81,28 @@ define(function() { }); socket.on('user.isOnline', function(err, data) { - if(getActiveSection().indexOf('online') === 0 && !loadingMoreUsers) { + var section = getActiveSection(); + if((section.indexOf('online') === 0 || section.indexOf('users') === 0) && !loadingMoreUsers) { startLoading('users:online', 0, true); - socket.emit('user.getOnlineAnonCount', {} , function(err, anonCount) { - if(parseInt(anonCount, 10) > 0) { - $('#users-container .anon-user').removeClass('hide'); - $('#online_anon_count').html(anonCount); - } else { - $('#users-container .anon-user').addClass('hide'); - } - }); + updateAnonCount(); } }); + socket.on('user.anonDisconnect', updateAnonCount); + socket.on('user.anonConnect', updateAnonCount) + + function updateAnonCount() { + socket.emit('user.getOnlineAnonCount', {} , function(err, anonCount) { + + if(parseInt(anonCount, 10) > 0) { + $('#users-container .anon-user').removeClass('hide'); + $('#online_anon_count').html(anonCount); + } else { + $('#users-container .anon-user').addClass('hide'); + } + }); + } + function onUsersLoaded(users, emptyContainer) { var html = templates.prepare(templates['users'].blocks['users']).parse({ users: users diff --git a/public/templates/topic.tpl b/public/templates/topic.tpl index 1643fc76f1..c68438c48b 100644 --- a/public/templates/topic.tpl +++ b/public/templates/topic.tpl @@ -54,7 +54,7 @@
@@ -72,7 +72,7 @@
+ +
+ + + +
+
@@ -129,7 +140,7 @@
- [[topic:reputation]]: {posts.user_rep} | [[topic:posts]]: {posts.user_postcount} + [[topic:reputation]]: {posts.user_rep} | [[topic:posts]]: {posts.user_postcount} | {posts.custom_profile_info.content} diff --git a/src/favourites.js b/src/favourites.js index 935e4542da..5cfc0486c4 100644 --- a/src/favourites.js +++ b/src/favourites.js @@ -8,29 +8,178 @@ var async = require('async'), (function (Favourites) { "use strict"; - Favourites.favourite = function (pid, room_id, uid, socket) { + function vote(type, unvote, pid, room_id, uid, socket, callback) { var websockets = require('./socket.io'); if (uid === 0) { + return socket.emit('event:alert', { + alert_id: 'post_vote', + title: '[[topic:vote.not_logged_in.title]]', + message: '[[topic:vote.not_logged_in.message]]', + type: 'danger', + timeout: 5000 + }); + } - translator.mget(['topic:favourites.not_logged_in.message', 'topic:favourites.not_logged_in.title'], function(err, results) { + posts.getPostFields(pid, ['uid', 'timestamp'], function (err, postData) { + if (uid === parseInt(postData.uid, 10)) { socket.emit('event:alert', { - alert_id: 'post_favourite', - title: results[1], - message: results[0], + alert_id: 'post_vote', + title: '[[topic:vote.cant_vote_self.title]]', + message: '[[topic:vote.cant_vote_self.message]]', type: 'danger', timeout: 5000 }); + + if (callback) { + callback(false); + } + + return false; + } + + db[type === 'upvote' || !unvote ? 'sortedSetAdd' : 'sortedSetRemove']('uid:' + uid + ':upvote', postData.timestamp, pid); + db[type === 'upvote' || unvote ? 'sortedSetRemove' : 'sortedSetAdd']('uid:' + uid + ':downvote', postData.timestamp, pid); + + user[type === 'upvote' ? 'incrementUserFieldBy' : 'decrementUserFieldBy'](postData.uid, 'reputation', 1, function (err, newreputation) { + db.sortedSetAdd('users:reputation', newreputation, postData.uid); + }); + + if (room_id) { + websockets.in(room_id).emit('event:' + (type === 'upvote' ? 'rep_up' : 'rep_down'), { + uid: postData.uid, + pid: pid + }); + } + + socket.emit('posts.' + (unvote ? 'unvote' : type), { + pid: pid + }); + + adjustPostVotes(pid, uid, type, unvote, function() { + if (callback) { + callback(); + } + }); + }); + } + + function adjustPostVotes(pid, uid, type, unvote, callback) { + var notType = (type === 'upvote' ? 'downvote' : 'upvote'); + + async.series([ + function(next) { + if (unvote) { + db.setRemove('pid:' + pid + ':' + type, uid, function(err) { + next(err); + }); + } else { + db.setAdd('pid:' + pid + ':' + type, uid, function(err) { + next(err); + }); + } + }, + function(next) { + db.setRemove('pid:' + pid + ':' + notType, uid, function(err) { + next(err); + }); + } + ], function(err) { + async.parallel({ + upvotes: function(next) { + db.setCount('pid:' + pid + ':upvote', next); + }, + downvotes: function(next) { + db.setCount('pid:' + pid + ':downvote', next); + } + }, function(err, results) { + posts.setPostField(pid, 'votes', parseInt(results.upvotes, 10) - parseInt(results.downvotes, 10)); + }); + + if (callback) { + callback(); + } + }); + } + + Favourites.upvote = function(pid, room_id, uid, socket) { + Favourites.unvote(pid, room_id, uid, socket, function(err) { + vote('upvote', false, pid, room_id, uid, socket); + }); + }; + + Favourites.downvote = function(pid, room_id, uid, socket) { + Favourites.unvote(pid, room_id, uid, socket, function(err) { + vote('downvote', false, pid, room_id, uid, socket); + }); + }; + + Favourites.unvote = function(pid, room_id, uid, socket, callback) { + var websockets = require('./socket.io'); + + Favourites.hasVoted(pid, uid, function(err, voteStatus) { + if (voteStatus.upvoted || voteStatus.downvoted) { + socket.emit('posts.unvote', { + pid: pid + }); + + return vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, room_id, uid, socket, function() { + if (callback) { + callback(err); + } + }); + } + + if (callback) { + callback(err); + } + }); + }; + + Favourites.hasVoted = function(pid, uid, callback) { + async.parallel({ + upvoted: function(next) { + db.isSetMember('pid:' + pid + ':upvote', uid, next); + }, + downvoted: function(next) { + db.isSetMember('pid:' + pid + ':downvote', uid, next); + } + }, function(err, results) { + callback(err, results) + }); + }; + + Favourites.getVoteStatusByPostIDs = function(pids, uid, callback) { + var data = {}; + + function iterator(pid, next) { + Favourites.hasVoted(pid, uid, function(err, voteStatus) { + data[pid] = voteStatus; + next() }); - return; } - posts.getPostFields(pid, ['uid', 'timestamp'], function (err, postData) { + async.each(pids, iterator, function(err) { + callback(data); + }); + }; - Favourites.hasFavourited(pid, uid, function (err, hasFavourited) { + Favourites.favourite = function (pid, room_id, uid, socket) { + var websockets = require('./socket.io'); - if (!hasFavourited) { + if (uid === 0) { + return socket.emit('event:alert', { + alert_id: 'post_favourite', + title: '[[topic:favourites.not_logged_in.title]]', + message: '[[topic:favourites.not_logged_in.message]]', + type: 'danger', + timeout: 5000 + }); + } + posts.getPostFields(pid, ['uid', 'timestamp'], function (err, postData) { + Favourites.hasFavourited(pid, uid, function (err, hasFavourited) { + if (!hasFavourited) { db.sortedSetAdd('uid:' + uid + ':favourites', postData.timestamp, pid); db.setAdd('pid:' + pid + ':users_favourited', uid, function(err) { db.setCount('pid:' + pid + ':users_favourited', function(err, count) { @@ -38,15 +187,8 @@ var async = require('async'), }); }); - - if (uid !== postData.uid) { - user.incrementUserFieldBy(postData.uid, 'reputation', 1, function (err, newreputation) { - db.sortedSetAdd('users:reputation', newreputation, postData.uid); - }); - } - if (room_id) { - websockets.in(room_id).emit('event:rep_up', { + websockets.in(room_id).emit('event:favourited', { uid: uid !== postData.uid ? postData.uid : 0, pid: pid }); @@ -60,7 +202,7 @@ var async = require('async'), }); }; - Favourites.unfavourite = function (pid, room_id, uid, socket) { + Favourites.unfavourite = function(pid, room_id, uid, socket) { var websockets = require('./socket.io'); if (uid === 0) { @@ -70,23 +212,15 @@ var async = require('async'), posts.getPostField(pid, 'uid', function (err, uid_of_poster) { Favourites.hasFavourited(pid, uid, function (err, hasFavourited) { if (hasFavourited) { - db.sortedSetRemove('uid:' + uid + ':favourites', pid); - db.setRemove('pid:' + pid + ':users_favourited', uid, function(err) { db.setCount('pid:' + pid + ':users_favourited', function(err, count) { posts.setPostField(pid, 'reputation', count); }); }); - if (uid !== uid_of_poster) { - user.incrementUserFieldBy(uid_of_poster, 'reputation', -1, function (err, newreputation) { - db.sortedSetAdd('users:reputation', newreputation, uid_of_poster); - }); - } - if (room_id) { - websockets.in(room_id).emit('event:rep_down', { + websockets.in(room_id).emit('event:unfavourited', { uid: uid !== uid_of_poster ? uid_of_poster : 0, pid: pid }); @@ -100,15 +234,15 @@ var async = require('async'), }); }; - Favourites.hasFavourited = function (pid, uid, callback) { + Favourites.hasFavourited = function(pid, uid, callback) { db.isSetMember('pid:' + pid + ':users_favourited', uid, callback); }; - Favourites.getFavouritesByPostIDs = function (pids, uid, callback) { + Favourites.getFavouritesByPostIDs = function(pids, uid, callback) { var data = {}; function iterator(pid, next) { - Favourites.hasFavourited(pid, uid, function (err, hasFavourited) { + Favourites.hasFavourited(pid, uid, function(err, hasFavourited) { data[pid] = hasFavourited; next() }); @@ -119,7 +253,7 @@ var async = require('async'), }); }; - Favourites.getFavouritedUidsByPids = function (pids, callback) { + Favourites.getFavouritedUidsByPids = function(pids, callback) { var data = {}; function getUids(pid, next) { diff --git a/src/posts.js b/src/posts.js index dd24aeed5a..25e0c2dd4c 100644 --- a/src/posts.js +++ b/src/posts.js @@ -479,8 +479,8 @@ var db = require('./database'), async.each(pids, reIndex, callback); } + // this function should really be called User.getFavouritePosts Posts.getFavourites = function(uid, start, end, callback) { - db.getSortedSetRevRange('uid:' + uid + ':favourites', start, end, function(err, pids) { if (err) { return callback(err); diff --git a/src/routes/user.js b/src/routes/user.js index 348cd7636c..9fee9d62c5 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -490,18 +490,26 @@ var fs = require('fs'), }); } - function getOnlineUsers(req, res) { + function getOnlineUsers(req, res, next) { var websockets = require('../socket.io'); user.getUsers('users:online', 0, 49, function (err, data) { + if(err) { + return next(err); + } var onlineUsers = []; uid = 0; if (req.user) { uid = req.user.uid; } + user.isAdministrator(uid, function (err, isAdministrator) { - if (true != isAdministrator) { + if(err) { + return next(err); + } + + if (!isAdministrator) { data = data.filter(function(item) { return item.status !== 'offline'; }); diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index b4f292af19..b2f6b73742 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -64,11 +64,12 @@ SocketAdmin.user.createUser = function(socket, user, callback) { SocketAdmin.user.banUser = function(socket, theirid) { admin.user.banUser(socket.uid, theirid, socket, function(isBanned) { if(isBanned) { - if(index.userSockets[theirid]) { - for(var i=0; i 0; + return Sockets.getUserSockets(uid).length > 0; } Sockets.updateRoomBrowsingText = updateRoomBrowsingText; @@ -292,11 +298,8 @@ function emitTopicPostStats(callback) { Sockets.emitOnlineUserCount = emitOnlineUserCount; function emitOnlineUserCount(callback) { - var anon = Sockets.userSockets[0] ? Sockets.userSockets[0].length : 0; - var registered = Object.keys(Sockets.userSockets).length; - if (anon) { - registered = registered - 1; - } + var anon = Sockets.getOnlineAnonCount(0); + var registered = Sockets.getConnectedClients().length; var returnObj = { users: registered + anon, diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index 4a89b32ff4..4d7e8f53d5 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -113,39 +113,29 @@ SocketModules.chats.send = function(socket, data) { usersData[0].uid = socket.uid; usersData[1].uid = touid; - Messaging.parse(msg, socket.uid, socket.uid, usersData[1], usersData[0], true, function(parsed) { - Messaging.addMessage(socket.uid, touid, msg, function(err, message) { - var numSockets = 0, - x; - - if (server.userSockets[touid]) { - numSockets = server.userSockets[touid].length; - - for (x = 0; x < numSockets; ++x) { - server.userSockets[touid][x].emit('event:chats.receive', { - fromuid: socket.uid, - username: username, - message: parsed, - timestamp: Date.now() - }); - } - } - - if (server.userSockets[socket.uid]) { - - numSockets = server.userSockets[socket.uid].length; - - for (x = 0; x < numSockets; ++x) { - server.userSockets[socket.uid][x].emit('event:chats.receive', { - fromuid: touid, - username: toUsername, - message: parsed, - timestamp: Date.now() - }); - } - } - }); - }); + Messaging.parse(msg, socket.uid, socket.uid, usersData[1], usersData[0], true, function(parsed) { + Messaging.addMessage(socket.uid, touid, msg, function(err, message) { + + + server.getUserSockets(touid).forEach(function(s) { + s.emit('event:chats.receive', { + fromuid: socket.uid, + username: username, + message: parsed, + timestamp: Date.now() + }); + }); + + server.getUserSockets(socket.uid).forEach(function(s) { + s.emit('event:chats.receive', { + fromuid: touid, + username: toUsername, + message: parsed, + timestamp: Date.now() + }); + }); + }); + }); }); }; diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 250cca3589..f8fc4b33c8 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -73,6 +73,24 @@ SocketPosts.reply = function(socket, data, callback) { }); }; +SocketPosts.upvote = function(socket, data) { + if(data && data.pid && data.room_id) { + favourites.upvote(data.pid, data.room_id, socket.uid, socket); + } +}; + +SocketPosts.downvote = function(socket, data) { + if(data && data.pid && data.room_id) { + favourites.downvote(data.pid, data.room_id, socket.uid, socket); + } +}; + +SocketPosts.unvote = function(socket, data) { + if(data && data.pid && data.room_id) { + favourites.unvote(data.pid, data.room_id, socket.uid, socket); + } +}; + SocketPosts.favourite = function(socket, data) { if(data && data.pid && data.room_id) { favourites.favourite(data.pid, data.room_id, socket.uid, socket); diff --git a/src/topics.js b/src/topics.js index 653ffb2fc3..5545826e51 100644 --- a/src/topics.js +++ b/src/topics.js @@ -341,6 +341,12 @@ var async = require('async'), }); } + function getVoteStatusData(next) { + favourites.getVoteStatusByPostIDs(pids, current_user, function(vote_data) { + next(null, vote_data); + }) + } + function addUserInfoToPosts(next) { function iterator(post, callback) { posts.addUserInfoToPost(post, function() { @@ -370,17 +376,21 @@ var async = require('async'), } } - async.parallel([getFavouritesData, addUserInfoToPosts, getPrivileges], function(err, results) { + async.parallel([getFavouritesData, addUserInfoToPosts, getPrivileges, getVoteStatusData], function(err, results) { if(err) { return callback(err); } var fav_data = results[0], - privileges = results[2]; + privileges = results[2], + voteStatus = results[3]; for (var i = 0; i < postData.length; ++i) { var pid = postData[i].pid; postData[i].favourited = fav_data[pid]; + postData[i].upvoted = voteStatus[pid].upvoted; + postData[i].downvoted = voteStatus[pid].downvoted; + postData[i].votes = postData[i].votes || 0; postData[i].display_moderator_tools = (current_user != 0) && privileges[pid].editable; postData[i].display_move_tools = privileges[pid].move; if(parseInt(postData[i].deleted, 10) === 1 && !privileges[pid].view_deleted) { @@ -647,8 +657,7 @@ var async = require('async'), var websockets = require('./socket.io'); if (!uids) { - clients = websockets.getConnectedClients(); - uids = Object.keys(clients); + uids = websockets.getConnectedClients(); } else if (!Array.isArray(uids)) { uids = [uids]; } diff --git a/src/upgrade.js b/src/upgrade.js index c3205b0579..5f7140d31d 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -19,7 +19,7 @@ var db = require('./database'), Upgrade.check = function(callback) { // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - var latestSchema = new Date(2014, 0, 30, 16, 0).getTime(); + var latestSchema = new Date(2014, 1, 2, 16, 0).getTime(); db.get('schemaDate', function(err, value) { if (parseInt(value, 10) >= latestSchema) { @@ -431,6 +431,8 @@ Upgrade.upgrade = function(callback) { } }, function(next) { + thisSchemaDate = new Date(2014, 0, 30, 16, 0).getTime(); + function updateTopic(tid, next) { Topics.getTopicFields(tid, ['postcount', 'viewcount'], function(err, topicData) { if(err) { @@ -454,7 +456,6 @@ Upgrade.upgrade = function(callback) { }); } - thisSchemaDate = new Date(2014, 0, 30, 16, 0).getTime(); if (schemaDate < thisSchemaDate) { updatesMade = true; @@ -476,7 +477,44 @@ Upgrade.upgrade = function(callback) { winston.info('[2014/1/30] Adding new topic sets -- skipped'); next(); } - } + }, + function(next) { + thisSchemaDate = new Date(2014, 1, 2, 16, 0).getTime(); + if (schemaDate < thisSchemaDate) { + updatesMade = true; + + winston.info('[2014/2/6] Upvoting all favourited posts for each user'); + + User.getUsers('users:joindate', 0, -1, function (err, users) { + function getFavourites(user, next) { + function upvote(post, next) { + var pid = post.pid, + uid = user.uid; + + if (post.uid !== uid) { + db.setAdd('pid:' + pid + ':upvote', uid); + db.sortedSetAdd('uid:' + uid + ':upvote', post.timestamp, pid); + db.incrObjectField('post:' + pid, 'votes'); + } + + next(); + } + + Posts.getFavourites(user.uid, 0, -1, function(err, posts) { + async.each(posts.posts, upvote, function(err) { + next(err); + }); + }); + } + async.each(users, getFavourites, function(err) { + next(err); + }); + }); + } else { + winston.info('[2014/2/6] Upvoting all favourited posts for each user -- skipped'); + next(); + } + }, // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 17!!! ], function(err) {