feat: #7743, posts module

v1.18.x
Barış Soner Uşaklı 6 years ago
parent 930ffd074f
commit 1b2b308a7e

@ -1,99 +1,68 @@
'use strict'; 'use strict';
var async = require('async'); const db = require('../database');
const plugins = require('../plugins');
var db = require('../database');
var plugins = require('../plugins');
module.exports = function (Posts) { module.exports = function (Posts) {
Posts.bookmark = function (pid, uid, callback) { Posts.bookmark = async function (pid, uid) {
toggleBookmark('bookmark', pid, uid, callback); return await toggleBookmark('bookmark', pid, uid);
}; };
Posts.unbookmark = function (pid, uid, callback) { Posts.unbookmark = async function (pid, uid) {
toggleBookmark('unbookmark', pid, uid, callback); return await toggleBookmark('unbookmark', pid, uid);
}; };
function toggleBookmark(type, pid, uid, callback) { async function toggleBookmark(type, pid, uid) {
if (parseInt(uid, 10) <= 0) { if (parseInt(uid, 10) <= 0) {
return callback(new Error('[[error:not-logged-in]]')); throw new Error('[[error:not-logged-in]]');
} }
var isBookmarking = type === 'bookmark'; const isBookmarking = type === 'bookmark';
var postData;
var hasBookmarked;
var owner;
async.waterfall([
function (next) {
async.parallel({
owner: function (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 (results, next) {
owner = results.owner;
postData = results.postData;
hasBookmarked = results.hasBookmarked;
if (isBookmarking && hasBookmarked) { const [postData, hasBookmarked] = await Promise.all([
return callback(new Error('[[error:already-bookmarked]]')); Posts.getPostFields(pid, ['pid', 'uid']),
} Posts.hasBookmarked(pid, uid),
]);
if (!isBookmarking && !hasBookmarked) { if (isBookmarking && hasBookmarked) {
return callback(new Error('[[error:already-unbookmarked]]')); throw new Error('[[error:already-bookmarked]]');
} }
if (isBookmarking) { if (!isBookmarking && !hasBookmarked) {
db.sortedSetAdd('uid:' + uid + ':bookmarks', Date.now(), pid, next); throw new Error('[[error:already-unbookmarked]]');
} 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, { if (isBookmarking) {
pid: pid, await db.sortedSetAdd('uid:' + uid + ':bookmarks', Date.now(), pid);
uid: uid, } else {
owner: owner, await db.sortedSetRemove('uid:' + uid + ':bookmarks', pid);
current: current, }
}); await db[isBookmarking ? 'setAdd' : 'setRemove']('pid:' + pid + ':users_bookmarked', uid);
postData.bookmarks = await db.setCount('pid:' + pid + ':users_bookmarked');
await Posts.setPostField(pid, 'bookmarks', postData.bookmarks);
plugins.fireHook('action:post.' + type, {
pid: pid,
uid: uid,
owner: postData.uid,
current: hasBookmarked ? 'bookmarked' : 'unbookmarked',
});
next(null, { return {
post: postData, post: postData,
isBookmarked: isBookmarking, isBookmarked: isBookmarking,
}); };
},
], callback);
} }
Posts.hasBookmarked = function (pid, uid, callback) { Posts.hasBookmarked = async function (pid, uid) {
if (parseInt(uid, 10) <= 0) { if (parseInt(uid, 10) <= 0) {
return callback(null, Array.isArray(pid) ? pid.map(() => false) : false); return Array.isArray(pid) ? pid.map(() => false) : false;
} }
if (Array.isArray(pid)) { if (Array.isArray(pid)) {
var sets = pid.map(pid => 'pid:' + pid + ':users_bookmarked'); const sets = pid.map(pid => 'pid:' + pid + ':users_bookmarked');
db.isMemberOfSets(sets, uid, callback); return await db.isMemberOfSets(sets, uid);
} else {
db.isSetMember('pid:' + pid + ':users_bookmarked', uid, callback);
} }
return await db.isSetMember('pid:' + pid + ':users_bookmarked', uid);
}; };
}; };

@ -1,83 +1,41 @@
'use strict'; 'use strict';
var async = require('async');
var _ = require('lodash');
var db = require('../database'); const _ = require('lodash');
var topics = require('../topics');
const db = require('../database');
const topics = require('../topics');
module.exports = function (Posts) { module.exports = function (Posts) {
Posts.getCidByPid = function (pid, callback) { Posts.getCidByPid = async function (pid) {
async.waterfall([ const tid = await Posts.getPostField(pid, 'tid');
function (next) { return await topics.getTopicField(tid, 'cid');
Posts.getPostField(pid, 'tid', next);
},
function (tid, next) {
topics.getTopicField(tid, 'cid', next);
},
], callback);
}; };
Posts.getCidsByPids = function (pids, callback) { Posts.getCidsByPids = async function (pids) {
var tids; const postData = await Posts.getPostsFields(pids, ['tid']);
var postData; const tids = _.uniq(postData.map(post => post && post.tid).filter(Boolean));
async.waterfall([ const topicData = await topics.getTopicsFields(tids, ['cid']);
function (next) { const tidToTopic = _.zipObject(tids, topicData);
Posts.getPostsFields(pids, ['tid'], next); const cids = postData.map(post => tidToTopic[post.tid].cid);
}, return cids;
function (_postData, next) {
postData = _postData;
tids = _.uniq(postData.map(post => post && post.tid).filter(Boolean));
topics.getTopicsFields(tids, ['cid'], next);
},
function (topicData, next) {
var map = {};
topicData.forEach(function (topic, index) {
if (topic) {
map[tids[index]] = topic.cid;
}
});
var cids = postData.map(post => map[post.tid]);
next(null, cids);
},
], callback);
}; };
Posts.filterPidsByCid = function (pids, cid, callback) { Posts.filterPidsByCid = async function (pids, cid) {
if (!cid) { if (!cid) {
return setImmediate(callback, null, pids); return pids;
} }
if (!Array.isArray(cid) || cid.length === 1) { if (!Array.isArray(cid) || cid.length === 1) {
return filterPidsBySingleCid(pids, cid, callback); return await filterPidsBySingleCid(pids, cid);
} }
const pidsArr = await Promise.all(cid.map(c => Posts.filterPidsByCid(pids, c)));
async.waterfall([ return _.union.apply(_, pidsArr);
function (next) {
async.map(cid, function (cid, next) {
Posts.filterPidsByCid(pids, cid, next);
}, next);
},
function (pidsArr, next) {
next(null, _.union.apply(_, pidsArr));
},
], callback);
}; };
function filterPidsBySingleCid(pids, cid, callback) { async function filterPidsBySingleCid(pids, cid) {
async.waterfall([ const isMembers = await db.isSortedSetMembers('cid:' + parseInt(cid, 10) + ':pids', pids);
function (next) { return pids.filter((pid, index) => pid && isMembers[index]);
db.isSortedSetMembers('cid:' + parseInt(cid, 10) + ':pids', pids, next);
},
function (isMembers, next) {
pids = pids.filter(function (pid, index) {
return pid && isMembers[index];
});
next(null, pids);
},
], callback);
} }
}; };

@ -1,6 +1,5 @@
'use strict'; 'use strict';
var async = require('async');
var _ = require('lodash'); var _ = require('lodash');
var meta = require('../meta'); var meta = require('../meta');
@ -13,102 +12,73 @@ var groups = require('../groups');
var utils = require('../utils'); var utils = require('../utils');
module.exports = function (Posts) { module.exports = function (Posts) {
Posts.create = function (data, callback) { Posts.create = async function (data) {
// This is an internal method, consider using Topics.reply instead // This is an internal method, consider using Topics.reply instead
var uid = data.uid; const uid = data.uid;
var tid = data.tid; const tid = data.tid;
var content = data.content.toString(); const content = data.content.toString();
var timestamp = data.timestamp || Date.now(); const timestamp = data.timestamp || Date.now();
var isMain = data.isMain || false; const isMain = data.isMain || false;
if (!uid && parseInt(uid, 10) !== 0) { if (!uid && parseInt(uid, 10) !== 0) {
return callback(new Error('[[error:invalid-uid]]')); throw new Error('[[error:invalid-uid]]');
} }
if (data.toPid && !utils.isNumber(data.toPid)) { if (data.toPid && !utils.isNumber(data.toPid)) {
return callback(new Error('[[error:invalid-pid]]')); throw new Error('[[error:invalid-pid]]');
} }
var postData; const pid = await db.incrObjectField('global', 'nextPid');
let postData = {
pid: pid,
uid: uid,
tid: tid,
content: content,
timestamp: timestamp,
deleted: 0,
};
async.waterfall([ if (data.toPid) {
function (next) { postData.toPid = data.toPid;
db.incrObjectField('global', 'nextPid', next); }
}, if (data.ip && meta.config.trackIpPerPost) {
function (pid, next) { postData.ip = data.ip;
postData = { }
pid: pid, if (data.handle && !parseInt(uid, 10)) {
uid: uid, postData.handle = data.handle;
tid: tid, }
content: content,
timestamp: timestamp,
deleted: 0,
};
if (data.toPid) { let result = await plugins.fireHook('filter:post.create', { post: postData, data: data });
postData.toPid = data.toPid; postData = result.post;
} await db.setObject('post:' + postData.pid, postData);
if (data.ip && meta.config.trackIpPerPost) { const topicData = await topics.getTopicFields(tid, ['cid', 'pinned']);
postData.ip = data.ip; postData.cid = topicData.cid;
}
if (data.handle && !parseInt(uid, 10)) { await Promise.all([
postData.handle = data.handle; db.sortedSetAdd('posts:pid', timestamp, postData.pid),
} db.incrObjectField('global', 'postCount'),
user.onNewPostMade(postData),
topics.onNewPostMade(postData),
categories.onNewPostMade(topicData.cid, topicData.pinned, postData),
groups.onNewPostMade(postData),
addReplyTo(postData, timestamp),
Posts.uploads.sync(postData.pid),
]);
plugins.fireHook('filter:post.create', { post: postData, data: data }, next); result = await plugins.fireHook('filter:post.get', { post: postData, uid: data.uid });
}, result.post.isMain = isMain;
function (data, next) { plugins.fireHook('action:post.save', { post: _.clone(result.post) });
postData = data.post; return result.post;
db.setObject('post:' + postData.pid, postData, next);
},
function (next) {
topics.getTopicFields(tid, ['cid', 'pinned'], next);
},
function (topicData, next) {
postData.cid = topicData.cid;
async.parallel([
function (next) {
user.onNewPostMade(postData, next);
},
function (next) {
topics.onNewPostMade(postData, next);
},
function (next) {
categories.onNewPostMade(topicData.cid, topicData.pinned, postData, next);
},
function (next) {
groups.onNewPostMade(postData, next);
},
function (next) {
db.sortedSetAdd('posts:pid', timestamp, postData.pid, next);
},
function (next) {
if (!postData.toPid) {
return next(null);
}
async.parallel([
async.apply(db.sortedSetAdd, 'pid:' + postData.toPid + ':replies', timestamp, postData.pid),
async.apply(db.incrObjectField, 'post:' + postData.toPid, 'replies'),
], next);
},
function (next) {
db.incrObjectField('global', 'postCount', next);
},
async.apply(Posts.uploads.sync, postData.pid),
], function (err) {
next(err);
});
},
function (next) {
plugins.fireHook('filter:post.get', { post: postData, uid: data.uid }, next);
},
function (data, next) {
data.post.isMain = isMain;
plugins.fireHook('action:post.save', { post: _.clone(data.post) });
next(null, data.post);
},
], callback);
}; };
async function addReplyTo(postData, timestamp) {
if (!postData.toPid) {
return;
}
await Promise.all([
db.sortedSetAdd('pid:' + postData.toPid + ':replies', timestamp, postData.pid),
db.incrObjectField('post:' + postData.toPid, 'replies'),
]);
}
}; };

@ -1,10 +1,8 @@
'use strict'; 'use strict';
var async = require('async'); const db = require('../database');
const plugins = require('../plugins');
var db = require('../database'); const utils = require('../utils');
var plugins = require('../plugins');
var utils = require('../utils');
const intFields = [ const intFields = [
'uid', 'pid', 'tid', 'deleted', 'timestamp', 'uid', 'pid', 'tid', 'deleted', 'timestamp',
@ -12,67 +10,49 @@ const intFields = [
]; ];
module.exports = function (Posts) { module.exports = function (Posts) {
Posts.getPostsFields = function (pids, fields, callback) { Posts.getPostsFields = async function (pids, fields) {
if (!Array.isArray(pids) || !pids.length) { if (!Array.isArray(pids) || !pids.length) {
return callback(null, []); return [];
} }
const keys = pids.map(pid => 'post:' + pid);
async.waterfall([ let postData;
function (next) { if (fields.length) {
const keys = pids.map(pid => 'post:' + pid); postData = await db.getObjectsFields(keys, fields);
if (fields.length) { } else {
db.getObjectsFields(keys, fields, next); postData = await db.getObjects(keys);
} else { }
db.getObjects(keys, next); const result = await plugins.fireHook('filter:post.getFields', { posts: postData, fields: fields });
} result.posts.forEach(post => modifyPost(post, fields));
}, return Array.isArray(result.posts) ? result.posts : null;
function (posts, next) {
plugins.fireHook('filter:post.getFields', { posts: posts, fields: fields }, next);
},
function (data, next) {
data.posts.forEach(post => modifyPost(post, fields));
next(null, Array.isArray(data.posts) ? data.posts : null);
},
], callback);
}; };
Posts.getPostData = function (pid, callback) { Posts.getPostData = async function (pid) {
Posts.getPostsFields([pid], [], function (err, posts) { const posts = await Posts.getPostsFields([pid], []);
callback(err, posts && posts.length ? posts[0] : null); return posts && posts.length ? posts[0] : null;
});
}; };
Posts.getPostsData = function (pids, callback) { Posts.getPostsData = async function (pids) {
Posts.getPostsFields(pids, [], callback); return await Posts.getPostsFields(pids, []);
}; };
Posts.getPostField = function (pid, field, callback) { Posts.getPostField = async function (pid, field) {
Posts.getPostFields(pid, [field], function (err, post) { const post = await Posts.getPostFields(pid, [field]);
callback(err, post ? post[field] : null); return post ? post[field] : null;
});
}; };
Posts.getPostFields = function (pid, fields, callback) { Posts.getPostFields = async function (pid, fields) {
Posts.getPostsFields([pid], fields, function (err, posts) { const posts = await Posts.getPostsFields([pid], fields);
callback(err, posts ? posts[0] : null); return posts ? posts[0] : null;
});
}; };
Posts.setPostField = function (pid, field, value, callback) { Posts.setPostField = async function (pid, field, value) {
Posts.setPostFields(pid, { [field]: value }, callback); await Posts.setPostFields(pid, { [field]: value });
}; };
Posts.setPostFields = function (pid, data, callback) { Posts.setPostFields = async function (pid, data) {
async.waterfall([ await db.setObject('post:' + pid, data);
function (next) { data.pid = pid;
db.setObject('post:' + pid, data, next); plugins.fireHook('action:post.setFields', { data: data });
},
function (next) {
data.pid = pid;
plugins.fireHook('action:post.setFields', { data: data });
next();
},
], callback);
}; };
}; };

@ -1,212 +1,132 @@
'use strict'; 'use strict';
var async = require('async'); const _ = require('lodash');
var _ = require('lodash');
var db = require('../database'); const db = require('../database');
var topics = require('../topics'); const topics = require('../topics');
var user = require('../user'); const categories = require('../categories');
var groups = require('../groups'); const user = require('../user');
var notifications = require('../notifications'); const groups = require('../groups');
var plugins = require('../plugins'); const notifications = require('../notifications');
const plugins = require('../plugins');
module.exports = function (Posts) { module.exports = function (Posts) {
Posts.delete = function (pid, uid, callback) { Posts.delete = async function (pid, uid) {
deleteOrRestore('delete', pid, uid, callback); return await deleteOrRestore('delete', pid, uid);
}; };
Posts.restore = function (pid, uid, callback) { Posts.restore = async function (pid, uid) {
deleteOrRestore('restore', pid, uid, callback); return await deleteOrRestore('restore', pid, uid);
}; };
function deleteOrRestore(type, pid, uid, callback) { async function deleteOrRestore(type, pid, uid) {
var postData;
const isDeleting = type === 'delete'; const isDeleting = type === 'delete';
async.waterfall([ await plugins.fireHook('filter:post.' + type, { pid: pid, uid: uid });
function (next) { await Posts.setPostFields(pid, {
plugins.fireHook('filter:post.' + type, { pid: pid, uid: uid }, next); deleted: isDeleting ? 1 : 0,
}, deleterUid: isDeleting ? uid : 0,
function (data, next) { });
Posts.setPostFields(pid, { const postData = await Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'timestamp']);
deleted: isDeleting ? 1 : 0, const topicData = await topics.getTopicFields(postData.tid, ['tid', 'cid', 'pinned']);
deleterUid: isDeleting ? uid : 0, postData.cid = topicData.cid;
}, next); await Promise.all([
}, topics.updateLastPostTimeFromLastPid(postData.tid),
function (next) { topics.updateTeaser(postData.tid),
Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'timestamp'], next); isDeleting ?
}, db.sortedSetRemove('cid:' + topicData.cid + ':pids', pid) :
function (_post, next) { db.sortedSetAdd('cid:' + topicData.cid + ':pids', postData.timestamp, pid),
postData = _post; ]);
topics.getTopicFields(_post.tid, ['tid', 'cid', 'pinned'], next); plugins.fireHook('action:post.' + type, { post: _.clone(postData), uid: uid });
}, return postData;
function (topicData, next) {
postData.cid = topicData.cid;
async.parallel([
function (next) {
topics.updateLastPostTimeFromLastPid(postData.tid, next);
},
function (next) {
if (isDeleting) {
db.sortedSetRemove('cid:' + topicData.cid + ':pids', pid, next);
} else {
db.sortedSetAdd('cid:' + topicData.cid + ':pids', postData.timestamp, pid, next);
}
},
function (next) {
topics.updateTeaser(postData.tid, next);
},
], next);
},
function (results, next) {
plugins.fireHook('action:post.' + type, { post: _.clone(postData), uid: uid });
next(null, postData);
},
], callback);
} }
Posts.purge = function (pid, uid, callback) { Posts.purge = async function (pid, uid) {
let postData; const postData = await Posts.getPostData(pid);
async.waterfall([ if (!postData) {
function (next) { return;
Posts.getPostData(pid, next); }
},
function (_postData, next) { await plugins.fireHook('filter:post.purge', { post: postData, pid: pid, uid: uid });
postData = _postData; await Promise.all([
if (!postData) { deletePostFromTopicUserNotification(postData),
return callback(); deletePostFromCategoryRecentPosts(pid),
} deletePostFromUsersBookmarks(pid),
plugins.fireHook('filter:post.purge', { post: postData, pid: pid, uid: uid }, next); deletePostFromUsersVotes(pid),
}, deletePostFromReplies(postData),
function (data, next) { deletePostFromGroups(postData),
async.parallel([ db.sortedSetsRemove(['posts:pid', 'posts:votes', 'posts:flagged'], pid),
async.apply(deletePostFromTopicUserNotification, postData), ]);
async.apply(deletePostFromCategoryRecentPosts, pid), plugins.fireHook('action:post.purge', { post: postData, uid: uid });
async.apply(deletePostFromUsersBookmarks, pid), await db.delete('post:' + pid);
async.apply(deletePostFromUsersVotes, pid),
async.apply(deletePostFromReplies, postData),
async.apply(deletePostFromGroups, postData),
async.apply(db.sortedSetsRemove, ['posts:pid', 'posts:votes', 'posts:flagged'], pid),
], err => next(err));
},
function (next) {
plugins.fireHook('action:post.purge', { post: postData, uid: uid });
db.delete('post:' + pid, next);
},
], callback);
}; };
function deletePostFromTopicUserNotification(postData, callback) { async function deletePostFromTopicUserNotification(postData) {
async.waterfall([ await db.sortedSetsRemove([
function (next) { 'tid:' + postData.tid + ':posts',
db.sortedSetsRemove([ 'tid:' + postData.tid + ':posts:votes',
'tid:' + postData.tid + ':posts', 'uid:' + postData.uid + ':posts',
'tid:' + postData.tid + ':posts:votes', ], postData.pid);
'uid:' + postData.uid + ':posts', const topicData = await topics.getTopicFields(postData.tid, ['tid', 'cid', 'pinned']);
], postData.pid, next); const tasks = [
}, db.decrObjectField('global', 'postCount'),
function (next) { db.decrObjectField('category:' + topicData.cid, 'post_count'),
topics.getTopicFields(postData.tid, ['tid', 'cid', 'pinned'], next); db.sortedSetRemove('cid:' + topicData.cid + ':uid:' + postData.uid + ':pids', postData.pid),
}, db.sortedSetRemove('cid:' + topicData.cid + ':uid:' + postData.uid + ':pids:votes', postData.pid),
function (topicData, next) { topics.decreasePostCount(postData.tid),
const tasks = [ topics.updateTeaser(postData.tid),
async.apply(db.decrObjectField, 'global', 'postCount'), topics.updateLastPostTimeFromLastPid(postData.tid),
async.apply(db.decrObjectField, 'category:' + topicData.cid, 'post_count'), db.sortedSetIncrBy('tid:' + postData.tid + ':posters', -1, postData.uid),
async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':uid:' + postData.uid + ':pids', postData.pid), user.incrementUserPostCountBy(postData.uid, -1),
async.apply(db.sortedSetRemove, 'cid:' + topicData.cid + ':uid:' + postData.uid + ':pids:votes', postData.pid), notifications.rescind('new_post:tid:' + postData.tid + ':pid:' + postData.pid + ':uid:' + postData.uid),
async.apply(topics.decreasePostCount, postData.tid), ];
async.apply(topics.updateTeaser, postData.tid), if (!topicData.pinned) {
async.apply(topics.updateLastPostTimeFromLastPid, postData.tid), tasks.push(db.sortedSetIncrBy, 'cid:' + topicData.cid + ':tids:posts', -1, postData.tid);
async.apply(db.sortedSetIncrBy, 'tid:' + postData.tid + ':posters', -1, postData.uid), }
async.apply(user.incrementUserPostCountBy, postData.uid, -1), await Promise.all(tasks);
async.apply(notifications.rescind, 'new_post:tid:' + postData.tid + ':pid:' + postData.pid + ':uid:' + postData.uid),
];
if (!topicData.pinned) {
tasks.push(async.apply(db.sortedSetIncrBy, 'cid:' + topicData.cid + ':tids:posts', -1, postData.tid));
}
async.parallel(tasks, next);
},
], function (err) {
callback(err);
});
} }
function deletePostFromCategoryRecentPosts(pid, callback) { async function deletePostFromCategoryRecentPosts(pid) {
async.waterfall([ const cids = await categories.getAllCidsFromSet('categories:cid');
function (next) { const sets = cids.map(cid => 'cid:' + cid + ':pids');
db.getSortedSetRange('categories:cid', 0, -1, next); await db.sortedSetsRemove(sets, pid);
},
function (cids, next) {
const sets = cids.map(cid => 'cid:' + cid + ':pids');
db.sortedSetsRemove(sets, pid, next);
},
], callback);
} }
function deletePostFromUsersBookmarks(pid, callback) { async function deletePostFromUsersBookmarks(pid) {
async.waterfall([ const uids = await db.getSetMembers('pid:' + pid + ':users_bookmarked');
function (next) { const sets = uids.map(uid => 'uid:' + uid + ':bookmarks');
db.getSetMembers('pid:' + pid + ':users_bookmarked', next); await db.sortedSetsRemove(sets, pid);
}, await db.delete('pid:' + pid + ':users_bookmarked');
function (uids, next) {
const sets = uids.map(uid => 'uid:' + uid + ':bookmarks');
db.sortedSetsRemove(sets, pid, next);
},
function (next) {
db.delete('pid:' + pid + ':users_bookmarked', next);
},
], callback);
} }
function deletePostFromUsersVotes(pid, callback) { async function deletePostFromUsersVotes(pid) {
async.waterfall([ const [upvoters, downvoters] = await Promise.all([
function (next) { db.getSetMembers('pid:' + pid + ':upvote'),
async.parallel({ db.getSetMembers('pid:' + pid + ':downvote'),
upvoters: function (next) { ]);
db.getSetMembers('pid:' + pid + ':upvote', next); const upvoterSets = upvoters.map(uid => 'uid:' + uid + ':upvote');
}, const downvoterSets = downvoters.map(uid => 'uid:' + uid + ':downvote');
downvoters: function (next) { await Promise.all([
db.getSetMembers('pid:' + pid + ':downvote', next); db.sortedSetsRemove(upvoterSets.concat(downvoterSets), pid),
}, db.deleteAll(['pid:' + pid + ':upvote', 'pid:' + pid + ':downvote']),
}, next); ]);
},
function (results, next) {
async.parallel([
function (next) {
const upvoterSets = results.upvoters.map(uid => 'uid:' + uid + ':upvote');
const downvoterSets = results.downvoters.map(uid => 'uid:' + uid + ':downvote');
db.sortedSetsRemove(upvoterSets.concat(downvoterSets), pid, next);
},
function (next) {
db.deleteAll(['pid:' + pid + ':upvote', 'pid:' + pid + ':downvote'], next);
},
], next);
},
], callback);
} }
function deletePostFromReplies(postData, callback) { async function deletePostFromReplies(postData) {
if (!parseInt(postData.toPid, 10)) { if (!parseInt(postData.toPid, 10)) {
return setImmediate(callback); return;
} }
async.parallel([ await Promise.all([
async.apply(db.sortedSetRemove, 'pid:' + postData.toPid + ':replies', postData.pid), db.sortedSetRemove('pid:' + postData.toPid + ':replies', postData.pid),
async.apply(db.decrObjectField, 'post:' + postData.toPid, 'replies'), db.decrObjectField('post:' + postData.toPid, 'replies'),
], callback); ]);
} }
function deletePostFromGroups(postData, callback) { async function deletePostFromGroups(postData) {
if (!parseInt(postData.uid, 10)) { if (!parseInt(postData.uid, 10)) {
return setImmediate(callback); return;
} }
async.waterfall([ const groupNames = await groups.getUserGroupMembership('groups:visible:createtime', [postData.uid]);
function (next) { const keys = groupNames[0].map(groupName => 'group:' + groupName + ':member:pids');
groups.getUserGroupMembership('groups:visible:createtime', [postData.uid], next); await db.sortedSetsRemove(keys, postData.pid);
},
function (groupNames, next) {
groupNames = groupNames[0];
const keys = groupNames.map(groupName => 'group:' + groupName + ':member:pids');
db.sortedSetsRemove(keys, postData.pid, next);
},
], callback);
} }
}; };

@ -901,6 +901,14 @@ describe('Post\'s', function () {
done(); done();
}); });
}); });
it('should filter pids by multiple cids', function (done) {
posts.filterPidsByCid([postData.pid, 100, 101], [cid], function (err, pids) {
assert.ifError(err);
assert.deepEqual([postData.pid], pids);
done();
});
});
}); });
it('should error if user does not exist', function (done) { it('should error if user does not exist', function (done) {

Loading…
Cancel
Save