Cleaned all usages of proxy in helpers

- the proxy should always be used to access other parts of Ghost, including the urlService etc
- use consistent ES6 style for requires
- minimise use of lodash where possible
- remove circular dependency between proxy and template util
- End goal here is to enforce that the only link between helpers + the rest of Ghost is the proxy
This commit is contained in:
Hannah Wolfe 2020-03-30 21:23:02 +01:00
parent 957da0bfc5
commit 658a6dd284
26 changed files with 86 additions and 159 deletions

View File

@ -9,19 +9,16 @@
//
// Block helper: `{{#author}}{{/author}}`
// This is the default handlebars behaviour of dropping into the author object scope
const proxy = require('./proxy'),
_ = require('lodash'),
urlService = require('../services/url'),
SafeString = proxy.SafeString,
handlebars = proxy.hbs.handlebars,
templates = proxy.templates;
const {urlService, SafeString, hbs, templates} = require('./proxy');
const buildInHelpers = hbs.handlebars.helpers;
const _ = require('lodash');
/**
* @deprecated: will be removed in Ghost 3.0
*/
module.exports = function author(options) {
if (options.fn) {
return handlebars.helpers.with.call(this, this.author, options);
return buildInHelpers.with.call(this, this.author, options);
}
const autolink = _.isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true;

View File

@ -6,10 +6,8 @@
// By default, authors are separated by commas.
//
// Note that the standard {{#each authors}} implementation is unaffected by this helper.
const proxy = require('./proxy');
const _ = require('lodash');
const urlService = require('../services/url');
const {SafeString, templates} = proxy;
const {SafeString, templates, urlService} = require('./proxy');
const ghostHelperUtils = require('@tryghost/helpers').utils;
module.exports = function authors(options = {}) {

View File

@ -6,11 +6,7 @@
//
// Defaults to class="cancel-subscription-link" errorClass="cancel-subscription-error" cancelLabel="Cancel subscription" continueLabel="Continue subscription"
const proxy = require('./proxy');
const templates = proxy.templates;
const errors = proxy.errors;
const i18n = proxy.i18n;
const {templates, errors, i18n} = require('./proxy');
module.exports = function excerpt(options) {
let truncateOptions = (options || {}).hash || {};

View File

@ -5,10 +5,9 @@
//
// Defaults to words="50"
var proxy = require('./proxy'),
_ = require('lodash'),
SafeString = proxy.SafeString,
getMetaDataExcerpt = proxy.metaData.getMetaDataExcerpt;
const {SafeString, metaData} = require('./proxy');
const _ = require('lodash');
const getMetaDataExcerpt = metaData.getMetaDataExcerpt;
module.exports = function excerpt(options) {
let truncateOptions = (options || {}).hash || {};

View File

@ -1,20 +1,12 @@
// # Get Helper
// Usage: `{{#get "posts" limit="5"}}`, `{{#get "tags" limit="all"}}`
// Fetches data from the API
var proxy = require('./proxy'),
_ = require('lodash'),
Promise = require('bluebird'),
jsonpath = require('jsonpath'),
const {config, logging, errors, i18n, hbs, api} = require('./proxy');
const _ = require('lodash');
const Promise = require('bluebird');
const jsonpath = require('jsonpath');
config = proxy.config,
logging = proxy.logging,
errors = proxy.errors,
i18n = proxy.i18n,
createFrame = proxy.hbs.handlebars.createFrame,
api = proxy.api,
pathAliases,
get;
const createFrame = hbs.handlebars.createFrame;
const RESOURCES = {
posts: {
@ -32,7 +24,7 @@ const RESOURCES = {
};
// Short forms of paths which we should understand
pathAliases = {
const pathAliases = {
'post.tags': 'post.tags[*].slug',
'post.author': 'post.author.slug'
};
@ -115,7 +107,7 @@ function parseOptions(globals, data, options) {
* @param {Object} options
* @returns {Promise}
*/
get = function get(resource, options) {
module.exports = function get(resource, options) {
options = options || {};
options.hash = options.hash || {};
options.data = options.data || {};
@ -188,5 +180,3 @@ get = function get(resource, options) {
}
});
};
module.exports = get;

View File

@ -2,10 +2,8 @@
// Usage: `{{ghost_foot}}`
//
// Outputs scripts and other assets at the bottom of a Ghost theme
var proxy = require('./proxy'),
_ = require('lodash'),
SafeString = proxy.SafeString,
settingsCache = proxy.settingsCache;
const {SafeString, settingsCache} = require('./proxy');
const _ = require('lodash');
// We use the name ghost_foot to match the helper for consistency:
module.exports = function ghost_foot(options) { // eslint-disable-line camelcase

View File

@ -2,19 +2,12 @@
// Usage: `{{ghost_head}}`
//
// Outputs scripts and other assets at the top of a Ghost theme
var proxy = require('./proxy'),
_ = require('lodash'),
debug = require('ghost-ignition').debug('ghost_head'),
const {metaData, escapeExpression, SafeString, logging, settingsCache, config, blogIcon, labs} = require('./proxy');
const _ = require('lodash');
const debug = require('ghost-ignition').debug('ghost_head');
getMetaData = proxy.metaData.get,
getAssetUrl = proxy.metaData.getAssetUrl,
escapeExpression = proxy.escapeExpression,
SafeString = proxy.SafeString,
logging = proxy.logging,
settingsCache = proxy.settingsCache,
config = proxy.config,
blogIconUtils = proxy.blogIcon,
labs = proxy.labs;
const getMetaData = metaData.get;
const getAssetUrl = metaData.getAssetUrl;
function writeMetaTag(property, content, type) {
type = type || property.substring(0, 7) === 'twitter' ? 'name' : 'property';
@ -108,8 +101,8 @@ module.exports = function ghost_head(options) { // eslint-disable-line camelcase
globalCodeinjection = settingsCache.get('ghost_head'),
useStructuredData = !config.isPrivacyDisabled('useStructuredData'),
referrerPolicy = config.get('referrerPolicy') ? config.get('referrerPolicy') : 'no-referrer-when-downgrade',
favicon = blogIconUtils.getIconUrl(),
iconType = blogIconUtils.getIconType(favicon);
favicon = blogIcon.getIconUrl(),
iconType = blogIcon.getIconType(favicon);
debug('preparation complete, begin fetch');

View File

@ -4,10 +4,8 @@
//
// Checks if a post has a particular property
const proxy = require('./proxy');
const {logging, i18n} = require('./proxy');
const _ = require('lodash');
const logging = proxy.logging;
const i18n = proxy.i18n;
const validAttrs = ['tag', 'author', 'slug','visibility', 'id', 'number', 'index', 'any', 'all'];
function handleCount(ctxAttr, data) {

View File

@ -9,21 +9,21 @@
const url = require('url');
const _ = require('lodash');
const proxy = require('./proxy');
const urlUtils = proxy.urlUtils;
const {urlUtils, logging, i18n} = require('./proxy');
const STATIC_IMAGE_URL_PREFIX = `${urlUtils.STATIC_IMAGE_URL_PREFIX}`;
module.exports = function imgUrl(requestedImageUrl, options) {
// CASE: if no url is passed, e.g. `{{img_url}}` we show a warning
if (arguments.length < 2) {
proxy.logging.warn(proxy.i18n.t('warnings.helpers.img_url.attrIsRequired'));
logging.warn(i18n.t('warnings.helpers.img_url.attrIsRequired'));
return;
}
// CASE: if url is passed, but it is undefined, then the attribute was
// an unknown value, e.g. {{img_url feature_img}} and we also show a warning
if (requestedImageUrl === undefined) {
proxy.logging.warn(proxy.i18n.t('warnings.helpers.img_url.attrIsRequired'));
logging.warn(i18n.t('warnings.helpers.img_url.attrIsRequired'));
return;
}

View File

@ -1,10 +1,8 @@
// # Is Helper
// Usage: `{{#is "paged"}}`, `{{#is "index, paged"}}`
// Checks whether we're in a given context.
var proxy = require('./proxy'),
_ = require('lodash'),
logging = proxy.logging,
i18n = proxy.i18n;
const {logging, i18n} = require('./proxy');
const _ = require('lodash');
module.exports = function is(context, options) {
options = options || {};
@ -29,4 +27,3 @@ module.exports = function is(context, options) {
}
return options.inverse(this);
};

View File

@ -2,8 +2,8 @@
// Usage: `{{meta_description}}`
//
// Page description used for sharing and SEO
var proxy = require('./proxy'),
getMetaDataDescription = proxy.metaData.getMetaDataDescription;
const {metaData} = require('./proxy');
const {getMetaDataDescription} = metaData;
// We use the name meta_description to match the helper for consistency:
module.exports = function meta_description(options) { // eslint-disable-line camelcase

View File

@ -2,8 +2,8 @@
// Usage: `{{meta_title}}`
//
// Page title used for sharing and SEO
var proxy = require('./proxy'),
getMetaDataTitle = proxy.metaData.getMetaDataTitle;
const {metaData} = require('./proxy');
const {getMetaDataTitle} = metaData;
// We use the name meta_title to match the helper for consistency:
module.exports = function meta_title(options) { // eslint-disable-line camelcase

View File

@ -2,14 +2,10 @@
// `{{navigation}}`
// Outputs navigation menu of static urls
var proxy = require('./proxy'),
string = require('../../server/lib/security/string'),
_ = require('lodash'),
SafeString = proxy.SafeString,
createFrame = proxy.hbs.handlebars.createFrame,
i18n = proxy.i18n,
errors = proxy.errors,
templates = proxy.templates;
const {SafeString, i18n, errors, templates, hbs} = require('./proxy');
const {slugify} = require('@tryghost/string');
const _ = require('lodash');
const createFrame = hbs.handlebars.createFrame;
module.exports = function navigation(options) {
options = options || {};
@ -49,10 +45,6 @@ module.exports = function navigation(options) {
});
}
function _slugify(label) {
return string.safe(label);
}
// strips trailing slashes and compares urls
function _isCurrentUrl(href, currentUrl) {
if (!currentUrl) {
@ -73,7 +65,7 @@ module.exports = function navigation(options) {
var out = {};
out.current = _isCurrentUrl(e.url, currentUrl);
out.label = e.label;
out.slug = _slugify(e.label);
out.slug = slugify(e.label);
out.url = e.url;
out.secure = self.secure;
return out;

View File

@ -4,8 +4,8 @@
// `{{page_url 2}}`
//
// Returns the URL for the page specified in the current object context.
var proxy = require('./proxy'),
getPaginatedUrl = proxy.metaData.getPaginatedUrl;
const {metaData} = require('./proxy');
const getPaginatedUrl = metaData.getPaginatedUrl;
// We use the name page_url to match the helper for consistency:
module.exports = function page_url(page, options) { // eslint-disable-line camelcase

View File

@ -2,15 +2,11 @@
// `{{pagination}}`
// Outputs previous and next buttons, along with info about the current page
var proxy = require('./proxy'),
_ = require('lodash'),
errors = proxy.errors,
i18n = proxy.i18n,
createFrame = proxy.hbs.handlebars.createFrame,
templates = proxy.templates,
pagination;
const {errors, i18n, templates, hbs} = require('./proxy');
const _ = require('lodash');
const createFrame = hbs.handlebars.createFrame;
pagination = function (options) {
module.exports = function pagination(options) {
options = options || {};
options.hash = options.hash || {};
options.data = options.data || {};
@ -50,5 +46,3 @@ pagination = function (options) {
return templates.execute('pagination', this, {data});
};
module.exports = pagination;

View File

@ -10,15 +10,12 @@
// The 3rd argument is the string that will be output if the variable's value is 1
// The 4th argument is the string that will be output if the variable's value is 2+
var proxy = require('./proxy'),
_ = require('lodash'),
errors = proxy.errors,
i18n = proxy.i18n,
SafeString = proxy.SafeString;
const {errors, i18n, SafeString} = require('./proxy');
const isUndefined = require('lodash/isUndefined');
module.exports = function plural(number, options) {
if (_.isUndefined(options.hash) || _.isUndefined(options.hash.empty) ||
_.isUndefined(options.hash.singular) || _.isUndefined(options.hash.plural)) {
if (isUndefined(options.hash) || isUndefined(options.hash.empty) ||
isUndefined(options.hash.singular) || isUndefined(options.hash.plural)) {
throw new errors.IncorrectUsageError({
message: i18n.t('warnings.helpers.plural.valuesMustBeDefined')
});

View File

@ -2,9 +2,7 @@
// Usage: `{{post_class}}`
//
// Output classes for the body element
var proxy = require('./proxy'),
_ = require('lodash'),
SafeString = proxy.SafeString;
const {SafeString} = require('./proxy');
// We use the name post_class to match the helper for consistency:
module.exports = function post_class() { // eslint-disable-line camelcase
@ -32,8 +30,9 @@ module.exports = function post_class() { // eslint-disable-line camelcase
classes.push('page');
}
classes = _.reduce(classes, function (memo, item) {
classes = classes.reduce(function (memo, item) {
return memo + ' ' + item;
}, '');
return new SafeString(classes.trim());
};

View File

@ -3,22 +3,14 @@
// `{{#prev_post}}<a href ="{{url}}>previous post</a>{{/prev_post}}'
// `{{#next_post}}<a href ="{{url absolute="true">next post</a>{{/next_post}}'
var proxy = require('./proxy'),
_ = require('lodash'),
Promise = require('bluebird'),
moment = require('moment'),
const {logging, i18n, api, hbs, checks} = require('./proxy');
const get = require('lodash/get');
const Promise = require('bluebird');
const moment = require('moment');
logging = proxy.logging,
i18n = proxy.i18n,
createFrame = proxy.hbs.handlebars.createFrame,
const createFrame = hbs.handlebars.createFrame;
api = proxy.api,
isPost = proxy.checks.isPost,
fetch,
buildApiOptions;
buildApiOptions = function buildApiOptions(options, post) {
const buildApiOptions = function buildApiOptions(options, post) {
var publishedAt = moment(post.published_at).format('YYYY-MM-DD HH:mm:ss'),
slug = post.slug,
op = options.name === 'prev_post' ? '<=' : '>',
@ -35,12 +27,12 @@ buildApiOptions = function buildApiOptions(options, post) {
filter: "slug:-" + slug + "+published_at:" + op + "'" + publishedAt + "'" // eslint-disable-line quotes
};
if (_.get(options, 'hash.in')) {
if (options.hash.in === 'primary_tag' && _.get(post, 'primary_tag.slug')) {
if (get(options, 'hash.in')) {
if (options.hash.in === 'primary_tag' && get(post, 'primary_tag.slug')) {
apiOptions.filter += '+primary_tag:' + post.primary_tag.slug;
} else if (options.hash.in === 'primary_author' && _.get(post, 'primary_author.slug')) {
} else if (options.hash.in === 'primary_author' && get(post, 'primary_author.slug')) {
apiOptions.filter += '+primary_author:' + post.primary_author.slug;
} else if (options.hash.in === 'author' && _.get(post, 'author.slug')) {
} else if (options.hash.in === 'author' && get(post, 'author.slug')) {
apiOptions.filter += '+author:' + post.author.slug;
}
}
@ -48,7 +40,7 @@ buildApiOptions = function buildApiOptions(options, post) {
return apiOptions;
};
fetch = function fetch(options, data) {
const fetch = function fetch(options, data) {
const self = this;
const apiOptions = buildApiOptions(options, this);
const apiVersion = data.root._locals.apiVersion;
@ -95,7 +87,7 @@ module.exports = function prevNext(options) {
}
// Guard against trying to execute prev/next on pages, or other resources
if (!isPost(this) || this.page) {
if (!checks.isPost(this) || this.page) {
return Promise.resolve(options.inverse(this, {data: data}));
}

View File

@ -4,7 +4,7 @@
//
// Returns amount equal to the dominant denomintation of the currency.
// For example, if 2100 is passed, it will return 21.
const _ = require('lodash');
const isNumber = require('lodash/isNumber');
const {errors, i18n} = require('./proxy');
module.exports = function price(amount) {
@ -22,7 +22,7 @@ module.exports = function price(amount) {
});
}
if (!_.isNumber(amount)) {
if (!isNumber(amount)) {
throw new errors.IncorrectUsageError({
message: i18n.t('warnings.helpers.price.attrMustBeNumeric')
});

View File

@ -10,9 +10,7 @@
//
// Returns estimated reading time for post
const proxy = require('./proxy');
const schema = require('../../server/data/schema').checks;
const SafeString = proxy.SafeString;
const {SafeString, checks} = require('./proxy');
const calculateReadingTime = require('@tryghost/helpers').readingTime;
module.exports = function reading_time(options) {// eslint-disable-line camelcase
@ -20,7 +18,7 @@ module.exports = function reading_time(options) {// eslint-disable-line camelcas
options.hash = options.hash || {};
// only calculate reading time for posts
if (!schema.isPost(this)) {
if (!checks.isPost(this)) {
return null;
}

View File

@ -1,7 +1,5 @@
var hbs = require('../services/themes/engine'),
Promise = require('bluebird'),
config = require('../../server/config'),
proxy = require('./proxy');
const Promise = require('bluebird');
const {config, hbs, errors, logging} = require('./proxy');
// Register an async handlebars helper for a given handlebars instance
function asyncHelperWrapper(hbs, name, fn) {
@ -16,7 +14,7 @@ function asyncHelperWrapper(hbs, name, fn) {
Promise.resolve(fn.call(this, context, options)).then(function asyncHelperSuccess(result) {
cb(result);
}).catch(function asyncHelperError(err) {
var wrappedErr = err instanceof proxy.errors.GhostError ? err : new proxy.errors.IncorrectUsageError({
var wrappedErr = err instanceof errors.GhostError ? err : new errors.IncorrectUsageError({
err: err,
context: 'registerAsyncThemeHelper: ' + name,
errorDetails: {
@ -25,7 +23,7 @@ function asyncHelperWrapper(hbs, name, fn) {
}),
result = config.get('env') === 'development' ? wrappedErr : '';
proxy.logging.error(wrappedErr);
logging.error(wrappedErr);
cb(new hbs.SafeString(result));
});

View File

@ -5,14 +5,10 @@
// By default, tags are separated by commas.
//
// Note that the standard {{#each tags}} implementation is unaffected by this helper
const proxy = require('./proxy');
const {urlService, SafeString, templates} = require('./proxy');
const _ = require('lodash');
const ghostHelperUtils = require('@tryghost/helpers').utils;
const urlService = proxy.urlService;
const SafeString = proxy.SafeString;
const templates = proxy.templates;
module.exports = function tags(options) {
options = options || {};
options.hash = options.hash || {};

View File

@ -1,9 +1,9 @@
var templates = {},
_ = require('lodash'),
proxy = require('./proxy'),
hbs = require('../services/themes/engine');
// ## Template utils
const templates = {};
const _ = require('lodash');
const errors = require('@tryghost/errors');
const hbs = require('../services/themes/engine');
const i18n = require('../../server/lib/common/i18n');
// Execute a template helper
// All template helpers are register as partial view.
@ -11,8 +11,8 @@ templates.execute = function execute(name, context, data) {
var partial = hbs.handlebars.partials[name];
if (partial === undefined) {
throw new proxy.errors.IncorrectUsageError({
message: proxy.i18n.t('warnings.helpers.template.templateNotFound', {name: name})
throw new errors.IncorrectUsageError({
message: i18n.t('warnings.helpers.template.templateNotFound', {name: name})
});
}

View File

@ -3,9 +3,7 @@
//
// Overrides the standard behaviour of `{[title}}` to ensure the content is correctly escaped
var proxy = require('./proxy'),
SafeString = proxy.SafeString,
escapeExpression = proxy.escapeExpression;
const {SafeString, escapeExpression} = require('./proxy');
module.exports = function title() {
return new SafeString(escapeExpression(this.title || ''));

View File

@ -2,9 +2,7 @@
// Usage: `{{twitter_url}}` or `{{twitter_url author.twitter}}`
//
// Output a url for a twitter username
var proxy = require('./proxy'),
socialUrls = proxy.socialUrls,
localUtils = proxy.localUtils;
const {socialUrls, localUtils} = require('./proxy');
// We use the name twitter_url to match the helper for consistency:
module.exports = function twitter_url(username, options) { // eslint-disable-line camelcase

View File

@ -4,9 +4,8 @@
// Returns the URL for the current object scope i.e. If inside a post scope will return post permalink
// `absolute` flag outputs absolute URL, else URL is relative
var proxy = require('./proxy'),
SafeString = proxy.SafeString,
getMetaDataUrl = proxy.metaData.getMetaDataUrl;
const {SafeString, metaData} = require('./proxy');
const {getMetaDataUrl} = metaData;
module.exports = function url(options) {
var absolute = options && options.hash.absolute && options.hash.absolute !== 'false',