diff --git a/ghost/admin/app/controllers/signup.js b/ghost/admin/app/controllers/signup.js index 485308ee14..189f194025 100644 --- a/ghost/admin/app/controllers/signup.js +++ b/ghost/admin/app/controllers/signup.js @@ -2,94 +2,179 @@ import Controller from 'ember-controller'; import RSVP from 'rsvp'; import injectService from 'ember-service/inject'; import {isEmberArray} from 'ember-array/utils'; - +import {task} from 'ember-concurrency'; import ValidationEngine from 'ghost-admin/mixins/validation-engine'; -import {isVersionMismatchError} from 'ghost-admin/services/ajax'; +import { + VersionMismatchError, + isVersionMismatchError +} from 'ghost-admin/services/ajax'; +import {assign} from 'ember-platform'; const {Promise} = RSVP; export default Controller.extend(ValidationEngine, { + ajax: injectService(), + config: injectService(), + ghostPaths: injectService(), + notifications: injectService(), + session: injectService(), + settings: injectService(), + torii: injectService(), + // ValidationEngine settings validationType: 'signup', - submitting: false, flowErrors: '', image: null, - ghostPaths: injectService(), - config: injectService(), - notifications: injectService(), - session: injectService(), - ajax: injectService(), + authenticate: task(function* (authStrategy, authentication) { + try { + let authResult = yield this.get('session') + .authenticate(authStrategy, ...authentication); - sendImage() { + // fetch settings for synchronous access + yield this.get('settings').fetch(); + + return authResult; + + } catch (error) { + if (error && error.errors) { + // we don't get back an ember-data/ember-ajax error object + // back so we need to pass in a null status in order to + // test against the payload + if (isVersionMismatchError(null, error)) { + let versionMismatchError = new VersionMismatchError(error); + return this.get('notifications').showAPIError(versionMismatchError); + } + + error.errors.forEach((err) => { + err.message = err.message.htmlSafe(); + }); + + this.set('flowErrors', error.errors[0].message.string); + + if (error.errors[0].message.string.match(/user with that email/)) { + this.get('model.errors').add('identification', ''); + } + + if (error.errors[0].message.string.match(/password is incorrect/)) { + this.get('model.errors').add('password', ''); + } + } else { + // Connection errors don't return proper status message, only req.body + this.get('notifications').showAlert('There was a problem on the server.', {type: 'error', key: 'session.authenticate.failed'}); + throw error; + } + } + }).drop(), + + authenticateWithGhostOrg: task(function* () { + let authStrategy = 'authenticator:oauth2-ghost'; + let inviteToken = this.get('model.token'); + let email = this.get('model.email'); + + this.set('flowErrors', ''); + + try { + let authentication = yield this.get('torii') + .open('ghost-oauth2', {email, type: 'invite'}); + + authentication = assign(authentication, {inviteToken}); + + return yield this.get('authenticate').perform(authStrategy, [authentication]); + + } catch (error) { + this.set('flowErrors', 'Authentication with Ghost.org denied or failed'); + throw error; + } + }).drop(), + + signup: task(function* () { + let setupProperties = ['name', 'email', 'password', 'token']; + let notifications = this.get('notifications'); + + this.set('flowErrors', ''); + this.get('hasValidated').addObjects(setupProperties); + + try { + yield this.validate(); + yield this._completeInvitation(); + + try { + yield this._authenticateWithPassword(); + yield this._sendImage(); + } catch (error) { + notifications.showAPIError(error, {key: 'signup.complete'}); + } + + } catch (error) { + // ValidationEngine throws undefined + if (!error) { + this.set('flowErrors', 'Please fill out the form to complete your sign-up'); + } + + if (error && error.errors && isEmberArray(error.errors)) { + if (isVersionMismatchError(error)) { + notifications.showAPIError(error); + } + this.set('flowErrors', error.errors[0].message); + } else { + notifications.showAPIError(error, {key: 'signup.complete'}); + } + } + }), + + _completeInvitation() { + let authUrl = this.get('ghostPaths.url').api('authentication', 'invitation'); + let model = this.get('model'); + + return this.get('ajax').post(authUrl, { + dataType: 'json', + data: { + invitation: [{ + name: model.get('name'), + email: model.get('email'), + password: model.get('password'), + token: model.get('token') + }] + } + }); + }, + + _authenticateWithPassword() { + let email = this.get('model.email'); + let password = this.get('model.password'); + + return this.get('session') + .authenticate('authenticator:oauth2', email, password); + }, + + _sendImage() { let image = this.get('image'); - this.get('session.user').then((user) => { - return new Promise((resolve, reject) => { - image.formData = {}; - image.submit() - .success((response) => { - let usersUrl = this.get('ghostPaths.url').api('users', user.id.toString()); - user.image = response; - this.get('ajax').put(usersUrl, { - data: { - users: [user] - } - }).then(resolve).catch(reject); - }) - .error(reject); + if (image) { + return this.get('session.user').then((user) => { + return new Promise((resolve, reject) => { + image.formData = {}; + image.submit() + .success((response) => { + let usersUrl = this.get('ghostPaths.url').api('users', user.id.toString()); + user.image = response; + this.get('ajax').put(usersUrl, { + data: { + users: [user] + } + }).then(resolve).catch(reject); + }) + .error(reject); + }); }); - }); + } }, actions: { signup() { - let model = this.get('model'); - let setupProperties = ['name', 'email', 'password', 'token']; - let data = model.getProperties(setupProperties); - let image = this.get('image'); - let notifications = this.get('notifications'); - - this.set('flowErrors', ''); - - this.get('hasValidated').addObjects(setupProperties); - return this.validate().then(() => { - let authUrl = this.get('ghostPaths.url').api('authentication', 'invitation'); - this.toggleProperty('submitting'); - return this.get('ajax').post(authUrl, { - dataType: 'json', - data: { - invitation: [{ - name: data.name, - email: data.email, - password: data.password, - token: data.token - }] - } - }).then(() => { - return this.get('session').authenticate('authenticator:oauth2', this.get('model.email'), this.get('model.password')).then(() => { - if (image) { - this.sendImage(); - } - }).catch((resp) => { - notifications.showAPIError(resp, {key: 'signup.complete'}); - }); - }).catch((error) => { - this.toggleProperty('submitting'); - - if (error && error.errors && isEmberArray(error.errors)) { - if (isVersionMismatchError(error)) { - notifications.showAPIError(error); - } - this.set('flowErrors', error.errors[0].message); - } else { - notifications.showAPIError(error, {key: 'signup.complete'}); - } - }); - }).catch(() => { - this.set('flowErrors', 'Please fill out the form to complete your sign-up'); - }); + this.get('signup').perform(); }, setImage(image) { diff --git a/ghost/admin/app/routes/signup.js b/ghost/admin/app/routes/signup.js index ddfd688759..5f76fc92f8 100644 --- a/ghost/admin/app/routes/signup.js +++ b/ghost/admin/app/routes/signup.js @@ -2,12 +2,6 @@ import Route from 'ember-route'; import RSVP from 'rsvp'; import injectService from 'ember-service/inject'; import EmberObject from 'ember-object'; -import {assign} from 'ember-platform'; -import { - VersionMismatchError, - isVersionMismatchError -} from 'ghost-admin/services/ajax'; - import DS from 'ember-data'; import UnauthenticatedRouteMixin from 'ghost-admin/mixins/unauthenticated-route-mixin'; import styleBody from 'ghost-admin/mixins/style-body'; @@ -79,64 +73,5 @@ export default Route.extend(styleBody, UnauthenticatedRouteMixin, { // clear the properties that hold the sensitive data from the controller this.controllerFor('signup').setProperties({email: '', password: '', token: ''}); - }, - - actions: { - authenticateWithGhostOrg() { - let authStrategy = 'authenticator:oauth2-ghost'; - let inviteToken = this.get('controller.model.token'); - let email = this.get('controller.model.email'); - - this.toggleProperty('controller.loggingIn'); - this.set('controller.flowErrors', ''); - - this.get('torii') - .open('ghost-oauth2', {email, type: 'invite'}) - .then((authentication) => { - let _authentication = assign({}, authentication, {inviteToken}); - this.send('authenticate', authStrategy, [_authentication]); - }) - .catch(() => { - this.toggleProperty('controller.loggingIn'); - this.set('controller.flowErrors', 'Authentication with Ghost.org denied or failed'); - }); - }, - - // TODO: this is duplicated with the signin route - maybe extract into a mixin? - authenticate(strategy, authentication) { - // Authentication transitions to posts.index, we can leave spinner running unless there is an error - this.get('session') - .authenticate(strategy, ...authentication) - .catch((error) => { - this.toggleProperty('controller.loggingIn'); - - if (error && error.errors) { - // we don't get back an ember-data/ember-ajax error object - // back so we need to pass in a null status in order to - // test against the payload - if (isVersionMismatchError(null, error)) { - let versionMismatchError = new VersionMismatchError(error); - return this.get('notifications').showAPIError(versionMismatchError); - } - - error.errors.forEach((err) => { - err.message = err.message.htmlSafe(); - }); - - this.set('controller.flowErrors', error.errors[0].message.string); - - if (error.errors[0].message.string.match(/user with that email/)) { - this.get('controller.model.errors').add('identification', ''); - } - - if (error.errors[0].message.string.match(/password is incorrect/)) { - this.get('controller.model.errors').add('password', ''); - } - } else { - // Connection errors don't return proper status message, only req.body - this.get('notifications').showAlert('There was a problem on the server.', {type: 'error', key: 'session.authenticate.failed'}); - } - }); - } } }); diff --git a/ghost/admin/app/templates/signup.hbs b/ghost/admin/app/templates/signup.hbs index 35dc774904..60857fe9d6 100644 --- a/ghost/admin/app/templates/signup.hbs +++ b/ghost/admin/app/templates/signup.hbs @@ -12,10 +12,13 @@
+ {{else}}{{{flowErrors}}}