merge master

This commit is contained in:
riley
2017-05-22 13:10:43 -06:00
105 changed files with 2436 additions and 435 deletions
+2 -1
View File
@@ -2,6 +2,7 @@ const ActionModel = require('../../models/action');
const ActionsService = require('../../services/actions');
const UsersService = require('../../services/users');
const errors = require('../../errors');
const {CREATE_ACTION, DELETE_ACTION} = require('../../perms/constants');
/**
* Creates an action on a item. If the item is a user flag, sets the user's status to
@@ -45,7 +46,7 @@ const deleteAction = ({user}, {id}) => {
};
module.exports = (context) => {
if (context.user && context.user.can('mutation:createAction', 'mutation:deleteAction')) {
if (context.user && context.user.can(CREATE_ACTION, DELETE_ACTION)) {
return {
Action: {
create: (action) => createAction(context, action),
+109 -5
View File
@@ -1,11 +1,19 @@
const errors = require('../../errors');
const ActionModel = require('../../models/action');
const AssetsService = require('../../services/assets');
const ActionsService = require('../../services/actions');
const TagsService = require('../../services/tags');
const CommentsService = require('../../services/comments');
const KarmaService = require('../../services/karma');
const linkify = require('linkify-it')();
const Wordlist = require('../../services/wordlist');
const {
CREATE_COMMENT,
SET_COMMENT_STATUS,
ADD_COMMENT_TAG,
EDIT_COMMENT
} = require('../../perms/constants');
const debug = require('debug')('talk:graph:mutators:tags');
const plugins = require('../../services/plugins');
@@ -47,7 +55,7 @@ const resolveTagsForComment = async ({user, loaders: {Tags}}, {asset_id, tags =
}
// Add the staff tag for comments created as a staff member.
if (user.hasRoles('ADMIN') || user.hasRoles('MODERATOR')) {
if (user.can(ADD_COMMENT_TAG)) {
tags.push(TagsService.newTagLink(user, {
name: 'STAFF',
item_type
@@ -57,6 +65,82 @@ const resolveTagsForComment = async ({user, loaders: {Tags}}, {asset_id, tags =
return tags;
};
/**
* adjustKarma will adjust the affected user's karma depending on the moderators
* action.
*/
const adjustKarma = (Comments, id, status) => async () => {
try {
// Use the dataloader to get the comment that was just moderated and
// get the flag user's id's so we can adjust their karma too.
let [
comment,
flagUserIDs
] = await Promise.all([
// Load the comment that was just made/updated by the setCommentStatus
// operation.
Comments.get.load(id),
// Find all the flag actions that were referenced by this comment
// at this point in time.
ActionModel.find({
item_id: id,
item_type: 'COMMENTS',
action_type: 'FLAG'
}).then((actions) => {
// This is to ensure that this is always an array.
if (!actions) {
return [];
}
return actions.map(({user_id}) => user_id);
})
]);
debug(`Comment[${id}] by User[${comment.author_id}] was Status[${status}]`);
switch (status) {
case 'REJECTED':
// Reduce the user's karma.
debug(`CommentUser[${comment.author_id}] had their karma reduced`);
// Decrease the flag user's karma, the moderator disagreed with this
// action.
debug(`FlaggingUser[${flagUserIDs.join(', ')}] had their karma increased`);
await Promise.all([
KarmaService.modifyUser(comment.author_id, -1, 'comment'),
KarmaService.modifyUser(flagUserIDs, 1, 'flag', true)
]);
break;
case 'ACCEPTED':
// Increase the user's karma.
debug(`CommentUser[${comment.author_id}] had their karma increased`);
// Increase the flag user's karma, the moderator agreed with this
// action.
debug(`FlaggingUser[${flagUserIDs.join(', ')}] had their karma reduced`);
await Promise.all([
KarmaService.modifyUser(comment.author_id, 1, 'comment'),
KarmaService.modifyUser(flagUserIDs, -1, 'flag', true)
]);
break;
}
return;
} catch (e) {
console.error(e);
}
};
/**
* Creates a new comment.
* @param {Object} user the user performing the request
@@ -132,6 +216,7 @@ const filterNewComment = (context, {body, asset_id}) => {
* @return {Promise} resolves to the comment's status
*/
const resolveNewCommentStatus = async (context, {asset_id, body}, wordlist = {}, settings = {}) => {
let {user} = context;
// Check to see if the body is too short, if it is, then complain about it!
if (body.length < 2) {
@@ -169,6 +254,22 @@ const resolveNewCommentStatus = async (context, {asset_id, body}, wordlist = {},
return 'REJECTED';
}
if (user && user.metadata) {
// If the user is not a reliable commenter (passed the unreliability
// threshold by having too many rejected comments) then we can change the
// status of the comment to `PREMOD`, therefore pushing the user's comments
// away from the public eye until a moderator can manage them. This of
// course can only be applied if the comment's current status is `NONE`,
// we don't want to interfere if the comment was rejected.
if (KarmaService.isReliable('comment', user.metadata.trust) === false) {
// Update the response from the comment creation to add the PREMOD so that
// that user's UI will reflect the fact that their comment is in pre-mod.
return 'PREMOD';
}
}
return moderation === 'PRE' ? 'PREMOD' : 'NONE';
};
@@ -225,7 +326,6 @@ const createPublicComment = async (context, commentInput) => {
* @param {String} id identifier of the comment (uuid)
* @param {String} status the new status of the comment
*/
const setStatus = async ({user, loaders: {Comments}}, {id, status}) => {
let comment = await CommentsService.pushStatus(id, status, user ? user.id : null);
@@ -242,6 +342,10 @@ const setStatus = async ({user, loaders: {Comments}}, {id, status}) => {
Comments.countByAssetID.clear(comment.asset_id);
// postSetCommentStatus will use the arguments from the mutation and
// adjust the affected user's karma in the next tick.
process.nextTick(adjustKarma(Comments, id, status));
return comment;
};
@@ -274,15 +378,15 @@ module.exports = (context) => {
}
};
if (context.user && context.user.can('mutation:createComment')) {
if (context.user && context.user.can(CREATE_COMMENT)) {
mutators.Comment.create = (comment) => createPublicComment(context, comment);
}
if (context.user && context.user.can('mutation:setCommentStatus')) {
if (context.user && context.user.can(SET_COMMENT_STATUS)) {
mutators.Comment.setStatus = (action) => setStatus(context, action);
}
if (context.user && context.user.can('mutation:editComment')) {
if (context.user && context.user.can(EDIT_COMMENT)) {
mutators.Comment.edit = (action) => edit(context, action);
}
+3 -2
View File
@@ -1,5 +1,6 @@
const TagsService = require('../../services/tags');
const errors = require('../../errors');
const {ADD_COMMENT_TAG, REMOVE_COMMENT_TAG} = require('../../perms/constants');
/**
* Modifies the targeted model with the specified operation to add/remove a tag.
@@ -26,11 +27,11 @@ module.exports = (context) => {
}
};
if (context.user && context.user.can('mutation:addTag')) {
if (context.user && context.user.can(ADD_COMMENT_TAG)) {
mutators.Tag.add = (tag) => modify(context, TagsService.add, tag);
}
if (context.user && context.user.can('mutation:removeTag')) {
if (context.user && context.user.can(REMOVE_COMMENT_TAG)) {
mutators.Tag.remove = (tag) => modify(context, TagsService.remove, tag);
}
+14 -4
View File
@@ -1,12 +1,17 @@
const errors = require('../../errors');
const UsersService = require('../../services/users');
const {SET_USER_STATUS, SUSPEND_USER, REJECT_USERNAME} = require('../../perms/constants');
const setUserStatus = ({user}, {id, status}) => {
return UsersService.setStatus(id, status);
};
const suspendUser = ({user}, {id, message}) => {
return UsersService.suspendUser(id, message);
const suspendUser = ({user}, {id, message, until}) => {
return UsersService.suspendUser(id, message, until);
};
const rejectUsername = ({user}, {id, message}) => {
return UsersService.rejectUsername(id, message);
};
const ignoreUser = ({user}, userToIgnore) => {
@@ -22,18 +27,23 @@ module.exports = (context) => {
User: {
setUserStatus: () => Promise.reject(errors.ErrNotAuthorized),
suspendUser: () => Promise.reject(errors.ErrNotAuthorized),
rejectUsername: () => Promise.reject(errors.ErrNotAuthorized),
ignoreUser: (action) => ignoreUser(context, action),
stopIgnoringUser: (action) => stopIgnoringUser(context, action),
}
};
if (context.user && context.user.can('mutation:setUserStatus')) {
if (context.user && context.user.can(SET_USER_STATUS)) {
mutators.User.setUserStatus = (action) => setUserStatus(context, action);
}
if (context.user && context.user.can('mutation:suspendUser')) {
if (context.user && context.user.can(SUSPEND_USER)) {
mutators.User.suspendUser = (action) => suspendUser(context, action);
}
if (context.user && context.user.can(REJECT_USERNAME)) {
mutators.User.rejectUsername = (action) => rejectUsername(context, action);
}
return mutators;
};