2018-01-10 23:57:43 +01:00
/* eslint-disable ghost/ember/alias-model-in-controller */
2017-10-30 10:38:01 +01:00
import Controller , { inject as controller } from '@ember/controller' ;
2020-01-16 18:01:12 +01:00
// TODO: remove usage of Ember Data's private `Errors` class when refactoring validations
// eslint-disable-next-line
2017-05-29 20:50:03 +02:00
import DS from 'ember-data' ;
2018-10-29 13:19:46 +01:00
import Ember from 'ember' ;
2016-07-06 21:47:30 +02:00
import RSVP from 'rsvp' ;
2019-01-22 14:09:38 +01:00
import validator from 'validator' ;
2017-08-22 09:53:26 +02:00
import { alias } from '@ember/object/computed' ;
import { computed } from '@ember/object' ;
import { A as emberA } from '@ember/array' ;
import { htmlSafe } from '@ember/string' ;
2017-07-20 12:08:34 +02:00
import { isInvalidError } from 'ember-ajax/errors' ;
2017-08-22 09:53:26 +02:00
import { run } from '@ember/runloop' ;
2017-10-30 10:38:01 +01:00
import { inject as service } from '@ember/service' ;
2017-04-19 12:27:32 +02:00
import { task , timeout } from 'ember-concurrency' ;
2015-03-29 20:10:53 +02:00
2015-10-28 12:36:45 +01:00
const { Errors } = DS ;
2015-08-10 06:40:27 +02:00
2015-10-28 12:36:45 +01:00
export default Controller . extend ( {
2017-10-30 10:38:01 +01:00
two : controller ( 'setup/two' ) ,
2018-01-11 18:43:23 +01:00
notifications : service ( ) ,
users : '' ,
2015-10-28 12:36:45 +01:00
errors : Errors . create ( ) ,
hasValidated : emberA ( ) ,
ownerEmail : alias ( 'two.email' ) ,
2015-08-27 10:41:24 +02:00
2015-10-28 12:36:45 +01:00
usersArray : computed ( 'users' , function ( ) {
2019-03-06 14:53:54 +01:00
let errors = this . errors ;
let users = this . users . split ( '\n' ) . filter ( function ( email ) {
2015-05-28 15:58:52 +02:00
return email . trim ( ) . length > 0 ;
} ) ;
2015-08-27 10:41:24 +02:00
// remove "no users to invite" error if we have users
if ( users . uniq ( ) . length > 0 && errors . get ( 'users.length' ) === 1 ) {
if ( errors . get ( 'users.firstObject' ) . message . match ( /no users/i ) ) {
errors . remove ( 'users' ) ;
}
}
2015-05-28 15:58:52 +02:00
return users . uniq ( ) ;
} ) ,
2015-08-27 10:41:24 +02:00
2015-10-28 12:36:45 +01:00
validUsersArray : computed ( 'usersArray' , 'ownerEmail' , function ( ) {
2019-03-06 14:53:54 +01:00
let ownerEmail = this . ownerEmail ;
2015-08-27 10:41:24 +02:00
2019-03-06 14:53:54 +01:00
return this . usersArray . filter ( function ( user ) {
2018-03-19 18:56:09 +01:00
return validator . isEmail ( user || '' ) && user !== ownerEmail ;
2015-03-29 20:10:53 +02:00
} ) ;
} ) ,
2015-05-28 15:58:52 +02:00
2015-10-28 12:36:45 +01:00
invalidUsersArray : computed ( 'usersArray' , 'ownerEmail' , function ( ) {
2019-03-06 14:53:54 +01:00
let ownerEmail = this . ownerEmail ;
2015-08-27 10:41:24 +02:00
2019-03-06 14:53:54 +01:00
return this . usersArray . reject ( user => validator . isEmail ( user || '' ) || user === ownerEmail ) ;
2015-05-28 15:58:52 +02:00
} ) ,
2015-08-27 10:41:24 +02:00
2015-10-28 12:36:45 +01:00
validationResult : computed ( 'invalidUsersArray' , function ( ) {
let errors = [ ] ;
2015-08-27 10:41:24 +02:00
2019-03-06 14:53:54 +01:00
this . invalidUsersArray . forEach ( ( user ) => {
2015-08-27 10:41:24 +02:00
errors . push ( {
2015-10-28 12:36:45 +01:00
user ,
2015-08-27 10:41:24 +02:00
error : 'email'
} ) ;
} ) ;
if ( errors . length === 0 ) {
// ensure we aren't highlighting fields when everything is fine
2019-03-06 14:53:54 +01:00
this . errors . clear ( ) ;
2015-08-27 10:41:24 +02:00
return true ;
} else {
return errors ;
}
2015-05-28 15:58:52 +02:00
} ) ,
2015-10-28 12:36:45 +01:00
buttonText : computed ( 'errors.users' , 'validUsersArray' , 'invalidUsersArray' , function ( ) {
let usersError = this . get ( 'errors.users.firstObject.message' ) ;
2019-03-06 14:53:54 +01:00
let validNum = this . validUsersArray . length ;
let invalidNum = this . invalidUsersArray . length ;
2015-10-28 12:36:45 +01:00
let userCount ;
2015-08-27 10:41:24 +02:00
if ( usersError && usersError . match ( /no users/i ) ) {
return usersError ;
}
if ( invalidNum > 0 ) {
userCount = invalidNum === 1 ? 'email address' : 'email addresses' ;
return ` ${ invalidNum } invalid ${ userCount } ` ;
}
if ( validNum > 0 ) {
userCount = validNum === 1 ? 'user' : 'users' ;
2015-10-28 12:36:45 +01:00
userCount = ` ${ validNum } ${ userCount } ` ;
2015-05-28 15:58:52 +02:00
} else {
2015-08-27 10:41:24 +02:00
userCount = 'some users' ;
2015-05-28 15:58:52 +02:00
}
2015-10-28 12:36:45 +01:00
return ` Invite ${ userCount } ` ;
2015-03-29 20:10:53 +02:00
} ) ,
2015-08-27 10:41:24 +02:00
2015-10-28 12:36:45 +01:00
buttonClass : computed ( 'validationResult' , 'usersArray.length' , function ( ) {
2019-03-06 14:53:54 +01:00
if ( this . validationResult === true && this . get ( 'usersArray.length' ) > 0 ) {
2017-02-16 20:50:05 +01:00
return 'gh-btn-green' ;
2015-05-28 15:58:52 +02:00
} else {
2017-02-16 20:50:05 +01:00
return 'gh-btn-minor' ;
2015-05-28 15:58:52 +02:00
}
2015-03-29 20:10:53 +02:00
} ) ,
2015-08-27 10:41:24 +02:00
2015-10-28 12:36:45 +01:00
authorRole : computed ( function ( ) {
2018-01-05 16:38:23 +01:00
return this . store . findAll ( 'role' , { reload : true } ) . then ( roles => roles . findBy ( 'name' , 'Author' ) ) ;
2015-03-29 20:10:53 +02:00
} ) ,
2015-08-27 10:41:24 +02:00
2018-01-11 18:43:23 +01:00
actions : {
validate ( ) {
this . validate ( ) ;
} ,
invite ( ) {
2019-03-06 14:53:54 +01:00
this . invite . perform ( ) ;
2018-01-11 18:43:23 +01:00
} ,
skipInvite ( ) {
this . send ( 'loadServerNotifications' ) ;
2019-03-21 10:33:14 +01:00
this . transitionToRoute ( 'home' ) ;
2018-01-11 18:43:23 +01:00
}
} ,
validate ( ) {
2019-03-06 14:53:54 +01:00
let errors = this . errors ;
let validationResult = this . validationResult ;
2018-01-11 18:43:23 +01:00
let property = 'users' ;
errors . clear ( ) ;
// If property isn't in the `hasValidated` array, add it to mark that this field can show a validation result
2019-03-06 14:53:54 +01:00
this . hasValidated . addObject ( property ) ;
2018-01-11 18:43:23 +01:00
if ( validationResult === true ) {
return true ;
}
validationResult . forEach ( ( error ) => {
// Only one error type here so far, but one day the errors might be more detailed
switch ( error . error ) {
case 'email' :
errors . add ( property , ` ${ error . user } is not a valid email. ` ) ;
}
} ) ;
return false ;
} ,
2016-01-08 18:01:23 +01:00
_transitionAfterSubmission ( ) {
if ( ! this . _hasTransitioned ) {
this . _hasTransitioned = true ;
2019-03-21 10:33:14 +01:00
this . transitionToRoute ( 'home' ) ;
2016-01-08 18:01:23 +01:00
}
} ,
2017-04-19 12:27:32 +02:00
invite : task ( function * ( ) {
2019-03-06 14:53:54 +01:00
let users = this . validUsersArray ;
2017-04-19 12:27:32 +02:00
if ( this . validate ( ) && users . length > 0 ) {
this . _hasTransitioned = false ;
2019-03-06 14:53:54 +01:00
this . _slowSubmissionTimeout . perform ( ) ;
2017-04-19 12:27:32 +02:00
2019-03-06 14:53:54 +01:00
let authorRole = yield this . authorRole ;
2017-04-19 12:27:32 +02:00
let invites = yield this . _saveInvites ( authorRole ) ;
2019-03-06 14:53:54 +01:00
this . _slowSubmissionTimeout . cancelAll ( ) ;
2017-04-19 12:27:32 +02:00
this . _showNotifications ( invites ) ;
run . schedule ( 'actions' , this , function ( ) {
this . send ( 'loadServerNotifications' ) ;
this . _transitionAfterSubmission ( ) ;
} ) ;
} else if ( users . length === 0 ) {
2019-03-06 14:53:54 +01:00
this . errors . add ( 'users' , 'No users to invite' ) ;
2017-04-19 12:27:32 +02:00
}
} ) . drop ( ) ,
_slowSubmissionTimeout : task ( function * ( ) {
yield timeout ( 4000 ) ;
this . _transitionAfterSubmission ( ) ;
} ) . drop ( ) ,
_saveInvites ( authorRole ) {
2019-03-06 14:53:54 +01:00
let users = this . validUsersArray ;
2017-04-19 12:27:32 +02:00
return RSVP . Promise . all (
users . map ( ( user ) => {
let invite = this . store . createRecord ( 'invite' , {
email : user ,
role : authorRole
} ) ;
2018-01-05 16:38:23 +01:00
return invite . save ( ) . then ( ( ) => ( {
email : user ,
success : invite . get ( 'status' ) === 'sent'
} ) ) . catch ( error => ( {
error ,
email : user ,
success : false
} ) ) ;
2017-04-19 12:27:32 +02:00
} )
) ;
} ,
_showNotifications ( invites ) {
2019-03-06 14:53:54 +01:00
let notifications = this . notifications ;
2017-04-19 12:27:32 +02:00
let erroredEmails = [ ] ;
let successCount = 0 ;
let invitationsString , message ;
invites . forEach ( ( invite ) => {
if ( invite . success ) {
2018-01-05 16:38:23 +01:00
successCount += 1 ;
2017-07-20 12:08:34 +02:00
} else if ( isInvalidError ( invite . error ) ) {
2017-11-03 23:59:39 +01:00
message = ` ${ invite . email } was invalid: ${ invite . error . payload . errors [ 0 ] . message } ` ;
2017-07-20 12:08:34 +02:00
notifications . showAlert ( message , { type : 'error' , delayed : true , key : ` signup.send-invitations. ${ invite . email } ` } ) ;
2017-04-19 12:27:32 +02:00
} else {
erroredEmails . push ( invite . email ) ;
}
} ) ;
if ( erroredEmails . length > 0 ) {
invitationsString = erroredEmails . length > 1 ? ' invitations: ' : ' invitation: ' ;
message = ` Failed to send ${ erroredEmails . length } ${ invitationsString } ` ;
2018-10-29 13:19:46 +01:00
message += Ember . Handlebars . Utils . escapeExpression ( erroredEmails . join ( ', ' ) ) ;
2021-01-19 01:18:59 +01:00
message += '. Please check your email configuration, see <a href=\'https://ghost.org/docs/config/#mail\' target=\'_blank\'>https://ghost.org/docs/config/#mail</a> for instructions' ;
2017-04-19 12:27:32 +02:00
message = htmlSafe ( message ) ;
notifications . showAlert ( message , { type : 'error' , delayed : successCount > 0 , key : 'signup.send-invitations.failed' } ) ;
}
if ( successCount > 0 ) {
// pluralize
invitationsString = successCount > 1 ? 'invitations' : 'invitation' ;
notifications . showAlert ( ` ${ successCount } ${ invitationsString } sent! ` , { type : 'success' , delayed : true , key : 'signup.send-invitations.success' } ) ;
}
2015-03-29 20:10:53 +02:00
}
} ) ;