mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-21 01:41:46 +03:00
060d791a63
no issue The `settings` service has been a source of confusion when writing with modern Ember patterns because it's use of the deprecated `ProxyMixin` forced all property access/setting to go via `.get()` and `.set()` whereas the rest of the system has mostly (there are a few other uses of ProxyObjects remaining) eliminated the use of the non-native get/set methods. - removed use of `ProxyMixin` in the `settings` service by grabbing the attributes off the setting model after fetching and using `Object.defineProperty()` to add native getters/setters that pass through to the model's getters/setters. Ember's autotracking automatically works across the native getters/setters so we can then use the service as if it was any other native object - updated all code to use `settings.{attrName}` directly for getting/setting instead of `.get()` and `.set()` - removed use of observer in the `customViews` service because it was being set up before the native properties had been added on the settings service meaning autotracking wasn't able to set up properly
171 lines
4.7 KiB
JavaScript
171 lines
4.7 KiB
JavaScript
import classic from 'ember-classic-decorator';
|
|
import {action, computed} from '@ember/object';
|
|
import {inject as service} from '@ember/service';
|
|
/* eslint-disable ghost/ember/alias-model-in-controller */
|
|
import Controller from '@ember/controller';
|
|
import generatePassword from 'ghost-admin/utils/password-generator';
|
|
import {
|
|
IMAGE_EXTENSIONS,
|
|
IMAGE_MIME_TYPES
|
|
} from 'ghost-admin/components/gh-image-uploader';
|
|
import {TrackedObject} from 'tracked-built-ins';
|
|
import {run} from '@ember/runloop';
|
|
import {task} from 'ember-concurrency';
|
|
import {tracked} from '@glimmer/tracking';
|
|
|
|
function randomPassword() {
|
|
let word = generatePassword(6);
|
|
let randomN = Math.floor(Math.random() * 1000);
|
|
|
|
return word + randomN;
|
|
}
|
|
|
|
@classic
|
|
export default class GeneralController extends Controller {
|
|
@service config;
|
|
@service ghostPaths;
|
|
@service notifications;
|
|
@service session;
|
|
@service settings;
|
|
@service frontend;
|
|
@service ui;
|
|
|
|
@tracked scratchValues = new TrackedObject();
|
|
|
|
availableTimezones = null;
|
|
imageExtensions = IMAGE_EXTENSIONS;
|
|
imageMimeTypes = IMAGE_MIME_TYPES;
|
|
|
|
@computed('config.blogUrl', 'settings.publicHash')
|
|
get privateRSSUrl() {
|
|
let blogUrl = this.get('config.blogUrl');
|
|
let publicHash = this.settings.publicHash;
|
|
|
|
return `${blogUrl}/${publicHash}/rss`;
|
|
}
|
|
|
|
@action
|
|
save() {
|
|
this.saveTask.perform();
|
|
}
|
|
|
|
@action
|
|
setTimezone(timezone) {
|
|
this.settings.timezone = timezone.name;
|
|
}
|
|
|
|
@action
|
|
removeImage(image) {
|
|
// setting `null` here will error as the server treats it as "null"
|
|
this.settings[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) {
|
|
event?.target.closest('.gh-setting-action')?.querySelector('input[type="file"]')?.click();
|
|
}
|
|
|
|
/**
|
|
* 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[property] = results[0].url;
|
|
}
|
|
}
|
|
|
|
@action
|
|
toggleIsPrivate(isPrivate) {
|
|
let settings = this.settings;
|
|
|
|
settings.isPrivate = isPrivate;
|
|
settings.errors.remove('password');
|
|
|
|
let changedAttrs = settings.changedAttributes();
|
|
|
|
// set a new random password when isPrivate is enabled
|
|
if (isPrivate && changedAttrs.isPrivate) {
|
|
settings.password = randomPassword();
|
|
|
|
// reset the password when isPrivate is disabled
|
|
} else if (changedAttrs.password) {
|
|
settings.password = changedAttrs.password[0];
|
|
}
|
|
}
|
|
|
|
@action
|
|
setScratchValue(property, value) {
|
|
this.scratchValues[property] = value;
|
|
}
|
|
|
|
clearScratchValues() {
|
|
this.scratchValues = new TrackedObject();
|
|
}
|
|
|
|
_deleteTheme() {
|
|
let theme = this.store.peekRecord('theme', this.themeToDelete.name);
|
|
|
|
if (!theme) {
|
|
return;
|
|
}
|
|
|
|
return theme.destroyRecord().catch((error) => {
|
|
this.notifications.showAPIError(error);
|
|
});
|
|
}
|
|
|
|
@task
|
|
*saveTask() {
|
|
let notifications = this.notifications;
|
|
let config = this.config;
|
|
|
|
try {
|
|
let changedAttrs = this.settings.changedAttributes();
|
|
let settings = yield this.settings.save();
|
|
|
|
this.clearScratchValues();
|
|
|
|
config.set('blogTitle', settings.title);
|
|
|
|
if (changedAttrs.password) {
|
|
this.frontend.loginIfNeeded();
|
|
}
|
|
|
|
// this forces the document title to recompute after a blog title change
|
|
this.ui.updateDocumentTitle();
|
|
|
|
return settings;
|
|
} catch (error) {
|
|
if (error) {
|
|
notifications.showAPIError(error, {key: 'settings.save'});
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
@action
|
|
saveViaKeyboard(event) {
|
|
event.preventDefault();
|
|
|
|
// trigger any set-on-blur actions
|
|
const focusedElement = document.activeElement;
|
|
focusedElement?.blur();
|
|
|
|
// schedule save for when set-on-blur actions have finished
|
|
run.schedule('actions', this, function () {
|
|
focusedElement?.focus();
|
|
this.saveTask.perform();
|
|
});
|
|
}
|
|
}
|