ghost 4.15.0 → 4.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +7 -1
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/built/screen.css.map +1 -1
- package/content/themes/casper/assets/css/screen.css +1 -1
- package/content/themes/casper/default.hbs +2 -2
- package/content/themes/casper/package.json +1 -1
- package/content/themes/casper/page.hbs +28 -26
- package/content/themes/casper/partials/post-card.hbs +2 -2
- package/content/themes/casper/post.hbs +67 -65
- package/content/themes/casper/tag.hbs +2 -2
- package/core/boot.js +7 -7
- package/core/bridge.js +4 -3
- package/core/built/assets/{chunk.3.4b1d9e20e57164ac9c29.js → chunk.3.b80d3e1e6b8556aaff3c.js} +72 -71
- package/core/built/assets/ghost-dark-f7bf2dd8d8c702716f75bfa4ccd92df2.css +1 -0
- package/core/built/assets/{ghost.min-e35cfee26d942c364166f57f3dcc9e75.js → ghost.min-52a5420ffcea6bf17761b5c59cf020e2.js} +979 -908
- package/core/built/assets/ghost.min-741246f42f000c073999a5363434ea2c.css +1 -0
- package/core/built/assets/icons/discount-bubble.svg +1 -0
- package/core/built/assets/{vendor.min-ca33abc718f21a51327841d58f8875d0.js → vendor.min-1bfc9d56d27508db88ef417deb55f16f.js} +454 -434
- package/core/frontend/apps/amp/lib/helpers/amp_analytics.js +2 -2
- package/core/frontend/apps/amp/lib/helpers/amp_components.js +2 -1
- package/core/frontend/apps/amp/lib/helpers/amp_content.js +5 -1
- package/core/frontend/apps/amp/lib/helpers/amp_style.js +1 -1
- package/core/frontend/apps/amp/lib/router.js +8 -4
- package/core/frontend/apps/private-blogging/index.js +13 -5
- package/core/frontend/apps/private-blogging/lib/helpers/input_password.js +1 -1
- package/core/frontend/apps/private-blogging/lib/middleware.js +8 -3
- package/core/frontend/helpers/asset.js +10 -2
- package/core/frontend/helpers/author.js +5 -3
- package/core/frontend/helpers/authors.js +4 -3
- package/core/frontend/helpers/body_class.js +1 -1
- package/core/frontend/helpers/cancel_link.js +9 -2
- package/core/frontend/helpers/concat.js +1 -1
- package/core/frontend/helpers/content.js +1 -1
- package/core/frontend/helpers/date.js +1 -1
- package/core/frontend/helpers/encode.js +1 -1
- package/core/frontend/helpers/excerpt.js +2 -1
- package/core/frontend/helpers/facebook_url.js +2 -1
- package/core/frontend/helpers/foreach.js +11 -2
- package/core/frontend/helpers/get.js +14 -3
- package/core/frontend/helpers/ghost_foot.js +2 -1
- package/core/frontend/helpers/ghost_head.js +10 -1
- package/core/frontend/helpers/has.js +8 -3
- package/core/frontend/helpers/img_url.js +9 -3
- package/core/frontend/helpers/is.js +7 -2
- package/core/frontend/helpers/lang.js +1 -1
- package/core/frontend/helpers/link.js +11 -2
- package/core/frontend/helpers/link_class.js +11 -2
- package/core/frontend/helpers/match.js +12 -3
- package/core/frontend/helpers/navigation.js +13 -4
- package/core/frontend/helpers/pagination.js +15 -5
- package/core/frontend/helpers/plural.js +8 -2
- package/core/frontend/helpers/post_class.js +1 -1
- package/core/frontend/helpers/prev_post.js +9 -2
- package/core/frontend/helpers/price.js +11 -6
- package/core/frontend/helpers/products.js +2 -1
- package/core/frontend/helpers/reading_time.js +4 -2
- package/core/frontend/helpers/t.js +1 -1
- package/core/frontend/helpers/tags.js +3 -1
- package/core/frontend/helpers/title.js +1 -1
- package/core/frontend/helpers/twitter_url.js +2 -1
- package/core/frontend/helpers/url.js +3 -1
- package/core/frontend/services/proxy.js +34 -57
- package/core/frontend/services/rendering.js +24 -0
- package/core/frontend/services/routing/controllers/channel.js +6 -2
- package/core/frontend/services/routing/controllers/collection.js +6 -2
- package/core/frontend/services/routing/middlewares/page-param.js +6 -2
- package/core/frontend/services/theme-engine/middleware.js +23 -6
- package/core/frontend/services/theme-engine/preview.js +31 -8
- package/core/server/adapters/scheduling/post-scheduling/scheduler-intergation.js +6 -4
- package/core/server/adapters/storage/LocalFileStorage.js +10 -4
- package/core/server/api/canary/custom-theme-settings.js +22 -0
- package/core/server/api/canary/index.js +4 -0
- package/core/server/api/canary/members.js +1 -1
- package/core/server/api/canary/redirects.js +5 -5
- package/core/server/api/canary/settings.js +16 -148
- package/core/server/api/canary/utils/serializers/output/custom-theme-settings.js +13 -0
- package/core/server/api/canary/utils/serializers/output/index.js +4 -0
- package/core/server/api/canary/utils/validators/input/settings.js +23 -1
- package/core/server/api/v2/redirects.js +3 -3
- package/core/server/api/v2/settings.js +3 -4
- package/core/server/api/v3/redirects.js +5 -5
- package/core/server/api/v3/settings.js +16 -136
- package/core/server/api/v3/utils/validators/input/settings.js +23 -1
- package/core/server/data/db/state-manager.js +1 -1
- package/core/server/data/exporter/table-lists.js +3 -1
- package/core/server/data/importer/import-manager.js +398 -0
- package/core/server/data/importer/importers/data/data-importer.js +162 -0
- package/core/server/data/importer/importers/data/index.js +1 -162
- package/core/server/data/importer/index.js +1 -379
- package/core/server/data/migrations/versions/4.16/01-add-custom-theme-settings-table.js +9 -0
- package/core/server/data/migrations/versions/4.17/01-add-custom-theme-settings-permissions.js +21 -0
- package/core/server/data/migrations/versions/4.17/02-add-offers-table.js +19 -0
- package/core/server/data/migrations/versions/4.17/03-add-offers-permissions.js +35 -0
- package/core/server/data/schema/fixtures/fixtures.json +32 -0
- package/core/server/data/schema/schema.js +33 -0
- package/core/server/models/custom-theme-setting.js +9 -0
- package/core/server/models/index.js +2 -0
- package/core/server/services/custom-theme-settings.js +8 -0
- package/core/server/services/members/api.js +4 -1
- package/core/server/services/redirects/index.js +15 -0
- package/core/{frontend → server}/services/redirects/settings.js +13 -6
- package/core/server/services/redirects/validation.js +44 -0
- package/core/{frontend/services/settings → server/services/route-settings}/default-routes.yaml +0 -0
- package/core/server/services/route-settings/default-settings-manager.js +62 -0
- package/core/server/services/route-settings/index.js +32 -1
- package/core/server/services/route-settings/route-settings.js +38 -12
- package/core/server/services/route-settings/settings-loader.js +102 -0
- package/core/{frontend/services/settings → server/services/route-settings}/validate.js +38 -28
- package/core/server/services/route-settings/yaml-parser.js +53 -0
- package/core/server/services/settings/index.js +13 -16
- package/core/server/services/settings/settings-bread-service.js +188 -0
- package/core/server/services/settings/settings-utils.js +32 -0
- package/core/server/services/themes/ThemeStorage.js +5 -4
- package/core/server/services/themes/activation-bridge.js +14 -0
- package/core/server/services/themes/validate.js +5 -2
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/api/canary/admin/routes.js +5 -1
- package/core/server/web/members/app.js +3 -0
- package/core/server/web/oauth/app.js +7 -8
- package/core/server/web/shared/middlewares/custom-redirects.js +82 -59
- package/core/server/web/site/routes.js +2 -2
- package/core/shared/config/defaults.json +2 -2
- package/core/shared/config/overrides.json +1 -1
- package/core/shared/custom-theme-settings-cache.js +3 -0
- package/core/shared/i18n/translations/en.json +2 -13
- package/core/shared/labs.js +2 -2
- package/package.json +42 -41
- package/yarn.lock +916 -901
- package/core/built/assets/ghost-dark-faf931d90e92535e6c03ca16793cbe7b.css +0 -1
- package/core/built/assets/ghost.min-7aa074ad556a8455155ac88ceaca03ab.css +0 -1
- package/core/frontend/services/redirects/index.js +0 -9
- package/core/frontend/services/redirects/validation.js +0 -28
- package/core/frontend/services/settings/ensure-settings.js +0 -47
- package/core/frontend/services/settings/index.js +0 -104
- package/core/frontend/services/settings/loader.js +0 -89
- package/core/frontend/services/settings/yaml-parser.js +0 -31
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Usage: `{{amp_analytics}}`
|
|
3
3
|
//
|
|
4
4
|
// Outputs inline scripts used for analytics
|
|
5
|
-
|
|
6
|
-
const {SafeString
|
|
5
|
+
const {settingsCache} = require('../../../../services/proxy');
|
|
6
|
+
const {SafeString} = require('../../../../services/rendering');
|
|
7
7
|
|
|
8
8
|
function ampComponents() {
|
|
9
9
|
let components = [];
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
// By default supported AMP HTML tags (no additional script tag necessary):
|
|
9
9
|
// amp-img, amp-ad, amp-embed, amp-video and amp-pixel.
|
|
10
10
|
// (less) dirty requires
|
|
11
|
-
const {
|
|
11
|
+
const {settingsCache} = require('../../../../services/proxy');
|
|
12
|
+
const {SafeString} = require('../../../../services/rendering');
|
|
12
13
|
|
|
13
14
|
function ampComponents() {
|
|
14
15
|
let components = [];
|
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
const Promise = require('bluebird');
|
|
10
10
|
|
|
11
11
|
const moment = require('moment');
|
|
12
|
-
const
|
|
12
|
+
const errors = require('@tryghost/errors');
|
|
13
|
+
const logging = require('@tryghost/logging');
|
|
14
|
+
|
|
15
|
+
const {SafeString} = require('../../../../services/rendering');
|
|
16
|
+
|
|
13
17
|
const amperizeCache = {};
|
|
14
18
|
let allowedAMPTags = [];
|
|
15
19
|
let allowedAMPAttributes = {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {SafeString, escapeExpression} = require('../../../../services/
|
|
1
|
+
const {SafeString, escapeExpression} = require('../../../../services/rendering');
|
|
2
2
|
|
|
3
3
|
module.exports = function amp_style(options) { // eslint-disable-line camelcase
|
|
4
4
|
if (options.data.site.accent_color) {
|
|
@@ -2,14 +2,18 @@ const path = require('path');
|
|
|
2
2
|
const express = require('../../../../shared/express');
|
|
3
3
|
const ampRouter = express.Router('amp');
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
const {i18n} = require('../../../services/proxy');
|
|
5
|
+
const tpl = require('@tryghost/tpl');
|
|
7
6
|
const errors = require('@tryghost/errors');
|
|
8
7
|
|
|
8
|
+
// Dirty requires
|
|
9
9
|
const urlService = require('../../../services/url');
|
|
10
10
|
const helpers = require('../../../services/routing/helpers');
|
|
11
11
|
const templateName = 'amp';
|
|
12
12
|
|
|
13
|
+
const messages = {
|
|
14
|
+
pageNotFound: 'Page not found.'
|
|
15
|
+
};
|
|
16
|
+
|
|
13
17
|
function _renderer(req, res, next) {
|
|
14
18
|
res.routerOptions = {
|
|
15
19
|
type: 'custom',
|
|
@@ -23,7 +27,7 @@ function _renderer(req, res, next) {
|
|
|
23
27
|
|
|
24
28
|
// CASE: we only support amp pages for posts that are not static pages
|
|
25
29
|
if (!data.post || data.post.page) {
|
|
26
|
-
return next(new errors.NotFoundError({message:
|
|
30
|
+
return next(new errors.NotFoundError({message: tpl(messages.pageNotFound)}));
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
// Render Call
|
|
@@ -61,7 +65,7 @@ function getPostData(req, res, next) {
|
|
|
61
65
|
|
|
62
66
|
if (!permalinks) {
|
|
63
67
|
return next(new errors.NotFoundError({
|
|
64
|
-
message:
|
|
68
|
+
message: tpl(messages.pageNotFound)
|
|
65
69
|
}));
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
const
|
|
2
|
-
const urlUtils = require('../../../shared/url-utils');
|
|
1
|
+
const tpl = require('@tryghost/tpl');
|
|
3
2
|
const logging = require('@tryghost/logging');
|
|
4
3
|
const errors = require('@tryghost/errors');
|
|
4
|
+
const urlUtils = require('../../../shared/url-utils');
|
|
5
5
|
const middleware = require('./lib/middleware');
|
|
6
6
|
const router = require('./lib/router');
|
|
7
7
|
const registerHelpers = require('./lib/helpers');
|
|
8
8
|
|
|
9
|
+
const messages = {
|
|
10
|
+
urlCannotContainPrivateSubdir: {
|
|
11
|
+
error: 'private subdirectory not allowed',
|
|
12
|
+
description: 'Your site url in config.js cannot contain a subdirectory called private.',
|
|
13
|
+
help: 'Please rename the subdirectory before restarting'
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
9
17
|
// routeKeywords.private: 'private'
|
|
10
18
|
const PRIVATE_KEYWORD = 'private';
|
|
11
19
|
|
|
@@ -17,9 +25,9 @@ let checkSubdir = function checkSubdir() {
|
|
|
17
25
|
|
|
18
26
|
if (paths.pop() === PRIVATE_KEYWORD) {
|
|
19
27
|
logging.error(new errors.GhostError({
|
|
20
|
-
message:
|
|
21
|
-
context:
|
|
22
|
-
help:
|
|
28
|
+
message: tpl(messages.urlCannotContainPrivateSubdir.error),
|
|
29
|
+
context: tpl(messages.urlCannotContainPrivateSubdir.description),
|
|
30
|
+
help: tpl(messages.urlCannotContainPrivateSubdir.help)
|
|
23
31
|
}));
|
|
24
32
|
|
|
25
33
|
// @TODO: why
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Password input used on private.hbs for password-protected blogs
|
|
5
5
|
|
|
6
6
|
// (less) dirty requires
|
|
7
|
-
const {SafeString, templates} = require('../../../../services/
|
|
7
|
+
const {SafeString, templates} = require('../../../../services/rendering');
|
|
8
8
|
|
|
9
9
|
// We use the name input_password to match the helper for consistency:
|
|
10
10
|
module.exports = function input_password(options) { // eslint-disable-line camelcase
|
|
@@ -5,12 +5,17 @@ const path = require('path');
|
|
|
5
5
|
const config = require('../../../../shared/config');
|
|
6
6
|
const urlUtils = require('../../../../shared/url-utils');
|
|
7
7
|
const constants = require('@tryghost/constants');
|
|
8
|
-
const
|
|
8
|
+
const tpl = require('@tryghost/tpl');
|
|
9
9
|
const errors = require('@tryghost/errors');
|
|
10
10
|
const settingsCache = require('../../../../shared/settings-cache');
|
|
11
11
|
// routeKeywords.private: 'private'
|
|
12
12
|
const privateRoute = '/private/';
|
|
13
13
|
|
|
14
|
+
const messages = {
|
|
15
|
+
pageNotFound: 'Page not found.',
|
|
16
|
+
wrongPassword: 'Wrong password'
|
|
17
|
+
};
|
|
18
|
+
|
|
14
19
|
function verifySessionHash(salt, hash) {
|
|
15
20
|
if (!salt || !hash) {
|
|
16
21
|
return false;
|
|
@@ -95,7 +100,7 @@ const privateBlogging = {
|
|
|
95
100
|
// CASE: RSS is disabled for private blogging e.g. they create overhead
|
|
96
101
|
if (req.path.match(/\/rss\/$/)) {
|
|
97
102
|
return next(new errors.NotFoundError({
|
|
98
|
-
message:
|
|
103
|
+
message: tpl(messages.pageNotFound)
|
|
99
104
|
}));
|
|
100
105
|
}
|
|
101
106
|
|
|
@@ -156,7 +161,7 @@ const privateBlogging = {
|
|
|
156
161
|
return res.redirect(urlUtils.urlFor({relativeUrl: forward}));
|
|
157
162
|
} else {
|
|
158
163
|
res.error = {
|
|
159
|
-
message:
|
|
164
|
+
message: tpl(messages.wrongPassword)
|
|
160
165
|
};
|
|
161
166
|
return next();
|
|
162
167
|
}
|
|
@@ -2,16 +2,24 @@
|
|
|
2
2
|
// Usage: `{{asset "css/screen.css"}}`
|
|
3
3
|
//
|
|
4
4
|
// Returns the path to the specified asset.
|
|
5
|
-
const {
|
|
5
|
+
const {metaData} = require('../services/proxy');
|
|
6
|
+
const {SafeString} = require('../services/rendering');
|
|
7
|
+
|
|
8
|
+
const errors = require('@tryghost/errors');
|
|
9
|
+
const tpl = require('@tryghost/tpl');
|
|
6
10
|
const get = require('lodash/get');
|
|
7
11
|
const {getAssetUrl} = metaData;
|
|
8
12
|
|
|
13
|
+
const messages = {
|
|
14
|
+
pathIsRequired: 'The {{asset}} helper must be passed a path'
|
|
15
|
+
};
|
|
16
|
+
|
|
9
17
|
module.exports = function asset(path, options) {
|
|
10
18
|
const hasMinFile = get(options, 'hash.hasMinFile');
|
|
11
19
|
|
|
12
20
|
if (!path) {
|
|
13
21
|
throw new errors.IncorrectUsageError({
|
|
14
|
-
message:
|
|
22
|
+
message: tpl(messages.pathIsRequired)
|
|
15
23
|
});
|
|
16
24
|
}
|
|
17
25
|
|
|
@@ -9,16 +9,18 @@
|
|
|
9
9
|
//
|
|
10
10
|
// Block helper: `{{#author}}{{/author}}`
|
|
11
11
|
// This is the default handlebars behaviour of dropping into the author object scope
|
|
12
|
-
const {urlService
|
|
13
|
-
const
|
|
12
|
+
const {urlService} = require('../services/proxy');
|
|
13
|
+
const {SafeString, escapeExpression, hbs, templates} = require('../services/rendering');
|
|
14
14
|
const isString = require('lodash/isString');
|
|
15
15
|
|
|
16
|
+
const builtInHelpers = hbs.handlebars.helpers;
|
|
17
|
+
|
|
16
18
|
/**
|
|
17
19
|
* @deprecated: single authors was superceded by multiple authors in Ghost 1.22.0
|
|
18
20
|
*/
|
|
19
21
|
module.exports = function author(options) {
|
|
20
22
|
if (options.fn) {
|
|
21
|
-
return
|
|
23
|
+
return builtInHelpers.with.call(this, this.author, options);
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
const autolink = isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true;
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
// By default, authors are separated by commas.
|
|
7
7
|
//
|
|
8
8
|
// Note that the standard {{#each authors}} implementation is unaffected by this helper.
|
|
9
|
+
const {urlService} = require('../services/proxy');
|
|
10
|
+
const {SafeString, escapeExpression, templates} = require('../services/rendering');
|
|
9
11
|
const isString = require('lodash/isString');
|
|
10
|
-
const {
|
|
11
|
-
const ghostHelperUtils = require('@tryghost/helpers').utils;
|
|
12
|
+
const {utils} = require('@tryghost/helpers');
|
|
12
13
|
|
|
13
14
|
module.exports = function authors(options = {}) {
|
|
14
15
|
options.hash = options.hash || {};
|
|
@@ -38,7 +39,7 @@ module.exports = function authors(options = {}) {
|
|
|
38
39
|
}) : escapeExpression(author.name);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
return
|
|
42
|
+
return utils.visibility.filter(authorsList, visibility, processAuthor);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
if (this.authors && this.authors.length) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Usage: `{{body_class}}`
|
|
3
3
|
//
|
|
4
4
|
// Output classes for the body element
|
|
5
|
-
const {SafeString} = require('../services/
|
|
5
|
+
const {SafeString} = require('../services/rendering');
|
|
6
6
|
|
|
7
7
|
// We use the name body_class to match the helper for consistency
|
|
8
8
|
module.exports = function body_class(options) { // eslint-disable-line camelcase
|
|
@@ -5,14 +5,21 @@
|
|
|
5
5
|
// Outputs cancel/renew links to manage subscription renewal after the subscription period ends.
|
|
6
6
|
//
|
|
7
7
|
// Defaults to class="cancel-subscription-link" errorClass="cancel-subscription-error" cancelLabel="Cancel subscription" continueLabel="Continue subscription"
|
|
8
|
+
const {labs} = require('../services/proxy');
|
|
9
|
+
const {templates} = require('../services/rendering');
|
|
8
10
|
|
|
9
|
-
const
|
|
11
|
+
const errors = require('@tryghost/errors');
|
|
12
|
+
const tpl = require('@tryghost/tpl');
|
|
13
|
+
|
|
14
|
+
const messages = {
|
|
15
|
+
invalidData: 'The {{cancel_link}} helper was used outside of a subscription context. See https://ghost.org/docs/themes/members/#cancel-links.'
|
|
16
|
+
};
|
|
10
17
|
|
|
11
18
|
function cancel_link(options) { // eslint-disable-line camelcase
|
|
12
19
|
let truncateOptions = (options || {}).hash || {};
|
|
13
20
|
|
|
14
21
|
if (this.id === undefined || this.cancel_at_period_end === undefined) {
|
|
15
|
-
throw new errors.IncorrectUsageError({message:
|
|
22
|
+
throw new errors.IncorrectUsageError({message: tpl(messages.invalidData)});
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
const data = {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
//
|
|
11
11
|
// Dev flag feature: In case of restricted content access for member-only posts, shows CTA box
|
|
12
12
|
|
|
13
|
-
const {templates, hbs, SafeString} = require('../services/
|
|
13
|
+
const {templates, hbs, SafeString} = require('../services/rendering');
|
|
14
14
|
const downsize = require('downsize');
|
|
15
15
|
const _ = require('lodash');
|
|
16
16
|
const createFrame = hbs.handlebars.createFrame;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Formats a date using moment-timezone.js. Formats published_at by default but will also take a date as a parameter
|
|
5
5
|
|
|
6
|
-
const {SafeString} = require('../services/
|
|
6
|
+
const {SafeString} = require('../services/rendering');
|
|
7
7
|
const moment = require('moment-timezone');
|
|
8
8
|
const _ = require('lodash');
|
|
9
9
|
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
//
|
|
6
6
|
// Defaults to words="50"
|
|
7
7
|
|
|
8
|
-
const {SafeString
|
|
8
|
+
const {SafeString} = require('../services/rendering');
|
|
9
|
+
const {metaData} = require('../services/proxy');
|
|
9
10
|
const _ = require('lodash');
|
|
10
11
|
const getMetaDataExcerpt = metaData.getMetaDataExcerpt;
|
|
11
12
|
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Usage: `{{facebook_url}}` or `{{facebook_url author.facebook}}`
|
|
3
3
|
//
|
|
4
4
|
// Output a url for a facebook username
|
|
5
|
-
const {socialUrls
|
|
5
|
+
const {socialUrls} = require('../services/proxy');
|
|
6
|
+
const {localUtils} = require('../services/rendering');
|
|
6
7
|
|
|
7
8
|
// We use the name facebook_url to match the helper for consistency:
|
|
8
9
|
module.exports = function facebook_url(username, options) { // eslint-disable-line camelcase
|
|
@@ -2,14 +2,23 @@
|
|
|
2
2
|
// Usage: `{{#foreach data}}{{/foreach}}`
|
|
3
3
|
//
|
|
4
4
|
// Block helper designed for looping through posts
|
|
5
|
+
const {checks} = require('../services/proxy');
|
|
6
|
+
const {hbs} = require('../services/rendering');
|
|
7
|
+
|
|
5
8
|
const _ = require('lodash');
|
|
6
|
-
const
|
|
9
|
+
const logging = require('@tryghost/logging');
|
|
10
|
+
const tpl = require('@tryghost/tpl');
|
|
11
|
+
|
|
7
12
|
const {Utils: hbsUtils, handlebars: {createFrame}} = hbs;
|
|
8
13
|
const ghostHelperUtils = require('@tryghost/helpers').utils;
|
|
9
14
|
|
|
15
|
+
const messages = {
|
|
16
|
+
iteratorNeeded: 'Need to pass an iterator to {{#foreach}}'
|
|
17
|
+
};
|
|
18
|
+
|
|
10
19
|
module.exports = function foreach(items, options) {
|
|
11
20
|
if (!options) {
|
|
12
|
-
logging.warn(
|
|
21
|
+
logging.warn(tpl(messages.iteratorNeeded));
|
|
13
22
|
}
|
|
14
23
|
|
|
15
24
|
if (hbsUtils.isFunction(items)) {
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
// # Get Helper
|
|
2
2
|
// Usage: `{{#get "posts" limit="5"}}`, `{{#get "tags" limit="all"}}`
|
|
3
3
|
// Fetches data from the API
|
|
4
|
-
const {config,
|
|
4
|
+
const {config, api, prepareContextResource} = require('../services/proxy');
|
|
5
|
+
const {hbs} = require('../services/rendering');
|
|
6
|
+
|
|
7
|
+
const logging = require('@tryghost/logging');
|
|
8
|
+
const errors = require('@tryghost/errors');
|
|
9
|
+
const tpl = require('@tryghost/tpl');
|
|
10
|
+
|
|
5
11
|
const _ = require('lodash');
|
|
6
12
|
const Promise = require('bluebird');
|
|
7
13
|
const jsonpath = require('jsonpath');
|
|
8
14
|
|
|
15
|
+
const messages = {
|
|
16
|
+
mustBeCalledAsBlock: 'The {{{helperName}}} helper must be called as a block. E.g. {{#{helperName}}}...{{/{helperName}}}',
|
|
17
|
+
invalidResource: 'Invalid resource given to get helper'
|
|
18
|
+
};
|
|
19
|
+
|
|
9
20
|
const createFrame = hbs.handlebars.createFrame;
|
|
10
21
|
|
|
11
22
|
const RESOURCES = {
|
|
@@ -121,13 +132,13 @@ module.exports = function get(resource, options) {
|
|
|
121
132
|
let returnedRowsCount;
|
|
122
133
|
|
|
123
134
|
if (!options.fn) {
|
|
124
|
-
data.error =
|
|
135
|
+
data.error = tpl(messages.mustBeCalledAsBlock, {helperName: 'get'});
|
|
125
136
|
logging.warn(data.error);
|
|
126
137
|
return Promise.resolve();
|
|
127
138
|
}
|
|
128
139
|
|
|
129
140
|
if (!RESOURCES[resource]) {
|
|
130
|
-
data.error =
|
|
141
|
+
data.error = tpl(messages.invalidResource);
|
|
131
142
|
logging.warn(data.error);
|
|
132
143
|
return Promise.resolve(options.inverse(self, {data: data}));
|
|
133
144
|
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Usage: `{{ghost_foot}}`
|
|
3
3
|
//
|
|
4
4
|
// Outputs scripts and other assets at the bottom of a Ghost theme
|
|
5
|
-
const {
|
|
5
|
+
const {settingsCache} = require('../services/proxy');
|
|
6
|
+
const {SafeString} = require('../services/rendering');
|
|
6
7
|
const _ = require('lodash');
|
|
7
8
|
|
|
8
9
|
// We use the name ghost_foot to match the helper for consistency:
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
// Usage: `{{ghost_head}}`
|
|
3
3
|
//
|
|
4
4
|
// Outputs scripts and other assets at the top of a Ghost theme
|
|
5
|
-
const {metaData,
|
|
5
|
+
const {metaData, settingsCache, config, blogIcon, urlUtils, labs} = require('../services/proxy');
|
|
6
|
+
const {escapeExpression, SafeString} = require('../services/rendering');
|
|
7
|
+
|
|
8
|
+
const logging = require('@tryghost/logging');
|
|
6
9
|
const _ = require('lodash');
|
|
7
10
|
const debug = require('@tryghost/debug')('ghost_head');
|
|
8
11
|
const templateStyles = require('./tpl/styles');
|
|
@@ -176,6 +179,12 @@ module.exports = function ghost_head(options) { // eslint-disable-line camelcase
|
|
|
176
179
|
head.push('<meta name="generator" content="Ghost ' +
|
|
177
180
|
escapeExpression(safeVersion) + '" />');
|
|
178
181
|
|
|
182
|
+
// Ghost analytics tag
|
|
183
|
+
if (labs.isSet('membersActivity')) {
|
|
184
|
+
const postId = (dataRoot && dataRoot.post) ? dataRoot.post.id : '';
|
|
185
|
+
head.push(writeMetaTag('ghost-analytics-id', postId, 'name'));
|
|
186
|
+
}
|
|
187
|
+
|
|
179
188
|
head.push('<link rel="alternate" type="application/rss+xml" title="' +
|
|
180
189
|
escapeExpression(meta.site.title) + '" href="' +
|
|
181
190
|
escapeExpression(meta.rssUrl) + '" />');
|
|
@@ -4,9 +4,14 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Checks if a post has a particular property
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const logging = require('@tryghost/logging');
|
|
8
|
+
const tpl = require('@tryghost/tpl');
|
|
8
9
|
const _ = require('lodash');
|
|
9
|
-
const validAttrs = ['tag', 'author', 'slug','visibility', 'id', 'number', 'index', 'any', 'all'];
|
|
10
|
+
const validAttrs = ['tag', 'author', 'slug', 'visibility', 'id', 'number', 'index', 'any', 'all'];
|
|
11
|
+
|
|
12
|
+
const messages = {
|
|
13
|
+
invalidAttribute: 'Invalid or no attribute given to has helper'
|
|
14
|
+
};
|
|
10
15
|
|
|
11
16
|
function handleCount(ctxAttr, data) {
|
|
12
17
|
if (!data || !_.isFinite(data.length)) {
|
|
@@ -155,7 +160,7 @@ module.exports = function has(options) {
|
|
|
155
160
|
let result;
|
|
156
161
|
|
|
157
162
|
if (_.isEmpty(attrs)) {
|
|
158
|
-
logging.warn(
|
|
163
|
+
logging.warn(tpl(messages.invalidAttribute));
|
|
159
164
|
return;
|
|
160
165
|
}
|
|
161
166
|
|
|
@@ -6,24 +6,30 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Returns the URL for the current object scope i.e. If inside a post scope will return image permalink
|
|
8
8
|
// `absolute` flag outputs absolute URL, else URL is relative.
|
|
9
|
+
const {urlUtils} = require('../services/proxy');
|
|
9
10
|
|
|
10
11
|
const url = require('url');
|
|
11
12
|
const _ = require('lodash');
|
|
12
|
-
const
|
|
13
|
+
const logging = require('@tryghost/logging');
|
|
14
|
+
const tpl = require('@tryghost/tpl');
|
|
15
|
+
|
|
16
|
+
const messages = {
|
|
17
|
+
attrIsRequired: 'Attribute is required e.g. {{img_url feature_image}}'
|
|
18
|
+
};
|
|
13
19
|
|
|
14
20
|
const STATIC_IMAGE_URL_PREFIX = `${urlUtils.STATIC_IMAGE_URL_PREFIX}`;
|
|
15
21
|
|
|
16
22
|
module.exports = function imgUrl(requestedImageUrl, options) {
|
|
17
23
|
// CASE: if no url is passed, e.g. `{{img_url}}` we show a warning
|
|
18
24
|
if (arguments.length < 2) {
|
|
19
|
-
logging.warn(
|
|
25
|
+
logging.warn(tpl(messages.attrIsRequired));
|
|
20
26
|
return;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
// CASE: if url is passed, but it is undefined, then the attribute was
|
|
24
30
|
// an unknown value, e.g. {{img_url feature_img}} and we also show a warning
|
|
25
31
|
if (requestedImageUrl === undefined) {
|
|
26
|
-
logging.warn(
|
|
32
|
+
logging.warn(tpl(messages.attrIsRequired));
|
|
27
33
|
return;
|
|
28
34
|
}
|
|
29
35
|
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
// # Is Helper
|
|
2
2
|
// Usage: `{{#is "paged"}}`, `{{#is "index, paged"}}`
|
|
3
3
|
// Checks whether we're in a given context.
|
|
4
|
-
const
|
|
4
|
+
const logging = require('@tryghost/logging');
|
|
5
|
+
const tpl = require('@tryghost/tpl');
|
|
5
6
|
const _ = require('lodash');
|
|
6
7
|
|
|
8
|
+
const messages = {
|
|
9
|
+
invalidAttribute: 'Invalid or no attribute given to is helper'
|
|
10
|
+
};
|
|
11
|
+
|
|
7
12
|
module.exports = function is(context, options) {
|
|
8
13
|
options = options || {};
|
|
9
14
|
|
|
10
15
|
const currentContext = options.data.root.context;
|
|
11
16
|
|
|
12
17
|
if (!_.isString(context)) {
|
|
13
|
-
logging.warn(
|
|
18
|
+
logging.warn(tpl(messages.invalidAttribute));
|
|
14
19
|
return;
|
|
15
20
|
}
|
|
16
21
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// Language tags in HTML and XML
|
|
13
13
|
// https://www.w3.org/International/articles/language-tags/
|
|
14
14
|
|
|
15
|
-
const {SafeString} = require('../services/
|
|
15
|
+
const {SafeString} = require('../services/rendering');
|
|
16
16
|
|
|
17
17
|
module.exports = function lang(options) {
|
|
18
18
|
const locale = options.data.site.locale;
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
// # link helper
|
|
2
|
+
const {config} = require('../services/proxy');
|
|
3
|
+
const {SafeString, localUtils} = require('../services/rendering');
|
|
4
|
+
|
|
2
5
|
const _ = require('lodash');
|
|
3
|
-
const
|
|
6
|
+
const errors = require('@tryghost/errors');
|
|
7
|
+
const tpl = require('@tryghost/tpl');
|
|
8
|
+
|
|
4
9
|
const {buildLinkClasses} = localUtils;
|
|
5
10
|
|
|
11
|
+
const messages = {
|
|
12
|
+
hrefIsRequired: 'The {{#link}}{{/link}} helper requires an href="" attribute.'
|
|
13
|
+
};
|
|
14
|
+
|
|
6
15
|
const managedAttributes = ['href', 'class', 'activeClass', 'parentActiveClass'];
|
|
7
16
|
|
|
8
17
|
function _formatAttrs(attributes) {
|
|
@@ -25,7 +34,7 @@ module.exports = function link(options) {
|
|
|
25
34
|
// If there is no href provided, this is theme dev error, so we throw an error to make this clear.
|
|
26
35
|
if (!_.has(options.hash, 'href')) {
|
|
27
36
|
throw new errors.IncorrectUsageError({
|
|
28
|
-
message:
|
|
37
|
+
message: tpl(messages.hrefIsRequired)
|
|
29
38
|
});
|
|
30
39
|
}
|
|
31
40
|
// If the href attribute is empty, this is probably a dynamic data problem, hard for theme devs to track down
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
// # link_class helper
|
|
2
|
+
const {config} = require('../services/proxy');
|
|
3
|
+
const {SafeString, localUtils} = require('../services/rendering');
|
|
4
|
+
|
|
2
5
|
const _ = require('lodash');
|
|
3
|
-
const
|
|
6
|
+
const errors = require('@tryghost/errors');
|
|
7
|
+
const tpl = require('@tryghost/tpl');
|
|
8
|
+
|
|
4
9
|
const {buildLinkClasses} = localUtils;
|
|
5
10
|
|
|
11
|
+
const messages = {
|
|
12
|
+
forIsRequired: 'The {{link_class}} helper requires a for="" attribute.'
|
|
13
|
+
};
|
|
14
|
+
|
|
6
15
|
module.exports = function link_class(options) { // eslint-disable-line camelcase
|
|
7
16
|
options = options || {};
|
|
8
17
|
options.hash = options.hash || {};
|
|
@@ -11,7 +20,7 @@ module.exports = function link_class(options) { // eslint-disable-line camelcase
|
|
|
11
20
|
// If there is no for provided, this is theme dev error, so we throw an error to make this clear.
|
|
12
21
|
if (!_.has(options.hash, 'for')) {
|
|
13
22
|
throw new errors.IncorrectUsageError({
|
|
14
|
-
message:
|
|
23
|
+
message: tpl(messages.forIsRequired)
|
|
15
24
|
});
|
|
16
25
|
}
|
|
17
26
|
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {labs} = require('../services/proxy');
|
|
2
|
+
const {SafeString} = require('../services/rendering');
|
|
3
|
+
|
|
4
|
+
const logging = require('@tryghost/logging');
|
|
5
|
+
const tpl = require('@tryghost/tpl');
|
|
6
|
+
|
|
2
7
|
const _ = require('lodash');
|
|
3
8
|
|
|
9
|
+
const messages = {
|
|
10
|
+
invalidAttribute: 'Invalid or no attribute given to match helper'
|
|
11
|
+
};
|
|
12
|
+
|
|
4
13
|
/**
|
|
5
14
|
* This is identical to the built-in if helper, except inverse/fn calls are replaced with false/true
|
|
6
15
|
* https://github.com/handlebars-lang/handlebars.js/blob/19bdace85a8d0bc5ed3a4dec4071cb08c8d003f2/lib/handlebars/helpers/if.js#L9-L20
|
|
@@ -58,7 +67,7 @@ function match(...attrs) {
|
|
|
58
67
|
let result;
|
|
59
68
|
|
|
60
69
|
if (_.isEmpty(attrs)) {
|
|
61
|
-
logging.warn(
|
|
70
|
+
logging.warn(tpl(messages.invalidAttribute));
|
|
62
71
|
return;
|
|
63
72
|
}
|
|
64
73
|
|
|
@@ -68,7 +77,7 @@ function match(...attrs) {
|
|
|
68
77
|
} else if (attrs.length === 3) {
|
|
69
78
|
result = handleMatch(attrs[0], attrs[1], attrs[2], options);
|
|
70
79
|
} else {
|
|
71
|
-
logging.warn(
|
|
80
|
+
logging.warn(tpl(messages.invalidAttribute));
|
|
72
81
|
return;
|
|
73
82
|
}
|
|
74
83
|
|