mirror of
https://github.com/wassname/talk.git
synced 2026-07-01 08:03:28 +08:00
Move previous suspendUser feature to rejectUsername
This commit is contained in:
@@ -3,7 +3,7 @@ import {connect} from 'react-redux';
|
||||
import {compose} from 'react-apollo';
|
||||
|
||||
import {modUserFlaggedQuery} from 'coral-admin/src/graphql/queries';
|
||||
import {banUser, setUserStatus, suspendUser} from 'coral-admin/src/graphql/mutations';
|
||||
import {banUser, setUserStatus, rejectUsername} from 'coral-admin/src/graphql/mutations';
|
||||
|
||||
import {
|
||||
fetchAccounts,
|
||||
@@ -113,7 +113,7 @@ class CommunityContainer extends Component {
|
||||
error={data.error}
|
||||
showBanUserDialog={props.showBanUserDialog}
|
||||
approveUser={props.approveUser}
|
||||
suspendUser={props.suspendUser}
|
||||
rejectUsername={props.rejectUsername}
|
||||
showSuspendUserDialog={props.showSuspendUserDialog}
|
||||
/>
|
||||
<BanUserDialog
|
||||
@@ -126,7 +126,7 @@ class CommunityContainer extends Component {
|
||||
open={community.suspendDialog}
|
||||
handleClose={props.hideSuspendUserDialog}
|
||||
user={community.user}
|
||||
suspendUser={props.suspendUser}
|
||||
rejectUsername={props.rejectUsername}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -165,5 +165,5 @@ export default compose(
|
||||
modUserFlaggedQuery,
|
||||
banUser,
|
||||
setUserStatus,
|
||||
suspendUser
|
||||
rejectUsername
|
||||
)(CommunityContainer);
|
||||
|
||||
@@ -34,7 +34,7 @@ class SuspendUserDialog extends Component {
|
||||
static propTypes = {
|
||||
stage: PropTypes.number,
|
||||
handleClose: PropTypes.func.isRequired,
|
||||
suspendUser: PropTypes.func.isRequired
|
||||
rejectUsername: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -46,13 +46,13 @@ class SuspendUserDialog extends Component {
|
||||
* handles the possible actions for that dialog.
|
||||
*/
|
||||
onActionClick = (stage, menuOption) => () => {
|
||||
const {suspendUser, user} = this.props;
|
||||
const {rejectUsername, user} = this.props;
|
||||
const {stage} = this.state;
|
||||
|
||||
const cancel = this.props.handleClose;
|
||||
const next = () => this.setState({stage: stage + 1});
|
||||
const suspend = () => {
|
||||
suspendUser({id: user.user.id, message: this.state.email, mustChangeUsername: true})
|
||||
rejectUsername({id: user.user.id, message: this.state.email})
|
||||
.then(() => {
|
||||
this.props.handleClose();
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import translations from 'coral-admin/src/translations';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
|
||||
import {modQueueQuery, getQueueCounts} from '../../graphql/queries';
|
||||
import {banUser, setCommentStatus} from '../../graphql/mutations';
|
||||
import {banUser, setCommentStatus, suspendUser} from '../../graphql/mutations';
|
||||
|
||||
import {fetchSettings} from 'actions/settings';
|
||||
import {updateAssets} from 'actions/assets';
|
||||
@@ -211,14 +211,24 @@ class ModerationContainer extends Component {
|
||||
<SuspendUserDialog
|
||||
open={moderation.suspendUserDialog.show}
|
||||
username={moderation.suspendUserDialog.username}
|
||||
userId={moderation.suspendUserDialog.userId}
|
||||
onCancel={props.hideSuspendUserDialog}
|
||||
onPerform={(result) => {
|
||||
toast(
|
||||
lang.t('suspenduser.notify_suspend_until',
|
||||
moderation.suspendUserDialog.username,
|
||||
lang.timeago(result.until)),
|
||||
{type: 'success'}
|
||||
);
|
||||
onPerform={(args) => {
|
||||
props.suspendUser(args)
|
||||
.then(() => {
|
||||
toast(
|
||||
lang.t('suspenduser.notify_suspend_until',
|
||||
moderation.suspendUserDialog.username,
|
||||
lang.timeago(args.until)),
|
||||
{type: 'success'}
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast(
|
||||
err,
|
||||
{type: 'error'}
|
||||
);
|
||||
});
|
||||
props.hideSuspendUserDialog();
|
||||
}}
|
||||
/>
|
||||
@@ -238,7 +248,7 @@ const mapStateToProps = (state) => ({
|
||||
assets: state.assets.get('assets')
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onClose: () => dispatch(toggleModal(false)),
|
||||
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
|
||||
...bindActionCreators({
|
||||
@@ -257,6 +267,7 @@ export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
setCommentStatus,
|
||||
getQueueCounts,
|
||||
banUser,
|
||||
suspendUser,
|
||||
modQueueQuery,
|
||||
banUser
|
||||
)(ModerationContainer);
|
||||
|
||||
@@ -40,8 +40,9 @@ class SuspendUserDialog extends React.Component {
|
||||
|
||||
handlePerform = () => {
|
||||
this.props.onPerform({
|
||||
until: dateAdd(new Date(), 'hour', this.state.duration),
|
||||
id: this.props.userId,
|
||||
message: this.state.message,
|
||||
until: dateAdd(new Date(), 'hour', this.state.duration),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import {graphql} from 'react-apollo';
|
||||
import SET_USER_STATUS from './setUserStatus.graphql';
|
||||
import SET_COMMENT_STATUS from './setCommentStatus.graphql';
|
||||
import SUSPEND_USER from './suspendUser.graphql';
|
||||
import REJECT_USERNAME from './rejectUsername.graphql';
|
||||
|
||||
export const banUser = graphql(SET_USER_STATUS, {
|
||||
props: ({mutate}) => ({
|
||||
@@ -43,6 +44,19 @@ export const suspendUser = graphql(SUSPEND_USER, {
|
||||
})
|
||||
});
|
||||
|
||||
export const rejectUsername = graphql(REJECT_USERNAME, {
|
||||
props: ({mutate}) => ({
|
||||
rejectUsername: (input) => {
|
||||
return mutate({
|
||||
variables: {
|
||||
input,
|
||||
},
|
||||
refetchQueries: ['Users']
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const views = ['all', 'premod', 'flagged', 'accepted', 'rejected'];
|
||||
export const setCommentStatus = graphql(SET_COMMENT_STATUS, {
|
||||
props: ({mutate}) => ({
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
mutation rejectUsername($input: RejectUsernameInput!) {
|
||||
rejectUsername(input: $input) {
|
||||
errors {
|
||||
translation_key
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ export default function moderation (state = initialState, action) {
|
||||
.mergeDeep({
|
||||
suspendUserDialog: {
|
||||
show: true,
|
||||
userId: action.userId,
|
||||
username: action.username,
|
||||
commentId: action.commentId,
|
||||
commentStatus: action.commentStatus,
|
||||
|
||||
@@ -196,8 +196,8 @@ export const facebookCallback = (err, data) => (dispatch, getState) => {
|
||||
dispatch(handleAuthToken(data.token));
|
||||
dispatch(signInFacebookSuccess(data.user));
|
||||
dispatch(hideSignInDialog());
|
||||
const {user: {canEditName, suspension}} = getState().auth.toJS();
|
||||
if (canEditName && !suspension.mustChangeUsername) {
|
||||
const {user: {canEditName, status}} = getState().auth.toJS();
|
||||
if (canEditName && status !== 'BANNED') {
|
||||
dispatch(showCreateUsernameDialog());
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
+11
-2
@@ -5,8 +5,12 @@ const setUserStatus = ({user}, {id, status}) => {
|
||||
return UsersService.setStatus(id, status);
|
||||
};
|
||||
|
||||
const suspendUser = ({user}, {id, message, mustChangeUsername, until}) => {
|
||||
return UsersService.suspendUser(id, message, mustChangeUsername, until);
|
||||
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,6 +26,7 @@ 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),
|
||||
}
|
||||
@@ -35,5 +40,9 @@ module.exports = (context) => {
|
||||
mutators.User.suspendUser = (action) => suspendUser(context, action);
|
||||
}
|
||||
|
||||
if (context.user && context.user.can('mutation:rejectUsername')) {
|
||||
mutators.User.rejectUsername = (action) => rejectUsername(context, action);
|
||||
}
|
||||
|
||||
return mutators;
|
||||
};
|
||||
|
||||
@@ -20,8 +20,11 @@ const RootMutation = {
|
||||
setUserStatus(_, {id, status}, {mutators: {User}}) {
|
||||
return wrapResponse(null)(User.setUserStatus({id, status}));
|
||||
},
|
||||
suspendUser(_, {input: {id, message, mustChangeUsername, until}}, {mutators: {User}}) {
|
||||
return wrapResponse(null)(User.suspendUser({id, message, mustChangeUsername, until}));
|
||||
suspendUser(_, {input: {id, message, until}}, {mutators: {User}}) {
|
||||
return wrapResponse(null)(User.suspendUser({id, message, until}));
|
||||
},
|
||||
rejectUsername(_, {input: {id, message}}, {mutators: {User}}) {
|
||||
return wrapResponse(null)(User.rejectUsername({id, message}));
|
||||
},
|
||||
ignoreUser(_, {id}, {mutators: {User}}) {
|
||||
return wrapResponse(null)(User.ignoreUser({id}));
|
||||
|
||||
+22
-3
@@ -679,13 +679,21 @@ input SuspendUserInput {
|
||||
# TODO: should this be required?
|
||||
message: String
|
||||
|
||||
# If set, the user is requested to change its username.
|
||||
mustChangeUsername: Boolean
|
||||
|
||||
# If set, the suspension lasts at least until specified date.
|
||||
until: Date
|
||||
}
|
||||
|
||||
# Input for rejectUsername mutation.
|
||||
input RejectUsernameInput {
|
||||
|
||||
# id of target user.
|
||||
id: ID!
|
||||
|
||||
# message to be sent to the user.
|
||||
# TODO: should this be required?
|
||||
message: String
|
||||
}
|
||||
|
||||
# DeleteActionResponse is the response returned with possibly some errors
|
||||
# relating to the delete action attempt.
|
||||
type DeleteActionResponse implements Response {
|
||||
@@ -710,6 +718,14 @@ type SuspendUserResponse implements Response {
|
||||
errors: [UserError]
|
||||
}
|
||||
|
||||
# RejectUsernameResponse is the response returned with possibly some errors
|
||||
# relating to the reject username action attempt.
|
||||
type RejectUsernameResponse implements Response {
|
||||
|
||||
# An array of errors relating to the mutation that occurred.
|
||||
errors: [UserError]
|
||||
}
|
||||
|
||||
# SetCommentStatusResponse is the response returned with possibly some errors
|
||||
# relating to the delete action attempt.
|
||||
type SetCommentStatusResponse implements Response {
|
||||
@@ -785,6 +801,9 @@ type RootMutation {
|
||||
# Suspends a user. Requires the `ADMIN` role.
|
||||
suspendUser(input: SuspendUserInput!): SuspendUserResponse
|
||||
|
||||
# Suspends a user. Requires the `ADMIN` role.
|
||||
rejectUsername(input: RejectUsernameInput!): RejectUsernameResponse
|
||||
|
||||
# Sets Comment status. Requires the `ADMIN` role.
|
||||
setCommentStatus(id: ID!, status: COMMENT_STATUS!): SetCommentStatusResponse
|
||||
|
||||
|
||||
+4
-11
@@ -112,11 +112,7 @@ const UserSchema = new mongoose.Schema({
|
||||
},
|
||||
|
||||
// User's suspension details.
|
||||
suspensionDetails: {
|
||||
mustChangeUsername: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
suspension: {
|
||||
until: {
|
||||
type: Date,
|
||||
default: null,
|
||||
@@ -151,7 +147,6 @@ const UserSchema = new mongoose.Schema({
|
||||
},
|
||||
|
||||
toJSON: {
|
||||
virtuals: true,
|
||||
transform: function (doc, ret) {
|
||||
delete ret.password;
|
||||
delete ret._id;
|
||||
@@ -160,10 +155,6 @@ const UserSchema = new mongoose.Schema({
|
||||
}
|
||||
});
|
||||
|
||||
UserSchema.virtual('suspended').get(function() {
|
||||
return this.suspensionDetails.mustChangeUsername || this.suspensionDetails.until > new Date();
|
||||
});
|
||||
|
||||
// Add the indixies on the user profile data.
|
||||
UserSchema.index({
|
||||
'profiles.id': 1,
|
||||
@@ -214,6 +205,7 @@ const USER_GRAPH_OPERATIONS = [
|
||||
'mutation:editName',
|
||||
'mutation:setUserStatus',
|
||||
'mutation:suspendUser',
|
||||
'mutation:rejectUsername',
|
||||
'mutation:setCommentStatus',
|
||||
'mutation:addCommentTag',
|
||||
'mutation:removeCommentTag',
|
||||
@@ -233,7 +225,8 @@ UserSchema.method('can', function(...actions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actions.some((action) => action === 'mutation:setUserStatus' || action === 'mutation:suspendUser' || action === 'mutation:setCommentStatus') && !this.hasRoles('ADMIN')) {
|
||||
const adminOnlyActions = ['mutation:setUserStatus', 'mutation:suspendUser', 'mutation:rejectUsername', 'mutation:setCommentStatus'];
|
||||
if (actions.some((action) => adminOnlyActions.indexOf(action) > 0 && !this.hasRoles('ADMIN'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+50
-19
@@ -450,28 +450,60 @@ module.exports = class UsersService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspend a user. It changes the status to BANNED and canEditName to True.
|
||||
* Suspend a user until specified time.
|
||||
* @param {String} id id of a user
|
||||
* @param {String} message message to be send to the user
|
||||
* @param {Boolean} mustChangeUsername if set the suspension lasts at least until user changed its username.
|
||||
* @param {Date} until if set the suspension lasts at least until date.
|
||||
* @param {Date} until date until the suspension is valid.
|
||||
*/
|
||||
static suspendUser(id, message, mustChangeUsername, until) {
|
||||
const changes = {
|
||||
$set: {
|
||||
suspensionDetails: {},
|
||||
}
|
||||
};
|
||||
if (mustChangeUsername) {
|
||||
changes.$set.status = 'BANNED';
|
||||
changes.$set.canEditName = true;
|
||||
changes.$set.suspensionDetails.mustChangeUsername = true;
|
||||
}
|
||||
if (until) {
|
||||
changes.$set.suspensionDetails.until = until;
|
||||
}
|
||||
return UserModel.findOneAndUpdate({id}, changes)
|
||||
static suspendUser(id, message, until) {
|
||||
console.log('SUSPEND');
|
||||
return UserModel.findOneAndUpdate(
|
||||
{id}, {
|
||||
$set: {
|
||||
suspension: {
|
||||
until,
|
||||
},
|
||||
}
|
||||
})
|
||||
.then((user) => {
|
||||
console.log(user);
|
||||
if (message) {
|
||||
let localProfile = user.profiles.find((profile) => profile.provider === 'local');
|
||||
|
||||
if (localProfile) {
|
||||
const options =
|
||||
{
|
||||
template: 'suspension', // needed to know which template to render!
|
||||
locals: { // specifies the template locals.
|
||||
body: message
|
||||
},
|
||||
subject: 'Email Suspension',
|
||||
to: localProfile.id // This only works if the user has registered via e-mail.
|
||||
// We may want a standard way to access a user's e-mail address in the future
|
||||
};
|
||||
|
||||
return MailerService.sendSimple(options);
|
||||
} else {
|
||||
return Promise.reject(errors.ErrMissingEmail);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject username. It changes the status to BANNED and canEditName to True.
|
||||
* @param {String} id id of a user
|
||||
* @param {String} message message to be send to the user
|
||||
* @param {Date} until date until the suspension is valid.
|
||||
*/
|
||||
static rejectUsername(id, message) {
|
||||
return UserModel.findOneAndUpdate(
|
||||
{id}, {
|
||||
$set: {
|
||||
status: 'BANNED',
|
||||
canEditName: true,
|
||||
}
|
||||
}).then((user) => {
|
||||
if (message) {
|
||||
let localProfile = user.profiles.find((profile) => profile.provider === 'local');
|
||||
|
||||
@@ -822,7 +854,6 @@ module.exports = class UsersService {
|
||||
lowercaseUsername: username.toLowerCase(),
|
||||
canEditName: false,
|
||||
status: 'PENDING',
|
||||
'suspensionDetails.mustChangeUsername': false,
|
||||
}
|
||||
})
|
||||
.then((result) => {
|
||||
|
||||
Reference in New Issue
Block a user