mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-02 05:40:43 +01:00
refactor: async/await socket.io/topics
This commit is contained in:
@@ -1,16 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
const topics = require('../topics');
|
||||
const posts = require('../posts');
|
||||
const user = require('../user');
|
||||
const meta = require('../meta');
|
||||
const apiController = require('../controllers/api');
|
||||
const privileges = require('../privileges');
|
||||
const socketHelpers = require('./helpers');
|
||||
|
||||
var topics = require('../topics');
|
||||
var posts = require('../posts');
|
||||
var user = require('../user');
|
||||
var meta = require('../meta');
|
||||
var apiController = require('../controllers/api');
|
||||
var privileges = require('../privileges');
|
||||
var socketHelpers = require('./helpers');
|
||||
|
||||
var SocketTopics = module.exports;
|
||||
const SocketTopics = module.exports;
|
||||
|
||||
require('./topics/unread')(SocketTopics);
|
||||
require('./topics/move')(SocketTopics);
|
||||
@@ -19,129 +17,99 @@ require('./topics/infinitescroll')(SocketTopics);
|
||||
require('./topics/tags')(SocketTopics);
|
||||
require('./topics/merge')(SocketTopics);
|
||||
|
||||
SocketTopics.post = function (socket, data, callback) {
|
||||
SocketTopics.post = async function (socket, data) {
|
||||
if (!data) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
socketHelpers.setDefaultPostData(data, socket);
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
meta.blacklist.test(data.req.ip, next);
|
||||
},
|
||||
function (next) {
|
||||
posts.shouldQueue(socket.uid, data, next);
|
||||
},
|
||||
function (shouldQueue, next) {
|
||||
if (shouldQueue) {
|
||||
posts.addToQueue(data, next);
|
||||
} else {
|
||||
postTopic(socket, data, next);
|
||||
}
|
||||
},
|
||||
], callback);
|
||||
await meta.blacklist.test(data.req.ip);
|
||||
const shouldQueue = await posts.shouldQueue(socket.uid, data);
|
||||
if (shouldQueue) {
|
||||
return await posts.addToQueue(data);
|
||||
}
|
||||
return await postTopic(socket, data);
|
||||
};
|
||||
|
||||
function postTopic(socket, data, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.post(data, next);
|
||||
},
|
||||
function (result, next) {
|
||||
next(null, result.topicData);
|
||||
async function postTopic(socket, data) {
|
||||
const result = await topics.post(data);
|
||||
|
||||
socket.emit('event:new_post', { posts: [result.postData] });
|
||||
socket.emit('event:new_topic', result.topicData);
|
||||
socket.emit('event:new_post', { posts: [result.postData] });
|
||||
socket.emit('event:new_topic', result.topicData);
|
||||
|
||||
socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
|
||||
},
|
||||
], callback);
|
||||
socketHelpers.notifyNew(socket.uid, 'newTopic', { posts: [result.postData], topic: result.topicData });
|
||||
return result.topicData;
|
||||
}
|
||||
|
||||
SocketTopics.postcount = function (socket, tid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
privileges.topics.can('topics:read', tid, socket.uid, next);
|
||||
},
|
||||
function (canRead, next) {
|
||||
if (!canRead) {
|
||||
return next(new Error('[[no-privileges]]'));
|
||||
}
|
||||
|
||||
topics.getTopicField(tid, 'postcount', next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
SocketTopics.bookmark = function (socket, data, callback) {
|
||||
if (!socket.uid || !data) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
SocketTopics.postcount = async function (socket, tid) {
|
||||
const canRead = await privileges.topics.can('topics:read', tid, socket.uid);
|
||||
if (!canRead) {
|
||||
throw new Error('[[no-privileges]]');
|
||||
}
|
||||
topics.setUserBookmark(data.tid, socket.uid, data.index, callback);
|
||||
return await topics.getTopicField(tid, 'postcount');
|
||||
};
|
||||
|
||||
SocketTopics.createTopicFromPosts = function (socket, data, callback) {
|
||||
SocketTopics.bookmark = async function (socket, data) {
|
||||
if (!socket.uid || !data) {
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
await topics.setUserBookmark(data.tid, socket.uid, data.index);
|
||||
};
|
||||
|
||||
SocketTopics.createTopicFromPosts = async function (socket, data) {
|
||||
if (!socket.uid) {
|
||||
return callback(new Error('[[error:not-logged-in]]'));
|
||||
throw new Error('[[error:not-logged-in]]');
|
||||
}
|
||||
|
||||
if (!data || !data.title || !data.pids || !Array.isArray(data.pids)) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
topics.createTopicFromPosts(socket.uid, data.title, data.pids, data.fromTid, callback);
|
||||
return await topics.createTopicFromPosts(socket.uid, data.title, data.pids, data.fromTid);
|
||||
};
|
||||
|
||||
SocketTopics.changeWatching = function (socket, data, callback) {
|
||||
SocketTopics.changeWatching = async function (socket, data) {
|
||||
if (!data || !data.tid || !data.type) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
var commands = ['follow', 'unfollow', 'ignore'];
|
||||
const commands = ['follow', 'unfollow', 'ignore'];
|
||||
if (!commands.includes(data.type)) {
|
||||
return callback(new Error('[[error:invalid-command]]'));
|
||||
throw new Error('[[error:invalid-command]]');
|
||||
}
|
||||
followCommand(topics[data.type], socket, data.tid, callback);
|
||||
await followCommand(topics[data.type], socket, data.tid);
|
||||
};
|
||||
|
||||
SocketTopics.follow = function (socket, tid, callback) {
|
||||
followCommand(topics.follow, socket, tid, callback);
|
||||
SocketTopics.follow = async function (socket, tid) {
|
||||
await followCommand(topics.follow, socket, tid);
|
||||
};
|
||||
|
||||
function followCommand(method, socket, tid, callback) {
|
||||
async function followCommand(method, socket, tid) {
|
||||
if (!socket.uid) {
|
||||
return callback(new Error('[[error:not-logged-in]]'));
|
||||
throw new Error('[[error:not-logged-in]]');
|
||||
}
|
||||
|
||||
method(tid, socket.uid, callback);
|
||||
await method(tid, socket.uid);
|
||||
}
|
||||
|
||||
SocketTopics.isFollowed = function (socket, tid, callback) {
|
||||
topics.isFollowing([tid], socket.uid, function (err, isFollowing) {
|
||||
callback(err, Array.isArray(isFollowing) && isFollowing.length ? isFollowing[0] : false);
|
||||
});
|
||||
SocketTopics.isFollowed = async function (socket, tid) {
|
||||
const isFollowing = await topics.isFollowing([tid], socket.uid);
|
||||
return isFollowing[0];
|
||||
};
|
||||
|
||||
SocketTopics.search = function (socket, data, callback) {
|
||||
SocketTopics.search = async function (socket, data) {
|
||||
if (!data) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
topics.search(data.tid, data.term, callback);
|
||||
return await topics.search(data.tid, data.term);
|
||||
};
|
||||
|
||||
SocketTopics.isModerator = function (socket, tid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.getTopicField(tid, 'cid', next);
|
||||
},
|
||||
function (cid, next) {
|
||||
user.isModerator(socket.uid, cid, next);
|
||||
},
|
||||
], callback);
|
||||
SocketTopics.isModerator = async function (socket, tid) {
|
||||
const cid = await topics.getTopicField(tid, 'cid');
|
||||
return await user.isModerator(socket.uid, cid);
|
||||
};
|
||||
|
||||
SocketTopics.getTopic = function (socket, tid, callback) {
|
||||
apiController.getTopicData(tid, socket.uid, callback);
|
||||
SocketTopics.getTopic = async function (socket, tid) {
|
||||
return await apiController.getTopicData(tid, socket.uid);
|
||||
};
|
||||
|
||||
require('../promisify')(SocketTopics);
|
||||
|
||||
@@ -1,87 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var topics = require('../../topics');
|
||||
const topics = require('../../topics');
|
||||
const categories = require('../../categories');
|
||||
var privileges = require('../../privileges');
|
||||
var meta = require('../../meta');
|
||||
var utils = require('../../utils');
|
||||
var social = require('../../social');
|
||||
const privileges = require('../../privileges');
|
||||
const meta = require('../../meta');
|
||||
const utils = require('../../utils');
|
||||
const social = require('../../social');
|
||||
|
||||
module.exports = function (SocketTopics) {
|
||||
SocketTopics.loadMore = function (socket, data, callback) {
|
||||
SocketTopics.loadMore = async function (socket, data) {
|
||||
if (!data || !data.tid || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
var userPrivileges;
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
privileges: function (next) {
|
||||
privileges.topics.get(data.tid, socket.uid, next);
|
||||
},
|
||||
topic: function (next) {
|
||||
topics.getTopicFields(data.tid, ['postcount', 'deleted'], next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
if (!results.privileges['topics:read'] || (results.topic.deleted && !results.privileges.view_deleted)) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
const [userPrivileges, topicData] = await Promise.all([
|
||||
privileges.topics.get(data.tid, socket.uid),
|
||||
topics.getTopicFields(data.tid, ['postcount', 'deleted']),
|
||||
]);
|
||||
|
||||
userPrivileges = results.privileges;
|
||||
if (!userPrivileges['topics:read'] || (topicData.deleted && !userPrivileges.view_deleted)) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
var set = data.topicPostSort === 'most_votes' ? 'tid:' + data.tid + ':posts:votes' : 'tid:' + data.tid + ':posts';
|
||||
var reverse = data.topicPostSort === 'newest_to_oldest' || data.topicPostSort === 'most_votes';
|
||||
var start = Math.max(0, parseInt(data.after, 10));
|
||||
const set = data.topicPostSort === 'most_votes' ? 'tid:' + data.tid + ':posts:votes' : 'tid:' + data.tid + ':posts';
|
||||
const reverse = data.topicPostSort === 'newest_to_oldest' || data.topicPostSort === 'most_votes';
|
||||
let start = Math.max(0, parseInt(data.after, 10));
|
||||
|
||||
var infScrollPostsPerPage = Math.max(0, Math.min(meta.config.postsPerPage || 20, parseInt(data.count, 10) || meta.config.postsPerPage || 20));
|
||||
const infScrollPostsPerPage = Math.max(0, Math.min(meta.config.postsPerPage || 20, parseInt(data.count, 10) || meta.config.postsPerPage || 20));
|
||||
|
||||
if (data.direction === -1) {
|
||||
start -= (infScrollPostsPerPage + 1);
|
||||
}
|
||||
if (data.direction === -1) {
|
||||
start -= (infScrollPostsPerPage + 1);
|
||||
}
|
||||
|
||||
var stop = start + infScrollPostsPerPage - 1;
|
||||
let stop = start + infScrollPostsPerPage - 1;
|
||||
|
||||
start = Math.max(0, start);
|
||||
stop = Math.max(0, stop);
|
||||
start = Math.max(0, start);
|
||||
stop = Math.max(0, stop);
|
||||
|
||||
async.parallel({
|
||||
mainPost: function (next) {
|
||||
if (start > 0) {
|
||||
return next();
|
||||
}
|
||||
topics.getMainPost(data.tid, socket.uid, next);
|
||||
},
|
||||
posts: function (next) {
|
||||
topics.getTopicPosts(data.tid, set, start, stop, socket.uid, reverse, next);
|
||||
},
|
||||
postSharing: function (next) {
|
||||
social.getActivePostSharing(next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (topicData, next) {
|
||||
if (topicData.mainPost) {
|
||||
topicData.posts = [topicData.mainPost].concat(topicData.posts);
|
||||
}
|
||||
const [mainPost, posts, postSharing] = await Promise.all([
|
||||
start > 0 ? null : topics.getMainPost(data.tid, socket.uid),
|
||||
topics.getTopicPosts(data.tid, set, start, stop, socket.uid, reverse),
|
||||
social.getActivePostSharing(),
|
||||
]);
|
||||
|
||||
topicData.privileges = userPrivileges;
|
||||
topicData['reputation:disabled'] = meta.config['reputation:disabled'] === 1;
|
||||
topicData['downvote:disabled'] = meta.config['downvote:disabled'] === 1;
|
||||
if (mainPost) {
|
||||
topicData.mainPost = mainPost;
|
||||
topicData.posts = [mainPost].concat(posts);
|
||||
} else {
|
||||
topicData.posts = posts;
|
||||
}
|
||||
|
||||
topics.modifyPostsByPrivilege(topicData, userPrivileges);
|
||||
next(null, topicData);
|
||||
},
|
||||
], callback);
|
||||
topicData.privileges = userPrivileges;
|
||||
topicData.postSharing = postSharing;
|
||||
topicData['reputation:disabled'] = meta.config['reputation:disabled'] === 1;
|
||||
topicData['downvote:disabled'] = meta.config['downvote:disabled'] === 1;
|
||||
|
||||
topics.modifyPostsByPrivilege(topicData, userPrivileges);
|
||||
return topicData;
|
||||
};
|
||||
|
||||
SocketTopics.loadMoreSortedTopics = function (socket, data, callback) {
|
||||
SocketTopics.loadMoreSortedTopics = async function (socket, data) {
|
||||
if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
const { start, stop } = calculateStartStop(data);
|
||||
const params = {
|
||||
@@ -93,42 +73,35 @@ module.exports = function (SocketTopics) {
|
||||
};
|
||||
if (data.sort === 'unread') {
|
||||
params.cid = data.cid;
|
||||
topics.getUnreadTopics(params, callback);
|
||||
return;
|
||||
return await topics.getUnreadTopics(params);
|
||||
}
|
||||
params.cids = data.cid;
|
||||
params.sort = data.sort;
|
||||
params.term = data.term;
|
||||
topics.getSortedTopics(params, callback);
|
||||
return await topics.getSortedTopics(params);
|
||||
};
|
||||
|
||||
SocketTopics.loadMoreFromSet = function (socket, data, callback) {
|
||||
SocketTopics.loadMoreFromSet = async function (socket, data) {
|
||||
if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0 || !data.set) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
const { start, stop } = calculateStartStop(data);
|
||||
topics.getTopicsFromSet(data.set, socket.uid, start, stop, callback);
|
||||
return await topics.getTopicsFromSet(data.set, socket.uid, start, stop);
|
||||
};
|
||||
|
||||
SocketTopics.loadMoreUserTopics = function (socket, data, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
categories.getCidsByPrivilege('categories:cid', socket.uid, 'topics:read', next);
|
||||
},
|
||||
function (cids, next) {
|
||||
data.set = cids.map(c => 'cid:' + c + ':uid:' + data.uid + ':tids');
|
||||
SocketTopics.loadMoreFromSet(socket, data, next);
|
||||
},
|
||||
], callback);
|
||||
SocketTopics.loadMoreUserTopics = async function (socket, data) {
|
||||
const cids = await categories.getCidsByPrivilege('categories:cid', socket.uid, 'topics:read');
|
||||
data.set = cids.map(c => 'cid:' + c + ':uid:' + data.uid + ':tids');
|
||||
return await SocketTopics.loadMoreFromSet(socket, data);
|
||||
};
|
||||
|
||||
function calculateStartStop(data) {
|
||||
var itemsPerPage = Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20);
|
||||
var start = Math.max(0, parseInt(data.after, 10));
|
||||
const itemsPerPage = Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20);
|
||||
let start = Math.max(0, parseInt(data.after, 10));
|
||||
if (data.direction === -1) {
|
||||
start -= itemsPerPage;
|
||||
}
|
||||
var stop = start + Math.max(0, itemsPerPage - 1);
|
||||
const stop = start + Math.max(0, itemsPerPage - 1);
|
||||
return { start: Math.max(0, start), stop: Math.max(0, stop) };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,27 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var topics = require('../../topics');
|
||||
var privileges = require('../../privileges');
|
||||
const topics = require('../../topics');
|
||||
const privileges = require('../../privileges');
|
||||
|
||||
module.exports = function (SocketTopics) {
|
||||
SocketTopics.merge = function (socket, tids, callback) {
|
||||
SocketTopics.merge = async function (socket, tids) {
|
||||
if (!Array.isArray(tids)) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.map(tids, function (tid, next) {
|
||||
privileges.topics.isAdminOrMod(tid, socket.uid, next);
|
||||
}, next);
|
||||
},
|
||||
function (allowed, next) {
|
||||
if (allowed.includes(false)) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
topics.merge(tids, socket.uid, next);
|
||||
},
|
||||
], callback);
|
||||
const allowed = await Promise.all(tids.map(tid => privileges.topics.isAdminOrMod(tid, socket.uid)));
|
||||
if (allowed.includes(false)) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
await topics.merge(tids, socket.uid);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,70 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var topics = require('../../topics');
|
||||
var categories = require('../../categories');
|
||||
var privileges = require('../../privileges');
|
||||
var socketHelpers = require('../helpers');
|
||||
const async = require('async');
|
||||
const topics = require('../../topics');
|
||||
const categories = require('../../categories');
|
||||
const privileges = require('../../privileges');
|
||||
const socketHelpers = require('../helpers');
|
||||
|
||||
module.exports = function (SocketTopics) {
|
||||
SocketTopics.move = function (socket, data, callback) {
|
||||
SocketTopics.move = async function (socket, data) {
|
||||
if (!data || !Array.isArray(data.tids) || !data.cid) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
async.eachLimit(data.tids, 10, function (tid, next) {
|
||||
var topicData;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
privileges.topics.isAdminOrMod(tid, socket.uid, next);
|
||||
},
|
||||
function (canMove, next) {
|
||||
if (!canMove) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
await async.eachLimit(data.tids, 10, async function (tid) {
|
||||
const canMove = await privileges.topics.isAdminOrMod(tid, socket.uid);
|
||||
if (!canMove) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
const topicData = await topics.getTopicFields(tid, ['tid', 'cid', 'slug']);
|
||||
data.uid = socket.uid;
|
||||
await topics.tools.move(tid, data);
|
||||
|
||||
topics.getTopicFields(tid, ['cid', 'slug'], next);
|
||||
},
|
||||
function (_topicData, next) {
|
||||
topicData = _topicData;
|
||||
topicData.tid = tid;
|
||||
data.uid = socket.uid;
|
||||
topics.tools.move(tid, data, next);
|
||||
},
|
||||
function (next) {
|
||||
socketHelpers.emitToTopicAndCategory('event:topic_moved', topicData);
|
||||
socketHelpers.emitToTopicAndCategory('event:topic_moved', topicData);
|
||||
|
||||
socketHelpers.sendNotificationToTopicOwner(tid, socket.uid, 'move', 'notifications:moved_your_topic');
|
||||
|
||||
next();
|
||||
},
|
||||
], next);
|
||||
}, callback);
|
||||
socketHelpers.sendNotificationToTopicOwner(tid, socket.uid, 'move', 'notifications:moved_your_topic');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
SocketTopics.moveAll = function (socket, data, callback) {
|
||||
SocketTopics.moveAll = async function (socket, data) {
|
||||
if (!data || !data.cid || !data.currentCid) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
const canMove = await privileges.categories.canMoveAllTopics(data.currentCid, data.cid, socket.uid);
|
||||
if (!canMove) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
privileges.categories.canMoveAllTopics(data.currentCid, data.cid, socket.uid, next);
|
||||
},
|
||||
function (canMove, next) {
|
||||
if (!canMove) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
|
||||
categories.getAllTopicIds(data.currentCid, 0, -1, next);
|
||||
},
|
||||
function (tids, next) {
|
||||
data.uid = socket.uid;
|
||||
async.eachLimit(tids, 50, function (tid, next) {
|
||||
topics.tools.move(tid, data, next);
|
||||
}, next);
|
||||
},
|
||||
], callback);
|
||||
const tids = await categories.getAllTopicIds(data.currentCid, 0, -1);
|
||||
data.uid = socket.uid;
|
||||
await async.eachLimit(tids, 50, async function (tid) {
|
||||
await topics.tools.move(tid, data);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,67 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var db = require('../../database');
|
||||
var topics = require('../../topics');
|
||||
var privileges = require('../../privileges');
|
||||
var utils = require('../../utils');
|
||||
const topics = require('../../topics');
|
||||
const categories = require('../../categories');
|
||||
const privileges = require('../../privileges');
|
||||
const utils = require('../../utils');
|
||||
|
||||
module.exports = function (SocketTopics) {
|
||||
SocketTopics.isTagAllowed = function (socket, data, callback) {
|
||||
SocketTopics.isTagAllowed = async function (socket, data) {
|
||||
if (!data || !utils.isNumber(data.cid) || !data.tag) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRange('cid:' + data.cid + ':tag:whitelist', 0, -1, next);
|
||||
},
|
||||
function (tagWhitelist, next) {
|
||||
next(null, !tagWhitelist.length || tagWhitelist.includes(data.tag));
|
||||
},
|
||||
], callback);
|
||||
|
||||
const tagWhitelist = await categories.getTagWhitelist([data.cid]);
|
||||
return !tagWhitelist[0].length || tagWhitelist[0].includes(data.tag);
|
||||
};
|
||||
|
||||
SocketTopics.autocompleteTags = function (socket, data, callback) {
|
||||
topics.autocompleteTags(data, callback);
|
||||
SocketTopics.autocompleteTags = async function (socket, data) {
|
||||
return await topics.autocompleteTags(data);
|
||||
};
|
||||
|
||||
SocketTopics.searchTags = function (socket, data, callback) {
|
||||
searchTags(socket.uid, topics.searchTags, data, callback);
|
||||
SocketTopics.searchTags = async function (socket, data) {
|
||||
return await searchTags(socket.uid, topics.searchTags, data);
|
||||
};
|
||||
|
||||
SocketTopics.searchAndLoadTags = function (socket, data, callback) {
|
||||
searchTags(socket.uid, topics.searchAndLoadTags, data, callback);
|
||||
SocketTopics.searchAndLoadTags = async function (socket, data) {
|
||||
return await searchTags(socket.uid, topics.searchAndLoadTags, data);
|
||||
};
|
||||
|
||||
function searchTags(uid, method, data, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
privileges.global.can('search:tags', uid, next);
|
||||
},
|
||||
function (allowed, next) {
|
||||
if (!allowed) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
method(data, next);
|
||||
},
|
||||
], callback);
|
||||
async function searchTags(uid, method, data) {
|
||||
const allowed = await privileges.global.can('search:tags', uid);
|
||||
if (!allowed) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
return await method(data);
|
||||
}
|
||||
|
||||
SocketTopics.loadMoreTags = function (socket, data, callback) {
|
||||
SocketTopics.loadMoreTags = async function (socket, data) {
|
||||
if (!data || !utils.isNumber(data.after)) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
var start = parseInt(data.after, 10);
|
||||
var stop = start + 99;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.getTags(start, stop, next);
|
||||
},
|
||||
function (tags, next) {
|
||||
tags = tags.filter(Boolean);
|
||||
next(null, { tags: tags, nextStart: stop + 1 });
|
||||
},
|
||||
], callback);
|
||||
const start = parseInt(data.after, 10);
|
||||
const stop = start + 99;
|
||||
const tags = await topics.getTags(start, stop);
|
||||
|
||||
return { tags: tags.filter(Boolean), nextStart: stop + 1 };
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,127 +1,104 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var topics = require('../../topics');
|
||||
var events = require('../../events');
|
||||
var privileges = require('../../privileges');
|
||||
var plugins = require('../../plugins');
|
||||
var socketHelpers = require('../helpers');
|
||||
const topics = require('../../topics');
|
||||
const events = require('../../events');
|
||||
const privileges = require('../../privileges');
|
||||
const plugins = require('../../plugins');
|
||||
const socketHelpers = require('../helpers');
|
||||
|
||||
module.exports = function (SocketTopics) {
|
||||
SocketTopics.loadTopicTools = function (socket, data, callback) {
|
||||
SocketTopics.loadTopicTools = async function (socket, data) {
|
||||
if (!socket.uid) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
if (!data) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
topic: function (next) {
|
||||
topics.getTopicData(data.tid, next);
|
||||
},
|
||||
privileges: function (next) {
|
||||
privileges.topics.get(data.tid, socket.uid, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
if (!results.topic) {
|
||||
return next(new Error('[[error:no-topic]]'));
|
||||
}
|
||||
const [topicData, userPrivileges] = await Promise.all([
|
||||
topics.getTopicData(data.tid),
|
||||
privileges.topics.get(data.tid, socket.uid),
|
||||
]);
|
||||
|
||||
results.topic.privileges = results.privileges;
|
||||
plugins.fireHook('filter:topic.thread_tools', { topic: results.topic, uid: socket.uid, tools: [] }, next);
|
||||
},
|
||||
function (data, next) {
|
||||
data.topic.thread_tools = data.tools;
|
||||
next(null, data.topic);
|
||||
},
|
||||
], callback);
|
||||
if (!topicData) {
|
||||
throw new Error('[[error:no-topic]]');
|
||||
}
|
||||
if (!userPrivileges['topics:read']) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
topicData.privileges = userPrivileges;
|
||||
const result = await plugins.fireHook('filter:topic.thread_tools', { topic: topicData, uid: socket.uid, tools: [] });
|
||||
result.topic.thread_tools = result.tools;
|
||||
return result.topic;
|
||||
};
|
||||
|
||||
SocketTopics.delete = function (socket, data, callback) {
|
||||
SocketTopics.doTopicAction('delete', 'event:topic_deleted', socket, data, callback);
|
||||
SocketTopics.delete = async function (socket, data) {
|
||||
await SocketTopics.doTopicAction('delete', 'event:topic_deleted', socket, data);
|
||||
};
|
||||
|
||||
SocketTopics.restore = function (socket, data, callback) {
|
||||
SocketTopics.doTopicAction('restore', 'event:topic_restored', socket, data, callback);
|
||||
SocketTopics.restore = async function (socket, data) {
|
||||
await SocketTopics.doTopicAction('restore', 'event:topic_restored', socket, data);
|
||||
};
|
||||
|
||||
SocketTopics.purge = function (socket, data, callback) {
|
||||
SocketTopics.doTopicAction('purge', 'event:topic_purged', socket, data, callback);
|
||||
SocketTopics.purge = async function (socket, data) {
|
||||
await SocketTopics.doTopicAction('purge', 'event:topic_purged', socket, data);
|
||||
};
|
||||
|
||||
SocketTopics.lock = function (socket, data, callback) {
|
||||
SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, data, callback);
|
||||
SocketTopics.lock = async function (socket, data) {
|
||||
await SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, data);
|
||||
};
|
||||
|
||||
SocketTopics.unlock = function (socket, data, callback) {
|
||||
SocketTopics.doTopicAction('unlock', 'event:topic_unlocked', socket, data, callback);
|
||||
SocketTopics.unlock = async function (socket, data) {
|
||||
await SocketTopics.doTopicAction('unlock', 'event:topic_unlocked', socket, data);
|
||||
};
|
||||
|
||||
SocketTopics.pin = function (socket, data, callback) {
|
||||
SocketTopics.doTopicAction('pin', 'event:topic_pinned', socket, data, callback);
|
||||
SocketTopics.pin = async function (socket, data) {
|
||||
await SocketTopics.doTopicAction('pin', 'event:topic_pinned', socket, data);
|
||||
};
|
||||
|
||||
SocketTopics.unpin = function (socket, data, callback) {
|
||||
SocketTopics.doTopicAction('unpin', 'event:topic_unpinned', socket, data, callback);
|
||||
SocketTopics.unpin = async function (socket, data) {
|
||||
await SocketTopics.doTopicAction('unpin', 'event:topic_unpinned', socket, data);
|
||||
};
|
||||
|
||||
SocketTopics.doTopicAction = function (action, event, socket, data, callback) {
|
||||
callback = callback || function () {};
|
||||
SocketTopics.doTopicAction = async function (action, event, socket, data) {
|
||||
if (!socket.uid) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
if (!data || !Array.isArray(data.tids) || !data.cid) {
|
||||
return callback(new Error('[[error:invalid-tid]]'));
|
||||
throw new Error('[[error:invalid-tid]]');
|
||||
}
|
||||
|
||||
if (typeof topics.tools[action] !== 'function') {
|
||||
return callback();
|
||||
return;
|
||||
}
|
||||
|
||||
async.each(data.tids, function (tid, next) {
|
||||
var title;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.getTopicField(tid, 'title', next);
|
||||
},
|
||||
function (_title, next) {
|
||||
title = _title;
|
||||
topics.tools[action](tid, socket.uid, next);
|
||||
},
|
||||
function (data, next) {
|
||||
socketHelpers.emitToTopicAndCategory(event, data);
|
||||
logTopicAction(action, socket, tid, title, next);
|
||||
},
|
||||
], next);
|
||||
}, callback);
|
||||
await Promise.all(data.tids.map(async function (tid) {
|
||||
const title = await topics.getTopicField(tid, 'title');
|
||||
const data = await topics.tools[action](tid, socket.uid);
|
||||
socketHelpers.emitToTopicAndCategory(event, data);
|
||||
await logTopicAction(action, socket, tid, title);
|
||||
}));
|
||||
};
|
||||
|
||||
function logTopicAction(action, socket, tid, title, callback) {
|
||||
async function logTopicAction(action, socket, tid, title) {
|
||||
var actionsToLog = ['delete', 'restore', 'purge'];
|
||||
if (!actionsToLog.includes(action)) {
|
||||
return setImmediate(callback);
|
||||
return;
|
||||
}
|
||||
events.log({
|
||||
await events.log({
|
||||
type: 'topic-' + action,
|
||||
uid: socket.uid,
|
||||
ip: socket.ip,
|
||||
tid: tid,
|
||||
title: String(title),
|
||||
}, callback);
|
||||
});
|
||||
}
|
||||
|
||||
SocketTopics.orderPinnedTopics = function (socket, data, callback) {
|
||||
SocketTopics.orderPinnedTopics = async function (socket, data) {
|
||||
if (!Array.isArray(data)) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
|
||||
topics.tools.orderPinnedTopics(socket.uid, data, callback);
|
||||
await topics.tools.orderPinnedTopics(socket.uid, data);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,122 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
|
||||
var user = require('../../user');
|
||||
var topics = require('../../topics');
|
||||
const user = require('../../user');
|
||||
const topics = require('../../topics');
|
||||
|
||||
module.exports = function (SocketTopics) {
|
||||
SocketTopics.markAsRead = function (socket, tids, callback) {
|
||||
SocketTopics.markAsRead = async function (socket, tids) {
|
||||
if (!Array.isArray(tids) || socket.uid <= 0) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.markAsRead(tids, socket.uid, next);
|
||||
},
|
||||
function (hasMarked, next) {
|
||||
if (hasMarked) {
|
||||
topics.pushUnreadCount(socket.uid);
|
||||
const hasMarked = await topics.markAsRead(tids, socket.uid);
|
||||
if (hasMarked) {
|
||||
topics.pushUnreadCount(socket.uid);
|
||||
|
||||
topics.markTopicNotificationsRead(tids, socket.uid);
|
||||
}
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
topics.markTopicNotificationsRead(tids, socket.uid);
|
||||
}
|
||||
};
|
||||
|
||||
SocketTopics.markTopicNotificationsRead = function (socket, tids, callback) {
|
||||
SocketTopics.markTopicNotificationsRead = async function (socket, tids) {
|
||||
if (!Array.isArray(tids) || !socket.uid) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
topics.markTopicNotificationsRead(tids, socket.uid, callback);
|
||||
await topics.markTopicNotificationsRead(tids, socket.uid);
|
||||
};
|
||||
|
||||
SocketTopics.markAllRead = function (socket, data, callback) {
|
||||
SocketTopics.markAllRead = async function (socket) {
|
||||
if (socket.uid <= 0) {
|
||||
return callback(new Error('[[error:invalid-uid]]'));
|
||||
throw new Error('[[error:invalid-uid]]');
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.markAllRead(socket.uid, next);
|
||||
},
|
||||
function (next) {
|
||||
topics.pushUnreadCount(socket.uid);
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
await topics.markAllRead(socket.uid);
|
||||
topics.pushUnreadCount(socket.uid);
|
||||
};
|
||||
|
||||
SocketTopics.markCategoryTopicsRead = function (socket, cid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.getUnreadTids({ cid: cid, uid: socket.uid, filter: '' }, next);
|
||||
},
|
||||
function (tids, next) {
|
||||
SocketTopics.markAsRead(socket, tids, next);
|
||||
},
|
||||
], callback);
|
||||
SocketTopics.markCategoryTopicsRead = async function (socket, cid) {
|
||||
const tids = await topics.getUnreadTids({ cid: cid, uid: socket.uid, filter: '' });
|
||||
await SocketTopics.markAsRead(socket, tids);
|
||||
};
|
||||
|
||||
SocketTopics.markUnread = function (socket, tid, callback) {
|
||||
SocketTopics.markUnread = async function (socket, tid) {
|
||||
if (!tid || socket.uid <= 0) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.markUnread(tid, socket.uid, next);
|
||||
},
|
||||
function (next) {
|
||||
topics.pushUnreadCount(socket.uid);
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
await topics.markUnread(tid, socket.uid);
|
||||
topics.pushUnreadCount(socket.uid);
|
||||
};
|
||||
|
||||
SocketTopics.markAsUnreadForAll = function (socket, tids, callback) {
|
||||
SocketTopics.markAsUnreadForAll = async function (socket, tids) {
|
||||
if (!Array.isArray(tids)) {
|
||||
return callback(new Error('[[error:invalid-tid]]'));
|
||||
throw new Error('[[error:invalid-tid]]');
|
||||
}
|
||||
|
||||
if (socket.uid <= 0) {
|
||||
return callback(new Error('[[error:no-privileges]]'));
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
const isAdmin = await user.isAdministrator(socket.uid);
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.isAdministrator(socket.uid, next);
|
||||
},
|
||||
function (isAdmin, next) {
|
||||
async.each(tids, function (tid, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
topics.exists(tid, next);
|
||||
},
|
||||
function (exists, next) {
|
||||
if (!exists) {
|
||||
return next(new Error('[[error:no-topic]]'));
|
||||
}
|
||||
topics.getTopicField(tid, 'cid', next);
|
||||
},
|
||||
function (cid, next) {
|
||||
user.isModerator(socket.uid, cid, next);
|
||||
},
|
||||
function (isMod, next) {
|
||||
if (!isAdmin && !isMod) {
|
||||
return next(new Error('[[error:no-privileges]]'));
|
||||
}
|
||||
topics.markAsUnreadForAll(tid, next);
|
||||
},
|
||||
function (next) {
|
||||
topics.updateRecent(tid, Date.now(), next);
|
||||
},
|
||||
], next);
|
||||
}, next);
|
||||
},
|
||||
function (next) {
|
||||
topics.pushUnreadCount(socket.uid);
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
await Promise.all(tids.map(async (tid) => {
|
||||
const topicData = await topics.getTopicFields(tid, ['tid', 'cid']);
|
||||
if (!topicData.tid) {
|
||||
throw new Error('[[error:no-topic]]');
|
||||
}
|
||||
const isMod = await user.isModerator(socket.uid, topicData.cid);
|
||||
if (!isAdmin && !isMod) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
await topics.markAsUnreadForAll(tid);
|
||||
await topics.updateRecent(tid, Date.now());
|
||||
}));
|
||||
topics.pushUnreadCount(socket.uid);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var db = require('../database');
|
||||
var posts = require('../posts');
|
||||
var notifications = require('../notifications');
|
||||
var privileges = require('../privileges');
|
||||
var plugins = require('../plugins');
|
||||
var utils = require('../utils');
|
||||
const db = require('../database');
|
||||
const posts = require('../posts');
|
||||
const notifications = require('../notifications');
|
||||
const privileges = require('../privileges');
|
||||
const plugins = require('../plugins');
|
||||
const utils = require('../utils');
|
||||
|
||||
module.exports = function (Topics) {
|
||||
Topics.toggleFollow = async function (tid, uid) {
|
||||
@@ -90,9 +90,7 @@ module.exports = function (Topics) {
|
||||
return tids.map(() => ({ following: false, ignoring: false }));
|
||||
}
|
||||
const keys = [];
|
||||
tids.forEach((tid) => {
|
||||
keys.push('tid:' + tid + ':followers', 'tid:' + tid + ':ignorers');
|
||||
});
|
||||
tids.forEach(tid => keys.push('tid:' + tid + ':followers', 'tid:' + tid + ':ignorers'));
|
||||
|
||||
const data = await db.isMemberOfSets(keys, uid);
|
||||
|
||||
@@ -113,7 +111,7 @@ module.exports = function (Topics) {
|
||||
if (parseInt(uid, 10) <= 0) {
|
||||
return tids.map(() => false);
|
||||
}
|
||||
var keys = tids.map(tid => 'tid:' + tid + ':' + set);
|
||||
const keys = tids.map(tid => 'tid:' + tid + ':' + set);
|
||||
return await db.isMemberOfSets(keys, uid);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user