diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js
index b57bc799f..ceec3f1a9 100644
--- a/client/coral-embed-stream/src/Embed.js
+++ b/client/coral-embed-stream/src/Embed.js
@@ -11,6 +11,7 @@ const {fetchAssetSuccess} = assetActions;
import {queryStream} from 'coral-framework/graphql/queries';
import {postComment, postAction, deleteAction} from 'coral-framework/graphql/mutations';
+import {editName} from 'coral-framework/actions/user';
import {Notification, notificationActions, authActions, assetActions, pym} from 'coral-framework';
import Stream from './Stream';
@@ -123,6 +124,7 @@ class Embed extends Component {
}>
{
@@ -200,6 +202,7 @@ const mapDispatchToProps = dispatch => ({
loadAsset: (asset) => dispatch(fetchAssetSuccess(asset)),
addNotification: (type, text) => dispatch(addNotification(type, text)),
clearNotification: () => dispatch(clearNotification()),
+ editName: (displayName) => dispatch(editName(displayName)),
showSignInDialog: (offset) => dispatch(showSignInDialog(offset)),
logout: () => dispatch(logout()),
dispatch: d => dispatch(d)
diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js
index 4620262d3..42d2e0398 100644
--- a/client/coral-framework/actions/user.js
+++ b/client/coral-framework/actions/user.js
@@ -1,4 +1,3 @@
-import * as actions from '../constants/user';
import {addNotification} from '../actions/notification';
import coralApi from '../helpers/response';
@@ -6,16 +5,9 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from './../translations';
const lang = new I18n(translations);
-const saveBioRequest = () => ({type: actions.SAVE_BIO_REQUEST});
-const saveBioSuccess = settings => ({type: actions.SAVE_BIO_SUCCESS, settings});
-const saveBioFailure = error => ({type: actions.SAVE_BIO_FAILURE, error});
-
-export const saveBio = (user_id, formData) => dispatch => {
- dispatch(saveBioRequest());
- coralApi('/account/settings', {method: 'PUT', body: formData})
+export const editName = (displayName) => (dispatch) => {
+ return coralApi('/account/displayname', {method: 'PUT', body: {displayName}})
.then(() => {
- dispatch(addNotification('success', lang.t('successBioUpdate')));
- dispatch(saveBioSuccess(formData));
- })
- .catch(error => dispatch(saveBioFailure(error)));
+ dispatch(addNotification('success', lang.t('successNameUpdate')));
+ });
};
diff --git a/client/coral-framework/components/RestrictedContent.css b/client/coral-framework/components/RestrictedContent.css
index 47ced7b0b..ca5a6517c 100644
--- a/client/coral-framework/components/RestrictedContent.css
+++ b/client/coral-framework/components/RestrictedContent.css
@@ -2,3 +2,13 @@
background: #D8D8D8;
padding: 25px;
}
+
+.editNameInput {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.alert {
+ margin-top: 10px;
+ color: #B71C1C;
+}
diff --git a/client/coral-framework/components/SuspendedAccount.js b/client/coral-framework/components/SuspendedAccount.js
index e6c53d038..0c6347fca 100644
--- a/client/coral-framework/components/SuspendedAccount.js
+++ b/client/coral-framework/components/SuspendedAccount.js
@@ -1,15 +1,79 @@
-import React from 'react';
+import React, {Component, PropTypes} from 'react';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-framework/translations.json';
const lang = new I18n(translations);
import styles from './RestrictedContent.css';
+import {Button} from 'coral-ui';
+import validate from '../helpers/validate';
-export default ({canEditName}) => (
-
-
{
+class SuspendedAccount extends Component {
+
+ static propTypes = {
+ canEditName: PropTypes.bool,
+ editName: PropTypes.func.isRequired
+ }
+
+ state = {
+ displayName: '',
+ alert: ''
+ }
+
+ onSubmitClick = (e) => {
+ const {editName} = this.props;
+ const {displayName} = this.state;
+ e.preventDefault();
+ if (validate.displayName(displayName)) {
+ editName(displayName)
+ .then(() => window.reload())
+ .catch((error) => {
+ this.setState({alert: lang.t(`error.${error.message}`)});
+ });
+ } else {
+ this.setState({alert: lang.t('editName.error')});
+ }
+
+ }
+
+ render () {
+ const {canEditName} = this.props;
+ const {displayName, alert} = this.state;
+
+ return
+ {
+ canEditName ?
+ lang.t('editName.msg')
+ : lang.t('bannedAccountMsg')
+ }
+ {
canEditName ?
- lang.t('editNameMsg')
- : lang.t('bannedAccountMsg')
- }
-
-);
+
+
+ {alert}
+
+
+
this.setState({displayName: e.target.value})}
+ rows={3}/>
+
+
: null
+ }
+ ;
+ }
+}
+
+export default SuspendedAccount;
diff --git a/client/coral-framework/constants/user.js b/client/coral-framework/constants/user.js
index 9c6f508fe..a8e4585db 100644
--- a/client/coral-framework/constants/user.js
+++ b/client/coral-framework/constants/user.js
@@ -1,6 +1,6 @@
-export const SAVE_BIO_REQUEST = 'SAVE_BIO_REQUEST';
-export const SAVE_BIO_SUCCESS = 'SAVE_BIO_SUCCESS';
-export const SAVE_BIO_FAILURE = 'SAVE_BIO_FAILURE';
+export const EDIT_NAME_REQUEST = 'EDIT_NAME_REQUEST';
+export const EDIT_NAME_SUCCESS = 'EDIT_NAME_SUCCESS';
+export const EDIT_NAME_FAILURE = 'EDIT_NAME_FAILURE';
export const COMMENTS_BY_USER_REQUEST = 'COMMENTS_BY_USER_REQUEST';
export const COMMENTS_BY_USER_SUCCESS = 'COMMENTS_BY_USER_SUCCESS';
export const COMMENTS_BY_USER_FAILURE = 'COMMENTS_BY_USER_FAILURE';
diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json
index a61213aab..80e865f8e 100644
--- a/client/coral-framework/translations.json
+++ b/client/coral-framework/translations.json
@@ -1,10 +1,15 @@
{
"en": {
"successUpdateSettings": "The changes you have made have been applied to the comment stream on this article",
- "successBioUpdate": "Your Bio has been updated",
+ "successNameUpdate": "Your display name has been updated",
"contentNotAvailable": "This content is not available",
"bannedAccountMsg": "Your account is currently suspended. This means that you cannot Like, Flag, or write comments. Please contact moderator@fakeurl.com for more information",
- "editNameMsg": "Your account is currently suspended because your username has been deemed inappropriate. To restore your account, please enter a new username. You may contact moderator@fakeurl.com for more information.",
+ "editName": {
+ "msg": "Your account is currently suspended because your display name has been deemed inappropriate. To restore your account, please enter a new username. You may contact moderator@fakeurl.com for more information.",
+ "label": "New Display Name",
+ "button": "Submit",
+ "error": "Display names can contain letters, numbers and _ only"
+ },
"error": {
"email": "Not a valid E-Mail",
"password": "Password must be at least 8 characters",
diff --git a/graph/mutators/index.js b/graph/mutators/index.js
index b799cf83d..58d0ed62c 100644
--- a/graph/mutators/index.js
+++ b/graph/mutators/index.js
@@ -2,7 +2,6 @@ const _ = require('lodash');
const Comment = require('./comment');
const Action = require('./action');
-const User = require('./user');
module.exports = (context) => {
@@ -10,7 +9,6 @@ module.exports = (context) => {
return _.merge(...[
Comment,
Action,
- User,
].map((mutators) => {
// Each set of mutators is a function which takes the context.
diff --git a/graph/mutators/user.js b/graph/mutators/user.js
deleted file mode 100644
index 96049bbab..000000000
--- a/graph/mutators/user.js
+++ /dev/null
@@ -1,31 +0,0 @@
-const UsersService = require('../../services/users');
-
-/**
- * Updates a users settings.
- * @param {Object} user the user performing the request
- * @param {String} bio the new user bio
- * @return {Promise}
- */
-const updateUserSettings = ({user}, {bio}) => {
- return UsersService.updateSettings(user.id, {bio});
-};
-
-module.exports = (context) => {
-
- // TODO: refactor to something that'll return an error in the event an attempt
- // is made to mutate state while not logged in. There's got to be a better way
- // to do this.
- if (context.user && context.user.can('mutation:updateUserSettings')) {
- return {
- User: {
- updateSettings: (settings) => updateUserSettings(context, settings)
- }
- };
- }
-
- return {
- User: {
- updateSettings: () => {}
- }
- };
-};
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index 1c1470f58..b1f9b82f7 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -145,7 +145,7 @@ type Asset {
# The scraped title of the asset.
title: String
- # The URL that the asset is locaed on.
+ # The URL that the asset is located on.
url: String
# The top level comments that are attached to the asset.
diff --git a/models/user.js b/models/user.js
index 7de99d211..e5db70ea6 100644
--- a/models/user.js
+++ b/models/user.js
@@ -146,7 +146,7 @@ const USER_GRAPH_OPERATIONS = [
'mutation:createComment',
'mutation:createAction',
'mutation:deleteAction',
- 'mutation:updateUserSettings'
+ 'mutation:editName'
];
/**
diff --git a/routes/api/account/index.js b/routes/api/account/index.js
index 4dd6add7c..b08bdfc2e 100644
--- a/routes/api/account/index.js
+++ b/routes/api/account/index.js
@@ -117,11 +117,17 @@ router.put('/password/reset', (req, res, next) => {
router.put('/displayname', authorization.needed(), (req, res, next) => {
UsersService
- .editUsername(req.user.id, req.body.displayName)
+ .editName(req.user.id, req.body.displayName)
.then(() => {
res.status(204).end();
})
- .catch(next);
+ .catch(error => {
+ if (error.code === 11000) {
+ next(errors.ErrDisplayTaken);
+ } else {
+ next(error);
+ }
+ });
});
module.exports = router;
diff --git a/routes/api/users/index.js b/routes/api/users/index.js
index 8b22adb36..5d1783cfe 100644
--- a/routes/api/users/index.js
+++ b/routes/api/users/index.js
@@ -59,7 +59,7 @@ router.post('/:user_id/status', authorization.needed('ADMIN'), (req, res, next)
router.post('/:user_id/username-enable', authorization.needed('ADMIN'), (req, res, next) => {
UsersService
- .toggleUsernameEdit(req.params.user_id, true)
+ .toggleNameEdit(req.params.user_id, true)
.then(() => {
res.status(204).end();
})
diff --git a/services/users.js b/services/users.js
index 42278f1c4..ec0967c3b 100644
--- a/services/users.js
+++ b/services/users.js
@@ -619,7 +619,7 @@ module.exports = class UsersService {
* @param {Boolean} canEditName sets whether the user can edit their name.
* @return {Promise}
*/
- static toggleUsernameEdit(id, canEditName) {
+ static toggleNameEdit(id, canEditName) {
return UserModel.update({id}, {
$set: {canEditName}
});
@@ -631,14 +631,15 @@ module.exports = class UsersService {
* @param {String} displayName The new displayname for the user.
* @return {Promise}
*/
- static editUsername(id, displayName) {
+ static editName(id, displayName) {
return UserModel.findOne({id})
.then((user) => {
return user.canEditName ?
UserModel.update({id}, {
$set: {
displayName: displayName.toLowerCase(),
- canEditName: false
+ canEditName: false,
+ status: 'PENDING'
}
})
: Promise.reject(new Error('Display name editing disabled for this account.'));
diff --git a/test/services/users.js b/test/services/users.js
index ef027a317..5774d45ae 100644
--- a/test/services/users.js
+++ b/test/services/users.js
@@ -209,10 +209,10 @@ describe('services.UsersService', () => {
});
});
- describe('#toggleUsernameEdit', () => {
+ describe('#toggleNameEdit', () => {
it('should toggle the canEditName field', () => {
return UsersService
- .toggleUsernameEdit(mockUsers[0].id, true)
+ .toggleNameEdit(mockUsers[0].id, true)
.then(() => UsersService.findById(mockUsers[0].id))
.then((user) => {
expect(user).to.have.property('canEditName', true);
@@ -220,11 +220,11 @@ describe('services.UsersService', () => {
});
});
- describe('#editUsername', () => {
+ describe('#editName', () => {
it('should let the user edit their username if the proper toggle is set', () => {
return UsersService
- .toggleUsernameEdit(mockUsers[0].id, true)
- .then(() => UsersService.editUsername(mockUsers[0].id, 'Jojo'))
+ .toggleNameEdit(mockUsers[0].id, true)
+ .then(() => UsersService.editName(mockUsers[0].id, 'Jojo'))
.then(() => UsersService.findById(mockUsers[0].id))
.then((user) => {
expect(user).to.have.property('displayName', 'jojo');
@@ -234,7 +234,7 @@ describe('services.UsersService', () => {
it('should return an error if canEditName is false', (done) => {
UsersService
- .editUsername(mockUsers[0].id, 'Jojo')
+ .editName(mockUsers[0].id, 'Jojo')
.then(() => UsersService.findById(mockUsers[0].id))
.then(() => {
done(new Error('Error expected'));
@@ -247,8 +247,8 @@ describe('services.UsersService', () => {
it('should return an error if the username is already taken', (done) => {
UsersService
- .toggleUsernameEdit(mockUsers[0].id, true)
- .then(() => UsersService.editUsername(mockUsers[0].id, 'Marvel'))
+ .toggleNameEdit(mockUsers[0].id, true)
+ .then(() => UsersService.editName(mockUsers[0].id, 'Marvel'))
.then(() => UsersService.findById(mockUsers[0].id))
.then(() => {
done(new Error('Error expected'));