2022-02-01 12:34:03 +03:00
import classic from 'ember-classic-decorator' ;
import { action , computed } from '@ember/object' ;
import { inject as service } from '@ember/service' ;
2018-01-11 01:57:43 +03:00
/* eslint-disable ghost/ember/alias-model-in-controller */
2017-08-22 10:53:26 +03:00
import Controller from '@ember/controller' ;
2019-06-17 13:41:04 +03:00
import generatePassword from 'ghost-admin/utils/password-generator' ;
2019-01-22 16:09:38 +03:00
import validator from 'validator' ;
2017-05-23 11:50:04 +03:00
import {
2017-05-29 21:50:03 +03:00
IMAGE _EXTENSIONS ,
IMAGE _MIME _TYPES
2017-05-23 11:50:04 +03:00
} from 'ghost-admin/components/gh-image-uploader' ;
2020-05-11 13:37:35 +03:00
import { task } from 'ember-concurrency' ;
2015-05-11 18:35:55 +03:00
2019-06-17 13:41:04 +03:00
function randomPassword ( ) {
let word = generatePassword ( 6 ) ;
let randomN = Math . floor ( Math . random ( ) * 1000 ) ;
return word + randomN ;
}
2022-02-01 12:34:03 +03:00
@ classic
export default class GeneralController extends Controller {
2022-02-01 20:03:45 +03:00
@ service config ;
@ service ghostPaths ;
@ service notifications ;
@ service session ;
@ service settings ;
@ service frontend ;
@ service ui ;
2022-02-01 12:34:03 +03:00
availableTimezones = null ;
imageExtensions = IMAGE _EXTENSIONS ;
imageMimeTypes = IMAGE _MIME _TYPES ;
_scratchFacebook = null ;
_scratchTwitter = null ;
@ computed ( 'config.blogUrl' , 'settings.publicHash' )
get privateRSSUrl ( ) {
2017-10-05 12:59:14 +03:00
let blogUrl = this . get ( 'config.blogUrl' ) ;
2018-01-11 01:57:43 +03:00
let publicHash = this . get ( 'settings.publicHash' ) ;
2017-10-05 12:59:14 +03:00
return ` ${ blogUrl } / ${ publicHash } /rss ` ;
2022-02-01 12:34:03 +03:00
}
@ action
save ( ) {
2022-02-10 13:20:03 +03:00
this . saveTask . perform ( ) ;
2022-02-01 12:34:03 +03:00
}
@ action
setTimezone ( timezone ) {
this . set ( 'settings.timezone' , timezone . name ) ;
}
@ action
removeImage ( image ) {
// setting `null` here will error as the server treats it as "null"
this . settings . set ( image , '' ) ;
}
/ * *
* Opens a file selection dialog - Triggered by "Upload Image" buttons ,
* searches for the hidden file input within the . gh - setting element
* containing the clicked button then simulates a click
* @ param { MouseEvent } event - MouseEvent fired by the button click
* /
@ action
triggerFileDialog ( event ) {
2022-05-26 13:05:14 +03:00
event ? . target . closest ( '.gh-setting-action' ) ? . find ( 'input[type="file"]' ) ? . click ( ) ;
2022-02-01 12:34:03 +03:00
}
/ * *
* Fired after an image upload completes
* @ param { string } property - Property name to be set on ` this.settings `
* @ param { UploadResult [ ] } results - Array of UploadResult objects
* @ return { string } The URL that was set on ` this.settings.property `
* /
@ action
imageUploaded ( property , results ) {
if ( results [ 0 ] ) {
return this . settings . set ( property , results [ 0 ] . url ) ;
}
}
2018-03-09 17:42:27 +03:00
2022-02-01 12:34:03 +03:00
@ action
toggleIsPrivate ( isPrivate ) {
let settings = this . settings ;
2018-03-09 17:42:27 +03:00
2022-02-01 12:34:03 +03:00
settings . set ( 'isPrivate' , isPrivate ) ;
settings . get ( 'errors' ) . remove ( 'password' ) ;
2018-01-11 17:16:42 +03:00
2022-02-01 12:34:03 +03:00
let changedAttrs = settings . changedAttributes ( ) ;
2018-03-09 17:42:27 +03:00
2022-02-01 12:34:03 +03:00
// set a new random password when isPrivate is enabled
if ( isPrivate && changedAttrs . isPrivate ) {
settings . set ( 'password' , randomPassword ( ) ) ;
2018-01-11 17:16:42 +03:00
2022-02-01 12:34:03 +03:00
// reset the password when isPrivate is disabled
} else if ( changedAttrs . password ) {
settings . set ( 'password' , changedAttrs . password [ 0 ] ) ;
}
}
2017-10-04 13:49:30 +03:00
2022-02-01 12:34:03 +03:00
@ action
toggleLeaveSettingsModal ( transition ) {
let leaveTransition = this . leaveSettingsTransition ;
2017-10-04 13:49:30 +03:00
2022-02-01 12:34:03 +03:00
if ( ! transition && this . showLeaveSettingsModal ) {
this . set ( 'leaveSettingsTransition' , null ) ;
this . set ( 'showLeaveSettingsModal' , false ) ;
return ;
}
2017-10-04 13:49:30 +03:00
2022-02-01 12:34:03 +03:00
if ( ! leaveTransition || transition . targetName === leaveTransition . targetName ) {
this . set ( 'leaveSettingsTransition' , transition ) ;
2017-10-04 13:49:30 +03:00
2022-02-01 12:34:03 +03:00
// if a save is running, wait for it to finish then transition
2022-02-10 13:20:03 +03:00
if ( this . saveTask . isRunning ) {
return this . saveTask . last . then ( ( ) => {
2022-02-01 12:34:03 +03:00
transition . retry ( ) ;
} ) ;
2017-10-04 13:49:30 +03:00
}
2022-02-01 12:34:03 +03:00
// we genuinely have unsaved data, show the modal
this . set ( 'showLeaveSettingsModal' , true ) ;
}
}
2017-10-04 13:49:30 +03:00
2022-02-01 12:34:03 +03:00
@ action
leaveSettings ( ) {
let transition = this . leaveSettingsTransition ;
let settings = this . settings ;
if ( ! transition ) {
this . notifications . showAlert ( 'Sorry, there was an error in the application. Please let the Ghost team know what happened.' , { type : 'error' } ) ;
return ;
}
2017-10-04 13:49:30 +03:00
2022-02-01 12:34:03 +03:00
// roll back changes on settings props
settings . rollbackAttributes ( ) ;
2017-10-04 13:49:30 +03:00
2022-02-01 12:34:03 +03:00
return transition . retry ( ) ;
}
2017-10-04 13:49:30 +03:00
2022-02-01 12:34:03 +03:00
@ action
validateFacebookUrl ( ) {
let newUrl = this . _scratchFacebook ;
let oldUrl = this . get ( 'settings.facebook' ) ;
let errMessage = '' ;
2016-03-03 11:52:27 +03:00
2022-02-01 12:34:03 +03:00
// reset errors and validation
this . get ( 'settings.errors' ) . remove ( 'facebook' ) ;
this . get ( 'settings.hasValidated' ) . removeObject ( 'facebook' ) ;
2017-11-10 19:38:30 +03:00
2022-02-01 12:34:03 +03:00
if ( newUrl === '' ) {
// Clear out the Facebook url
this . set ( 'settings.facebook' , '' ) ;
return ;
}
2016-03-03 11:52:27 +03:00
2022-02-01 12:34:03 +03:00
// _scratchFacebook will be null unless the user has input something
if ( ! newUrl ) {
newUrl = oldUrl ;
}
try {
// strip any facebook URLs out
newUrl = newUrl . replace ( /(https?:\/\/)?(www\.)?facebook\.com/i , '' ) ;
2016-05-17 21:14:14 +03:00
2022-02-01 12:34:03 +03:00
// don't allow any non-facebook urls
if ( newUrl . match ( /^(http|\/\/)/i ) ) {
throw 'invalid url' ;
2016-03-03 11:52:27 +03:00
}
2022-02-01 12:34:03 +03:00
// strip leading / if we have one then concat to full facebook URL
newUrl = newUrl . replace ( /^\// , '' ) ;
newUrl = ` https://www.facebook.com/ ${ newUrl } ` ;
2016-03-03 11:52:27 +03:00
2022-02-01 12:34:03 +03:00
// don't allow URL if it's not valid
if ( ! validator . isURL ( newUrl ) ) {
throw 'invalid url' ;
}
2017-11-10 19:38:30 +03:00
2022-02-01 12:34:03 +03:00
this . settings . set ( 'facebook' , newUrl ) ;
this . settings . notifyPropertyChange ( 'facebook' ) ;
} catch ( e ) {
if ( e === 'invalid url' ) {
errMessage = 'The URL must be in a format like '
+ 'https://www.facebook.com/yourPage' ;
this . get ( 'settings.errors' ) . add ( 'facebook' , errMessage ) ;
2016-03-03 11:52:27 +03:00
return ;
}
2022-02-01 12:34:03 +03:00
throw e ;
} finally {
this . get ( 'settings.hasValidated' ) . pushObject ( 'facebook' ) ;
}
}
2016-05-17 21:14:14 +03:00
2022-02-01 12:34:03 +03:00
@ action
validateTwitterUrl ( ) {
let newUrl = this . _scratchTwitter ;
let oldUrl = this . get ( 'settings.twitter' ) ;
let errMessage = '' ;
2016-03-03 11:52:27 +03:00
2022-02-01 12:34:03 +03:00
// reset errors and validation
this . get ( 'settings.errors' ) . remove ( 'twitter' ) ;
this . get ( 'settings.hasValidated' ) . removeObject ( 'twitter' ) ;
2016-03-03 11:52:27 +03:00
2022-02-01 12:34:03 +03:00
if ( newUrl === '' ) {
// Clear out the Twitter url
this . set ( 'settings.twitter' , '' ) ;
return ;
}
2016-03-03 11:52:27 +03:00
2022-02-01 12:34:03 +03:00
// _scratchTwitter will be null unless the user has input something
if ( ! newUrl ) {
newUrl = oldUrl ;
}
2016-05-16 21:16:40 +03:00
2022-02-01 12:34:03 +03:00
if ( newUrl . match ( /(?:twitter\.com\/)(\S+)/ ) || newUrl . match ( /([a-z\d.]+)/i ) ) {
let username = [ ] ;
2016-05-16 21:16:40 +03:00
2022-02-01 12:34:03 +03:00
if ( newUrl . match ( /(?:twitter\.com\/)(\S+)/ ) ) {
[ , username ] = newUrl . match ( /(?:twitter\.com\/)(\S+)/ ) ;
2016-05-16 21:16:40 +03:00
} else {
2022-02-01 12:34:03 +03:00
[ username ] = newUrl . match ( /([^/]+)\/?$/mi ) ;
}
// check if username starts with http or www and show error if so
if ( username . match ( /^(http|www)|(\/)/ ) || ! username . match ( /^[a-z\d._]{1,15}$/mi ) ) {
errMessage = ! username . match ( /^[a-z\d._]{1,15}$/mi ) ? 'Your Username is not a valid Twitter Username' : 'The URL must be in a format like https://twitter.com/yourUsername' ;
2018-01-11 01:57:43 +03:00
this . get ( 'settings.errors' ) . add ( 'twitter' , errMessage ) ;
this . get ( 'settings.hasValidated' ) . pushObject ( 'twitter' ) ;
2016-05-16 21:16:40 +03:00
return ;
2016-03-03 11:52:27 +03:00
}
2022-02-01 12:34:03 +03:00
newUrl = ` https://twitter.com/ ${ username } ` ;
this . settings . get ( 'hasValidated' ) . pushObject ( 'twitter' ) ;
this . settings . set ( 'twitter' , newUrl ) ;
this . settings . notifyPropertyChange ( 'twitter' ) ;
} else {
errMessage = 'The URL must be in a format like '
+ 'https://twitter.com/yourUsername' ;
this . get ( 'settings.errors' ) . add ( 'twitter' , errMessage ) ;
this . get ( 'settings.hasValidated' ) . pushObject ( 'twitter' ) ;
return ;
2014-08-19 02:56:28 +04:00
}
2022-02-01 12:34:03 +03:00
}
2018-01-11 20:43:23 +03:00
_deleteTheme ( ) {
2019-03-06 16:53:54 +03:00
let theme = this . store . peekRecord ( 'theme' , this . themeToDelete . name ) ;
2018-01-11 20:43:23 +03:00
if ( ! theme ) {
return ;
}
return theme . destroyRecord ( ) . catch ( ( error ) => {
2019-03-06 16:53:54 +03:00
this . notifications . showAPIError ( error ) ;
2018-01-11 20:43:23 +03:00
} ) ;
2022-02-01 12:34:03 +03:00
}
2018-01-11 20:43:23 +03:00
2022-02-01 12:34:03 +03:00
@ task ( function * ( ) {
2019-03-06 16:53:54 +03:00
let notifications = this . notifications ;
let config = this . config ;
2018-01-11 20:43:23 +03:00
2020-09-23 14:28:48 +03:00
if ( this . settings . get ( 'twitter' ) !== this . _scratchTwitter ) {
this . send ( 'validateTwitterUrl' ) ;
}
if ( this . settings . get ( 'facebook' ) !== this . _scratchFacebook ) {
this . send ( 'validateFacebookUrl' ) ;
}
2018-01-11 20:43:23 +03:00
try {
2021-10-28 15:59:41 +03:00
let changedAttrs = this . settings . changedAttributes ( ) ;
2019-03-06 16:53:54 +03:00
let settings = yield this . settings . save ( ) ;
2018-01-11 20:43:23 +03:00
config . set ( 'blogTitle' , settings . get ( 'title' ) ) ;
2021-10-28 15:59:41 +03:00
if ( changedAttrs . password ) {
this . frontend . loginIfNeeded ( ) ;
}
2019-05-20 18:16:19 +03:00
// this forces the document title to recompute after a blog title change
this . ui . updateDocumentTitle ( ) ;
2018-01-11 20:43:23 +03:00
return settings ;
} catch ( error ) {
if ( error ) {
notifications . showAPIError ( error , { key : 'settings.save' } ) ;
}
throw error ;
}
} )
2022-02-10 13:41:36 +03:00
saveTask ;
2022-02-01 12:34:03 +03:00
}