{t('community.filter_role')}
@@ -188,6 +203,9 @@ class People extends React.Component {
user
),
[styles.actionsMenuBanned]: isBanned(user),
+ [styles.actionsMenuAlwaysPremod]: isAlwaysPremod(
+ user
+ ),
},
'talk-admin-user-detail-actions-button'
)}
@@ -232,6 +250,27 @@ class People extends React.Component {
{t('modqueue.ban_user_actions')}
)}
+
+ {isAlwaysPremod(user) ? (
+
+ this.removeAlwaysPremodUser({ id: user.id })
+ }
+ >
+ Remove Always Premoderate
+
+ ) : (
+
+ this.showAlwaysPremodUserDialog({
+ userId: user.id,
+ username: user.username,
+ })
+ }
+ >
+ {t('modqueue.always_premod_user_actions')}
+
+ )}
@@ -294,8 +333,10 @@ People.propTypes = {
viewUserDetail: PropTypes.func.isRequired,
unbanUser: PropTypes.func.isRequired,
unsuspendUser: PropTypes.func.isRequired,
+ removeAlwaysPremodUser: PropTypes.func.isRequired,
showSuspendUserDialog: PropTypes.func,
showBanUserDialog: PropTypes.func,
+ showAlwaysPremodUserDialog: PropTypes.func,
loadMore: PropTypes.func.isRequired,
};
diff --git a/client/coral-admin/src/routes/Community/containers/People.js b/client/coral-admin/src/routes/Community/containers/People.js
index 7f47795b4..57fb7fa8e 100644
--- a/client/coral-admin/src/routes/Community/containers/People.js
+++ b/client/coral-admin/src/routes/Community/containers/People.js
@@ -8,9 +8,11 @@ import {
withUnbanUser,
withUnsuspendUser,
withSetUserRole,
+ withRemoveAlwaysPremodUser,
} from 'coral-framework/graphql/mutations';
import { showBanUserDialog } from 'actions/banUserDialog';
import { showSuspendUserDialog } from 'actions/suspendUserDialog';
+import { showAlwaysPremodUserDialog } from 'actions/alwaysPremodUserDialog';
import { viewUserDetail } from '../../../actions/userDetail';
import { appendNewNodes } from 'plugin-api/beta/client/utils';
import update from 'immutability-helper';
@@ -30,6 +32,7 @@ class PeopleContainer extends React.PureComponent {
active: { suspended: false, banned: false },
suspended: { suspended: true },
banned: { banned: true },
+ alwaysPremod: { alwaysPremod: true },
};
onFilterChange = filter => e =>
@@ -40,7 +43,6 @@ class PeopleContainer extends React.PureComponent {
getFilterState = () => {
const { role, status } = this.state;
-
return {
status: this.statusToQuery[status] || null,
role: role || null,
@@ -106,8 +108,10 @@ class PeopleContainer extends React.PureComponent {
setUserRole={this.props.setUserRole}
showSuspendUserDialog={this.props.showSuspendUserDialog}
showBanUserDialog={this.props.showBanUserDialog}
+ showAlwaysPremodUserDialog={this.props.showAlwaysPremodUserDialog}
unbanUser={this.props.unbanUser}
unsuspendUser={this.props.unsuspendUser}
+ removeAlwaysPremodUser={this.props.removeAlwaysPremodUser}
data={this.props.data}
root={this.props.root}
users={this.props.root.users}
@@ -122,9 +126,11 @@ PeopleContainer.propTypes = {
setUserRole: PropTypes.func.isRequired,
unbanUser: PropTypes.func.isRequired,
unsuspendUser: PropTypes.func.isRequired,
+ removeAlwaysPremodUser: PropTypes.func.isRequired,
viewUserDetail: PropTypes.func.isRequired,
showSuspendUserDialog: PropTypes.func,
showBanUserDialog: PropTypes.func,
+ showAlwaysPremodUserDialog: PropTypes.func,
data: PropTypes.object,
root: PropTypes.object,
};
@@ -153,6 +159,9 @@ const LOAD_MORE_QUERY = gql`
banned {
status
}
+ alwaysPremod {
+ status
+ }
suspension {
until
}
@@ -187,6 +196,9 @@ const FILTER_QUERY = gql`
banned {
status
}
+ alwaysPremod {
+ status
+ }
suspension {
until
}
@@ -203,6 +215,7 @@ const mapDispatchToProps = dispatch =>
viewUserDetail,
showSuspendUserDialog,
showBanUserDialog,
+ showAlwaysPremodUserDialog,
},
dispatch
);
@@ -215,6 +228,7 @@ export default compose(
withSetUserRole,
withUnsuspendUser,
withUnbanUser,
+ withRemoveAlwaysPremodUser,
withQuery(
gql`
query TalkAdmin_Community_People {
@@ -236,6 +250,9 @@ export default compose(
banned {
status
}
+ alwaysPremod {
+ status
+ }
suspension {
until
}
diff --git a/client/coral-embed-stream/src/containers/Embed.js b/client/coral-embed-stream/src/containers/Embed.js
index 836def259..ef7c361a5 100644
--- a/client/coral-embed-stream/src/containers/Embed.js
+++ b/client/coral-embed-stream/src/containers/Embed.js
@@ -175,6 +175,9 @@ const USER_BANNED_SUBSCRIPTION = gql`
banned {
status
}
+ alwaysPremod {
+ status
+ }
suspension {
until
}
@@ -196,6 +199,9 @@ const USER_SUSPENDED_SUBSCRIPTION = gql`
banned {
status
}
+ alwaysPremod {
+ status
+ }
suspension {
until
}
@@ -217,6 +223,9 @@ const USERNAME_REJECTED_SUBSCRIPTION = gql`
banned {
status
}
+ alwaysPremod {
+ status
+ }
suspension {
until
}
@@ -253,6 +262,9 @@ const EMBED_QUERY = gql`
banned {
status
}
+ alwaysPremod {
+ status
+ }
suspension {
until
}
diff --git a/client/coral-embed-stream/src/tabs/stream/containers/Stream.js b/client/coral-embed-stream/src/tabs/stream/containers/Stream.js
index 1f32fcedf..a379e6dbd 100644
--- a/client/coral-embed-stream/src/tabs/stream/containers/Stream.js
+++ b/client/coral-embed-stream/src/tabs/stream/containers/Stream.js
@@ -398,6 +398,9 @@ const fragments = {
banned {
status
}
+ alwaysPremod {
+ status
+ }
suspension {
until
}
diff --git a/client/coral-framework/graphql/fragments.js b/client/coral-framework/graphql/fragments.js
index 778214b8b..976776933 100644
--- a/client/coral-framework/graphql/fragments.js
+++ b/client/coral-framework/graphql/fragments.js
@@ -3,6 +3,7 @@ import { createDefaultResponseFragments } from '../utils';
// fragments defined here are automatically registered.
export default {
...createDefaultResponseFragments(
+ 'AlwaysPremodUserResponse',
'BanUsersResponse',
'ChangeUsernameResponse',
'CloseAssetResponse',
@@ -14,6 +15,7 @@ export default {
'IgnoreUserResponse',
'ModifyTagResponse',
'PostFlagResponse',
+ 'RemoveAlwaysPremodUserResponse',
'SetCommentStatusResponse',
'SetUsernameResponse',
'SetUsernameStatusResponse',
diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js
index 8191c2cd0..78c044270 100644
--- a/client/coral-framework/graphql/mutations.js
+++ b/client/coral-framework/graphql/mutations.js
@@ -412,6 +412,48 @@ export const withSetUsername = withMutation(
}
);
+export const withAlwaysPremodUser = withMutation(
+ gql`
+ mutation AlwaysPremodUser($input: AlwaysPremodUserInput!) {
+ alwaysPremodUser(input: $input) {
+ ...AlwaysPremodUserResponse
+ }
+ }
+ `,
+ {
+ props: ({ mutate }) => ({
+ alwaysPremodUser: input => {
+ return mutate({
+ variables: {
+ input,
+ },
+ });
+ },
+ }),
+ }
+);
+
+export const withRemoveAlwaysPremodUser = withMutation(
+ gql`
+ mutation RemoveAlwaysPremodUser($input: RemoveAlwaysPremodUserInput!) {
+ removeAlwaysPremodUser(input: $input) {
+ ...RemoveAlwaysPremodUserResponse
+ }
+ }
+ `,
+ {
+ props: ({ mutate }) => ({
+ removeAlwaysPremodUser: input => {
+ return mutate({
+ variables: {
+ input,
+ },
+ });
+ },
+ }),
+ }
+);
+
export const withBanUser = withMutation(
gql`
mutation BanUser($input: BanUserInput!) {
diff --git a/client/coral-framework/utils/user.js b/client/coral-framework/utils/user.js
index cdc0884d5..174815bfb 100644
--- a/client/coral-framework/utils/user.js
+++ b/client/coral-framework/utils/user.js
@@ -35,6 +35,15 @@ export const isBanned = user => {
return get(user, 'state.status.banned.status');
};
+/**
+ * isAlwaysPremod
+ * retrieves boolean based on the user premod status
+ */
+
+export const isAlwaysPremod = user => {
+ return get(user, 'state.status.alwaysPremod.status');
+};
+
/**
* isUsernameRejected
* retrieves boolean based on the username status
diff --git a/graph/loaders/users.js b/graph/loaders/users.js
index 11d35ac95..cc91af340 100644
--- a/graph/loaders/users.js
+++ b/graph/loaders/users.js
@@ -11,7 +11,7 @@ const mergeState = (query, state) => {
}
if (status) {
- const { username, banned, suspended } = status;
+ const { username, banned, suspended, alwaysPremod } = status;
if (typeof username !== 'undefined' && username && username.length > 0) {
query.merge({
@@ -27,6 +27,12 @@ const mergeState = (query, state) => {
});
}
+ if (typeof alwaysPremod !== 'undefined' && alwaysPremod !== null) {
+ query.merge({
+ 'status.alwaysPremod.status': alwaysPremod,
+ });
+ }
+
if (typeof suspended !== 'undefined' && suspended !== null) {
if (suspended) {
query.merge({
diff --git a/graph/mutators/user.js b/graph/mutators/user.js
index fa6a18ee5..3732ee0de 100644
--- a/graph/mutators/user.js
+++ b/graph/mutators/user.js
@@ -10,6 +10,7 @@ const {
SET_USERNAME,
SET_USER_USERNAME_STATUS,
SET_USER_BAN_STATUS,
+ SET_USER_ALWAYS_PREMOD_STATUS,
SET_USER_SUSPENSION_STATUS,
UPDATE_USER_ROLES,
DELETE_OTHER_USER,
@@ -32,6 +33,13 @@ const setUserBanStatus = async (ctx, id, status = false, message = null) => {
}
};
+const setUserAlwaysPremodStatus = async (ctx, id, status = false) => {
+ const user = await Users.setAlwaysPremodStatus(id, status, ctx.user.id);
+ if (user.alwaysPremod) {
+ ctx.pubsub.publish('userAlwaysPremod', user);
+ }
+};
+
const setUserSuspensionStatus = async (
ctx,
id,
@@ -220,6 +228,7 @@ module.exports = ctx => {
setRole: () => Promise.reject(new ErrNotAuthorized()),
setUserBanStatus: () => Promise.reject(new ErrNotAuthorized()),
setUserSuspensionStatus: () => Promise.reject(new ErrNotAuthorized()),
+ setUserAlwaysPremodStatus: () => Promise.reject(new ErrNotAuthorized()),
setUserUsernameStatus: () => Promise.reject(new ErrNotAuthorized()),
setUsername: () => Promise.reject(new ErrNotAuthorized()),
stopIgnoringUser: () => Promise.reject(new ErrNotAuthorized()),
@@ -256,6 +265,11 @@ module.exports = ctx => {
setUserBanStatus(ctx, id, status, message);
}
+ if (ctx.user.can(SET_USER_ALWAYS_PREMOD_STATUS)) {
+ mutators.User.setUserAlwaysPremodStatus = (id, status) =>
+ setUserAlwaysPremodStatus(ctx, id, status);
+ }
+
if (ctx.user.can(SET_USER_SUSPENSION_STATUS)) {
mutators.User.setUserSuspensionStatus = (id, until, message) =>
setUserSuspensionStatus(ctx, id, until, message);
diff --git a/graph/resolvers/always_premod_status_history.js b/graph/resolvers/always_premod_status_history.js
new file mode 100644
index 000000000..9fccae1d7
--- /dev/null
+++ b/graph/resolvers/always_premod_status_history.js
@@ -0,0 +1,7 @@
+const { decorateUserField } = require('./util');
+
+const AlwaysPremodStatusHistory = {};
+
+decorateUserField(AlwaysPremodStatusHistory, 'assigned_by');
+
+module.exports = AlwaysPremodStatusHistory;
diff --git a/graph/resolvers/index.js b/graph/resolvers/index.js
index 4bc9d9624..7dc9d24c8 100644
--- a/graph/resolvers/index.js
+++ b/graph/resolvers/index.js
@@ -3,6 +3,7 @@ const debug = require('debug')('talk:graph:resolvers');
const Action = require('./action');
const ActionSummary = require('./action_summary');
+const AlwaysPremodStatusHistory = require('./always_premod_status_history');
const Asset = require('./asset');
const AssetActionSummary = require('./asset_action_summary');
const BannedStatusHistory = require('./banned_status_history');
@@ -37,6 +38,7 @@ const plugins = require('../../services/plugins');
let resolvers = {
Action,
ActionSummary,
+ AlwaysPremodStatusHistory,
Asset,
AssetActionSummary,
BannedStatusHistory,
diff --git a/graph/resolvers/root_mutation.js b/graph/resolvers/root_mutation.js
index d2c2965e0..6572cdca2 100644
--- a/graph/resolvers/root_mutation.js
+++ b/graph/resolvers/root_mutation.js
@@ -74,6 +74,16 @@ const RootMutation = {
unbanUser: async (obj, { input: { id } }, { mutators: { User } }) => {
await User.setUserBanStatus(id, false);
},
+ alwaysPremodUser: async (obj, { input: { id } }, { mutators: { User } }) => {
+ await User.setUserAlwaysPremodStatus(id, true);
+ },
+ removeAlwaysPremodUser: async (
+ obj,
+ { input: { id } },
+ { mutators: { User } }
+ ) => {
+ await User.setUserAlwaysPremodStatus(id, false);
+ },
ignoreUser: async (_, { id }, { mutators: { User } }) => {
await User.ignoreUser({ id });
},
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index ebbf8203d..d1f2ca5a5 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -142,6 +142,10 @@ input UserStatusInput {
# suspended will restrict the returned users to only those that are, or are not
# suspended. If not provided, no filtering will be performed.
suspended: Boolean
+
+ # alwaysPremod will restrict the returned users to only those that are, or are not
+ # marked as always premoderate. If not provided, no filtering will be performed.
+ alwaysPremod: Boolean
}
type UsernameStatusHistory {
@@ -166,6 +170,17 @@ type BannedStatus {
history: [BannedStatusHistory!]
}
+type AlwaysPremodStatusHistory {
+ status: Boolean!
+ assigned_by: User
+ created_at: Date!
+}
+
+type AlwaysPremodStatus {
+ status: Boolean!
+ history: [AlwaysPremodStatusHistory!]
+}
+
type SuspensionStatusHistory {
until: Date
assigned_by: User
@@ -184,6 +199,9 @@ type UserStatus {
# banned is the bool that determines if the user is banned or not.
banned: BannedStatus!
+ # alwaysPremod is the bool that determines if the user comments are always pushed to the premod queue or not
+ alwaysPremod: AlwaysPremodStatus!
+
# suspension is the date that the user is suspended until.
suspension: SuspensionStatus!
}
@@ -1446,6 +1464,22 @@ type UnbanUserResponse implements Response {
errors: [UserError!]
}
+input AlwaysPremodUserInput {
+ id: ID!
+}
+
+input RemoveAlwaysPremodUserInput {
+ id: ID!
+}
+
+type AlwaysPremodUserResponse implements Response {
+ errors: [UserError!]
+}
+
+type RemoveAlwaysPremodUserResponse implements Response {
+ errors: [UserError!]
+}
+
input SuspendUserInput {
id: ID!
message: String!
@@ -1545,6 +1579,14 @@ type RootMutation {
# Mutation is restricted.
unsuspendUser(input: UnsuspendUserInput!): UnsuspendUserResponse
+ # Sets the always premod status on a given user. Requires the `MODERATOR` role.
+ # Mutation is restricted.
+ alwaysPremodUser(input: AlwaysPremodUserInput!): AlwaysPremodUserResponse
+
+ # Sets the always premod status on a given user. Requires the `MODERATOR` role.
+ # Mutation is restricted.
+ removeAlwaysPremodUser(input: RemoveAlwaysPremodUserInput!): RemoveAlwaysPremodUserResponse
+
# Sets the ban status on a given user. Requires the `MODERATOR` role.
# Mutation is restricted.
banUser(input: BanUserInput!): BanUsersResponse
diff --git a/locales/en.yml b/locales/en.yml
index ad831f5a3..dc5653ed1 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -3,6 +3,13 @@ en:
sort_comments: 'Sort Comments'
view_options: 'View Options'
already_flagged_username: 'You have already flagged this username.'
+ alwayspremoddialog:
+ are_you_sure: 'Are you sure you would like to always premoderate {0}?'
+ always_premod_user: 'Always premoderate user?'
+ cancel: Cancel
+ note: 'Note: {0}'
+ note_always_premod_user: 'Always premoderating this user will place all of their comments in the Pre-Moderate queue.'
+ yes_always_premod_user: 'Yes, always premoderate user'
bandialog:
are_you_sure: 'Are you sure you would like to ban {0}?'
ban_user: 'Ban User?'
@@ -64,6 +71,7 @@ en:
admin: Administrator
ads_marketing: 'This looks like an ad/marketing'
all: 'All'
+ always_premod: 'Always premoderated'
are_you_sure: 'Are you sure you would like to ban {0}?'
ban_user: 'Ban User?'
banned: Banned
@@ -382,6 +390,7 @@ en:
actions: Actions
all: all
all_streams: 'All Streams'
+ always_premod_user_actions: 'Always Premod User'
approve: Approve
approved: Approved
ban_user: Ban
@@ -528,6 +537,8 @@ en:
username_flags: 'flags for this username'
user_detail:
all: All
+ always_premod: 'Always Premod User'
+ always_premoded: 'Always premoderated'
ban: 'Ban User'
banned: Banned
email: Email
@@ -539,6 +550,7 @@ en:
reject_rate: 'Reject Rate'
reject_username: 'Reject Username'
rejected: Rejected
+ remove_always_premod: 'Remove Always Premoderate'
remove_ban: 'Remove Ban'
remove_suspension: 'Remove Suspension'
suspend: 'Suspend User'
@@ -552,12 +564,14 @@ en:
username_rejected: 'Username rejected'
user_history:
action: Action
+ always_premod_removed: 'Always premoderate removed'
ban_removed: 'Ban removed'
date: Date
suspended: 'Suspended, {0}'
suspension_removed: 'Suspension removed'
system: System
taken_by: 'Taken By'
+ user_always_premoded: 'User always premoderated'
user_banned: 'User banned'
username_status: 'Username {0}'
user_impersonating: 'This user is impersonating'
diff --git a/locales/es.yml b/locales/es.yml
index cff505cbd..57a789510 100644
--- a/locales/es.yml
+++ b/locales/es.yml
@@ -3,6 +3,13 @@ es:
sort_comments: 'Ordenar comentarios'
view_options: 'Ver opciones'
already_flagged_username: 'Usted ya reportó a este usuario.'
+ alwayspremoddialog:
+ are_you_sure: '¿Estás seguro que quieres pre-moderar a {0}?'
+ always_premod_user: '¿Pre-moderar al Usuario?'
+ cancel: Cancelar
+ note: 'Nota: {0}'
+ note_always_premod_user: 'Pre-moderar a un usuario pondrá todos sus comentarios en la fila de Pre-Moderación.'
+ yes_always_premod_user: 'Sí, pre-moderar al usuario.'
bandialog:
are_you_sure: '¿Estás seguro que quieres suspender a {0}?'
ban_user: '¿Quieres suspender al Usuario?'
@@ -62,6 +69,7 @@ es:
admin: Administrator
ads_marketing: 'Esto parece ser un publicidad/acción de comercialización'
all: 'Todos'
+ always_premod: 'Pre-moderado'
are_you_sure: '¿Estás seguro que quieres suspender a {0}?'
ban_user: '¿Quieres suspender al Usuario?'
banned: Suspendido
@@ -355,6 +363,7 @@ es:
actions: Acciones
all: todos
all_streams: 'Todos los Hilos'
+ always_premod_user_actions: 'Siempre Pre-moderar Usuario'
approve: Aprobar
approved: Aprobado
ban_user: Bloquear
@@ -486,12 +495,15 @@ es:
username_flags: 'reportes para este nombre de usuario'
user_detail:
all: Todos
+ always_premod: 'Siempre pre-moderar usuario'
+ always_premoded: 'Siempre pre-moderado'
ban: 'Bloquear usuario'
banned: Baneado
email: 'Correo electrónico'
member_since: 'Miembro desde'
reject_rate: 'Promedio de rechazo'
rejected: Rechazado
+ remove_always_premod: 'Cancelar siempre pre-moderar'
remove_ban: 'Cancelar bloqueo'
remove_suspension: 'Cancelar suspensión'
suspend: 'Suspender usuario'
@@ -503,12 +515,14 @@ es:
username_rejected: 'Usuario rechazado'
user_history:
action: Acción
+ always_premod_removed: 'Siempre pre-moderar cancelado'
ban_removed: 'Bloqueo cancelado'
date: Fecha
suspended: 'Suspendido, {0}'
suspension_removed: 'Suspensión cancelada'
system: Sistema
taken_by: 'Está en uso'
+ user_always_premoded: 'Usuario siempre pre-moderado'
user_banned: 'Usuario bloqueado'
username_status: 'Nombre de usuario {0}'
user_impersonating: 'Este usuario suplanta a alguien'
diff --git a/models/schema/user.js b/models/schema/user.js
index 40dcd5ec8..d4dca3a1c 100644
--- a/models/schema/user.js
+++ b/models/schema/user.js
@@ -180,6 +180,30 @@ const User = new Schema(
},
],
},
+
+ // alwaysPremod stores the current user premod status as well as the history of
+ // changes.
+ alwaysPremod: {
+ // Status stores the current user premod status.
+ status: {
+ type: Boolean,
+ required: true,
+ default: false,
+ index: true,
+ },
+ history: [
+ {
+ // Status stores the historical premod status.
+ status: Boolean,
+
+ // assigned_by stores the user id of the user who assigned this status.
+ assigned_by: { type: String, default: null },
+
+ // created_at stores the date when this status was assigned.
+ created_at: { type: Date, default: Date.now },
+ },
+ ],
+ },
},
// IgnoresUsers is an array of user id's that the current user is ignoring.
@@ -359,6 +383,27 @@ User.virtual('banned')
});
});
+/**
+ * alwaysPremod returns true when the user is currently in always premod, and sets the alwaysPremod
+ * status locally.
+ */
+User.virtual('alwaysPremod')
+ .get(function() {
+ return this.status.alwaysPremod.status;
+ })
+ .set(function(status) {
+ this.status.alwaysPremod.status = status;
+
+ if (!this.status.alwaysPremod.history) {
+ this.status.alwaysPremod.history = [];
+ }
+
+ this.status.alwaysPremod.history.push({
+ status,
+ created_at: new Date(),
+ });
+ });
+
/**
* suspended returns true when the user is currently suspended, and sets the
* suspension status locally.
diff --git a/perms/constants/mutation.js b/perms/constants/mutation.js
index 2d4ae04cb..54b92e00b 100644
--- a/perms/constants/mutation.js
+++ b/perms/constants/mutation.js
@@ -7,6 +7,7 @@ module.exports = {
EDIT_COMMENT: 'EDIT_COMMENT',
SET_USER_USERNAME_STATUS: 'SET_USER_USERNAME_STATUS',
SET_USER_BAN_STATUS: 'SET_USER_BAN_STATUS',
+ SET_USER_ALWAYS_PREMOD_STATUS: 'SET_USER_ALWAYS_PREMOD_STATUS',
SET_USER_SUSPENSION_STATUS: 'SET_USER_SUSPENSION_STATUS',
SET_COMMENT_STATUS: 'SET_COMMENT_STATUS',
ADD_COMMENT_TAG: 'ADD_COMMENT_TAG',
diff --git a/perms/reducers/mutation.js b/perms/reducers/mutation.js
index 8133fcd47..93cb0ab24 100644
--- a/perms/reducers/mutation.js
+++ b/perms/reducers/mutation.js
@@ -47,6 +47,7 @@ module.exports = (user, perm) => {
case types.SET_USER_USERNAME_STATUS:
case types.SET_USER_BAN_STATUS:
case types.SET_USER_SUSPENSION_STATUS:
+ case types.SET_USER_ALWAYS_PREMOD_STATUS:
case types.UPDATE_ASSET_SETTINGS:
case types.UPDATE_ASSET_STATUS:
return check(user, ['ADMIN', 'MODERATOR']);
diff --git a/services/moderation/phases/premod.js b/services/moderation/phases/premod.js
index db1a3fd1b..36aba634e 100644
--- a/services/moderation/phases/premod.js
+++ b/services/moderation/phases/premod.js
@@ -9,9 +9,9 @@ module.exports = (
},
}
) => {
- // If the settings say that we're in premod mode, then the comment is in
- // premod status.
- if (moderation === 'PRE') {
+ // If the settings say that we're in premod mode, or the user is flagged as
+ // always premod, then the comment is in premod status.
+ if (moderation === 'PRE' || ctx.user.status.alwaysPremod.status === true) {
return {
status: 'PREMOD',
};
diff --git a/services/users.js b/services/users.js
index 2ce0b7c15..e85ed67a2 100644
--- a/services/users.js
+++ b/services/users.js
@@ -207,6 +207,50 @@ class Users {
return user;
}
+ static async setAlwaysPremodStatus(id, status, assignedBy = null) {
+ let user = await User.findOneAndUpdate(
+ {
+ id,
+ 'status.alwaysPremod.status': {
+ $ne: status,
+ },
+ },
+ {
+ $set: {
+ 'status.alwaysPremod.status': status,
+ },
+ $push: {
+ 'status.alwaysPremod.history': {
+ status,
+ assigned_by: assignedBy,
+ created_at: Date.now(),
+ },
+ },
+ },
+ {
+ new: true,
+ runValidators: true,
+ }
+ );
+
+ if (!user) {
+ user = await User.findOne({ id });
+ if (!user) {
+ throw new ErrNotFound();
+ }
+
+ if (user.status.alwaysPremod.status === status) {
+ return user;
+ }
+
+ throw new Error(
+ 'always premod status change edit failed for an unknown reason'
+ );
+ }
+
+ return user;
+ }
+
static async setBanStatus(id, status, assignedBy = null, message) {
let user = await User.findOneAndUpdate(
{
|