cleanup and tests

v1.18.x
Baris Usakli 8 years ago
parent 82cab53508
commit 062bced3dd

@ -313,16 +313,13 @@ topicsController.teaser = function (req, res, next) {
} }
posts.getPostSummaryByPids([pid], req.uid, { stripTags: false }, next); posts.getPostSummaryByPids([pid], req.uid, { stripTags: false }, next);
}, },
], function (err, posts) { function (posts) {
if (err) { if (!Array.isArray(posts) || !posts.length) {
return next(err); return res.status(404).json('not-found');
} }
res.json(posts[0]);
if (!Array.isArray(posts) || !posts.length) { },
return res.status(404).json('not-found'); ], next);
}
res.json(posts[0]);
});
}; };
topicsController.pagination = function (req, res, callback) { topicsController.pagination = function (req, res, callback) {

@ -108,14 +108,15 @@ function expose(exposedField, method, field, req, res, next) {
if (!req.params.hasOwnProperty(field)) { if (!req.params.hasOwnProperty(field)) {
return next(); return next();
} }
method(req.params[field], function (err, id) { async.waterfall([
if (err) { function (next) {
return next(err); method(req.params[field], next);
} },
function (id, next) {
res.locals[exposedField] = id; res.locals[exposedField] = id;
next(); next();
}); },
], next);
} }
middleware.privateUploads = function (req, res, next) { middleware.privateUploads = function (req, res, next) {

@ -1,38 +1,44 @@
'use strict'; 'use strict';
var async = require('async');
var nconf = require('nconf'); var nconf = require('nconf');
var meta = require('../meta'); var meta = require('../meta');
var user = require('../user'); var user = require('../user');
module.exports = function (middleware) { module.exports = function (middleware) {
middleware.maintenanceMode = function (req, res, next) { middleware.maintenanceMode = function (req, res, callback) {
if (parseInt(meta.config.maintenanceMode, 10) !== 1) { if (parseInt(meta.config.maintenanceMode, 10) !== 1) {
return next(); return callback();
} }
var url = req.url.replace(nconf.get('relative_path'), ''); var url = req.url.replace(nconf.get('relative_path'), '');
if (url.startsWith('/login') || url.startsWith('/api/login')) { if (url.startsWith('/login') || url.startsWith('/api/login')) {
return next(); return callback();
} }
var data;
async.waterfall([
function (next) {
user.isAdministrator(req.uid, next);
},
function (isAdmin, next) {
if (isAdmin) {
return callback();
}
res.status(503);
data = {
site_title: meta.config.title || 'NodeBB',
message: meta.config.maintenanceModeMessage,
};
user.isAdministrator(req.uid, function (err, isAdmin) { if (res.locals.isAPI) {
if (err || isAdmin) { return res.json(data);
return next(err); }
}
res.status(503); middleware.buildHeader(req, res, next);
var data = { },
site_title: meta.config.title || 'NodeBB', function () {
message: meta.config.maintenanceModeMessage,
};
if (res.locals.isAPI) {
return res.json(data);
}
middleware.buildHeader(req, res, function () {
res.render('503', data); res.render('503', data);
}); },
}); ], callback);
}; };
}; };

@ -18,69 +18,70 @@ module.exports = function (Posts) {
if (!parseInt(uid, 10)) { if (!parseInt(uid, 10)) {
return callback(new Error('[[error:not-logged-in]]')); return callback(new Error('[[error:not-logged-in]]'));
} }
var isBookmarking = type === 'bookmark';
async.parallel({ var isBookmarking = type === 'bookmark';
owner: function (next) { var postData;
Posts.getPostField(pid, 'uid', next); var hasBookmarked;
}, var owner;
postData: function (next) { async.waterfall([
Posts.getPostFields(pid, ['pid', 'uid'], next); function (next) {
}, async.parallel({
hasBookmarked: function (next) { owner: function (next) {
Posts.hasBookmarked(pid, uid, next); Posts.getPostField(pid, 'uid', next);
},
postData: function (next) {
Posts.getPostFields(pid, ['pid', 'uid'], next);
},
hasBookmarked: function (next) {
Posts.hasBookmarked(pid, uid, next);
},
}, next);
}, },
}, function (err, results) { function (results, next) {
if (err) { owner = results.owner;
return callback(err); postData = results.postData;
} hasBookmarked = results.hasBookmarked;
if (isBookmarking && results.hasBookmarked) {
return callback(new Error('[[error:already-bookmarked]]'));
}
if (!isBookmarking && !results.hasBookmarked) { if (isBookmarking && hasBookmarked) {
return callback(new Error('[[error:already-unbookmarked]]')); return callback(new Error('[[error:already-bookmarked]]'));
} }
async.waterfall([ if (!isBookmarking && !hasBookmarked) {
function (next) { return callback(new Error('[[error:already-unbookmarked]]'));
if (isBookmarking) {
db.sortedSetAdd('uid:' + uid + ':bookmarks', Date.now(), pid, next);
} else {
db.sortedSetRemove('uid:' + uid + ':bookmarks', pid, next);
}
},
function (next) {
db[isBookmarking ? 'setAdd' : 'setRemove']('pid:' + pid + ':users_bookmarked', uid, next);
},
function (next) {
db.setCount('pid:' + pid + ':users_bookmarked', next);
},
function (count, next) {
results.postData.bookmarks = count;
Posts.setPostField(pid, 'bookmarks', count, next);
},
], function (err) {
if (err) {
return callback(err);
} }
var current = results.hasBookmarked ? 'bookmarked' : 'unbookmarked'; if (isBookmarking) {
db.sortedSetAdd('uid:' + uid + ':bookmarks', Date.now(), pid, next);
} else {
db.sortedSetRemove('uid:' + uid + ':bookmarks', pid, next);
}
},
function (next) {
db[isBookmarking ? 'setAdd' : 'setRemove']('pid:' + pid + ':users_bookmarked', uid, next);
},
function (next) {
db.setCount('pid:' + pid + ':users_bookmarked', next);
},
function (count, next) {
postData.bookmarks = count;
Posts.setPostField(pid, 'bookmarks', count, next);
},
function (next) {
var current = hasBookmarked ? 'bookmarked' : 'unbookmarked';
plugins.fireHook('action:post.' + type, { plugins.fireHook('action:post.' + type, {
pid: pid, pid: pid,
uid: uid, uid: uid,
owner: results.owner, owner: owner,
current: current, current: current,
}); });
callback(null, { next(null, {
post: results.postData, post: postData,
isBookmarked: isBookmarking, isBookmarked: isBookmarking,
}); });
}); },
}); ], callback);
} }
Posts.hasBookmarked = function (pid, uid, callback) { Posts.hasBookmarked = function (pid, uid, callback) {

@ -102,12 +102,12 @@ module.exports = function (Posts) {
db.incrObjectField('global', 'postCount', next); db.incrObjectField('global', 'postCount', next);
}, },
], function (err) { ], function (err) {
if (err) { next(err);
return next(err);
}
plugins.fireHook('filter:post.get', { post: postData, uid: data.uid }, next);
}); });
}, },
function (next) {
plugins.fireHook('filter:post.get', { post: postData, uid: data.uid }, next);
},
function (data, next) { function (data, next) {
data.post.isMain = isMain; data.post.isMain = isMain;
plugins.fireHook('action:post.save', { post: _.clone(data.post) }); plugins.fireHook('action:post.save', { post: _.clone(data.post) });

@ -216,87 +216,89 @@ module.exports = function (Posts) {
} }
function deletePostFromCategoryRecentPosts(pid, callback) { function deletePostFromCategoryRecentPosts(pid, callback) {
db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) { async.waterfall([
if (err) { function (next) {
return callback(err); db.getSortedSetRange('categories:cid', 0, -1, next);
} },
function (cids, next) {
var sets = cids.map(function (cid) { var sets = cids.map(function (cid) {
return 'cid:' + cid + ':pids'; return 'cid:' + cid + ':pids';
}); });
db.sortedSetsRemove(sets, pid, callback); db.sortedSetsRemove(sets, pid, next);
}); },
], callback);
} }
function deletePostFromUsersBookmarks(pid, callback) { function deletePostFromUsersBookmarks(pid, callback) {
db.getSetMembers('pid:' + pid + ':users_bookmarked', function (err, uids) { async.waterfall([
if (err) { function (next) {
return callback(err); db.getSetMembers('pid:' + pid + ':users_bookmarked', next);
} },
function (uids, next) {
var sets = uids.map(function (uid) { var sets = uids.map(function (uid) {
return 'uid:' + uid + ':bookmarks'; return 'uid:' + uid + ':bookmarks';
}); });
db.sortedSetsRemove(sets, pid, function (err) {
if (err) {
return callback(err);
}
db.delete('pid:' + pid + ':users_bookmarked', callback); db.sortedSetsRemove(sets, pid, next);
}); },
}); function (next) {
db.delete('pid:' + pid + ':users_bookmarked', next);
},
], callback);
} }
function deletePostFromUsersVotes(pid, callback) { function deletePostFromUsersVotes(pid, callback) {
async.parallel({ async.waterfall([
upvoters: function (next) { function (next) {
db.getSetMembers('pid:' + pid + ':upvote', next); async.parallel({
}, upvoters: function (next) {
downvoters: function (next) { db.getSetMembers('pid:' + pid + ':upvote', next);
db.getSetMembers('pid:' + pid + ':downvote', next); },
downvoters: function (next) {
db.getSetMembers('pid:' + pid + ':downvote', next);
},
}, next);
}, },
}, function (err, results) { function (results, next) {
if (err) { var upvoterSets = results.upvoters.map(function (uid) {
return callback(err); return 'uid:' + uid + ':upvote';
} });
var upvoterSets = results.upvoters.map(function (uid) {
return 'uid:' + uid + ':upvote';
});
var downvoterSets = results.downvoters.map(function (uid) { var downvoterSets = results.downvoters.map(function (uid) {
return 'uid:' + uid + ':downvote'; return 'uid:' + uid + ':downvote';
}); });
async.parallel([ async.parallel([
function (next) { function (next) {
db.sortedSetsRemove(upvoterSets, pid, next); db.sortedSetsRemove(upvoterSets, pid, next);
}, },
function (next) { function (next) {
db.sortedSetsRemove(downvoterSets, pid, next); db.sortedSetsRemove(downvoterSets, pid, next);
}, },
function (next) { function (next) {
db.deleteAll(['pid:' + pid + ':upvote', 'pid:' + pid + ':downvote'], next); db.deleteAll(['pid:' + pid + ':upvote', 'pid:' + pid + ':downvote'], next);
}, },
], callback); ], next);
}); },
], callback);
} }
function deletePostFromReplies(pid, callback) { function deletePostFromReplies(pid, callback) {
Posts.getPostField(pid, 'toPid', function (err, toPid) { async.waterfall([
if (err) { function (next) {
return callback(err); Posts.getPostField(pid, 'toPid', next);
} },
if (!parseInt(toPid, 10)) { function (toPid, next) {
return callback(null); if (!parseInt(toPid, 10)) {
} return callback(null);
async.parallel([ }
async.apply(db.sortedSetRemove, 'pid:' + toPid + ':replies', pid), async.parallel([
async.apply(db.decrObjectField, 'post:' + toPid, 'replies'), async.apply(db.sortedSetRemove, 'pid:' + toPid + ':replies', pid),
], callback); async.apply(db.decrObjectField, 'post:' + toPid, 'replies'),
}); ], next);
},
], callback);
} }
function deletePostFromGroups(pid, callback) { function deletePostFromGroups(pid, callback) {

@ -83,74 +83,74 @@ module.exports = function (Posts) {
var tid = postData.tid; var tid = postData.tid;
var title = data.title ? data.title.trim() : ''; var title = data.title ? data.title.trim() : '';
async.parallel({ var topicData;
topic: function (next) { var results;
topics.getTopicFields(tid, ['cid', 'title', 'timestamp'], next); async.waterfall([
}, function (next) {
isMain: function (next) { async.parallel({
Posts.isMain(data.pid, next); topic: function (next) {
topics.getTopicFields(tid, ['cid', 'title', 'timestamp'], next);
},
isMain: function (next) {
Posts.isMain(data.pid, next);
},
}, next);
}, },
}, function (err, results) { function (_results, next) {
if (err) { results = _results;
return callback(err); if (!results.isMain) {
} return callback(null, {
tid: tid,
cid: results.topic.cid,
isMainPost: false,
renamed: false,
});
}
if (!results.isMain) { topicData = {
return callback(null, {
tid: tid, tid: tid,
cid: results.topic.cid, cid: results.topic.cid,
isMainPost: false, uid: postData.uid,
renamed: false, mainPid: data.pid,
};
if (title) {
topicData.title = title;
topicData.slug = tid + '/' + (utils.slugify(title) || 'topic');
}
topicData.thumb = data.thumb || '';
data.tags = data.tags || [];
plugins.fireHook('filter:topic.edit', { req: data.req, topic: topicData, data: data }, next);
},
function (results, next) {
db.setObject('topic:' + tid, results.topic, next);
},
function (next) {
topics.updateTags(tid, data.tags, next);
},
function (next) {
topics.getTopicTagsObjects(tid, next);
},
function (tags, next) {
topicData.tags = data.tags;
topicData.oldTitle = results.topic.title;
topicData.timestamp = results.topic.timestamp;
plugins.fireHook('action:topic.edit', { topic: topicData, uid: data.uid });
next(null, {
tid: tid,
cid: topicData.cid,
uid: postData.uid,
title: validator.escape(String(title)),
oldTitle: results.topic.title,
slug: topicData.slug,
isMainPost: true,
renamed: title !== results.topic.title,
tags: tags,
}); });
} },
], callback);
var topicData = {
tid: tid,
cid: results.topic.cid,
uid: postData.uid,
mainPid: data.pid,
};
if (title) {
topicData.title = title;
topicData.slug = tid + '/' + (utils.slugify(title) || 'topic');
}
topicData.thumb = data.thumb || '';
data.tags = data.tags || [];
async.waterfall([
function (next) {
plugins.fireHook('filter:topic.edit', { req: data.req, topic: topicData, data: data }, next);
},
function (results, next) {
db.setObject('topic:' + tid, results.topic, next);
},
function (next) {
topics.updateTags(tid, data.tags, next);
},
function (next) {
topics.getTopicTagsObjects(tid, next);
},
function (tags, next) {
topicData.tags = data.tags;
topicData.oldTitle = results.topic.title;
topicData.timestamp = results.topic.timestamp;
plugins.fireHook('action:topic.edit', { topic: topicData, uid: data.uid });
next(null, {
tid: tid,
cid: results.topic.cid,
uid: postData.uid,
title: validator.escape(String(title)),
oldTitle: results.topic.title,
slug: topicData.slug,
isMainPost: true,
renamed: title !== results.topic.title,
tags: tags,
});
},
], callback);
});
} }
}; };

@ -89,47 +89,48 @@ module.exports = function (Posts) {
function parsePosts(posts, options, callback) { function parsePosts(posts, options, callback) {
async.map(posts, function (post, next) { async.map(posts, function (post, next) {
if (!post.content || !options.parse) { async.waterfall([
if (options.stripTags) { function (next) {
post.content = stripTags(post.content); if (!post.content || !options.parse) {
} post.content = post.content ? validator.escape(String(post.content)) : post.content;
post.content = post.content ? validator.escape(String(post.content)) : post.content; return next(null, post);
return next(null, post); }
} Posts.parsePost(post, next);
},
Posts.parsePost(post, function (err, post) { function (post, next) {
if (err) { if (options.stripTags) {
return next(err); post.content = stripTags(post.content);
} }
if (options.stripTags) { next(null, post);
post.content = stripTags(post.content); },
} ], next);
next(null, post);
});
}, callback); }, callback);
} }
function getTopicAndCategories(tids, callback) { function getTopicAndCategories(tids, callback) {
topics.getTopicsFields(tids, ['uid', 'tid', 'title', 'cid', 'slug', 'deleted', 'postcount', 'mainPid'], function (err, topics) { var topicsData;
if (err) { async.waterfall([
return callback(err); function (next) {
} topics.getTopicsFields(tids, ['uid', 'tid', 'title', 'cid', 'slug', 'deleted', 'postcount', 'mainPid'], next);
},
var cids = topics.map(function (topic) { function (_topicsData, next) {
if (topic) { topicsData = _topicsData;
topic.title = String(topic.title); var cids = topicsData.map(function (topic) {
topic.deleted = parseInt(topic.deleted, 10) === 1; if (topic) {
} topic.title = String(topic.title);
return topic && topic.cid; topic.deleted = parseInt(topic.deleted, 10) === 1;
}).filter(function (topic, index, array) { }
return topic && array.indexOf(topic) === index; return topic && topic.cid;
}); }).filter(function (topic, index, array) {
return topic && array.indexOf(topic) === index;
categories.getCategoriesFields(cids, ['cid', 'name', 'icon', 'slug', 'parentCid', 'bgColor', 'color'], function (err, categories) { });
callback(err, { topics: topics, categories: categories });
}); categories.getCategoriesFields(cids, ['cid', 'name', 'icon', 'slug', 'parentCid', 'bgColor', 'color'], next);
}); },
function (categoriesData, next) {
next(null, { topics: topicsData, categories: categoriesData });
},
], callback);
} }
function toObject(key, data) { function toObject(key, data) {

@ -25,69 +25,67 @@ module.exports = function (Posts) {
}); });
groups.getGroupsData(groupTitles, next); groups.getGroupsData(groupTitles, next);
}, },
], function (err, groupsData) { function (groupsData, next) {
if (err) { groupsData.forEach(function (group) {
return callback(err); if (group && group.userTitleEnabled) {
} groupsMap[group.name] = {
name: group.name,
groupsData.forEach(function (group) { slug: group.slug,
if (group && group.userTitleEnabled) { labelColor: group.labelColor,
groupsMap[group.name] = { icon: group.icon,
name: group.name, userTitle: group.userTitle,
slug: group.slug, };
labelColor: group.labelColor,
icon: group.icon,
userTitle: group.userTitle,
};
}
});
userData.forEach(function (userData) {
userData.uid = userData.uid || 0;
userData.username = userData.username || '[[global:guest]]';
userData.userslug = userData.userslug || '';
userData.reputation = userData.reputation || 0;
userData.postcount = userData.postcount || 0;
userData.banned = parseInt(userData.banned, 10) === 1;
userData.picture = userData.picture || '';
userData.status = user.getStatus(userData);
userData.signature = validator.escape(String(userData.signature || ''));
userData.fullname = validator.escape(String(userData.fullname || ''));
});
async.map(userData, function (userData, next) {
async.parallel({
isMemberOfGroup: function (next) {
if (!userData.groupTitle || !groupsMap[userData.groupTitle]) {
return next();
}
groups.isMember(userData.uid, userData.groupTitle, next);
},
signature: function (next) {
if (!userData.signature || parseInt(meta.config.disableSignatures, 10) === 1) {
userData.signature = '';
return next();
}
Posts.parseSignature(userData, uid, next);
},
customProfileInfo: function (next) {
plugins.fireHook('filter:posts.custom_profile_info', { profile: [], uid: userData.uid }, next);
},
}, function (err, results) {
if (err) {
return next(err);
} }
});
if (results.isMemberOfGroup && userData.groupTitle && groupsMap[userData.groupTitle]) { userData.forEach(function (userData) {
userData.selectedGroup = groupsMap[userData.groupTitle]; userData.uid = userData.uid || 0;
} userData.username = userData.username || '[[global:guest]]';
userData.userslug = userData.userslug || '';
userData.reputation = userData.reputation || 0;
userData.postcount = userData.postcount || 0;
userData.banned = parseInt(userData.banned, 10) === 1;
userData.picture = userData.picture || '';
userData.status = user.getStatus(userData);
userData.signature = validator.escape(String(userData.signature || ''));
userData.fullname = validator.escape(String(userData.fullname || ''));
});
userData.custom_profile_info = results.customProfileInfo.profile; async.map(userData, function (userData, next) {
async.waterfall([
function (next) {
async.parallel({
isMemberOfGroup: function (next) {
if (!userData.groupTitle || !groupsMap[userData.groupTitle]) {
return next();
}
groups.isMember(userData.uid, userData.groupTitle, next);
},
signature: function (next) {
if (!userData.signature || parseInt(meta.config.disableSignatures, 10) === 1) {
userData.signature = '';
return next();
}
Posts.parseSignature(userData, uid, next);
},
customProfileInfo: function (next) {
plugins.fireHook('filter:posts.custom_profile_info', { profile: [], uid: userData.uid }, next);
},
}, next);
},
function (results, next) {
if (results.isMemberOfGroup && userData.groupTitle && groupsMap[userData.groupTitle]) {
userData.selectedGroup = groupsMap[userData.groupTitle];
}
plugins.fireHook('filter:posts.modifyUserInfo', userData, next); userData.custom_profile_info = results.customProfileInfo.profile;
});
}, callback); plugins.fireHook('filter:posts.modifyUserInfo', userData, next);
}); },
], next);
}, next);
},
], callback);
}; };
Posts.isOwner = function (pid, uid, callback) { Posts.isOwner = function (pid, uid, callback) {

@ -65,14 +65,14 @@ module.exports = function (Posts) {
if (!parseInt(uid, 10)) { if (!parseInt(uid, 10)) {
return callback(null, { upvoted: false, downvoted: false }); return callback(null, { upvoted: false, downvoted: false });
} }
async.waterfall([
db.isMemberOfSets(['pid:' + pid + ':upvote', 'pid:' + pid + ':downvote'], uid, function (err, hasVoted) { function (next) {
if (err) { db.isMemberOfSets(['pid:' + pid + ':upvote', 'pid:' + pid + ':downvote'], uid, next);
return callback(err); },
} function (hasVoted, next) {
next(null, { upvoted: hasVoted[0], downvoted: hasVoted[1] });
callback(null, { upvoted: hasVoted[0], downvoted: hasVoted[1] }); },
}); ], callback);
}; };
Posts.getVoteStatusByPostIDs = function (pids, uid, callback) { Posts.getVoteStatusByPostIDs = function (pids, uid, callback) {
@ -124,151 +124,157 @@ module.exports = function (Posts) {
} }
function toggleVote(type, pid, uid, callback) { function toggleVote(type, pid, uid, callback) {
unvote(pid, uid, type, function (err) { async.waterfall([
if (err) { function (next) {
return callback(err); unvote(pid, uid, type, function (err) {
} next(err);
});
vote(type, false, pid, uid, callback); },
}); function (next) {
vote(type, false, pid, uid, next);
},
], callback);
} }
function unvote(pid, uid, command, callback) { function unvote(pid, uid, command, callback) {
async.parallel({ async.waterfall([
owner: function (next) { function (next) {
Posts.getPostField(pid, 'uid', next); async.parallel({
}, owner: function (next) {
voteStatus: function (next) { Posts.getPostField(pid, 'uid', next);
Posts.hasVoted(pid, uid, next); },
}, voteStatus: function (next) {
reputation: function (next) { Posts.hasVoted(pid, uid, next);
user.getUserField(uid, 'reputation', next); },
reputation: function (next) {
user.getUserField(uid, 'reputation', next);
},
}, next);
}, },
}, function (err, results) { function (results, next) {
if (err) { if (parseInt(uid, 10) === parseInt(results.owner, 10)) {
return callback(err); return callback(new Error('self-vote'));
} }
if (parseInt(uid, 10) === parseInt(results.owner, 10)) {
return callback(new Error('self-vote'));
}
if (command === 'downvote' && parseInt(results.reputation, 10) < parseInt(meta.config['privileges:downvote'], 10)) { if (command === 'downvote' && parseInt(results.reputation, 10) < parseInt(meta.config['privileges:downvote'], 10)) {
return callback(new Error('[[error:not-enough-reputation-to-downvote]]')); return callback(new Error('[[error:not-enough-reputation-to-downvote]]'));
} }
var voteStatus = results.voteStatus; var voteStatus = results.voteStatus;
var hook; var hook;
var current = voteStatus.upvoted ? 'upvote' : 'downvote'; var current = voteStatus.upvoted ? 'upvote' : 'downvote';
if ((voteStatus.upvoted && command === 'downvote') || (voteStatus.downvoted && command === 'upvote')) { // e.g. User *has* upvoted, and clicks downvote if ((voteStatus.upvoted && command === 'downvote') || (voteStatus.downvoted && command === 'upvote')) { // e.g. User *has* upvoted, and clicks downvote
hook = command; hook = command;
} else if (voteStatus.upvoted || voteStatus.downvoted) { // e.g. User *has* upvoted, clicks upvote (so we "unvote") } else if (voteStatus.upvoted || voteStatus.downvoted) { // e.g. User *has* upvoted, clicks upvote (so we "unvote")
hook = 'unvote'; hook = 'unvote';
} else { // e.g. User *has not* voted, clicks upvote } else { // e.g. User *has not* voted, clicks upvote
hook = command; hook = command;
current = 'unvote'; current = 'unvote';
} }
plugins.fireHook('action:post.' + hook, { plugins.fireHook('action:post.' + hook, {
pid: pid, pid: pid,
uid: uid, uid: uid,
owner: results.owner, owner: results.owner,
current: current, current: current,
}); });
if (!voteStatus || (!voteStatus.upvoted && !voteStatus.downvoted)) { if (!voteStatus || (!voteStatus.upvoted && !voteStatus.downvoted)) {
return callback(); return callback();
} }
vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, uid, callback); vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, uid, next);
}); },
], callback);
} }
function vote(type, unvote, pid, uid, callback) { function vote(type, unvote, pid, uid, callback) {
uid = parseInt(uid, 10); uid = parseInt(uid, 10);
if (uid === 0) { if (!uid) {
return callback(new Error('[[error:not-logged-in]]')); return callback(new Error('[[error:not-logged-in]]'));
} }
var postData;
var newreputation;
async.waterfall([
function (next) {
Posts.getPostFields(pid, ['pid', 'uid', 'tid'], next);
},
function (_postData, next) {
postData = _postData;
var now = Date.now();
Posts.getPostFields(pid, ['pid', 'uid', 'tid'], function (err, postData) { if (type === 'upvote' && !unvote) {
if (err) { db.sortedSetAdd('uid:' + uid + ':upvote', now, pid);
return callback(err); } else {
} db.sortedSetRemove('uid:' + uid + ':upvote', pid);
}
var now = Date.now();
if (type === 'upvote' && !unvote) {
db.sortedSetAdd('uid:' + uid + ':upvote', now, pid);
} else {
db.sortedSetRemove('uid:' + uid + ':upvote', pid);
}
if (type === 'upvote' || unvote) {
db.sortedSetRemove('uid:' + uid + ':downvote', pid);
} else {
db.sortedSetAdd('uid:' + uid + ':downvote', now, pid);
}
user[type === 'upvote' ? 'incrementUserFieldBy' : 'decrementUserFieldBy'](postData.uid, 'reputation', 1, function (err, newreputation) { if (type === 'upvote' || unvote) {
if (err) { db.sortedSetRemove('uid:' + uid + ':downvote', pid);
return callback(err); } else {
db.sortedSetAdd('uid:' + uid + ':downvote', now, pid);
} }
user[type === 'upvote' ? 'incrementUserFieldBy' : 'decrementUserFieldBy'](postData.uid, 'reputation', 1, next);
},
function (_newreputation, next) {
newreputation = _newreputation;
if (parseInt(postData.uid, 10)) { if (parseInt(postData.uid, 10)) {
db.sortedSetAdd('users:reputation', newreputation, postData.uid); db.sortedSetAdd('users:reputation', newreputation, postData.uid);
} }
adjustPostVotes(postData, uid, type, unvote, function (err) { adjustPostVotes(postData, uid, type, unvote, next);
callback(err, { },
user: { function (next) {
reputation: newreputation, next(null, {
}, user: {
post: postData, reputation: newreputation,
upvote: type === 'upvote' && !unvote, },
downvote: type === 'downvote' && !unvote, post: postData,
}); upvote: type === 'upvote' && !unvote,
downvote: type === 'downvote' && !unvote,
}); });
}); },
}); ], callback);
} }
function adjustPostVotes(postData, uid, type, unvote, callback) { function adjustPostVotes(postData, uid, type, unvote, callback) {
var notType = (type === 'upvote' ? 'downvote' : 'upvote'); var notType = (type === 'upvote' ? 'downvote' : 'upvote');
async.waterfall([
async.series([
function (next) { function (next) {
if (unvote) { async.series([
db.setRemove('pid:' + postData.pid + ':' + type, uid, next); function (next) {
} else { if (unvote) {
db.setAdd('pid:' + postData.pid + ':' + type, uid, next); db.setRemove('pid:' + postData.pid + ':' + type, uid, next);
} } else {
db.setAdd('pid:' + postData.pid + ':' + type, uid, next);
}
},
function (next) {
db.setRemove('pid:' + postData.pid + ':' + notType, uid, next);
},
], function (err) {
next(err);
});
}, },
function (next) { function (next) {
db.setRemove('pid:' + postData.pid + ':' + notType, uid, next); async.parallel({
upvotes: function (next) {
db.setCount('pid:' + postData.pid + ':upvote', next);
},
downvotes: function (next) {
db.setCount('pid:' + postData.pid + ':downvote', next);
},
}, next);
}, },
], function (err) { function (results, next) {
if (err) {
return callback(err);
}
async.parallel({
upvotes: function (next) {
db.setCount('pid:' + postData.pid + ':upvote', next);
},
downvotes: function (next) {
db.setCount('pid:' + postData.pid + ':downvote', next);
},
}, function (err, results) {
if (err) {
return callback(err);
}
postData.upvotes = parseInt(results.upvotes, 10); postData.upvotes = parseInt(results.upvotes, 10);
postData.downvotes = parseInt(results.downvotes, 10); postData.downvotes = parseInt(results.downvotes, 10);
postData.votes = postData.upvotes - postData.downvotes; postData.votes = postData.upvotes - postData.downvotes;
Posts.updatePostVoteCount(postData, callback); Posts.updatePostVoteCount(postData, next);
}); },
}); ], callback);
} }
}; };

@ -8,119 +8,132 @@ var plugins = require('../plugins');
var translator = require('../translator'); var translator = require('../translator');
var db = require('../database'); var db = require('../database');
var widgets = {}; var widgets = module.exports;
widgets.render = function (uid, area, req, res, callback) { widgets.render = function (uid, area, req, res, callback) {
if (!area.locations || !area.template) { if (!area.locations || !area.template) {
return callback(new Error('[[error:invalid-data]]')); return callback(new Error('[[error:invalid-data]]'));
} }
widgets.getAreas(['global', area.template], area.locations, function (err, data) { async.waterfall([
if (err) { function (next) {
return callback(err); widgets.getAreas(['global', area.template], area.locations, next);
} },
function (data, next) {
var widgetsByLocation = {}; var widgetsByLocation = {};
async.map(area.locations, function (location, done) {
widgetsByLocation[location] = data.global[location].concat(data[area.template][location]);
if (!widgetsByLocation[location].length) { async.map(area.locations, function (location, done) {
return done(null, { location: location, widgets: [] }); widgetsByLocation[location] = data.global[location].concat(data[area.template][location]);
}
async.map(widgetsByLocation[location], function (widget, next) { if (!widgetsByLocation[location].length) {
if (!widget || !widget.data || return done(null, { location: location, widgets: [] });
(!!widget.data['hide-registered'] && uid !== 0) ||
(!!widget.data['hide-guests'] && uid === 0) ||
(!!widget.data['hide-mobile'] && area.isMobile)) {
return next();
} }
plugins.fireHook('filter:widget.render:' + widget.widget, { async.map(widgetsByLocation[location], function (widget, next) {
uid: uid, if (!widget || !widget.data ||
area: area, (!!widget.data['hide-registered'] && uid !== 0) ||
data: widget.data, (!!widget.data['hide-guests'] && uid === 0) ||
req: req, (!!widget.data['hide-mobile'] && area.isMobile)) {
res: res, return next();
}, function (err, data) {
if (err || data === null) {
return next(err);
}
var html = data;
if (typeof html !== 'string') {
html = data.html;
} else {
winston.warn('[widgets.render] passing a string is deprecated!, filter:widget.render:' + widget.widget + '. Please set hookData.html in your plugin.');
} }
if (widget.data.container && widget.data.container.match('{body}')) { renderWidget(widget, uid, area, req, res, next);
translator.translate(widget.data.title, function (title) { }, function (err, result) {
html = templates.parse(widget.data.container, { done(err, { location: location, widgets: result.filter(Boolean) });
title: title,
body: html,
});
next(null, { html: html });
});
} else {
next(null, { html: html });
}
}); });
}, function (err, result) { }, next);
done(err, { location: location, widgets: result.filter(Boolean) }); },
}); ], callback);
}, callback);
});
}; };
function renderWidget(widget, uid, area, req, res, callback) {
async.waterfall([
function (next) {
plugins.fireHook('filter:widget.render:' + widget.widget, {
uid: uid,
area: area,
data: widget.data,
req: req,
res: res,
}, next);
},
function (data, next) {
if (!data) {
return callback();
}
var html = data;
if (typeof html !== 'string') {
html = data.html;
} else {
winston.warn('[widgets.render] passing a string is deprecated!, filter:widget.render:' + widget.widget + '. Please set hookData.html in your plugin.');
}
if (widget.data.container && widget.data.container.match('{body}')) {
translator.translate(widget.data.title, function (title) {
html = templates.parse(widget.data.container, {
title: title,
body: html,
});
next(null, { html: html });
});
} else {
next(null, { html: html });
}
},
], callback);
}
widgets.getAreas = function (templates, locations, callback) { widgets.getAreas = function (templates, locations, callback) {
var keys = templates.map(function (tpl) { var keys = templates.map(function (tpl) {
return 'widgets:' + tpl; return 'widgets:' + tpl;
}); });
db.getObjectsFields(keys, locations, function (err, data) { async.waterfall([
if (err) { function (next) {
return callback(err); db.getObjectsFields(keys, locations, next);
} },
function (data, next) {
var returnData = {}; var returnData = {};
templates.forEach(function (template, index) { templates.forEach(function (template, index) {
returnData[template] = returnData[template] || {}; returnData[template] = returnData[template] || {};
locations.forEach(function (location) { locations.forEach(function (location) {
if (data && data[index] && data[index][location]) { if (data && data[index] && data[index][location]) {
try { try {
returnData[template][location] = JSON.parse(data[index][location]); returnData[template][location] = JSON.parse(data[index][location]);
} catch (err) { } catch (err) {
winston.error('can not parse widget data. template: ' + template + ' location: ' + location); winston.error('can not parse widget data. template: ' + template + ' location: ' + location);
returnData[template][location] = [];
}
} else {
returnData[template][location] = []; returnData[template][location] = [];
} }
} else { });
returnData[template][location] = [];
}
}); });
});
callback(null, returnData); next(null, returnData);
}); },
], callback);
}; };
widgets.getArea = function (template, location, callback) { widgets.getArea = function (template, location, callback) {
db.getObjectField('widgets:' + template, location, function (err, result) { async.waterfall([
if (err) { function (next) {
return callback(err); db.getObjectField('widgets:' + template, location, next);
} },
if (!result) { function (result, next) {
return callback(null, []); if (!result) {
} return callback(null, []);
try { }
result = JSON.parse(result); try {
} catch (err) { result = JSON.parse(result);
return callback(err); } catch (err) {
} return callback(err);
}
callback(null, result);
}); next(null, result);
},
], callback);
}; };
widgets.setArea = function (area, callback) { widgets.setArea = function (area, callback) {
@ -137,42 +150,42 @@ widgets.reset = function (callback) {
{ name: 'Draft Zone', template: 'global', location: 'footer' }, { name: 'Draft Zone', template: 'global', location: 'footer' },
{ name: 'Draft Zone', template: 'global', location: 'sidebar' }, { name: 'Draft Zone', template: 'global', location: 'sidebar' },
]; ];
var drafts;
async.parallel({ async.waterfall([
areas: function (next) { function (next) {
plugins.fireHook('filter:widgets.getAreas', defaultAreas, next); async.parallel({
areas: function (next) {
plugins.fireHook('filter:widgets.getAreas', defaultAreas, next);
},
drafts: function (next) {
widgets.getArea('global', 'drafts', next);
},
}, next);
}, },
drafts: function (next) { function (results, next) {
widgets.getArea('global', 'drafts', next); drafts = results.drafts || [];
async.each(results.areas, function (area, next) {
async.waterfall([
function (next) {
widgets.getArea(area.template, area.location, next);
},
function (areaData, next) {
drafts = drafts.concat(areaData);
area.widgets = [];
widgets.setArea(area, next);
},
], next);
}, next);
}, },
}, function (err, results) { function (next) {
if (err) {
return callback(err);
}
var drafts = results.drafts || [];
async.each(results.areas, function (area, next) {
widgets.getArea(area.template, area.location, function (err, areaData) {
if (err) {
return next(err);
}
drafts = drafts.concat(areaData);
area.widgets = [];
widgets.setArea(area, next);
});
}, function (err) {
if (err) {
return callback(err);
}
widgets.setArea({ widgets.setArea({
template: 'global', template: 'global',
location: 'drafts', location: 'drafts',
widgets: drafts, widgets: drafts,
}, callback); }, next);
}); },
}); ], callback);
}; };
module.exports = widgets; module.exports = widgets;

@ -138,6 +138,9 @@ function setupMockDefaults(callback) {
function (next) { function (next) {
db.emptydb(next); db.emptydb(next);
}, },
function (next) {
db.createIndices(next);
},
function (next) { function (next) {
winston.info('test_database flushed'); winston.info('test_database flushed');
setupDefaultConfigs(meta, next); setupDefaultConfigs(meta, next);

Loading…
Cancel
Save