mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-15 19:52:01 +03:00
85c2628049
closes https://github.com/TryGhost/Team/issues/535 To ensure accent color is always set for a site, this updates brand settings screen and launch wizard to not allow empty accent color to be set or updated, adding the relevant error on the page which needs to be fixed before saving or continuing on the screens. - Shows error message for empty accent color on brand settings and launch wizard - Shows error message for invalid accent color on brand settings and launch wizard - Blocks save and continue with invalid/empty accent color on brand settings and launch wizard Co-authored-by: Peter Zimon <zimo@ghost.org>
162 lines
4.7 KiB
JavaScript
162 lines
4.7 KiB
JavaScript
import Component from '@glimmer/component';
|
|
import config from 'ghost-admin/config/environment';
|
|
import {
|
|
ICON_EXTENSIONS,
|
|
ICON_MIME_TYPES,
|
|
IMAGE_EXTENSIONS,
|
|
IMAGE_MIME_TYPES
|
|
} from 'ghost-admin/components/gh-image-uploader';
|
|
import {action} from '@ember/object';
|
|
import {htmlSafe} from '@ember/string';
|
|
import {inject as service} from '@ember/service';
|
|
import {task} from 'ember-concurrency-decorators';
|
|
import {timeout} from 'ember-concurrency';
|
|
|
|
export default class GhBrandSettingsFormComponent extends Component {
|
|
@service ajax;
|
|
@service config;
|
|
@service ghostPaths;
|
|
@service settings;
|
|
|
|
iconExtensions = ICON_EXTENSIONS;
|
|
iconMimeTypes = ICON_MIME_TYPES;
|
|
imageExtensions = IMAGE_EXTENSIONS;
|
|
imageMimeTypes = IMAGE_MIME_TYPES;
|
|
|
|
get accentColor() {
|
|
const color = this.settings.get('accentColor');
|
|
if (color && color[0] === '#') {
|
|
return color.slice(1);
|
|
}
|
|
return color;
|
|
}
|
|
|
|
get accentColorPickerValue() {
|
|
return this.settings.get('accentColor') || '#ffffff';
|
|
}
|
|
|
|
get accentColorBgStyle() {
|
|
return htmlSafe(`background-color: ${this.accentColorPickerValue}`);
|
|
}
|
|
|
|
get previewData() {
|
|
const params = new URLSearchParams();
|
|
|
|
params.append('c', this.accentColorPickerValue);
|
|
params.append('icon', this.settings.get('icon'));
|
|
params.append('logo', this.settings.get('logo'));
|
|
params.append('cover', this.settings.get('coverImage'));
|
|
|
|
return params.toString();
|
|
}
|
|
|
|
constructor() {
|
|
super(...arguments);
|
|
this.updatePreviewTask.perform();
|
|
}
|
|
|
|
willDestroy() {
|
|
this.settings.errors.remove('accentColor');
|
|
this.settings.rollbackAttributes();
|
|
}
|
|
|
|
@action
|
|
triggerFileDialog({target}) {
|
|
target.closest('.gh-setting-action')?.querySelector('input[type="file"]')?.click();
|
|
}
|
|
|
|
@action
|
|
async imageUploaded(property, results) {
|
|
if (results[0]) {
|
|
this.settings.set(property, results[0].url);
|
|
this.updatePreviewTask.perform();
|
|
}
|
|
}
|
|
|
|
@action
|
|
async removeImage(imageName) {
|
|
this.settings.set(imageName, '');
|
|
this.updatePreviewTask.perform();
|
|
}
|
|
|
|
@action
|
|
async updateAccentColor(event) {
|
|
let newColor = event.target.value;
|
|
const oldColor = this.settings.get('accentColor');
|
|
|
|
// reset errors and validation
|
|
this.settings.errors.remove('accentColor');
|
|
this.settings.hasValidated.removeObject('accentColor');
|
|
|
|
if (newColor === '') {
|
|
if (newColor === oldColor) {
|
|
return;
|
|
}
|
|
|
|
// Don't allow empty accent color
|
|
this.settings.errors.add('accentColor', 'Please select an accent color');
|
|
this.settings.hasValidated.pushObject('accentColor');
|
|
return;
|
|
}
|
|
|
|
// accentColor will be null unless the user has input something
|
|
if (!newColor) {
|
|
newColor = oldColor;
|
|
}
|
|
|
|
if (newColor[0] !== '#') {
|
|
newColor = `#${newColor}`;
|
|
}
|
|
|
|
if (newColor.match(/#[0-9A-Fa-f]{6}$/)) {
|
|
if (newColor === oldColor) {
|
|
return;
|
|
}
|
|
|
|
this.settings.set('accentColor', newColor);
|
|
this.updatePreviewTask.perform();
|
|
} else {
|
|
this.settings.errors.add('accentColor', 'Please enter a color in hex format');
|
|
this.settings.hasValidated.pushObject('accentColor');
|
|
}
|
|
}
|
|
|
|
@task({restartable: true})
|
|
*debounceUpdateAccentColor(event) {
|
|
yield timeout(500);
|
|
this.updateAccentColor(event);
|
|
}
|
|
|
|
@task
|
|
*updatePreviewTask() {
|
|
// skip during testing because we don't have mocks for the front-end
|
|
if (config.environment === 'test') {
|
|
return;
|
|
}
|
|
|
|
// grab the preview html
|
|
const ajaxOptions = {
|
|
contentType: 'text/html;charset=utf-8',
|
|
dataType: 'text',
|
|
headers: {
|
|
'x-ghost-preview': this.previewData
|
|
}
|
|
};
|
|
const frontendUrl = this.config.get('blogUrl');
|
|
const previewContents = yield this.ajax.post(frontendUrl, ajaxOptions);
|
|
|
|
// inject extra CSS to disable navigation and prevent clicks
|
|
const injectedCss = `html { pointer-events: none; }`;
|
|
|
|
const domParser = new DOMParser();
|
|
const htmlDoc = domParser.parseFromString(previewContents, 'text/html');
|
|
|
|
const stylesheet = htmlDoc.querySelector('style');
|
|
const originalCSS = stylesheet.innerHTML;
|
|
stylesheet.innerHTML = `${originalCSS}\n\n${injectedCss}`;
|
|
|
|
// replace the iframe contents with the doctored preview html
|
|
this.args.replacePreviewContents(htmlDoc.documentElement.innerHTML);
|
|
}
|
|
}
|