Merge branch 'profile-slur-swear-blocker' into slur-swear-blocker

This commit is contained in:
CuriousMagpie
2023-12-01 15:04:46 -05:00
1675 changed files with 3653 additions and 262784 deletions
+153 -26
View File
@@ -26,7 +26,6 @@ import csvStringify from '../../libs/csvStringify';
import {
createTasks,
} from '../../libs/tasks';
import {
addUserJoinChallengeNotification,
getChallengeGroupResponse,
@@ -35,8 +34,12 @@ import {
createChallengeQuery,
} from '../../libs/challenges';
import apiError from '../../libs/apiError';
import common from '../../../common';
import {
clearFlags,
flagChallenge,
notifyOfFlaggedChallenge,
} from '../../libs/challenges/reporting';
const { MAX_SUMMARY_SIZE_FOR_CHALLENGES } = common.constants;
@@ -444,7 +447,7 @@ api.leaveChallenge = {
* @apiParam (Query) {Number} page This parameter can be used to specify the page number
for the user challenges result (the initial page is number 0).
* @apiParam (Query) {String} [member] If set to `true` it limits results to challenges where the
user is a member.
user is a member, or the user owns the challenge.
* @apiParam (Query) {String} [owned] If set to `owned` it limits results to challenges owned
by the user. If set to `not_owned` it limits results
to challenges not owned by the user.
@@ -472,10 +475,30 @@ api.getUserChallenges = {
if (validationErrors) throw validationErrors;
const CHALLENGES_PER_PAGE = 10;
const { page } = req.query;
const {
categories,
member,
owned,
page,
search,
} = req.query;
const { user } = res.locals;
const query = {
$and: [],
};
if (!user.hasPermission('moderator')) {
query.$and.push(
{
$or: [
{ flagCount: { $not: { $gt: 1 } } },
{ leader: user._id },
],
},
);
}
// Challenges the user owns
const orOptions = [{ leader: user._id }];
@@ -485,7 +508,7 @@ api.getUserChallenges = {
}
// Challenges in groups user is a member of, plus public challenges
if (!req.query.member) {
if (!member) {
const userGroups = await Group.getGroups({
user,
types: ['party', 'guilds', 'tavern'],
@@ -495,33 +518,29 @@ api.getUserChallenges = {
group: { $in: userGroupIds },
});
}
const query = {
$and: [{ $or: orOptions }],
};
const { owned } = req.query;
if (owned) {
if (owned === 'not_owned') {
query.$and.push({ leader: { $ne: user._id } });
}
if (owned === 'owned') {
query.$and.push({ leader: user._id });
}
if (owned === 'not_owned') {
query.leader = { $ne: user._id }; // Show only Challenges user does not own
} else if (owned === 'owned') {
query.leader = user._id; // Show only Challenges user owns
} else {
orOptions.push(
{ leader: user._id }, // Additionally show Challenges user owns
);
}
if (req.query.search) {
query.$and.push({ $or: orOptions });
if (search) {
const searchOr = { $or: [] };
const searchWords = _.escapeRegExp(req.query.search).split(' ').join('|');
const searchWords = _.escapeRegExp(search).split(' ').join('|');
const searchQuery = { $regex: new RegExp(`${searchWords}`, 'i') };
searchOr.$or.push({ name: searchQuery });
searchOr.$or.push({ description: searchQuery });
query.$and.push(searchOr);
}
if (req.query.categories) {
const categorySlugs = req.query.categories.split(',');
if (categories) {
const categorySlugs = categories.split(',');
query.categories = { $elemMatch: { slug: { $in: categorySlugs } } };
}
@@ -580,7 +599,7 @@ api.getGroupChallenges = {
url: '/challenges/groups/:groupId',
middlewares: [authWithHeaders({
// Some fields (including _id) are always loaded (see middlewares/auth)
userFieldsToInclude: ['party', 'guilds'], // Some fields are always loaded (see middlewares/auth)
userFieldsToInclude: ['party', 'guilds', 'contributor'], // Some fields are always loaded (see middlewares/auth)
})],
async handler (req, res) {
const { user } = res.locals;
@@ -602,7 +621,18 @@ api.getGroupChallenges = {
// .populate('leader', nameFields)
.exec();
const resChals = challenges.map(challenge => (new Challenge(challenge)).toJSON());
const resChals = challenges.map(challenge => {
// filter out challenges that the non-admin user isn't participating in, nor created
const nonParticipant = !user.challenges
|| (user.challenges
&& user.challenges.findIndex(cId => cId === challenge._id) === -1);
const isFlaggedForNonAdminUser = challenge.flagCount > 1
&& !user.hasPermission('moderator')
&& nonParticipant
&& challenge.leader !== user._id;
return isFlaggedForNonAdminUser ? null : (new Challenge(challenge)).toJSON();
}).filter(challenge => !!challenge);
// Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833
await Promise.all(resChals.map((chal, index) => User
@@ -651,6 +681,15 @@ api.getChallenge = {
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
const nonParticipant = !user.challenges
|| (user.challenges
&& user.challenges.findIndex(cId => cId === challenge._id) === -1);
const isFlaggedForNonAdminUser = challenge.flagCount > 1
&& !user.hasPermission('moderator')
&& nonParticipant
&& challenge.leader !== user._id;
if (isFlaggedForNonAdminUser) throw new NotFound(res.t('challengeNotFound'));
// Fetching basic group data
const group = await Group.getGroup({
user, groupId: challenge.group, fields: `${basicGroupFields} purchased`,
@@ -906,6 +945,15 @@ api.selectChallengeWinner = {
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyLeaderDeleteChal'));
const nonParticipant = !user.challenges
|| (user.challenges
&& user.challenges.findIndex(cId => cId === challenge._id) === -1);
const isFlaggedForNonAdminUser = challenge.flagCount > 1
&& !user.hasPermission('moderator')
&& nonParticipant
&& challenge.leader !== user._id;
if (isFlaggedForNonAdminUser) throw new NotFound(res.t('challengeNotFound'));
const winner = await User.findOne({ _id: req.params.winnerId }).exec();
if (!winner || winner.challenges.indexOf(challenge._id) === -1) throw new NotFound(res.t('winnerNotFound', { userId: req.params.winnerId }));
@@ -955,6 +1003,15 @@ api.cloneChallenge = {
const challengeToClone = await Challenge.findOne({ _id: req.params.challengeId }).exec();
if (!challengeToClone) throw new NotFound(res.t('challengeNotFound'));
const nonParticipant = !user.challenges
|| (user.challenges
&& user.challenges.findIndex(cId => cId === challengeToClone._id) === -1);
const isFlaggedForNonAdminUser = challengeToClone.flagCount > 1
&& !user.hasPermission('moderator')
&& nonParticipant
&& challengeToClone.leader !== user._id;
if (isFlaggedForNonAdminUser) throw new NotFound(res.t('challengeNotFound'));
const { savedChal } = await createChallenge(user, req, res);
const challengeTaskIds = [
@@ -988,4 +1045,74 @@ api.cloneChallenge = {
},
};
/**
* @api {post} /api/v3/challenges/:challengeId/flag Flag a challenge
* @apiName FlagChallenge
* @apiGroup Challenge
*
* @apiParam (Path) {UUID} challengeId The _id for the challenge to flag
* @apiParam (Body) {String} [comment] Why the message was flagged
*
* @apiSuccess {Object} data The flagged challenge message
*
* @apiUse ChallengeNotFound
*/
api.flagChallenge = {
method: 'POST',
url: '/challenges/:challengeId/flag',
middlewares: [authWithHeaders()],
async handler (req, res) {
const { user } = res.locals;
req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID();
const validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
const challenge = await Challenge.findOne({ _id: req.params.challengeId }).exec();
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
await flagChallenge(challenge, user, res);
await notifyOfFlaggedChallenge(challenge, user, req.body.comment);
res.respond(200, { challenge });
},
};
/**
* @api {post} /api/v3/challenges/:challengeId/clearflags Clears flags on a challenge
* @apiName ClearFlagsChallenge
* @apiGroup Challenge
*
* @apiParam (Path) {UUID} challengeId The _id for the challenge to clear flags from
*
* @apiSuccess {Object} data The flagged challenge message
*
* @apiUse ChallengeNotFound
*/
api.clearFlagsChallenge = {
method: 'POST',
url: '/challenges/:challengeId/clearflags',
middlewares: [authWithHeaders()],
async handler (req, res) {
const { user } = res.locals;
req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID();
const validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
if (!user.hasPermission('moderator')) {
throw new NotAuthorized(res.t('messageGroupChatAdminClearFlagCount'));
}
const challenge = await Challenge.findOne({ _id: req.params.challengeId }).exec();
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
await clearFlags(challenge, user);
res.respond(200, { challenge });
},
};
export default api;