From e803593fc7fb204b2a2bcd5a598e38772662f259 Mon Sep 17 00:00:00 2001 From: gaba Date: Mon, 30 Jan 2017 11:34:04 -0800 Subject: [PATCH 01/31] Move stuff around. --- client/coral-embed-stream/src/Embed.js | 11 +- client/coral-framework/actions/auth.js | 14 ++ .../components/CreateDisplayNameDialog.js | 44 ++++++ client/coral-framework/components/styles.css | 140 ++++++++++++++++++ client/coral-framework/constants/auth.js | 3 + .../containers/ChangeDisplayNameContainer.js | 140 ++++++++++++++++++ client/coral-framework/reducers/auth.js | 8 + client/coral-framework/translations.json | 15 ++ services/users.js | 12 ++ 9 files changed, 378 insertions(+), 9 deletions(-) create mode 100644 client/coral-framework/components/CreateDisplayNameDialog.js create mode 100644 client/coral-framework/components/styles.css create mode 100644 client/coral-framework/containers/ChangeDisplayNameContainer.js diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index 5922266fa..b6fee1184 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -19,6 +19,7 @@ import Count from 'coral-plugin-comment-count/CommentCount'; import CommentBox from 'coral-plugin-commentbox/CommentBox'; import UserBox from '../../coral-sign-in/components/UserBox'; import SignInContainer from '../../coral-sign-in/containers/SignInContainer'; +import ChangeDisplayNameContainer from '../../coral-framework/containers/ChangeDisplayNameContainer'; import SuspendedAccount from '../../coral-framework/components/SuspendedAccount'; import SettingsContainer from '../../coral-settings/containers/SettingsContainer'; import RestrictedContent from '../../coral-framework/components/RestrictedContent'; @@ -51,15 +52,6 @@ class Embed extends Component { } componentDidMount () { - - // stream id, logged in user, settings - - // Set up messaging between embedded Iframe an parent component - - // this.props.getStream(path || window.location); - // this.path = window.location.href.split('#')[0]; - // - pym.sendMessage('childReady'); pym.onMessage('DOMContentLoaded', hash => { @@ -138,6 +130,7 @@ class Embed extends Component { :

{asset.settings.closedMessage}

} {!loggedIn && } + {loggedIn && } ({type: actions.SHOW_SIGNIN_DIALOG, offset}); export const hideSignInDialog = () => ({type: actions.HIDE_SIGNIN_DIALOG}); +export const showCreateDisplayNameDialog = () => ({type: actions.SHOW_CREATEDISPLAYNAME_DIALOG}); +export const hideCreateDisplayNameDialog = () => ({type: actions.HIDE_CREATEDISPLAYNAME_DIALOG}); +export const createDisplayName = (formData) => (dispatch) => { + coralApi('/users', {method: 'POST', body: formData}) + .then(() => { + dispatch(createDisplayNameSuccess()); + dispatch(hideCreateDisplayNameDialog()); + }) + .catch(() => dispatch(createDisplayNameFailure(lang.t('createdisplay.errorCreate')))); +}; +const createDisplayNameSuccess = () => ({type: actions.CREATEDISPLAYNAME_SUCCESS}); +const createDisplayNameFailure = error => ({type: actions.CREATEDISPLAYNAME_FAILURE, error}); + export const changeView = view => dispatch => dispatch({ type: actions.CHANGE_VIEW, @@ -59,6 +72,7 @@ export const facebookCallback = (err, data) => dispatch => { const user = JSON.parse(data); dispatch(signInFacebookSuccess(user)); dispatch(hideSignInDialog()); + dispatch(showCreateDisplayNameDialog()); dispatch(addItem(user, 'users')); } catch (err) { dispatch(signInFacebookFailure(err)); diff --git a/client/coral-framework/components/CreateDisplayNameDialog.js b/client/coral-framework/components/CreateDisplayNameDialog.js new file mode 100644 index 000000000..8b61989aa --- /dev/null +++ b/client/coral-framework/components/CreateDisplayNameDialog.js @@ -0,0 +1,44 @@ +import React from 'react'; +import {Dialog} from 'coral-ui'; +import styles from './styles.css'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from '../translations'; +const lang = new I18n(translations); + +const CreateDisplayNameDialog = ({open, handleClose, offset, loggedIn}) => ( + + × +
+
+

+ {lang.t('createdisplay.writeyourusername')} +

+ Logged {loggedIn} +
+
+
+); + +//
+// +//
+// +//
+// + +export default CreateDisplayNameDialog; diff --git a/client/coral-framework/components/styles.css b/client/coral-framework/components/styles.css new file mode 100644 index 000000000..f9d0aec6d --- /dev/null +++ b/client/coral-framework/components/styles.css @@ -0,0 +1,140 @@ +.dialog { + border: none; + box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2); + width: 280px; + top: 10px; +} + +.header { + margin-bottom: 20px; +} + +.header h1, .separator h1{ + text-align: center; + font-size: 1.2em; +} + +.formField { + margin-top: 15px; +} + +.formField label { + font-size: 1.08em; + font-weight: bold; + margin-bottom: 5px; +} + +.formField input { + width: 100%; + display: block; + border: none; + outline: none; + border: 1px solid rgba(0,0,0,.12); + padding: 10px 6px; + box-sizing: border-box; + border-radius: 2px; + margin: 5px auto; +} + +.footer { + margin: 20px auto 10px; + text-align: center; +} + +.footer span { + display: block; + margin-bottom: 5px; +} + +.footer a { + color: #2c69b6; + cursor: pointer; + margin: 0 5px; +} + +.socialConnections { + margin-bottom: 20px; +} + +.signInButton { + margin-top: 10px; +} + +.close { + font-size: 20px; + line-height: 14px; + top: 10px; + right: 10px; + position: absolute; + display: block; + font-weight: bold; + color: #363636; + cursor: pointer; +} + +.close:hover { + color: #6b6b6b; +} + +input.error{ + border: solid 2px #f44336; +} + +.errorMsg, .hint { + color: grey; + font-weight: 600; + padding: 3px 0 16px; +} + +.alert { + padding: 10px; + margin-bottom: 20px; + border-radius: 2px; +} + +.alert--success { + border: solid 1px #1ec00e; + background: #cbf1b8; + color: #006900; +} + +.alert--error { + background: #FFEBEE; + color: #B71C1C; +} + +.userBox { + padding: 10px 0 20px; + letter-spacing: 0.1px; +} + +.userBox a { + color: black; + font-weight: bold; + cursor: pointer; + margin: 0px; + margin-left: 4px; + padding-bottom: 2px; + border-bottom: solid 1px black; +} + +.attention { + display: inline-block; + width: 15px; + height: 15px; + background: #B71C1C; + color: #FFEBEE; + font-weight: bolder; + padding: 4px; + vertical-align: middle; + border-radius: 20px; + box-sizing: border-box; + font-size: 9px; + line-height: 7px; + text-align: center; + margin-right: 5px; +} + +.action { + margin-top: 15px; +} diff --git a/client/coral-framework/constants/auth.js b/client/coral-framework/constants/auth.js index 5742adf75..27fa8890f 100644 --- a/client/coral-framework/constants/auth.js +++ b/client/coral-framework/constants/auth.js @@ -4,6 +4,9 @@ export const CLEAN_STATE = 'CLEAN_STATE'; export const SHOW_SIGNIN_DIALOG = 'SHOW_SIGNIN_DIALOG'; export const HIDE_SIGNIN_DIALOG = 'HIDE_SIGNIN_DIALOG'; +export const SHOW_CREATEDISPLAYNAME_DIALOG = 'SHOW_CREATEDISPLAYNAME_DIALOG'; +export const HIDE_CREATEDISPLAYNAME_DIALOG = 'HIDE_CREATEDISPLAYNAME_DIALOG'; + export const FETCH_SIGNUP_REQUEST = 'FETCH_SIGNUP_REQUEST'; export const FETCH_SIGNUP_FAILURE = 'FETCH_SIGNUP_FAILURE'; export const FETCH_SIGNUP_SUCCESS = 'FETCH_SIGNUP_SUCCESS'; diff --git a/client/coral-framework/containers/ChangeDisplayNameContainer.js b/client/coral-framework/containers/ChangeDisplayNameContainer.js new file mode 100644 index 000000000..17e681388 --- /dev/null +++ b/client/coral-framework/containers/ChangeDisplayNameContainer.js @@ -0,0 +1,140 @@ +import React, {Component} from 'react'; +import {connect} from 'react-redux'; + +import CreateDisplayNameDialog from '../components/CreateDisplayNameDialog'; + +// import validate from 'coral-framework/helpers/validate'; +// import errorMsj from 'coral-framework/helpers/error'; +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.handleSubmitForm = this.handleSubmitForm.bind(this); + this.handleClose = this.handleClose.bind(this); + this.addError = this.addError.bind(this); + } + + componentDidMount() { + const {formData} = this.state; + const errors = Object.keys(formData).reduce((map, prop) => { + map[prop] = lang.t('createdisplay.requiredField'); + return map; + }, {}); + this.setState({errors}); + } + + 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('displayName.requiredField')); + } 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}); + } + + handleSubmitForm(e) { + e.preventDefault(); + const {errors} = this.state; + const {validForm, invalidForm} = this.props; + this.displayErrors(); + if (this.isCompleted() && !Object.keys(errors).length) { + createDisplayName(this.state.formData); + validForm(); + } else { + invalidForm(lang.t('signIn.checkTheForm')); + } + } + + handleClose() { + this.props.hideCreateDisplayNameDialog(); + } + + render() { + const {loggedIn, auth, offset, user} = this.props; + return ( +
+ +
+ ); + } +} + +const mapStateToProps = state => ({ + auth: state.auth.toJS() +}); + +const mapDispatchToProps = dispatch => ({ + createDisplayName: formData => dispatch(createDisplayName(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-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index 36f8e0764..ff0f72b4a 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -7,6 +7,7 @@ const initialState = Map({ isAdmin: false, user: null, showSignInDialog: false, + showCreateDisplayNameDialog: false, view: 'SIGNIN', error: '', passwordRequestSuccess: null, @@ -35,6 +36,13 @@ export default function auth (state = initialState, action) { passwordRequestSuccess: null, successSignUp: false })); + case actions.SHOW_CREATEDISPLAYNAME_DIALOG : + return state + .set('showCreateDisplayNameDialog', true); + case actions.HIDE_CREATEDISPLAYNAME_DIALOG : + return state.merge(Map({ + showCreateDisplayNameDialog: false + })); case actions.CHANGE_VIEW : return state .set('error', '') diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json index 5bf9b40a9..5900aad3e 100644 --- a/client/coral-framework/translations.json +++ b/client/coral-framework/translations.json @@ -4,6 +4,14 @@ "successBioUpdate": "Your Bio has been updated", "contentNotAvailable": "This content is not available", "suspendedAccountMsg": "Your account is currently suspended. This means that you cannot Like, Flag, or write comments. Please contact moderator@fakeurl.com for more information", + "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" + }, "error": { "email": "Not a valid E-Mail", "password": "Password must be at least 8 characters", @@ -26,6 +34,13 @@ "successBioUpdate": "Tu bio fue actualizada", "contentNotAvailable": "El contenido no se encuentra disponible", "suspendedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios. Por favor, contacta moderator@fakeurl for more information", + "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", + "save": "Guardar", + "requiredField": "Campo necesario" + }, "error": { "email": "No es un email válido", "password": "La contraseña debe tener por lo menos 8 caracteres", diff --git a/services/users.js b/services/users.js index 0bf22e9e0..d1e7dce05 100644 --- a/services/users.js +++ b/services/users.js @@ -156,6 +156,18 @@ module.exports = class UsersService { }); } + static changeDisplayName(id, displayName) { + return UsersService.isValidDisplayName(displayName) + .then((displayName) => { // displayName is valid + return UserModel.update({id}, { + $inc: {__v: 1}, + $set: { + displayName: displayName + } + }); + }); + } + /** * Creates local users. * @param {Array} users Users to create From 80122dfdceb3cd190f1c3de82391efc8d4a9fc0f Mon Sep 17 00:00:00 2001 From: gaba Date: Tue, 31 Jan 2017 13:48:45 -0800 Subject: [PATCH 02/31] Adds formfield to components. --- client/coral-framework/components/FormField.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 client/coral-framework/components/FormField.js diff --git a/client/coral-framework/components/FormField.js b/client/coral-framework/components/FormField.js new file mode 100644 index 000000000..ceb2d939e --- /dev/null +++ b/client/coral-framework/components/FormField.js @@ -0,0 +1,18 @@ +import React from 'react'; +import styles from './styles.css'; + +const FormField = ({className, showErrors = false, errorMsg, label, ...props}) => ( +
+ + + {showErrors && errorMsg && !{errorMsg}} +
+); + +export default FormField; From a377f61bddf12e8ac4c905560eadf419951ab190 Mon Sep 17 00:00:00 2001 From: gaba Date: Tue, 31 Jan 2017 13:51:36 -0800 Subject: [PATCH 03/31] Adds service and endpoint to change displayname. --- routes/api/users/index.js | 9 +++++++++ services/users.js | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/routes/api/users/index.js b/routes/api/users/index.js index 3ea3f3213..50e09aba7 100644 --- a/routes/api/users/index.js +++ b/routes/api/users/index.js @@ -57,6 +57,15 @@ 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((status) => { + res.status(201).json(status); + }) + .catch(next); +}); + // /** // * SendEmailConfirmation sends a confirmation email to the user. // * @param {Request} req express request object diff --git a/services/users.js b/services/users.js index d1e7dce05..bc46c5e62 100644 --- a/services/users.js +++ b/services/users.js @@ -358,6 +358,24 @@ 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) { + + // Check to see if that the displayName is not empty. + if (displayName.length < 1) { + + // User displayName is required! Error out here. + return Promise.reject(new Error('display name should not be empty')); + } + + return UserModel.update({id}, {$set: {'displayName': displayName}}); + } + /** * Finds a user with the id. * @param {String} id user id (uuid) From 37eae516416edd9cf51554fbaba535baee5cc095 Mon Sep 17 00:00:00 2001 From: gaba Date: Tue, 31 Jan 2017 13:52:40 -0800 Subject: [PATCH 04/31] Adds component to coral-framework to deal with the change of displayname. --- client/coral-framework/actions/auth.js | 18 +++++++--- client/coral-framework/actions/items.js | 1 - .../components/CreateDisplayNameDialog.js | 33 +++++++++---------- client/coral-framework/constants/auth.js | 6 ++++ client/coral-framework/constants/user.js | 1 + .../containers/ChangeDisplayNameContainer.js | 18 +++++----- client/coral-framework/reducers/auth.js | 11 +++++++ client/coral-framework/translations.json | 8 +++-- 8 files changed, 63 insertions(+), 33 deletions(-) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index ee6f6a817..f99450901 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -3,22 +3,32 @@ import translations from './../translations'; const lang = new I18n(translations); import * as actions from '../constants/auth'; import coralApi, {base} from '../helpers/response'; -import {addItem} from './items'; +import {addItem, updateItem} from './items'; // Dialog Actions export const showSignInDialog = (offset = 0) => ({type: actions.SHOW_SIGNIN_DIALOG, offset}); export const hideSignInDialog = () => ({type: actions.HIDE_SIGNIN_DIALOG}); +export const createDisplayNameRequest = () => ({type: actions.CREATE_DISPLAYNAME_REQUEST}); export const showCreateDisplayNameDialog = () => ({type: actions.SHOW_CREATEDISPLAYNAME_DIALOG}); export const hideCreateDisplayNameDialog = () => ({type: actions.HIDE_CREATEDISPLAYNAME_DIALOG}); -export const createDisplayName = (formData) => (dispatch) => { - coralApi('/users', {method: 'POST', body: formData}) + +export const updateDisplayName = displayName => ({type: actions.UPDATE_DISPLAYNAME, displayName}); + +export const createDisplayName = (userId, formData) => dispatch => { + dispatch(createDisplayNameRequest()); + coralApi(`/users/${userId}/displayname`, {method: 'POST', body: formData}) .then(() => { dispatch(createDisplayNameSuccess()); dispatch(hideCreateDisplayNameDialog()); + dispatch(updateItem(userId, 'displayName', formData.displayName, 'users')); + dispatch(updateDisplayName(formData.displayName)); }) - .catch(() => dispatch(createDisplayNameFailure(lang.t('createdisplay.errorCreate')))); + .catch(() => { + dispatch(createDisplayNameFailure(lang.t('createdisplay.errorCreate'))); + }); }; + const createDisplayNameSuccess = () => ({type: actions.CREATEDISPLAYNAME_SUCCESS}); const createDisplayNameFailure = error => ({type: actions.CREATEDISPLAYNAME_FAILURE, error}); diff --git a/client/coral-framework/actions/items.js b/client/coral-framework/actions/items.js index e289e9f60..692ec5648 100644 --- a/client/coral-framework/actions/items.js +++ b/client/coral-framework/actions/items.js @@ -203,7 +203,6 @@ export function postItem (item, type, id, mutate) { parent_id: null } }).then(({data}) => { - console.log('it workt'); console.log(data); }); diff --git a/client/coral-framework/components/CreateDisplayNameDialog.js b/client/coral-framework/components/CreateDisplayNameDialog.js index 8b61989aa..02c6eae4d 100644 --- a/client/coral-framework/components/CreateDisplayNameDialog.js +++ b/client/coral-framework/components/CreateDisplayNameDialog.js @@ -1,11 +1,13 @@ import React from 'react'; +import FormField from './FormField'; +import Button from 'coral-ui/components/Button'; import {Dialog} from 'coral-ui'; import styles from './styles.css'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; const lang = new I18n(translations); -const CreateDisplayNameDialog = ({open, handleClose, offset, loggedIn}) => ( +const CreateDisplayNameDialog = ({open, handleClose, offset, formData, handleSubmitDisplayName, handleChange}) => ( (

{lang.t('createdisplay.writeyourusername')}

- Logged {loggedIn} + +
+

{lang.t('createdisplay.yourusername')}

+
+ + +
); -//
-// -//
-// -//
-// - export default CreateDisplayNameDialog; diff --git a/client/coral-framework/constants/auth.js b/client/coral-framework/constants/auth.js index 27fa8890f..a61975420 100644 --- a/client/coral-framework/constants/auth.js +++ b/client/coral-framework/constants/auth.js @@ -4,6 +4,10 @@ export const CLEAN_STATE = 'CLEAN_STATE'; export const SHOW_SIGNIN_DIALOG = 'SHOW_SIGNIN_DIALOG'; export const HIDE_SIGNIN_DIALOG = 'HIDE_SIGNIN_DIALOG'; +export const CREATE_DISPLAYNAME_REQUEST = 'CREATE_DISPLAYNAME_REQUEST'; +export const CREATEDISPLAYNAME_SUCCESS = 'CREATEDISPLAYNAME_SUCCESS'; +export const CREATEDISPLAYNAME_FAILURE = 'CREATEDISPLAYNAME_FAILURE'; +export const CREATE_DISPLAYNAME = 'CREATE_DISPLAYNAME'; export const SHOW_CREATEDISPLAYNAME_DIALOG = 'SHOW_CREATEDISPLAYNAME_DIALOG'; export const HIDE_CREATEDISPLAYNAME_DIALOG = 'HIDE_CREATEDISPLAYNAME_DIALOG'; @@ -35,3 +39,5 @@ export const CHECK_LOGIN_SUCCESS = 'CHECK_LOGIN_SUCCESS'; export const CHECK_LOGIN_FAILURE = 'CHECK_LOGIN_FAILURE'; export const CHECK_CSRF_TOKEN = 'CHECK_CSRF_TOKEN'; + +export const UPDATE_DISPLAYNAME = 'UPDATE_DISPLAYNAME'; diff --git a/client/coral-framework/constants/user.js b/client/coral-framework/constants/user.js index 9c6f508fe..57f4e706b 100644 --- a/client/coral-framework/constants/user.js +++ b/client/coral-framework/constants/user.js @@ -5,3 +5,4 @@ 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'; export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; +export const UPDATE_DISPLAYNAME = 'UPDATE_DISPLAYNAME'; diff --git a/client/coral-framework/containers/ChangeDisplayNameContainer.js b/client/coral-framework/containers/ChangeDisplayNameContainer.js index 17e681388..e252119de 100644 --- a/client/coral-framework/containers/ChangeDisplayNameContainer.js +++ b/client/coral-framework/containers/ChangeDisplayNameContainer.js @@ -30,12 +30,13 @@ class ChangeDisplayNameContainer extends Component { super(props); this.state = this.initialState; this.handleChange = this.handleChange.bind(this); - this.handleSubmitForm = this.handleSubmitForm.bind(this); + this.handleSubmitDisplayName = this.handleSubmitDisplayName.bind(this); this.handleClose = this.handleClose.bind(this); this.addError = this.addError.bind(this); } componentDidMount() { + window.authCallback = this.props.facebookCallback; const {formData} = this.state; const errors = Object.keys(formData).reduce((map, prop) => { map[prop] = lang.t('createdisplay.requiredField'); @@ -70,7 +71,7 @@ class ChangeDisplayNameContainer extends Component { const {addError} = this; if (!value.length) { - addError(name, lang.t('displayName.requiredField')); + addError(name, lang.t('createdisplay.requiredField')); } else { const { [name]: prop, ...errors } = this.state.errors; // eslint-disable-line // Removes Error @@ -87,16 +88,16 @@ class ChangeDisplayNameContainer extends Component { this.setState({showErrors: show}); } - handleSubmitForm(e) { + handleSubmitDisplayName(e) { e.preventDefault(); const {errors} = this.state; const {validForm, invalidForm} = this.props; this.displayErrors(); if (this.isCompleted() && !Object.keys(errors).length) { - createDisplayName(this.state.formData); + this.props.createDisplayName(this.props.user.id, this.state.formData); validForm(); } else { - invalidForm(lang.t('signIn.checkTheForm')); + invalidForm(lang.t('createdisplay.checkTheForm')); } } @@ -105,15 +106,16 @@ class ChangeDisplayNameContainer extends Component { } render() { - const {loggedIn, auth, offset, user} = this.props; + const {loggedIn, auth, offset} = this.props; return (
@@ -127,7 +129,7 @@ const mapStateToProps = state => ({ }); const mapDispatchToProps = dispatch => ({ - createDisplayName: formData => dispatch(createDisplayName(formData)), + createDisplayName: (userid, formData) => dispatch(createDisplayName(userid, formData)), showCreateDisplayNameDialog: () => dispatch(showCreateDisplayNameDialog()), hideCreateDisplayNameDialog: () => dispatch(hideCreateDisplayNameDialog()), invalidForm: error => dispatch(invalidForm(error)), diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index ff0f72b4a..cb7b102df 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -43,6 +43,14 @@ export default function auth (state = initialState, action) { return state.merge(Map({ showCreateDisplayNameDialog: false })); + case actions.CREATEDISPLAYNAME_SUCCESS : + return state.merge(Map({ + showCreateDisplayNameDialog: false, + error: '' + })); + case actions.CREATEDISPLAYNAME_FAILURE : + return state + .set('error', action.error); case actions.CHANGE_VIEW : return state .set('error', '') @@ -109,6 +117,9 @@ export default function auth (state = initialState, action) { return state .set('passwordRequestFailure', 'There was an error sending your password reset email. Please try again soon!') .set('passwordRequestSuccess', null); + case actions.UPDATE_DISPLAYNAME: + return state + .merge({'user': {'displayName': action.displayName}}); default : return state; } diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json index 5900aad3e..0e191d89d 100644 --- a/client/coral-framework/translations.json +++ b/client/coral-framework/translations.json @@ -10,7 +10,8 @@ "displayName": "Display Name", "save": "Save", "requiredField": "Required field", - "errorCreate": "Error when changing display name" + "errorCreate": "Error when changing display name", + "checkTheForm": "Invalid Form. Please, check the fields" }, "error": { "email": "Not a valid E-Mail", @@ -37,9 +38,10 @@ "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", + "displayName": "Nombre a mostrar", "save": "Guardar", - "requiredField": "Campo necesario" + "requiredField": "Campo necesario", + "checkTheForm": "Formulario Invalido. Por favor, verifica los campos" }, "error": { "email": "No es un email válido", From 282c7dc4f5ff861e46fa29173b8d00e1ce2632bd Mon Sep 17 00:00:00 2001 From: gaba Date: Tue, 31 Jan 2017 14:04:31 -0800 Subject: [PATCH 05/31] Removes duplicate func and adds tests. --- client/coral-embed-stream/src/Embed.js | 4 ++-- services/users.js | 26 ++++++-------------------- test/services/users.js | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index b6fee1184..1eec07213 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -98,7 +98,7 @@ class Embed extends Component { Settings Configure Stream - {loggedIn && } + {loggedIn && user && } { asset.closedAt === null @@ -130,7 +130,7 @@ class Embed extends Component { :

{asset.settings.closedMessage}

} {!loggedIn && } - {loggedIn && } + {loggedIn && user && } { // displayName is valid - return UserModel.update({id}, { - $inc: {__v: 1}, - $set: { - displayName: displayName - } - }); - }); - } - /** * Creates local users. * @param {Array} users Users to create @@ -366,14 +354,12 @@ module.exports = class UsersService { */ static setDisplayName(id, displayName) { - // Check to see if that the displayName is not empty. - if (displayName.length < 1) { - - // User displayName is required! Error out here. - return Promise.reject(new Error('display name should not be empty')); - } - - return UserModel.update({id}, {$set: {'displayName': displayName}}); + return UsersService.isValidDisplayName(displayName) + .then((displayName) => { // displayName is valid + return UserModel.update( + {id}, + {$set: {'displayName': displayName}}); + }); } /** 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 From c9b12fc5d649750b745690fcc53b079a45b1214b Mon Sep 17 00:00:00 2001 From: gaba Date: Wed, 1 Feb 2017 10:46:32 -0800 Subject: [PATCH 06/31] Thanks git-godesses for the log on finding a bad remove when merging. --- client/coral-framework/actions/auth.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index ab8a7caea..b0574e04c 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -91,6 +91,7 @@ export const facebookCallback = (err, data) => dispatch => { const user = JSON.parse(data); dispatch(signInFacebookSuccess(user)); dispatch(hideSignInDialog()); + dispatch(showCreateDisplayNameDialog()); } catch (err) { dispatch(signInFacebookFailure(err)); return; From a8e4ce466d26b8eef1735b4fae3336fca58e1019 Mon Sep 17 00:00:00 2001 From: gaba Date: Wed, 1 Feb 2017 12:09:27 -0800 Subject: [PATCH 07/31] Adds validation to the dialog. --- client/coral-framework/components/Alert.js | 13 +++++++++++++ .../components/CreateDisplayNameDialog.js | 4 +++- .../containers/ChangeDisplayNameContainer.js | 7 +++++-- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 client/coral-framework/components/Alert.js diff --git a/client/coral-framework/components/Alert.js b/client/coral-framework/components/Alert.js new file mode 100644 index 000000000..019c3456a --- /dev/null +++ b/client/coral-framework/components/Alert.js @@ -0,0 +1,13 @@ +import React from 'react'; +import styles from './styles.css'; + +const Alert = ({cStyle = 'error', children, className, ...props}) => ( +
+ {children} +
+); + +export default Alert; diff --git a/client/coral-framework/components/CreateDisplayNameDialog.js b/client/coral-framework/components/CreateDisplayNameDialog.js index 02c6eae4d..8b4a99f9a 100644 --- a/client/coral-framework/components/CreateDisplayNameDialog.js +++ b/client/coral-framework/components/CreateDisplayNameDialog.js @@ -1,5 +1,6 @@ import React from 'react'; import FormField from './FormField'; +import Alert from './Alert'; import Button from 'coral-ui/components/Button'; import {Dialog} from 'coral-ui'; import styles from './styles.css'; @@ -7,7 +8,7 @@ import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; const lang = new I18n(translations); -const CreateDisplayNameDialog = ({open, handleClose, offset, formData, handleSubmitDisplayName, handleChange}) => ( +const CreateDisplayNameDialog = ({open, handleClose, offset, formData, handleSubmitDisplayName, handleChange, ...props}) => (

{lang.t('createdisplay.yourusername')}

+ { props.auth.error && {props.auth.error} }
Date: Wed, 1 Feb 2017 12:34:02 -0800 Subject: [PATCH 08/31] Adds errors on field when not validated. --- .../coral-framework/components/CreateDisplayNameDialog.js | 1 + .../containers/ChangeDisplayNameContainer.js | 6 ------ client/coral-framework/translations.json | 7 +++++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/client/coral-framework/components/CreateDisplayNameDialog.js b/client/coral-framework/components/CreateDisplayNameDialog.js index 8b4a99f9a..6798bad5c 100644 --- a/client/coral-framework/components/CreateDisplayNameDialog.js +++ b/client/coral-framework/components/CreateDisplayNameDialog.js @@ -28,6 +28,7 @@ const CreateDisplayNameDialog = ({open, handleClose, offset, formData, handleSub

{lang.t('createdisplay.yourusername')}

{ props.auth.error && {props.auth.error} } + { props.errors.displayName && {lang.t('createdisplay.specialCharacters')} } { - map[prop] = lang.t('createdisplay.requiredField'); - return map; - }, {}); - this.setState({errors}); } handleChange(e) { diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json index c08c7e717..88886f8d1 100644 --- a/client/coral-framework/translations.json +++ b/client/coral-framework/translations.json @@ -11,7 +11,8 @@ "save": "Save", "requiredField": "Required field", "errorCreate": "Error when changing display name", - "checkTheForm": "Invalid Form. Please, check the fields" + "checkTheForm": "Invalid Form. Please, check the fields", + "specialCharacters": "Display names can contain letters, numbers and _ only" }, "error": { "emailNotVerified": "Email address {0} not verified.", @@ -42,7 +43,9 @@ "displayName": "Nombre a mostrar", "save": "Guardar", "requiredField": "Campo necesario", - "checkTheForm": "Formulario Invalido. Por favor, verifica los campos" + "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 _" }, "error": { "emailNotVerified": "Dirección de correo electrónico {0} no verificada.", From 0c360e62a64d8b5a98c80ae033ffd6c47ae25a22 Mon Sep 17 00:00:00 2001 From: gaba Date: Wed, 1 Feb 2017 13:20:17 -0800 Subject: [PATCH 09/31] Display errors that come from the backend. --- client/coral-framework/actions/auth.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index b0574e04c..bd3b81fdf 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -22,8 +22,8 @@ export const createDisplayName = (userId, formData) => dispatch => { dispatch(hideCreateDisplayNameDialog()); dispatch(updateDisplayName(formData.displayName)); }) - .catch(() => { - dispatch(createDisplayNameFailure(lang.t('createdisplay.errorCreate'))); + .catch(error => { + dispatch(createDisplayNameFailure(lang.t(`error.${error.message}`))); }); }; From 4df09ffbd2728b978cfdb875db833a7cf7d184fb Mon Sep 17 00:00:00 2001 From: gaba Date: Wed, 1 Feb 2017 13:29:47 -0800 Subject: [PATCH 10/31] Validation for the field underneath. --- .../coral-framework/components/CreateDisplayNameDialog.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/coral-framework/components/CreateDisplayNameDialog.js b/client/coral-framework/components/CreateDisplayNameDialog.js index 6798bad5c..ee3e133e6 100644 --- a/client/coral-framework/components/CreateDisplayNameDialog.js +++ b/client/coral-framework/components/CreateDisplayNameDialog.js @@ -28,7 +28,6 @@ const CreateDisplayNameDialog = ({open, handleClose, offset, formData, handleSub

{lang.t('createdisplay.yourusername')}

{ props.auth.error && {props.auth.error} } - { props.errors.displayName && {lang.t('createdisplay.specialCharacters')} } - - + { props.errors.displayName && {lang.t('createdisplay.specialCharacters')} } +
+ +
+
From 445eb97d7647dd25fa35e01980d1234e2c8a4679 Mon Sep 17 00:00:00 2001 From: gaba Date: Wed, 1 Feb 2017 14:00:09 -0800 Subject: [PATCH 11/31] Moves create displayname dialog to sign-in plugin. --- client/coral-embed-stream/src/Embed.js | 2 +- client/coral-framework/actions/auth.js | 6 +- client/coral-framework/components/Alert.js | 13 -- .../coral-framework/components/FormField.js | 18 --- client/coral-framework/components/styles.css | 140 ------------------ client/coral-framework/translations.json | 20 --- .../components/CreateDisplayNameDialog.js | 6 +- .../containers/ChangeDisplayNameContainer.js | 0 client/coral-sign-in/translations.js | 24 ++- 9 files changed, 29 insertions(+), 200 deletions(-) delete mode 100644 client/coral-framework/components/Alert.js delete mode 100644 client/coral-framework/components/FormField.js delete mode 100644 client/coral-framework/components/styles.css rename client/{coral-framework => coral-sign-in}/components/CreateDisplayNameDialog.js (84%) rename client/{coral-framework => coral-sign-in}/containers/ChangeDisplayNameContainer.js (100%) diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index a5011f853..43b4eecae 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -19,7 +19,7 @@ import Count from 'coral-plugin-comment-count/CommentCount'; import CommentBox from 'coral-plugin-commentbox/CommentBox'; import UserBox from '../../coral-sign-in/components/UserBox'; import SignInContainer from '../../coral-sign-in/containers/SignInContainer'; -import ChangeDisplayNameContainer from '../../coral-framework/containers/ChangeDisplayNameContainer'; +import ChangeDisplayNameContainer from '../../coral-sign-in/containers/ChangeDisplayNameContainer'; import SuspendedAccount from '../../coral-framework/components/SuspendedAccount'; import SettingsContainer from '../../coral-settings/containers/SettingsContainer'; import RestrictedContent from '../../coral-framework/components/RestrictedContent'; diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index bd3b81fdf..914b88095 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -12,6 +12,9 @@ export const createDisplayNameRequest = () => ({type: actions.CREATE_DISPLAYNAME export const showCreateDisplayNameDialog = () => ({type: actions.SHOW_CREATEDISPLAYNAME_DIALOG}); export const hideCreateDisplayNameDialog = () => ({type: actions.HIDE_CREATEDISPLAYNAME_DIALOG}); +const createDisplayNameSuccess = () => ({type: actions.CREATEDISPLAYNAME_SUCCESS}); +const createDisplayNameFailure = error => ({type: actions.CREATEDISPLAYNAME_FAILURE, error}); + export const updateDisplayName = displayName => ({type: actions.UPDATE_DISPLAYNAME, displayName}); export const createDisplayName = (userId, formData) => dispatch => { @@ -27,9 +30,6 @@ export const createDisplayName = (userId, formData) => dispatch => { }); }; -const createDisplayNameSuccess = () => ({type: actions.CREATEDISPLAYNAME_SUCCESS}); -const createDisplayNameFailure = error => ({type: actions.CREATEDISPLAYNAME_FAILURE, error}); - export const changeView = view => dispatch => dispatch({ type: actions.CHANGE_VIEW, diff --git a/client/coral-framework/components/Alert.js b/client/coral-framework/components/Alert.js deleted file mode 100644 index 019c3456a..000000000 --- a/client/coral-framework/components/Alert.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import styles from './styles.css'; - -const Alert = ({cStyle = 'error', children, className, ...props}) => ( -
- {children} -
-); - -export default Alert; diff --git a/client/coral-framework/components/FormField.js b/client/coral-framework/components/FormField.js deleted file mode 100644 index ceb2d939e..000000000 --- a/client/coral-framework/components/FormField.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import styles from './styles.css'; - -const FormField = ({className, showErrors = false, errorMsg, label, ...props}) => ( -
- - - {showErrors && errorMsg && !{errorMsg}} -
-); - -export default FormField; diff --git a/client/coral-framework/components/styles.css b/client/coral-framework/components/styles.css deleted file mode 100644 index f9d0aec6d..000000000 --- a/client/coral-framework/components/styles.css +++ /dev/null @@ -1,140 +0,0 @@ -.dialog { - border: none; - box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2); - width: 280px; - top: 10px; -} - -.header { - margin-bottom: 20px; -} - -.header h1, .separator h1{ - text-align: center; - font-size: 1.2em; -} - -.formField { - margin-top: 15px; -} - -.formField label { - font-size: 1.08em; - font-weight: bold; - margin-bottom: 5px; -} - -.formField input { - width: 100%; - display: block; - border: none; - outline: none; - border: 1px solid rgba(0,0,0,.12); - padding: 10px 6px; - box-sizing: border-box; - border-radius: 2px; - margin: 5px auto; -} - -.footer { - margin: 20px auto 10px; - text-align: center; -} - -.footer span { - display: block; - margin-bottom: 5px; -} - -.footer a { - color: #2c69b6; - cursor: pointer; - margin: 0 5px; -} - -.socialConnections { - margin-bottom: 20px; -} - -.signInButton { - margin-top: 10px; -} - -.close { - font-size: 20px; - line-height: 14px; - top: 10px; - right: 10px; - position: absolute; - display: block; - font-weight: bold; - color: #363636; - cursor: pointer; -} - -.close:hover { - color: #6b6b6b; -} - -input.error{ - border: solid 2px #f44336; -} - -.errorMsg, .hint { - color: grey; - font-weight: 600; - padding: 3px 0 16px; -} - -.alert { - padding: 10px; - margin-bottom: 20px; - border-radius: 2px; -} - -.alert--success { - border: solid 1px #1ec00e; - background: #cbf1b8; - color: #006900; -} - -.alert--error { - background: #FFEBEE; - color: #B71C1C; -} - -.userBox { - padding: 10px 0 20px; - letter-spacing: 0.1px; -} - -.userBox a { - color: black; - font-weight: bold; - cursor: pointer; - margin: 0px; - margin-left: 4px; - padding-bottom: 2px; - border-bottom: solid 1px black; -} - -.attention { - display: inline-block; - width: 15px; - height: 15px; - background: #B71C1C; - color: #FFEBEE; - font-weight: bolder; - padding: 4px; - vertical-align: middle; - border-radius: 20px; - box-sizing: border-box; - font-size: 9px; - line-height: 7px; - text-align: center; - margin-right: 5px; -} - -.action { - margin-top: 15px; -} diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json index 88886f8d1..07b719f6b 100644 --- a/client/coral-framework/translations.json +++ b/client/coral-framework/translations.json @@ -4,16 +4,6 @@ "successBioUpdate": "Your Bio has been updated", "contentNotAvailable": "This content is not available", "suspendedAccountMsg": "Your account is currently suspended. This means that you cannot Like, Flag, or write comments. Please contact moderator@fakeurl.com for more information", - "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" - }, "error": { "emailNotVerified": "Email address {0} not verified.", "email": "Not a valid E-Mail", @@ -37,16 +27,6 @@ "successBioUpdate": "Tu bio fue actualizada", "contentNotAvailable": "El contenido no se encuentra disponible", "suspendedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios. Por favor, contacta moderator@fakeurl for more information", - "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 _" - }, "error": { "emailNotVerified": "Dirección de correo electrónico {0} no verificada.", "email": "No es un email válido", diff --git a/client/coral-framework/components/CreateDisplayNameDialog.js b/client/coral-sign-in/components/CreateDisplayNameDialog.js similarity index 84% rename from client/coral-framework/components/CreateDisplayNameDialog.js rename to client/coral-sign-in/components/CreateDisplayNameDialog.js index ee3e133e6..4d8ef9fb3 100644 --- a/client/coral-framework/components/CreateDisplayNameDialog.js +++ b/client/coral-sign-in/components/CreateDisplayNameDialog.js @@ -1,5 +1,5 @@ import React from 'react'; -import FormField from './FormField'; +import FormField from 'coral-ui/components/FormField'; import Alert from './Alert'; import Button from 'coral-ui/components/Button'; import {Dialog} from 'coral-ui'; @@ -25,7 +25,7 @@ const CreateDisplayNameDialog = ({open, handleClose, offset, formData, handleSub
-

{lang.t('createdisplay.yourusername')}

+ { props.auth.error && {props.auth.error} }
- { props.errors.displayName && {lang.t('createdisplay.specialCharacters')} } + { props.errors.displayName && {lang.t('createdisplay.specialCharacters')} }
diff --git a/client/coral-framework/containers/ChangeDisplayNameContainer.js b/client/coral-sign-in/containers/ChangeDisplayNameContainer.js similarity index 100% rename from client/coral-framework/containers/ChangeDisplayNameContainer.js rename to client/coral-sign-in/containers/ChangeDisplayNameContainer.js diff --git a/client/coral-sign-in/translations.js b/client/coral-sign-in/translations.js index 137141d34..5751370a6 100644 --- a/client/coral-sign-in/translations.js +++ b/client/coral-sign-in/translations.js @@ -26,7 +26,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': { @@ -55,6 +65,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 _' + }, } }; From c272b8834023dd9ebcdc3cec5f0367944a322e81 Mon Sep 17 00:00:00 2001 From: gaba Date: Thu, 2 Feb 2017 15:24:55 -0800 Subject: [PATCH 12/31] It returns user now. --- routes/api/users/index.js | 4 ++-- services/users.js | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/routes/api/users/index.js b/routes/api/users/index.js index a7164dd7d..1c7bc7bb2 100644 --- a/routes/api/users/index.js +++ b/routes/api/users/index.js @@ -62,8 +62,8 @@ router.post('/:user_id/status', authorization.needed('ADMIN'), (req, res, next) router.post('/:user_id/displayname', authorization.needed(), (req, res, next) => { UsersService.setDisplayName(req.params.user_id, req.body.displayName) - .then(() => { - res.status(204).end(); + .then((user) => { + res.status(201).json(user); }) .catch(next); }); diff --git a/services/users.js b/services/users.js index c1a31bd45..1876abb7a 100644 --- a/services/users.js +++ b/services/users.js @@ -365,7 +365,10 @@ module.exports = class UsersService { .then((displayName) => { // displayName is valid return UserModel.update( {id}, - {$set: {'displayName': displayName}}); + {$set: {'displayName': displayName}}) + .then(() => { + return UserModel.findOne({'id': id}); + }); }); } From d74d2e9e87b64f5051b129e2153fb3dff15f7e5d Mon Sep 17 00:00:00 2001 From: gaba Date: Thu, 2 Feb 2017 15:25:45 -0800 Subject: [PATCH 13/31] Dispatchs the user. --- client/coral-framework/actions/auth.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 914b88095..d21870ac5 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -20,10 +20,10 @@ export const updateDisplayName = displayName => ({type: actions.UPDATE_DISPLAYNA export const createDisplayName = (userId, formData) => dispatch => { dispatch(createDisplayNameRequest()); coralApi(`/users/${userId}/displayname`, {method: 'POST', body: formData}) - .then(() => { + .then((user) => { dispatch(createDisplayNameSuccess()); dispatch(hideCreateDisplayNameDialog()); - dispatch(updateDisplayName(formData.displayName)); + dispatch(updateDisplayName(user)); }) .catch(error => { dispatch(createDisplayNameFailure(lang.t(`error.${error.message}`))); From 7da141ed29ab569cd683b4220cff0fcde4189cad Mon Sep 17 00:00:00 2001 From: gaba Date: Thu, 2 Feb 2017 15:44:45 -0800 Subject: [PATCH 14/31] Dispatch other action to only trigger change when sign up. --- client/coral-framework/actions/auth.js | 13 +++++++++++++ client/coral-framework/constants/auth.js | 1 + client/coral-framework/reducers/auth.js | 11 +++++++++-- client/coral-sign-in/components/SignUpContent.js | 2 +- .../containers/ChangeDisplayNameContainer.js | 6 +----- client/coral-sign-in/containers/SignInContainer.js | 2 ++ 6 files changed, 27 insertions(+), 8 deletions(-) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index d21870ac5..edd04de12 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -82,6 +82,19 @@ export const fetchSignInFacebook = () => dispatch => { ); }; +// Sign Up Facebook + +const signUpFacebookRequest = () => ({type: actions.FETCH_SIGNUP_FACEBOOK_REQUEST}); + +export const fetchSignUpFacebook = () => dispatch => { + dispatch(signUpFacebookRequest()); + window.open( + `${base}/auth/facebook`, + 'Continue with Facebook', + 'menubar=0,resizable=0,width=500,height=500,top=200,left=500' + ); +}; + export const facebookCallback = (err, data) => dispatch => { if (err) { signInFacebookFailure(err); diff --git a/client/coral-framework/constants/auth.js b/client/coral-framework/constants/auth.js index 651c82cd1..2b5f762c1 100644 --- a/client/coral-framework/constants/auth.js +++ b/client/coral-framework/constants/auth.js @@ -23,6 +23,7 @@ export const FETCH_SIGNIN_FACEBOOK_REQUEST = 'FETCH_SIGNIN_FACEBOOK_REQUEST'; export const FETCH_SIGNIN_FACEBOOK_FAILURE = 'FETCH_SIGNIN_FACEBOOK_FAILURE'; export const FETCH_SIGNIN_FACEBOOK_SUCCESS = 'FETCH_SIGNIN_FACEBOOK_SUCCESS'; +export const FETCH_SIGNUP_FACEBOOK_REQUEST = 'FETCH_SIGNUP_FACEBOOK_REQUEST'; export const FETCH_FORGOT_PASSWORD_REQUEST = 'FETCH_FORGOT_PASSWORD_REQUEST'; export const FETCH_FORGOT_PASSWORD_SUCCESS = 'FETCH_FORGOT_PASSWORD_SUCCESS'; export const FETCH_FORGOT_PASSWORD_FAILURE = 'FETCH_FORGOT_PASSWORD_FAILURE'; diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index dfbaede90..2da589507 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -15,7 +15,8 @@ const initialState = Map({ emailConfirmationFailure: false, emailConfirmationLoading: false, emailConfirmationSuccess: false, - successSignUp: false + successSignUp: false, + fromSignUp: false }); const purge = user => { @@ -88,6 +89,12 @@ export default function auth (state = initialState, action) { .set('isLoading', false) .set('error', action.error) .set('user', null); + case actions.FETCH_SIGNUP_FACEBOOK_REQUEST: + return state + .set('fromSignUp', true); + case actions.FETCH_SIGNIN_FACEBOOK_REQUEST: + return state + .set('fromSignUp', false); case actions.FETCH_SIGNIN_FACEBOOK_SUCCESS: return state .set('user', purge(action.user)) @@ -125,7 +132,7 @@ export default function auth (state = initialState, action) { .set('passwordRequestSuccess', null); case actions.UPDATE_DISPLAYNAME: return state - .merge({'user': {'displayName': action.displayName}}); + .set('user', purge(action.displayName)); case actions.EMAIL_CONFIRM_ERROR: return state .set('emailConfirmationFailure', true) diff --git a/client/coral-sign-in/components/SignUpContent.js b/client/coral-sign-in/components/SignUpContent.js index 246d9d884..80a6fcde4 100644 --- a/client/coral-sign-in/components/SignUpContent.js +++ b/client/coral-sign-in/components/SignUpContent.js @@ -14,7 +14,7 @@ const SignUpContent = ({handleChange, formData, ...props}) => (
-
diff --git a/client/coral-sign-in/containers/ChangeDisplayNameContainer.js b/client/coral-sign-in/containers/ChangeDisplayNameContainer.js index b1dcde6a6..bc7fe8267 100644 --- a/client/coral-sign-in/containers/ChangeDisplayNameContainer.js +++ b/client/coral-sign-in/containers/ChangeDisplayNameContainer.js @@ -36,10 +36,6 @@ class ChangeDisplayNameContainer extends Component { this.addError = this.addError.bind(this); } - componentDidMount() { - window.authCallback = this.props.facebookCallback; - } - handleChange(e) { const {name, value} = e.target; this.setState(state => ({ @@ -107,7 +103,7 @@ class ChangeDisplayNameContainer extends Component { return (
({ fetchSignUp: formData => dispatch(fetchSignUp(formData)), fetchSignIn: formData => dispatch(fetchSignIn(formData)), fetchSignInFacebook: () => dispatch(fetchSignInFacebook()), + fetchSignUpFacebook: () => dispatch(fetchSignUpFacebook()), fetchForgotPassword: formData => dispatch(fetchForgotPassword(formData)), requestConfirmEmail: email => dispatch(requestConfirmEmail(email)), showSignInDialog: () => dispatch(showSignInDialog()), From b5c9c1c73143a6e219fee80386d423b12d0fcda2 Mon Sep 17 00:00:00 2001 From: gaba Date: Thu, 2 Feb 2017 16:03:14 -0800 Subject: [PATCH 15/31] Returns the real displayname and not the one transformed. --- services/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/users.js b/services/users.js index 1876abb7a..fd39a50c1 100644 --- a/services/users.js +++ b/services/users.js @@ -362,7 +362,7 @@ module.exports = class UsersService { static setDisplayName(id, displayName) { return UsersService.isValidDisplayName(displayName) - .then((displayName) => { // displayName is valid + .then(() => { // displayName is valid return UserModel.update( {id}, {$set: {'displayName': displayName}}) From f7cddad6593e8ce9d7593c66bc9de348b5f48ee2 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Sat, 4 Feb 2017 00:47:32 +0800 Subject: [PATCH 16/31] Futureproof Embed Code * Add client/coral-embed-api module. It's meant to be included in an HTML page and define window.Coral * Serve coral-embed-api at /Coral.js * Update / handler (article view) to use new embed format * Update Talk Admin embed code section to use new embed format --- app.js | 1 + .../src/containers/Configure/EmbedLink.js | 20 ++- client/coral-embed-api/index.js | 164 ++++++++++++++++++ views/article.ejs | 72 +------- 4 files changed, 189 insertions(+), 68 deletions(-) create mode 100644 client/coral-embed-api/index.js diff --git a/app.js b/app.js index c04e0cb4a..38ac3c003 100644 --- a/app.js +++ b/app.js @@ -34,6 +34,7 @@ app.use(helmet({ })); app.use(bodyParser.json()); app.use('/client', express.static(path.join(__dirname, 'dist'))); +app.get('/Coral.js', (req, res) => res.sendFile(path.join(__dirname, 'client/coral-embed-api/index.js'))); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); diff --git a/client/coral-admin/src/containers/Configure/EmbedLink.js b/client/coral-admin/src/containers/Configure/EmbedLink.js index 105b531c5..32024bde2 100644 --- a/client/coral-admin/src/containers/Configure/EmbedLink.js +++ b/client/coral-admin/src/containers/Configure/EmbedLink.js @@ -25,8 +25,24 @@ class EmbedLink extends Component { } render () { - const embedText = `
`; - + const location = window.location; + const talkBaseUrl = [ + location.protocol, + '//', + location.hostname, + location.port ? (`:${ window.location.port}`) : '' + ].join(''); + const coralJsUrl = [talkBaseUrl, '/Coral.js'].join(''); + const nonce = String(Math.random()).slice(2); + const streamElementId = `coral_talk_${nonce}`; + const embedText = ` +
+ + `.trim(); return (

{this.props.title}

diff --git a/client/coral-embed-api/index.js b/client/coral-embed-api/index.js new file mode 100644 index 000000000..efc60ee75 --- /dev/null +++ b/client/coral-embed-api/index.js @@ -0,0 +1,164 @@ +/* eslint-disable no-var */ +/** + * @file Exposes the main 'window.Coral.Talk' API that developers can use to + * render Talk streams in their webpages + * @todo Currently implemented to be included directly via - - - From e376e19c2cf80aaf0c150b314a47af2071509636 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Sat, 4 Feb 2017 01:00:46 +0800 Subject: [PATCH 17/31] remove console.log from coral-embed-api --- client/coral-embed-api/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/coral-embed-api/index.js b/client/coral-embed-api/index.js index efc60ee75..1eccc7ffa 100644 --- a/client/coral-embed-api/index.js +++ b/client/coral-embed-api/index.js @@ -42,7 +42,6 @@ * stream will replies to this asset */ Talk.render = function (el, opts) { - console.log('Coral.Talk.render()', el, opts); if ( ! el) { throw new Error('Please provide Coral.Talk.render() the HTMLElement you want to render Talk in.'); } From fb60b088b380be94ef31abfc4c34ad3eb8cb0781 Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Sat, 4 Feb 2017 01:15:35 +0800 Subject: [PATCH 18/31] Rename coral-embd-api to coral-embed --- app.js | 2 +- client/{coral-embed-api => coral-embed}/index.js | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename client/{coral-embed-api => coral-embed}/index.js (100%) diff --git a/app.js b/app.js index 38ac3c003..2b2b656ae 100644 --- a/app.js +++ b/app.js @@ -34,7 +34,7 @@ app.use(helmet({ })); app.use(bodyParser.json()); app.use('/client', express.static(path.join(__dirname, 'dist'))); -app.get('/Coral.js', (req, res) => res.sendFile(path.join(__dirname, 'client/coral-embed-api/index.js'))); +app.get('/Coral.js', (req, res) => res.sendFile(path.join(__dirname, 'client/coral-embed/index.js'))); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); diff --git a/client/coral-embed-api/index.js b/client/coral-embed/index.js similarity index 100% rename from client/coral-embed-api/index.js rename to client/coral-embed/index.js From a6f7cf258b7efdf0515c4468212431d28946f572 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Fri, 3 Feb 2017 10:15:51 -0700 Subject: [PATCH 19/31] attempt to make e2e tests pass --- client/coral-sign-in/components/SignUpContent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-sign-in/components/SignUpContent.js b/client/coral-sign-in/components/SignUpContent.js index 80a6fcde4..2739c27a7 100644 --- a/client/coral-sign-in/components/SignUpContent.js +++ b/client/coral-sign-in/components/SignUpContent.js @@ -77,7 +77,7 @@ const SignUpContent = ({handleChange, formData, ...props}) => (
{lang.t('signIn.alreadyHaveAnAccount')} - props.changeView('SIGNIN')}> + props.changeView('SIGNIN')}> {lang.t('signIn.signIn')} From c4b934a542afbeb8e115f33624394fb9dce79b9c Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Fri, 3 Feb 2017 10:19:18 -0700 Subject: [PATCH 20/31] whoops. commited to the wrong branch --- client/coral-sign-in/components/SignUpContent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-sign-in/components/SignUpContent.js b/client/coral-sign-in/components/SignUpContent.js index 2739c27a7..80a6fcde4 100644 --- a/client/coral-sign-in/components/SignUpContent.js +++ b/client/coral-sign-in/components/SignUpContent.js @@ -77,7 +77,7 @@ const SignUpContent = ({handleChange, formData, ...props}) => (
{lang.t('signIn.alreadyHaveAnAccount')} - props.changeView('SIGNIN')}> + props.changeView('SIGNIN')}> {lang.t('signIn.signIn')} From f1010462ec16a3f36375a15e6415dda59e0bc5ba Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Sat, 4 Feb 2017 01:19:47 +0800 Subject: [PATCH 21/31] Dont' use const/let in coral-embed so it works in all browsers --- client/coral-embed/index.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/client/coral-embed/index.js b/client/coral-embed/index.js index 1eccc7ffa..eeef3103f 100644 --- a/client/coral-embed/index.js +++ b/client/coral-embed/index.js @@ -28,9 +28,9 @@ }(this, function () { // This function should return value of window.Coral - let pym = requirePym(); - let Coral = {}; - let Talk = Coral.Talk = {}; + var pym = requirePym(); + var Coral = {}; + var Talk = Coral.Talk = {}; /** * Render a Talk stream @@ -57,8 +57,8 @@ // ensure el has an id, as pym can't directly accept the HTMLElement if ( ! el.id) {el.id = `_${ String(Math.random())}`;} - let asset = opts.asset || window.location; - let pymParent = new pym.Parent( + var asset = opts.asset || window.location; + var pymParent = new pym.Parent( el.id, buildStreamIframeUrl(opts.talk, asset), { @@ -76,7 +76,7 @@ // build the URL to load in the pym iframe function buildStreamIframeUrl(talkBaseUrl, asset) { - let iframeUrl = [ + var iframeUrl = [ talkBaseUrl, (talkBaseUrl.match(/\/$/) ? '' : '/'), // make sure no double-'/' if opts.talk already ends with '/' 'embed/stream?asset_url=', @@ -88,8 +88,8 @@ // Set up postMessage listeners/handlers on the pymParent // e.g. to resize the iframe, and navigate the host page function configurePymParent(pymParent, assetUrl) { - let notificationOffset = 200; - let ready = false; + var notificationOffset = 200; + var ready = false; // Resize parent iframe height when child height changes pymParent.onMessage('height', function(height) { @@ -101,7 +101,7 @@ // Helps child show notifications at the right scrollTop pymParent.onMessage('getPosition', function() { - let position = viewport().height + document.body.scrollTop; + var position = viewport().height + document.body.scrollTop; if (position > notificationOffset) { position = position - notificationOffset; @@ -135,7 +135,7 @@ // get dimensions of viewport function viewport() { - let e = window, a = 'inner'; + var e = window, a = 'inner'; if ( !( 'innerWidth' in window ) ){ a = 'client'; e = document.documentElement || document.body; @@ -146,7 +146,7 @@ // return a reference to pym.js function requirePym() { - let pym; + var pym; // fake AMD `define` so that the pym.js copypasta doesn't create a global function define(createPym) { @@ -156,7 +156,7 @@ /* eslint-disable */ /* ! pym.js - v1.1.2 - 2016-10-25 */ - !function(a){typeof define == 'function' && define.amd ? define(a) : typeof module != 'undefined' && module.exports ? module.exports = a() : window.pym = a.call(this);}(function(){let a = 'xPYMx', b = {}, c = function(a){let b = new RegExp(`[\\?&]${ a.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')}=([^&#]*)`), c = b.exec(location.search);return c === null ? '' : decodeURIComponent(c[1].replace(/\+/g, ' '));}, d = function(a, b){if(b.xdomain === '*' || a.origin.match(new RegExp(`${b.xdomain}$`))){return!0;}}, e = function(b, c, d){let e = ['pym', b, c, d];return e.join(a);}, f = function(b){let c = ['pym', b, '(\\S+)', '(.*)'];return new RegExp(`^${ c.join(a)}$`);}, g = function(){for(var a = b.autoInitInstances.length, c = a - 1;c >= 0;c--){let d = b.autoInitInstances[c];d.el.getElementsByTagName('iframe').length && d.el.getElementsByTagName('iframe')[0].contentWindow || b.autoInitInstances.splice(c, 1);}};return b.autoInitInstances = [], b.autoInit = function(){let a = document.querySelectorAll('[data-pym-src]:not([data-pym-auto-initialized])'), c = a.length;g();for(var d = 0;d < c;++d){let e = a[d];e.setAttribute('data-pym-auto-initialized', ''), e.id === '' && (e.id = `pym-${d}`);let f = e.getAttribute('data-pym-src'), h = {xdomain:'string', title:'string', name:'string', id:'string', sandbox:'string', allowfullscreen:'boolean'}, i = {};for(let j in h){if(e.getAttribute(`data-pym-${j}`) !== null){switch(h[j]){case'boolean':i[j] = !(e.getAttribute(`data-pym-${j}`) === 'false');break;case'string':i[j] = e.getAttribute(`data-pym-${j}`);break;default:console.err('unrecognized attribute type');}}}let k = new b.Parent(e.id, f, i);b.autoInitInstances.push(k);}return b.autoInitInstances;}, b.Parent = function(a, b, c){this.id = a, this.url = b, this.el = document.getElementById(a), this.iframe = null, this.settings = {xdomain:'*'}, this.messageRegex = f(this.id), this.messageHandlers = {}, c = c || {}, this._constructIframe = function(){let a = this.el.offsetWidth.toString();this.iframe = document.createElement('iframe');let b = '', c = this.url.indexOf('#');for(c > -1 && (b = this.url.substring(c, this.url.length), this.url = this.url.substring(0, c)), this.url.indexOf('?') < 0 ? this.url += '?' : this.url += '&', this.iframe.src = `${this.url}initialWidth=${a}&childId=${this.id}&parentTitle=${encodeURIComponent(document.title)}&parentUrl=${encodeURIComponent(window.location.href)}${b}`, this.iframe.setAttribute('width', '100%'), this.iframe.setAttribute('scrolling', 'no'), this.iframe.setAttribute('marginheight', '0'), this.iframe.setAttribute('frameborder', '0'), this.settings.title && this.iframe.setAttribute('title', this.settings.title), void 0 !== this.settings.allowfullscreen && this.settings.allowfullscreen !== !1 && this.iframe.setAttribute('allowfullscreen', ''), void 0 !== this.settings.sandbox && typeof this.settings.sandbox == 'string' && this.iframe.setAttribute('sandbox', this.settings.sandbox), this.settings.id && (document.getElementById(this.settings.id) || this.iframe.setAttribute('id', this.settings.id)), this.settings.name && this.iframe.setAttribute('name', this.settings.name);this.el.firstChild;){this.el.removeChild(this.el.firstChild);}this.el.appendChild(this.iframe), window.addEventListener('resize', this._onResize);}, this._onResize = function(){this.sendWidth();}.bind(this), this._fire = function(a, b){if(a in this.messageHandlers){for(var c = 0;c < this.messageHandlers[a].length;c++){this.messageHandlers[a][c].call(this, b);}}}, this.remove = function(){window.removeEventListener('message', this._processMessage), window.removeEventListener('resize', this._onResize), this.el.removeChild(this.iframe), g();}, this._processMessage = function(a){if(d(a, this.settings) && typeof a.data == 'string'){let b = a.data.match(this.messageRegex);if(!b || b.length !== 3){return!1;}let c = b[1], e = b[2];this._fire(c, e);}}.bind(this), this._onHeightMessage = function(a){let b = parseInt(a);this.iframe.setAttribute('height', `${b}px`);}, this._onNavigateToMessage = function(a){document.location.href = a;}, this._onScrollToChildPosMessage = function(a){let b = document.getElementById(this.id).getBoundingClientRect().top + window.pageYOffset, c = b + parseInt(a);window.scrollTo(0, c);}, this.onMessage = function(a, b){a in this.messageHandlers || (this.messageHandlers[a] = []), this.messageHandlers[a].push(b);}, this.sendMessage = function(a, b){this.el.getElementsByTagName('iframe').length && (this.el.getElementsByTagName('iframe')[0].contentWindow ? this.el.getElementsByTagName('iframe')[0].contentWindow.postMessage(e(this.id, a, b), '*') : this.remove());}, this.sendWidth = function(){let a = this.el.offsetWidth.toString();this.sendMessage('width', a);};for(let h in c){this.settings[h] = c[h];}return this.onMessage('height', this._onHeightMessage), this.onMessage('navigateTo', this._onNavigateToMessage), this.onMessage('scrollToChildPos', this._onScrollToChildPosMessage), window.addEventListener('message', this._processMessage, !1), this._constructIframe(), this;}, b.Child = function(b){this.parentWidth = null, this.id = null, this.parentTitle = null, this.parentUrl = null, this.settings = {renderCallback:null, xdomain:'*', polling:0}, this.timerId = null, this.messageRegex = null, this.messageHandlers = {}, b = b || {}, this.onMessage = function(a, b){a in this.messageHandlers || (this.messageHandlers[a] = []), this.messageHandlers[a].push(b);}, this._fire = function(a, b){if(a in this.messageHandlers){for(var c = 0;c < this.messageHandlers[a].length;c++){this.messageHandlers[a][c].call(this, b);}}}, this._processMessage = function(a){if(d(a, this.settings) && typeof a.data == 'string'){let b = a.data.match(this.messageRegex);if(b && b.length === 3){let c = b[1], e = b[2];this._fire(c, e);}}}.bind(this), this._onWidthMessage = function(a){let b = parseInt(a);b !== this.parentWidth && (this.parentWidth = b, this.settings.renderCallback && this.settings.renderCallback(b), this.sendHeight());}, this.sendMessage = function(a, b){window.parent.postMessage(e(this.id, a, b), '*');}, this.sendHeight = function(){let a = document.getElementsByTagName('body')[0].offsetHeight.toString();return this.sendMessage('height', a), a;}.bind(this), this.scrollParentTo = function(a){this.sendMessage('navigateTo', `#${a}`);}, this.navigateParentTo = function(a){this.sendMessage('navigateTo', a);}, this.scrollParentToChildEl = function(a){let b = document.getElementById(a).getBoundingClientRect().top + window.pageYOffset;this.scrollParentToChildPos(b);}, this.scrollParentToChildPos = function(a){this.sendMessage('scrollToChildPos', a.toString());}, this._markWhetherEmbedded = function(a){let b, c = document.getElementsByTagName('html')[0], d = c.className;try{b = window.self !== window.top ? 'embedded' : 'not-embedded';}catch(a){b = 'embedded';}d.indexOf(b) < 0 && (c.className = d ? `${d} ${b}` : b, a && a(b));}, this.remove = function(){window.removeEventListener('message', this._processMessage), this.timerId && clearInterval(this.timerId);}, this.id = c('childId') || b.id, this.messageRegex = new RegExp(`^pym${ a }${this.id }${a }(\\S+)${ a}(.*)$`);let f = parseInt(c('initialWidth'));this.parentUrl = c('parentUrl'), this.parentTitle = c('parentTitle'), this.onMessage('width', this._onWidthMessage);for(let g in b){this.settings[g] = b[g];}return window.addEventListener('message', this._processMessage, !1), this.settings.renderCallback && this.settings.renderCallback(f), this.sendHeight(), this.settings.polling && (this.timerId = window.setInterval(this.sendHeight, this.settings.polling)), this._markWhetherEmbedded(b.onMarkedEmbeddedStatus), this;}, b.autoInit(), b;}); + !function(a){typeof define == 'function' && define.amd ? define(a) : typeof module != 'undefined' && module.exports ? module.exports = a() : window.pym = a.call(this);}(function(){var a = 'xPYMx', b = {}, c = function(a){var b = new RegExp(`[\\?&]${ a.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')}=([^&#]*)`), c = b.exec(location.search);return c === null ? '' : decodeURIComponent(c[1].replace(/\+/g, ' '));}, d = function(a, b){if(b.xdomain === '*' || a.origin.match(new RegExp(`${b.xdomain}$`))){return!0;}}, e = function(b, c, d){var e = ['pym', b, c, d];return e.join(a);}, f = function(b){var c = ['pym', b, '(\\S+)', '(.*)'];return new RegExp(`^${ c.join(a)}$`);}, g = function(){for(var a = b.autoInitInstances.length, c = a - 1;c >= 0;c--){var d = b.autoInitInstances[c];d.el.getElementsByTagName('iframe').length && d.el.getElementsByTagName('iframe')[0].contentWindow || b.autoInitInstances.splice(c, 1);}};return b.autoInitInstances = [], b.autoInit = function(){var a = document.querySelectorAll('[data-pym-src]:not([data-pym-auto-initialized])'), c = a.length;g();for(var d = 0;d < c;++d){var e = a[d];e.setAttribute('data-pym-auto-initialized', ''), e.id === '' && (e.id = `pym-${d}`);var f = e.getAttribute('data-pym-src'), h = {xdomain:'string', title:'string', name:'string', id:'string', sandbox:'string', allowfullscreen:'boolean'}, i = {};for(var j in h){if(e.getAttribute(`data-pym-${j}`) !== null){switch(h[j]){case'boolean':i[j] = !(e.getAttribute(`data-pym-${j}`) === 'false');break;case'string':i[j] = e.getAttribute(`data-pym-${j}`);break;default:console.err('unrecognized attribute type');}}}var k = new b.Parent(e.id, f, i);b.autoInitInstances.push(k);}return b.autoInitInstances;}, b.Parent = function(a, b, c){this.id = a, this.url = b, this.el = document.getElementById(a), this.iframe = null, this.settings = {xdomain:'*'}, this.messageRegex = f(this.id), this.messageHandlers = {}, c = c || {}, this._constructIframe = function(){var a = this.el.offsetWidth.toString();this.iframe = document.createElement('iframe');var b = '', c = this.url.indexOf('#');for(c > -1 && (b = this.url.substring(c, this.url.length), this.url = this.url.substring(0, c)), this.url.indexOf('?') < 0 ? this.url += '?' : this.url += '&', this.iframe.src = `${this.url}initialWidth=${a}&childId=${this.id}&parentTitle=${encodeURIComponent(document.title)}&parentUrl=${encodeURIComponent(window.location.href)}${b}`, this.iframe.setAttribute('width', '100%'), this.iframe.setAttribute('scrolling', 'no'), this.iframe.setAttribute('marginheight', '0'), this.iframe.setAttribute('frameborder', '0'), this.settings.title && this.iframe.setAttribute('title', this.settings.title), void 0 !== this.settings.allowfullscreen && this.settings.allowfullscreen !== !1 && this.iframe.setAttribute('allowfullscreen', ''), void 0 !== this.settings.sandbox && typeof this.settings.sandbox == 'string' && this.iframe.setAttribute('sandbox', this.settings.sandbox), this.settings.id && (document.getElementById(this.settings.id) || this.iframe.setAttribute('id', this.settings.id)), this.settings.name && this.iframe.setAttribute('name', this.settings.name);this.el.firstChild;){this.el.removeChild(this.el.firstChild);}this.el.appendChild(this.iframe), window.addEventListener('resize', this._onResize);}, this._onResize = function(){this.sendWidth();}.bind(this), this._fire = function(a, b){if(a in this.messageHandlers){for(var c = 0;c < this.messageHandlers[a].length;c++){this.messageHandlers[a][c].call(this, b);}}}, this.remove = function(){window.removeEventListener('message', this._processMessage), window.removeEventListener('resize', this._onResize), this.el.removeChild(this.iframe), g();}, this._processMessage = function(a){if(d(a, this.settings) && typeof a.data == 'string'){var b = a.data.match(this.messageRegex);if(!b || b.length !== 3){return!1;}var c = b[1], e = b[2];this._fire(c, e);}}.bind(this), this._onHeightMessage = function(a){var b = parseInt(a);this.iframe.setAttribute('height', `${b}px`);}, this._onNavigateToMessage = function(a){document.location.href = a;}, this._onScrollToChildPosMessage = function(a){var b = document.getElementById(this.id).getBoundingClientRect().top + window.pageYOffset, c = b + parseInt(a);window.scrollTo(0, c);}, this.onMessage = function(a, b){a in this.messageHandlers || (this.messageHandlers[a] = []), this.messageHandlers[a].push(b);}, this.sendMessage = function(a, b){this.el.getElementsByTagName('iframe').length && (this.el.getElementsByTagName('iframe')[0].contentWindow ? this.el.getElementsByTagName('iframe')[0].contentWindow.postMessage(e(this.id, a, b), '*') : this.remove());}, this.sendWidth = function(){var a = this.el.offsetWidth.toString();this.sendMessage('width', a);};for(var h in c){this.settings[h] = c[h];}return this.onMessage('height', this._onHeightMessage), this.onMessage('navigateTo', this._onNavigateToMessage), this.onMessage('scrollToChildPos', this._onScrollToChildPosMessage), window.addEventListener('message', this._processMessage, !1), this._constructIframe(), this;}, b.Child = function(b){this.parentWidth = null, this.id = null, this.parentTitle = null, this.parentUrl = null, this.settings = {renderCallback:null, xdomain:'*', polling:0}, this.timerId = null, this.messageRegex = null, this.messageHandlers = {}, b = b || {}, this.onMessage = function(a, b){a in this.messageHandlers || (this.messageHandlers[a] = []), this.messageHandlers[a].push(b);}, this._fire = function(a, b){if(a in this.messageHandlers){for(var c = 0;c < this.messageHandlers[a].length;c++){this.messageHandlers[a][c].call(this, b);}}}, this._processMessage = function(a){if(d(a, this.settings) && typeof a.data == 'string'){var b = a.data.match(this.messageRegex);if(b && b.length === 3){var c = b[1], e = b[2];this._fire(c, e);}}}.bind(this), this._onWidthMessage = function(a){var b = parseInt(a);b !== this.parentWidth && (this.parentWidth = b, this.settings.renderCallback && this.settings.renderCallback(b), this.sendHeight());}, this.sendMessage = function(a, b){window.parent.postMessage(e(this.id, a, b), '*');}, this.sendHeight = function(){var a = document.getElementsByTagName('body')[0].offsetHeight.toString();return this.sendMessage('height', a), a;}.bind(this), this.scrollParentTo = function(a){this.sendMessage('navigateTo', `#${a}`);}, this.navigateParentTo = function(a){this.sendMessage('navigateTo', a);}, this.scrollParentToChildEl = function(a){var b = document.getElementById(a).getBoundingClientRect().top + window.pageYOffset;this.scrollParentToChildPos(b);}, this.scrollParentToChildPos = function(a){this.sendMessage('scrollToChildPos', a.toString());}, this._markWhetherEmbedded = function(a){var b, c = document.getElementsByTagName('html')[0], d = c.className;try{b = window.self !== window.top ? 'embedded' : 'not-embedded';}catch(a){b = 'embedded';}d.indexOf(b) < 0 && (c.className = d ? `${d} ${b}` : b, a && a(b));}, this.remove = function(){window.removeEventListener('message', this._processMessage), this.timerId && clearInterval(this.timerId);}, this.id = c('childId') || b.id, this.messageRegex = new RegExp(`^pym${ a }${this.id }${a }(\\S+)${ a}(.*)$`);var f = parseInt(c('initialWidth'));this.parentUrl = c('parentUrl'), this.parentTitle = c('parentTitle'), this.onMessage('width', this._onWidthMessage);for(var g in b){this.settings[g] = b[g];}return window.addEventListener('message', this._processMessage, !1), this.settings.renderCallback && this.settings.renderCallback(f), this.sendHeight(), this.settings.polling && (this.timerId = window.setInterval(this.sendHeight, this.settings.polling)), this._markWhetherEmbedded(b.onMarkedEmbeddedStatus), this;}, b.autoInit(), b;}); /* eslint-enable */ return pym; } From b9c918eb881abc87c507ab772ccd102c2aa294ce Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Sat, 4 Feb 2017 01:29:24 +0800 Subject: [PATCH 22/31] Add pym.js LICENSE to coral-embed --- client/coral-embed/index.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/client/coral-embed/index.js b/client/coral-embed/index.js index eeef3103f..827809a7c 100644 --- a/client/coral-embed/index.js +++ b/client/coral-embed/index.js @@ -161,3 +161,29 @@ return pym; } })); + +/* +pym.js LICENSE (https://github.com/nprapps/pym.js/blob/master/LICENSE) +Copyright (c) 2014 NPR + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +*/ From 841d6add45f3ad765a5a8c3251ce54b09be1ec2f Mon Sep 17 00:00:00 2001 From: Benjamin Goering Date: Sat, 4 Feb 2017 01:48:57 +0800 Subject: [PATCH 23/31] Rename /Coral.js to /embed.js --- app.js | 1 - client/coral-admin/src/containers/Configure/EmbedLink.js | 2 +- routes/index.js | 2 ++ views/article.ejs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 2b2b656ae..c04e0cb4a 100644 --- a/app.js +++ b/app.js @@ -34,7 +34,6 @@ app.use(helmet({ })); app.use(bodyParser.json()); app.use('/client', express.static(path.join(__dirname, 'dist'))); -app.get('/Coral.js', (req, res) => res.sendFile(path.join(__dirname, 'client/coral-embed/index.js'))); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); diff --git a/client/coral-admin/src/containers/Configure/EmbedLink.js b/client/coral-admin/src/containers/Configure/EmbedLink.js index 32024bde2..db28e2369 100644 --- a/client/coral-admin/src/containers/Configure/EmbedLink.js +++ b/client/coral-admin/src/containers/Configure/EmbedLink.js @@ -32,7 +32,7 @@ class EmbedLink extends Component { location.hostname, location.port ? (`:${ window.location.port}`) : '' ].join(''); - const coralJsUrl = [talkBaseUrl, '/Coral.js'].join(''); + const coralJsUrl = [talkBaseUrl, '/embed.js'].join(''); const nonce = String(Math.random()).slice(2); const streamElementId = `coral_talk_${nonce}`; const embedText = ` 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/views/article.ejs b/views/article.ejs index 63469c56e..841b4a08e 100644 --- a/views/article.ejs +++ b/views/article.ejs @@ -23,7 +23,7 @@

<%= body %>

Admin - All Assets

-