Ghost/core/frontend/services/theme-engine/active.js
Hannah Wolfe 9ce407966f Improved theme locale handling
- when activating a theme, we need to load the current locale
- this request used to be buried deep in the themeI18n init call
- now we surface it in the bridge and pass it down, which is closer to what we want to do with eventually initialising the frontend
with everything it needs up front (or not initialising it, if it isn't needed)

- in the related helpers we depend on the site.locale value instead of proxy -> themeI18n -> settingsCache drastically simplifying the code and removing deep requires
- site.locale is updated via middleware and can be relied upon
2021-05-05 16:13:26 +01:00

149 lines
4.5 KiB
JavaScript

/**
* # Active Theme
*
* This file defines a class of active theme, and also controls the getting and setting a single instance, as there
* can only ever be one active theme. Unlike a singleton, the active theme can change, however only in a controlled way.
*
* There are several different patterns available for keeping data private. Elsewhere in Ghost we use the
* naming convention of the _ prefix. Even though this has the downside of not being truly private, it is still one
* of the preferred options for keeping data private with the new class syntax, therefore I have kept it.
*
* No properties marked with an _ should be used directly.
*
*/
const join = require('path').join;
const _ = require('lodash');
const themeConfig = require('./config');
const themeEngines = require('./engines');
const config = require('../../../shared/config');
const engine = require('./engine');
const themeI18n = require('./i18n');
// Current instance of ActiveTheme
let currentActiveTheme;
class ActiveTheme {
/**
* @TODO this API needs to be simpler, but for now should work!
* @param {object} loadedTheme - the loaded theme object from the theme list
* @param {object} checkedTheme - the result of gscan.format for the theme we're activating
* @param {object} error - bootstrap validates the active theme, we would like to remember this error
*/
constructor(settings, loadedTheme, checkedTheme, error) {
// Assign some data, mark it all as pseudo-private
this._name = loadedTheme.name;
this._path = loadedTheme.path;
this._mounted = false;
this._error = error;
// We get passed in a locale
this._locale = settings.locale || 'en';
// @TODO: get gscan to return validated, useful package.json fields for us!
this._packageInfo = loadedTheme['package.json'];
this._partials = checkedTheme.partials;
// all custom .hbs templates (e.g. custom-about)
this._customTemplates = checkedTheme.templates.custom;
// all .hbs templates
this._templates = checkedTheme.templates.all;
// Create a theme config object
this._config = themeConfig.create(this._packageInfo);
// Create a theme engines object
this._engines = themeEngines.create(this._packageInfo);
this.initI18n();
}
get name() {
return this._name;
}
get customTemplates() {
return this._customTemplates;
}
get path() {
return this._path;
}
get partialsPath() {
return this._partials.length > 0 ? join(this.path, 'partials') : null;
}
get mounted() {
return this._mounted;
}
get error() {
return this._error;
}
hasTemplate(templateName) {
return this._templates.indexOf(templateName) > -1;
}
updateTemplateOptions(options) {
engine.updateTemplateOptions(_.merge({}, engine.getTemplateOptions(), options));
}
config(key) {
return this._config[key];
}
engine(key) {
return this._engines[key];
}
/**
*
* @param {object} options
* @param {string} [options.activeTheme]
* @param {string} [options.locale]
*/
initI18n(options = {}) {
options.activeTheme = options.activeTheme || this._name;
options.locale = options.locale || this._locale;
themeI18n.init(options);
}
mount(siteApp) {
// reset the asset hash
// @TODO: set this on the theme instead of globally, or use proper file-based hash
config.set('assetHash', null);
// clear the view cache
siteApp.cache = {};
// Set the views and engine
siteApp.set('views', this.path);
siteApp.engine('hbs', engine.configure(this.partialsPath));
this._mounted = true;
}
}
module.exports = {
get() {
return currentActiveTheme;
},
/**
* Set theme
*
* At this point we trust that the theme has been validated.
* Any handling for invalid themes should happen before we get here
*
* @TODO this API needs to be simpler, but for now should work!
* @param {object} loadedTheme - the loaded theme object from the theme list
* @param {object} checkedTheme - the result of gscan.format for the theme we're activating
* @return {ActiveTheme}
*/
set(settings, loadedTheme, checkedTheme, error) {
currentActiveTheme = new ActiveTheme(settings, loadedTheme, checkedTheme, error);
return currentActiveTheme;
}
};