-
diff --git a/client/coral-sign-in/containers/ChangeDisplayNameContainer.js b/client/coral-sign-in/containers/ChangeDisplayNameContainer.js
new file mode 100644
index 000000000..bc7fe8267
--- /dev/null
+++ b/client/coral-sign-in/containers/ChangeDisplayNameContainer.js
@@ -0,0 +1,135 @@
+import React, {Component} from 'react';
+import {connect} from 'react-redux';
+
+import validate from 'coral-framework/helpers/validate';
+import errorMsj from 'coral-framework/helpers/error';
+
+import CreateDisplayNameDialog from '../components/CreateDisplayNameDialog';
+
+import I18n from 'coral-framework/modules/i18n/i18n';
+import translations from '../translations';
+const lang = new I18n(translations);
+
+import {
+ showCreateDisplayNameDialog,
+ hideCreateDisplayNameDialog,
+ invalidForm,
+ validForm,
+ createDisplayName
+} from '../../coral-framework/actions/auth';
+
+class ChangeDisplayNameContainer extends Component {
+ initialState = {
+ formData: {
+ displayName: '',
+ },
+ errors: {},
+ showErrors: false
+ };
+
+ constructor(props) {
+ super(props);
+ this.state = this.initialState;
+ this.handleChange = this.handleChange.bind(this);
+ this.handleSubmitDisplayName = this.handleSubmitDisplayName.bind(this);
+ this.handleClose = this.handleClose.bind(this);
+ this.addError = this.addError.bind(this);
+ }
+
+ handleChange(e) {
+ const {name, value} = e.target;
+ this.setState(state => ({
+ ...state,
+ formData: {
+ ...state.formData,
+ [name]: value
+ }
+ }), () => {
+ this.validation(name, value);
+ });
+ }
+
+ addError(name, error) {
+ return this.setState(state => ({
+ errors: {
+ ...state.errors,
+ [name]: error
+ }
+ }));
+ }
+
+ validation(name, value) {
+ const {addError} = this;
+
+ if (!value.length) {
+ addError(name, lang.t('createdisplay.requiredField'));
+ } else if (!validate[name](value)) {
+ addError(name, errorMsj[name]);
+ } else {
+ const { [name]: prop, ...errors } = this.state.errors; // eslint-disable-line
+ // Removes Error
+ this.setState(state => ({...state, errors}));
+ }
+ }
+
+ isCompleted() {
+ const {formData} = this.state;
+ return !Object.keys(formData).filter(prop => !formData[prop].length).length;
+ }
+
+ displayErrors(show = true) {
+ this.setState({showErrors: show});
+ }
+
+ handleSubmitDisplayName(e) {
+ e.preventDefault();
+ const {errors} = this.state;
+ const {validForm, invalidForm} = this.props;
+ this.displayErrors();
+ if (this.isCompleted() && !Object.keys(errors).length) {
+ this.props.createDisplayName(this.props.user.id, this.state.formData);
+ validForm();
+ } else {
+ invalidForm(lang.t('createdisplay.checkTheForm'));
+ }
+ }
+
+ handleClose() {
+ this.props.hideCreateDisplayNameDialog();
+ }
+
+ render() {
+ const {loggedIn, auth, offset} = this.props;
+ return (
+
+
+
+ );
+ }
+}
+
+const mapStateToProps = state => ({
+ auth: state.auth.toJS()
+});
+
+const mapDispatchToProps = dispatch => ({
+ createDisplayName: (userid, formData) => dispatch(createDisplayName(userid, formData)),
+ showCreateDisplayNameDialog: () => dispatch(showCreateDisplayNameDialog()),
+ hideCreateDisplayNameDialog: () => dispatch(hideCreateDisplayNameDialog()),
+ invalidForm: error => dispatch(invalidForm(error)),
+ validForm: () => dispatch(validForm())
+});
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(ChangeDisplayNameContainer);
diff --git a/client/coral-sign-in/containers/SignInContainer.js b/client/coral-sign-in/containers/SignInContainer.js
index f218f7fbb..c92fe024d 100644
--- a/client/coral-sign-in/containers/SignInContainer.js
+++ b/client/coral-sign-in/containers/SignInContainer.js
@@ -16,6 +16,7 @@ import {
showSignInDialog,
hideSignInDialog,
fetchSignInFacebook,
+ fetchSignUpFacebook,
fetchForgotPassword,
requestConfirmEmail,
facebookCallback,
@@ -184,6 +185,7 @@ const mapDispatchToProps = dispatch => ({
fetchSignUp: (formData, url) => dispatch(fetchSignUp(formData, url)),
fetchSignIn: formData => dispatch(fetchSignIn(formData)),
fetchSignInFacebook: () => dispatch(fetchSignInFacebook()),
+ fetchSignUpFacebook: () => dispatch(fetchSignUpFacebook()),
fetchForgotPassword: formData => dispatch(fetchForgotPassword(formData)),
requestConfirmEmail: (email, url) => dispatch(requestConfirmEmail(email, url)),
showSignInDialog: () => dispatch(showSignInDialog()),
diff --git a/client/coral-sign-in/translations.js b/client/coral-sign-in/translations.js
index 02d070c62..e8014014c 100644
--- a/client/coral-sign-in/translations.js
+++ b/client/coral-sign-in/translations.js
@@ -28,7 +28,17 @@ export default {
passwordsDontMatch: 'Passwords don\'t match.',
specialCharacters: 'Display names can contain letters, numbers and _ only',
checkTheForm: 'Invalid Form. Please, check the fields'
- }
+ },
+ 'createdisplay': {
+ writeyourusername: 'Write your username',
+ yourusername: 'Your username is publicly visible on all comments you post. A username is needed before you can post your first comment.',
+ displayName: 'Display Name',
+ save: 'Save',
+ requiredField: 'Required field',
+ errorCreate: 'Error when changing display name',
+ checkTheForm: 'Invalid Form. Please, check the fields',
+ specialCharacters: 'Display names can contain letters, numbers and _ only'
+ },
},
es: {
'signIn': {
@@ -59,6 +69,16 @@ export default {
passwordsDontMatch: 'Las contraseñas no coinciden',
specialCharacters: 'Los nombres pueden contener letras, números y _',
checkTheForm: 'Formulario Inválido. Por favor, completa los campos'
- }
+ },
+ 'createdisplay': {
+ writeyourusername: 'Escribe tu nombre',
+ yourusername: 'Tu nombre es visible publicamente en todos los comentarios que publiques. Es necesario tener un nombre de usuario antes de poder publicar tu primer comentario.',
+ displayName: 'Nombre a mostrar',
+ save: 'Guardar',
+ requiredField: 'Campo necesario',
+ errorCreate: 'Hubo un error al cambiar el nombre de usuario',
+ checkTheForm: 'Formulario Invalido. Por favor, verifica los campos',
+ specialCharacters: 'Sólo pueden contener letras, números y _'
+ },
}
};
diff --git a/routes/api/users/index.js b/routes/api/users/index.js
index 8db669618..f7cdea2be 100644
--- a/routes/api/users/index.js
+++ b/routes/api/users/index.js
@@ -60,6 +60,14 @@ router.post('/:user_id/status', authorization.needed('ADMIN'), (req, res, next)
.catch(next);
});
+router.post('/:user_id/displayname', authorization.needed(), (req, res, next) => {
+ UsersService.setDisplayName(req.params.user_id, req.body.displayName)
+ .then((user) => {
+ res.status(201).json(user);
+ })
+ .catch(next);
+});
+
router.post('/:user_id/email', authorization.needed('admin'), (req, res, next) => {
UsersService.findById(req.params.user_id)
.then(user => {
diff --git a/routes/index.js b/routes/index.js
index a1c18bbe2..add288b10 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -1,9 +1,11 @@
const express = require('express');
+const path = require('path');
const router = express.Router();
router.use('/api/v1', require('./api'));
router.use('/admin', require('./admin'));
router.use('/embed', require('./embed'));
+router.get('/embed.js', (req, res) => res.sendFile(path.join(__dirname, '../client/coral-embed/index.js')));
router.use('/assets', require('./assets'));
router.get('/', (req, res) => {
diff --git a/services/users.js b/services/users.js
index 70472bea7..fd39a50c1 100644
--- a/services/users.js
+++ b/services/users.js
@@ -353,6 +353,25 @@ module.exports = class UsersService {
return UserModel.update({id}, {$set: {status}});
}
+ /**
+ * Set the display name of a user.
+ * @param {String} id id of a user
+ * @param {String} displayName display name to set
+ * @param {Function} done callback after the operation is complete
+ */
+ static setDisplayName(id, displayName) {
+
+ return UsersService.isValidDisplayName(displayName)
+ .then(() => { // displayName is valid
+ return UserModel.update(
+ {id},
+ {$set: {'displayName': displayName}})
+ .then(() => {
+ return UserModel.findOne({'id': id});
+ });
+ });
+ }
+
/**
* Finds a user with the id.
* @param {String} id user id (uuid)
diff --git a/test/e2e/pages/embedStreamPage.js b/test/e2e/pages/embedStreamPage.js
index bdf2f9440..372b94b66 100644
--- a/test/e2e/pages/embedStreamPage.js
+++ b/test/e2e/pages/embedStreamPage.js
@@ -6,8 +6,8 @@ const embedStreamCommands = {
ready() {
return this
.waitForElementVisible('body', 4000)
- .waitForElementVisible('iframe#coralStreamIframe')
- .api.frame('coralStreamIframe');
+ .waitForElementVisible('#coralStreamEmbed > iframe')
+ .api.frame('coralStreamEmbed_iframe');
},
signUp(user) {
return this
diff --git a/test/e2e/tests/EmbedStreamTests.js b/test/e2e/tests/EmbedStreamTests.js
index 7343c3d0a..1b1fb40ab 100644
--- a/test/e2e/tests/EmbedStreamTests.js
+++ b/test/e2e/tests/EmbedStreamTests.js
@@ -26,7 +26,7 @@ module.exports = {
// Load Page
client.resizeWindow(1200, 800)
.url(client.globals.baseUrl)
- .frame('coralStreamIframe')
+ .frame('coralStreamEmbed_iframe')
// Register and Log In
.waitForElementVisible('#coralSignInButton', 2000)
@@ -65,7 +65,7 @@ module.exports = {
// Load Page
client.url(client.globals.baseUrl)
- .frame('coralStreamIframe');
+ .frame('coralStreamEmbed_iframe');
// Post a comment
client.waitForElementVisible('.coral-plugin-commentbox-button', 2000)
@@ -91,7 +91,7 @@ module.exports = {
// Load Page
client.resizeWindow(1200, 800)
.url(client.globals.baseUrl)
- .frame('coralStreamIframe');
+ .frame('coralStreamEmbed_iframe');
// Post a comment
client.waitForElementVisible('.coral-plugin-commentbox-button', 2000)
@@ -138,7 +138,7 @@ module.exports = {
// Load Page
client.resizeWindow(1200, 800)
.url(client.globals.baseUrl)
- .frame('coralStreamIframe');
+ .frame('coralStreamEmbed_iframe');
// Post a reply
client.waitForElementVisible('.coral-plugin-replies-reply-button', 5000)
@@ -161,7 +161,7 @@ module.exports = {
'Total comment count premod on': client => {
client.perform((client, done) => {
client.url(client.globals.baseUrl)
- .frame('coralStreamIframe');
+ .frame('coralStreamEmbed_iframe');
// Verify that comment count is correct
client.waitForElementVisible('.coral-plugin-comment-count-text', 2000)
diff --git a/test/services/users.js b/test/services/users.js
index e2e5655e4..ba2bedcc3 100644
--- a/test/services/users.js
+++ b/test/services/users.js
@@ -178,6 +178,25 @@ describe('services.UsersService', () => {
});
});
+ describe('#setDisplayName', () => {
+ it('should set the display name to a new unique one', () => {
+ return UsersService
+ .setDisplayName(mockUsers[0].id, 'maria')
+ .then(() => UsersService.findById(mockUsers[0].id))
+ .then((user) => {
+ expect(user).to.have.property('displayName', 'maria');
+ });
+ });
+
+ it('should return an error when the displayName is not unique', () => {
+ return UsersService
+ .setDisplayName(mockUsers[0].id, 'marvel')
+ .catch((error) => {
+ expect(error).to.not.be.null;
+ });
+ });
+ });
+
describe('#ban', () => {
it('should set the status to banned', () => {
return UsersService
diff --git a/views/article.ejs b/views/article.ejs
index 7382f6c35..72fc69ce3 100644
--- a/views/article.ejs
+++ b/views/article.ejs
@@ -23,72 +23,12 @@
<%= body %>
Admin - All Assets
+
-
-
-