mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-30 11:19:54 +01:00
feat: #7743 , user/block, user/categories
This commit is contained in:
@@ -16,117 +16,84 @@ module.exports = function (User) {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
User.blocks.is = function (targetUid, uid, callback) {
|
User.blocks.is = async function (targetUid, uid) {
|
||||||
User.blocks.list(uid, function (err, blocks) {
|
const blocks = await User.blocks.list(uid);
|
||||||
callback(err, blocks.includes(parseInt(targetUid, 10)));
|
return blocks.includes(parseInt(targetUid, 10));
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.blocks.can = function (callerUid, blockerUid, blockeeUid, callback) {
|
User.blocks.can = async function (callerUid, blockerUid, blockeeUid) {
|
||||||
// Guests can't block
|
// Guests can't block
|
||||||
if (blockerUid === 0 || blockeeUid === 0) {
|
if (blockerUid === 0 || blockeeUid === 0) {
|
||||||
return setImmediate(callback, new Error('[[error:cannot-block-guest]]'));
|
throw new Error('[[error:cannot-block-guest]]');
|
||||||
} else if (blockerUid === blockeeUid) {
|
} else if (blockerUid === blockeeUid) {
|
||||||
return setImmediate(callback, new Error('[[error:cannot-block-self]]'));
|
throw new Error('[[error:cannot-block-self]]');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Administrators and global moderators cannot be blocked
|
// Administrators and global moderators cannot be blocked
|
||||||
// Only admins/mods can block users as another user
|
// Only admins/mods can block users as another user
|
||||||
async.waterfall([
|
const [isCallerAdminOrMod, isBlockeeAdminOrMod] = await Promise.all([
|
||||||
function (next) {
|
User.isAdminOrGlobalMod(callerUid),
|
||||||
async.parallel({
|
User.isAdminOrGlobalMod(blockeeUid),
|
||||||
isCallerAdminOrMod: function (next) {
|
]);
|
||||||
User.isAdminOrGlobalMod(callerUid, next);
|
if (isBlockeeAdminOrMod) {
|
||||||
},
|
throw new Error('[[error:cannot-block-privileged]]');
|
||||||
isBlockeeAdminOrMod: function (next) {
|
}
|
||||||
User.isAdminOrGlobalMod(blockeeUid, next);
|
if (parseInt(callerUid, 10) !== parseInt(blockerUid, 10) && !isCallerAdminOrMod) {
|
||||||
},
|
throw new Error();
|
||||||
}, next);
|
}
|
||||||
},
|
|
||||||
function (results, next) {
|
|
||||||
if (results.isBlockeeAdminOrMod) {
|
|
||||||
return callback(new Error('[[error:cannot-block-privileged]]'));
|
|
||||||
}
|
|
||||||
if (parseInt(callerUid, 10) !== parseInt(blockerUid, 10) && !results.isCallerAdminOrMod) {
|
|
||||||
return callback(new Error());
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.blocks.list = function (uid, callback) {
|
User.blocks.list = async function (uid) {
|
||||||
if (User.blocks._cache.has(parseInt(uid, 10))) {
|
if (User.blocks._cache.has(parseInt(uid, 10))) {
|
||||||
return setImmediate(callback, null, User.blocks._cache.get(parseInt(uid, 10)));
|
return User.blocks._cache.get(parseInt(uid, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getSortedSetRange('uid:' + uid + ':blocked_uids', 0, -1, function (err, blocked) {
|
let blocked = await db.getSortedSetRange('uid:' + uid + ':blocked_uids', 0, -1);
|
||||||
if (err) {
|
blocked = blocked.map(uid => parseInt(uid, 10)).filter(Boolean);
|
||||||
return callback(err);
|
User.blocks._cache.set(parseInt(uid, 10), blocked);
|
||||||
}
|
return blocked;
|
||||||
|
|
||||||
blocked = blocked.map(uid => parseInt(uid, 10)).filter(Boolean);
|
|
||||||
User.blocks._cache.set(parseInt(uid, 10), blocked);
|
|
||||||
callback(null, blocked);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pubsub.on('user:blocks:cache:del', function (uid) {
|
pubsub.on('user:blocks:cache:del', function (uid) {
|
||||||
User.blocks._cache.del(uid);
|
User.blocks._cache.del(uid);
|
||||||
});
|
});
|
||||||
|
|
||||||
User.blocks.add = function (targetUid, uid, callback) {
|
User.blocks.add = async function (targetUid, uid) {
|
||||||
async.waterfall([
|
await User.blocks.applyChecks('block', targetUid, uid);
|
||||||
async.apply(User.blocks.applyChecks, true, targetUid, uid),
|
await db.sortedSetAdd('uid:' + uid + ':blocked_uids', Date.now(), targetUid);
|
||||||
async.apply(db.sortedSetAdd.bind(db), 'uid:' + uid + ':blocked_uids', Date.now(), targetUid),
|
await User.incrementUserFieldBy(uid, 'blocksCount', 1);
|
||||||
async.apply(User.incrementUserFieldBy, uid, 'blocksCount', 1),
|
User.blocks._cache.del(parseInt(uid, 10));
|
||||||
function (_blank, next) {
|
pubsub.publish('user:blocks:cache:del', parseInt(uid, 10));
|
||||||
User.blocks._cache.del(parseInt(uid, 10));
|
|
||||||
pubsub.publish('user:blocks:cache:del', parseInt(uid, 10));
|
|
||||||
setImmediate(next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.blocks.remove = function (targetUid, uid, callback) {
|
User.blocks.remove = async function (targetUid, uid) {
|
||||||
async.waterfall([
|
await User.blocks.applyChecks('unblock', targetUid, uid);
|
||||||
async.apply(User.blocks.applyChecks, false, targetUid, uid),
|
await db.sortedSetRemove('uid:' + uid + ':blocked_uids', targetUid);
|
||||||
async.apply(db.sortedSetRemove.bind(db), 'uid:' + uid + ':blocked_uids', targetUid),
|
await User.decrementUserFieldBy(uid, 'blocksCount', 1);
|
||||||
async.apply(User.decrementUserFieldBy, uid, 'blocksCount', 1),
|
User.blocks._cache.del(parseInt(uid, 10));
|
||||||
function (_blank, next) {
|
pubsub.publish('user:blocks:cache:del', parseInt(uid, 10));
|
||||||
User.blocks._cache.del(parseInt(uid, 10));
|
|
||||||
pubsub.publish('user:blocks:cache:del', parseInt(uid, 10));
|
|
||||||
setImmediate(next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.blocks.applyChecks = function (block, targetUid, uid, callback) {
|
User.blocks.applyChecks = async function (type, targetUid, uid) {
|
||||||
User.blocks.can(uid, uid, targetUid, function (err) {
|
await User.blocks.can(uid, uid, targetUid);
|
||||||
if (err) {
|
const isBlock = type === 'block';
|
||||||
return callback(err);
|
const is = await User.blocks.is(targetUid, uid);
|
||||||
}
|
if (is === isBlock) {
|
||||||
|
throw new Error('[[error:already-' + (isBlock ? 'blocked' : 'unblocked') + ']]');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
User.blocks.is(targetUid, uid, function (err, is) {
|
User.blocks.filterUids = async function (targetUid, uids) {
|
||||||
callback(err || (is === block ? new Error('[[error:already-' + (block ? 'blocked' : 'unblocked') + ']]') : null));
|
return await async.filter(uids, async function (uid) {
|
||||||
});
|
const isBlocked = await User.blocks.is(targetUid, uid);
|
||||||
|
return !isBlocked;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
User.blocks.filterUids = function (targetUid, uids, callback) {
|
User.blocks.filter = async function (uid, property, set) {
|
||||||
async.filter(uids, function (uid, next) {
|
|
||||||
User.blocks.is(targetUid, uid, function (err, blocked) {
|
|
||||||
next(err, !blocked);
|
|
||||||
});
|
|
||||||
}, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
User.blocks.filter = function (uid, property, set, callback) {
|
|
||||||
// Given whatever is passed in, iterates through it, and removes entries made by blocked uids
|
// Given whatever is passed in, iterates through it, and removes entries made by blocked uids
|
||||||
// property is optional
|
// property is optional
|
||||||
if (Array.isArray(property) && typeof set === 'function' && !callback) {
|
if (Array.isArray(property) && typeof set === 'undefined') {
|
||||||
callback = set;
|
|
||||||
set = property;
|
set = property;
|
||||||
property = 'uid';
|
property = 'uid';
|
||||||
}
|
}
|
||||||
@@ -139,20 +106,17 @@ module.exports = function (User) {
|
|||||||
const check = item.hasOwnProperty(property) ? item[property] : item;
|
const check = item.hasOwnProperty(property) ? item[property] : item;
|
||||||
return ['number', 'string'].includes(typeof check);
|
return ['number', 'string'].includes(typeof check);
|
||||||
})) {
|
})) {
|
||||||
return callback(null, set);
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPlain = typeof set[0] !== 'object';
|
const isPlain = typeof set[0] !== 'object';
|
||||||
User.blocks.list(uid, function (err, blocked_uids) {
|
const blocked_uids = await User.blocks.list(uid);
|
||||||
if (err) {
|
const blockedSet = new Set(blocked_uids);
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
set = set.filter(function (item) {
|
set = set.filter(function (item) {
|
||||||
return !blocked_uids.includes(parseInt(isPlain ? item : item[property], 10));
|
return !blockedSet.has(parseInt(isPlain ? item : item[property], 10));
|
||||||
});
|
|
||||||
|
|
||||||
callback(null, set);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return set;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,89 +1,64 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const async = require('async');
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
const db = require('../database');
|
const db = require('../database');
|
||||||
const categories = require('../categories');
|
const categories = require('../categories');
|
||||||
|
|
||||||
module.exports = function (User) {
|
module.exports = function (User) {
|
||||||
User.setCategoryWatchState = function (uid, cid, state, callback) {
|
User.setCategoryWatchState = async function (uid, cid, state) {
|
||||||
if (!(parseInt(uid, 10) > 0)) {
|
if (!(parseInt(uid, 10) > 0)) {
|
||||||
return setImmediate(callback);
|
return;
|
||||||
}
|
}
|
||||||
const isStateValid = Object.keys(categories.watchStates).some(key => categories.watchStates[key] === parseInt(state, 10));
|
const isStateValid = Object.values(categories.watchStates).includes(parseInt(state, 10));
|
||||||
if (!isStateValid) {
|
if (!isStateValid) {
|
||||||
return setImmediate(callback, new Error('[[error:invalid-watch-state]]'));
|
throw new Error('[[error:invalid-watch-state]]');
|
||||||
}
|
}
|
||||||
async.waterfall([
|
const exists = await categories.exists(cid);
|
||||||
function (next) {
|
if (!exists) {
|
||||||
categories.exists(cid, next);
|
throw new Error('[[error:no-category]]');
|
||||||
},
|
}
|
||||||
function (exists, next) {
|
await db.sortedSetAdd('cid:' + cid + ':uid:watch:state', state, uid);
|
||||||
if (!exists) {
|
|
||||||
return next(new Error('[[error:no-category]]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
db.sortedSetAdd('cid:' + cid + ':uid:watch:state', state, uid, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getCategoryWatchState = function (uid, callback) {
|
User.getCategoryWatchState = async function (uid) {
|
||||||
if (parseInt(uid, 10) <= 0) {
|
|
||||||
return setImmediate(callback, null, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
let cids;
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
categories.getAllCidsFromSet('categories:cid', next);
|
|
||||||
},
|
|
||||||
function (_cids, next) {
|
|
||||||
cids = _cids;
|
|
||||||
categories.getWatchState(cids, uid, next);
|
|
||||||
},
|
|
||||||
function (states, next) {
|
|
||||||
next(null, _.zipObject(cids, states));
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
User.getIgnoredCategories = function (uid, callback) {
|
|
||||||
if (parseInt(uid, 10) <= 0) {
|
|
||||||
return setImmediate(callback, null, []);
|
|
||||||
}
|
|
||||||
User.getCategoriesByStates(uid, [categories.watchStates.ignoring], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
User.getWatchedCategories = function (uid, callback) {
|
|
||||||
if (parseInt(uid, 10) <= 0) {
|
|
||||||
return setImmediate(callback, null, []);
|
|
||||||
}
|
|
||||||
User.getCategoriesByStates(uid, [categories.watchStates.watching], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
User.getCategoriesByStates = function (uid, states, callback) {
|
|
||||||
if (!(parseInt(uid, 10) > 0)) {
|
if (!(parseInt(uid, 10) > 0)) {
|
||||||
return categories.getAllCidsFromSet('categories:cid', callback);
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
async.waterfall([
|
const cids = await categories.getAllCidsFromSet('categories:cid');
|
||||||
function (next) {
|
const states = await categories.getWatchState(cids, uid);
|
||||||
User.getCategoryWatchState(uid, next);
|
return _.zipObject(cids, states);
|
||||||
},
|
|
||||||
function (userState, next) {
|
|
||||||
const cids = Object.keys(userState);
|
|
||||||
next(null, cids.filter(cid => states.includes(userState[cid])));
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.ignoreCategory = function (uid, cid, callback) {
|
User.getIgnoredCategories = async function (uid) {
|
||||||
User.setCategoryWatchState(uid, cid, categories.watchStates.ignoring, callback);
|
if (!(parseInt(uid, 10) > 0)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return await User.getCategoriesByStates(uid, [categories.watchStates.ignoring]);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.watchCategory = function (uid, cid, callback) {
|
User.getWatchedCategories = async function (uid) {
|
||||||
User.setCategoryWatchState(uid, cid, categories.watchStates.watching, callback);
|
if (!(parseInt(uid, 10) > 0)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return await User.getCategoriesByStates(uid, [categories.watchStates.watching]);
|
||||||
|
};
|
||||||
|
|
||||||
|
User.getCategoriesByStates = async function (uid, states) {
|
||||||
|
if (!(parseInt(uid, 10) > 0)) {
|
||||||
|
return await categories.getAllCidsFromSet('categories:cid');
|
||||||
|
}
|
||||||
|
const userState = await User.getCategoryWatchState(uid);
|
||||||
|
const cids = Object.keys(userState);
|
||||||
|
return cids.filter(cid => states.includes(userState[cid]));
|
||||||
|
};
|
||||||
|
|
||||||
|
User.ignoreCategory = async function (uid, cid) {
|
||||||
|
await User.setCategoryWatchState(uid, cid, categories.watchStates.ignoring);
|
||||||
|
};
|
||||||
|
|
||||||
|
User.watchCategory = async function (uid, cid) {
|
||||||
|
await User.setCategoryWatchState(uid, cid, categories.watchStates.watching);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user