mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-14 18:52:05 +03:00
Merge pull request #5359 from josephwegner/onboarding-invites
invite users after signing up during setup
This commit is contained in:
commit
9e5d9e975f
@ -1,41 +1,134 @@
|
||||
import Ember from 'ember';
|
||||
import ValidationEngine from 'ghost/mixins/validation-engine';
|
||||
|
||||
var SetupThreeController = Ember.Controller.extend(ValidationEngine, {
|
||||
var SetupThreeController = Ember.Controller.extend({
|
||||
notifications: Ember.inject.service(),
|
||||
users: '',
|
||||
usersArray: Ember.computed('users', function () {
|
||||
return this.get('users').split('\n').filter(function (user) {
|
||||
var users = this.get('users').split('\n').filter(function (email) {
|
||||
return email.trim().length > 0;
|
||||
});
|
||||
|
||||
return users.uniq();
|
||||
}),
|
||||
validUsersArray: Ember.computed('usersArray', function () {
|
||||
return this.get('usersArray').filter(function (user) {
|
||||
return validator.isEmail(user);
|
||||
});
|
||||
}),
|
||||
numUsers: Ember.computed('usersArray', function () {
|
||||
return this.get('usersArray').length;
|
||||
validateUsers: Ember.computed('usersArray', function () {
|
||||
var errors = [];
|
||||
|
||||
this.get('usersArray').forEach(function (user) {
|
||||
if (!validator.isEmail(user)) {
|
||||
errors.push({
|
||||
user: user,
|
||||
error: 'email'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return errors.length === 0 ? true : errors;
|
||||
}),
|
||||
buttonText: Ember.computed('numUsers', function () {
|
||||
var user = this.get('numUsers') === 1 ? 'user' : 'users';
|
||||
return this.get('numUsers') > 0 ?
|
||||
'Invite ' + this.get('numUsers') + ' ' + user : 'I\'ll do this later, take me to my blog!';
|
||||
numUsers: Ember.computed('validUsersArray', function () {
|
||||
return this.get('validUsersArray').length;
|
||||
}),
|
||||
buttonClass: Ember.computed('numUsers', function () {
|
||||
return this.get('numUsers') > 0 ? 'btn-green' : 'btn-minor';
|
||||
buttonText: Ember.computed('usersArray', function () {
|
||||
var num = this.get('usersArray').length,
|
||||
user;
|
||||
|
||||
if (num > 0) {
|
||||
user = num === 1 ? 'user' : 'users';
|
||||
user = num + ' ' + user;
|
||||
} else {
|
||||
user = 'some users';
|
||||
}
|
||||
|
||||
return 'Invite ' + user;
|
||||
}),
|
||||
buttonClass: Ember.computed('validateUsers', 'numUsers', function () {
|
||||
if (this.get('validateUsers') === true && this.get('numUsers') > 0) {
|
||||
return 'btn-green';
|
||||
} else {
|
||||
return 'btn-minor';
|
||||
}
|
||||
}),
|
||||
authorRole: Ember.computed(function () {
|
||||
return this.store.find('role').then(function (roles) {
|
||||
return roles.findBy('name', 'Author');
|
||||
});
|
||||
}),
|
||||
actions: {
|
||||
invite: function () {
|
||||
console.log('inviting', this.get('usersArray'));
|
||||
var self = this,
|
||||
validationErrors = this.get('validateUsers'),
|
||||
users = this.get('usersArray'),
|
||||
errorMessages,
|
||||
notifications = this.get('notifications'),
|
||||
invitationsString;
|
||||
|
||||
if (this.get('numUsers') === 0) {
|
||||
this.sendAction('signin');
|
||||
if (validationErrors === true && users.length > 0) {
|
||||
this.get('authorRole').then(function (authorRole) {
|
||||
Ember.RSVP.Promise.all(
|
||||
users.map(function (user) {
|
||||
var newUser = self.store.createRecord('user', {
|
||||
email: user,
|
||||
status: 'invited',
|
||||
role: authorRole
|
||||
});
|
||||
|
||||
return newUser.save().then(function () {
|
||||
return {
|
||||
email: user,
|
||||
success: newUser.get('status') === 'invited'
|
||||
};
|
||||
}).catch(function () {
|
||||
return {
|
||||
email: user,
|
||||
success: false
|
||||
};
|
||||
});
|
||||
})
|
||||
).then(function (invites) {
|
||||
var successCount = 0,
|
||||
erroredEmails = [],
|
||||
message;
|
||||
|
||||
invites.forEach(function (invite) {
|
||||
if (invite.success) {
|
||||
successCount++;
|
||||
} else {
|
||||
erroredEmails.push(invite.email);
|
||||
}
|
||||
});
|
||||
|
||||
if (erroredEmails.length > 0) {
|
||||
message = 'Failed to send ' + erroredEmails.length + ' invitations: ';
|
||||
message += erroredEmails.join(', ');
|
||||
notifications.showError(message, {delayed: successCount > 0});
|
||||
}
|
||||
|
||||
if (successCount > 0) {
|
||||
// pluralize
|
||||
invitationsString = successCount > 1 ? 'invitations' : 'invitation';
|
||||
|
||||
notifications.showSuccess(successCount + ' ' + invitationsString + ' sent!', {delayed: true});
|
||||
self.transitionTo('posts.index');
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (users.length === 0) {
|
||||
notifications.showError('No users to invite.');
|
||||
} else {
|
||||
errorMessages = validationErrors.map(function (error) {
|
||||
// Only one error type here so far, but one day the errors might be more detailed
|
||||
switch (error.error) {
|
||||
case 'email':
|
||||
return {message: error.user + ' is not a valid email.'};
|
||||
}
|
||||
});
|
||||
|
||||
notifications.showErrors(errorMessages);
|
||||
}
|
||||
|
||||
// TODO: do invites
|
||||
},
|
||||
signin: function () {
|
||||
var self = this;
|
||||
|
||||
this.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', {
|
||||
identification: self.get('email'),
|
||||
password: self.get('password')
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -14,6 +14,7 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
|
||||
ghostPaths: Ember.inject.service('ghost-paths'),
|
||||
notifications: Ember.inject.service(),
|
||||
application: Ember.inject.controller(),
|
||||
|
||||
gravatarUrl: Ember.computed('email', function () {
|
||||
var email = this.get('email'),
|
||||
@ -55,9 +56,15 @@ export default Ember.Controller.extend(ValidationEngine, {
|
||||
}]
|
||||
}
|
||||
}).then(function () {
|
||||
// Don't call the success handler, otherwise we will be redirected to admin
|
||||
self.get('application').set('skipAuthSuccessHandler', true);
|
||||
|
||||
self.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', {
|
||||
identification: self.get('email'),
|
||||
password: self.get('password')
|
||||
}).then(function () {
|
||||
self.set('password', '');
|
||||
self.transitionToRoute('setup.three');
|
||||
});
|
||||
}).catch(function (resp) {
|
||||
self.toggleProperty('submitting');
|
||||
|
@ -208,6 +208,13 @@
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.gh-flow-content .gh-flow-skip {
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
color: #7d878a;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.gh-flow-content .gh-flow-create {
|
||||
position: relative;
|
||||
margin: 70px auto 30px;
|
||||
|
@ -12,7 +12,10 @@
|
||||
sally.sanders@example.com" value=users}}
|
||||
</form>
|
||||
|
||||
<button {{action "signin"}} class="btn btn-default btn-lg btn-block {{buttonClass}}">
|
||||
<button {{action 'invite'}} class="btn btn-default btn-lg btn-block {{buttonClass}}">
|
||||
{{buttonText}}
|
||||
</button>
|
||||
</section>
|
||||
{{#link-to "posts" class="gh-flow-skip"}}
|
||||
I'll do this later, take me to my blog!
|
||||
{{/link-to}}
|
||||
</section>
|
||||
|
@ -239,7 +239,7 @@ http = function http(apiMethod) {
|
||||
|
||||
return apiMethod(object, options).tap(function onSuccess(response) {
|
||||
// Add X-Cache-Invalidate, Location, and Content-Disposition headers
|
||||
return addHeaders(apiMethod, req, res, response);
|
||||
return addHeaders(apiMethod, req, res, (response || {}));
|
||||
}).then(function then(response) {
|
||||
// Send a properly formatting HTTP response containing the data with correct headers
|
||||
res.json(response || {});
|
||||
|
@ -1,9 +1,8 @@
|
||||
// # Setup Test
|
||||
// Test that setup works correctly
|
||||
|
||||
/*global CasperTest, casper, email, user, password */
|
||||
|
||||
CasperTest.begin('Ghost setup fails properly', 5, function suite(test) {
|
||||
CasperTest.begin('Ghost setup fails properly', 12, function suite(test) {
|
||||
casper.thenOpenAndWaitForPageLoad('setup', function then() {
|
||||
test.assertUrlMatch(/ghost\/setup\/one\/$/, 'Landed on the correct URL');
|
||||
});
|
||||
@ -27,12 +26,42 @@ CasperTest.begin('Ghost setup fails properly', 5, function suite(test) {
|
||||
// This can take quite a long time
|
||||
casper.wait(5000);
|
||||
|
||||
casper.waitForResource(/\d+/, function testForDashboard() {
|
||||
test.assertUrlMatch(/ghost\/\d+\/$/, 'Landed on the correct URL');
|
||||
test.assertExists('.gh-nav-main-content.active', 'Now we are on Content');
|
||||
}, function onTimeOut() {
|
||||
test.fail('Failed to signin');
|
||||
}, 20000);
|
||||
casper.waitForScreenLoad('setup.three', function inviteUsers() {
|
||||
casper.thenClick('.gh-flow-content .btn');
|
||||
});
|
||||
|
||||
casper.waitForSelector('.notification-error', function onSuccess() {
|
||||
test.assert(true, 'Got error notification');
|
||||
test.assertSelectorHasText('.notification-error', 'No users to invite.');
|
||||
|
||||
test.assertExists('.gh-flow-content .btn-minor', 'Submit button is not minor');
|
||||
test.assertSelectorHasText('.gh-flow-content .btn', 'Invite some users', 'Submit button has wrong text');
|
||||
}, function onTimeout() {
|
||||
test.assert(false, 'No error notification for empty invitation list');
|
||||
});
|
||||
|
||||
casper.then(function fillInvitationForm() {
|
||||
casper.fill('form.gh-flow-invite', {users: 'test@example.com'});
|
||||
test.assertSelectorHasText('.gh-flow-content .btn', 'Invite 1 user', 'One invitation button text is incorrect');
|
||||
|
||||
test.assertExists('.gh-flow-content .btn-green', 'Submit button is not green');
|
||||
|
||||
casper.fill('form.gh-flow-invite', {users: 'test@example.com\ntest2@example.com'});
|
||||
test.assertSelectorHasText('.gh-flow-content .btn', 'Invite 2 users', 'Two invitations button text is incorrect');
|
||||
});
|
||||
|
||||
casper.thenClick('.gh-flow-content .btn');
|
||||
|
||||
// This might take awhile
|
||||
casper.wait(5000);
|
||||
|
||||
// These invitations will fail, because Casper can't send emails
|
||||
casper.waitForSelector('.notification-error', function onSuccess() {
|
||||
test.assert(true, 'Got error notification');
|
||||
test.assertSelectorHasText('.notification-error', 'Failed to send 2 invitations: test@example.com, test2@example.com');
|
||||
}, function onTimeout() {
|
||||
test.assert(false, 'No error notification after invite.');
|
||||
});
|
||||
}, true);
|
||||
|
||||
CasperTest.begin('Authenticated user is redirected', 6, function suite(test) {
|
||||
|
Loading…
Reference in New Issue
Block a user