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
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const i18n = require('../../../shared/i18n');
|
|
3
|
-
const errors = require('@tryghost/errors');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Redirects are file based at the moment, but they will live in the database in the future.
|
|
7
|
-
* See V2 of https://github.com/TryGhost/Ghost/issues/7707.
|
|
8
|
-
*/
|
|
9
|
-
const validate = (redirects) => {
|
|
10
|
-
if (!_.isArray(redirects)) {
|
|
11
|
-
throw new errors.ValidationError({
|
|
12
|
-
message: i18n.t('errors.utils.redirectsWrongFormat'),
|
|
13
|
-
help: 'https://ghost.org/docs/themes/routing/#redirects'
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
_.each(redirects, function (redirect) {
|
|
18
|
-
if (!redirect.from || !redirect.to) {
|
|
19
|
-
throw new errors.ValidationError({
|
|
20
|
-
message: i18n.t('errors.utils.redirectsWrongFormat'),
|
|
21
|
-
context: redirect,
|
|
22
|
-
help: 'https://ghost.org/docs/themes/routing/#redirects'
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
module.exports.validate = validate;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const Promise = require('bluebird');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const debug = require('@tryghost/debug')('frontend:services:settings:ensure-settings');
|
|
5
|
-
const {i18n} = require('../proxy');
|
|
6
|
-
const errors = require('@tryghost/errors');
|
|
7
|
-
const config = require('../../../shared/config');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Makes sure that all supported settings files are in the
|
|
11
|
-
* `/content/settings` directory. If not, copy the default files
|
|
12
|
-
* over.
|
|
13
|
-
* @param {Array} knownSettings
|
|
14
|
-
* @returns {Promise}
|
|
15
|
-
* @description Reads the `/settings` folder of the content path and makes
|
|
16
|
-
* sure that the associated yaml file for each setting exists. If it doesn't
|
|
17
|
-
* copy the default yaml file over.
|
|
18
|
-
*/
|
|
19
|
-
module.exports = function ensureSettingsFiles(knownSettings) {
|
|
20
|
-
const contentPath = config.getContentPath('settings');
|
|
21
|
-
const defaultSettingsPath = config.get('paths').defaultSettings;
|
|
22
|
-
|
|
23
|
-
return Promise.each(knownSettings, function (setting) {
|
|
24
|
-
const fileName = `${setting}.yaml`;
|
|
25
|
-
const defaultFileName = `default-${fileName}`;
|
|
26
|
-
const filePath = path.join(contentPath, fileName);
|
|
27
|
-
|
|
28
|
-
return Promise.resolve(fs.readFile(filePath, 'utf8'))
|
|
29
|
-
.catch({code: 'ENOENT'}, () => {
|
|
30
|
-
const defaultFilePath = path.join(defaultSettingsPath, defaultFileName);
|
|
31
|
-
// CASE: file doesn't exist, copy it from our defaults
|
|
32
|
-
return fs.copy(
|
|
33
|
-
defaultFilePath,
|
|
34
|
-
filePath
|
|
35
|
-
).then(() => {
|
|
36
|
-
debug(`'${defaultFileName}' copied to ${contentPath}.`);
|
|
37
|
-
});
|
|
38
|
-
}).catch((error) => {
|
|
39
|
-
// CASE: we might have a permission error, as we can't access the directory
|
|
40
|
-
throw new errors.GhostError({
|
|
41
|
-
message: i18n.t('errors.services.settings.ensureSettings', {path: contentPath}),
|
|
42
|
-
err: error,
|
|
43
|
-
context: error.path
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
};
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const crypto = require('crypto');
|
|
3
|
-
const debug = require('@tryghost/debug')('frontend:services:settings:index');
|
|
4
|
-
const SettingsLoader = require('./loader');
|
|
5
|
-
const ensureSettingsFiles = require('./ensure-settings');
|
|
6
|
-
|
|
7
|
-
const errors = require('@tryghost/errors');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* md5 hashes of default settings
|
|
11
|
-
*/
|
|
12
|
-
const defaultHashes = {
|
|
13
|
-
routes: '3d180d52c663d173a6be791ef411ed01'
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const calculateHash = (data) => {
|
|
17
|
-
return crypto.createHash('md5')
|
|
18
|
-
.update(data, 'binary')
|
|
19
|
-
.digest('hex');
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
module.exports = {
|
|
23
|
-
init: function () {
|
|
24
|
-
const knownSettings = this.knownSettings();
|
|
25
|
-
|
|
26
|
-
debug('init settings service for:', knownSettings);
|
|
27
|
-
|
|
28
|
-
// Make sure that supported settings files are available
|
|
29
|
-
// inside of the `content/setting` directory
|
|
30
|
-
return ensureSettingsFiles(knownSettings);
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Global place to switch on more available settings.
|
|
35
|
-
*/
|
|
36
|
-
knownSettings: function knownSettings() {
|
|
37
|
-
return ['routes'];
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Getter for YAML settings.
|
|
42
|
-
* Example: `settings.get('routes').then(...)`
|
|
43
|
-
* will return an Object like this:
|
|
44
|
-
* {routes: {}, collections: {}, resources: {}}
|
|
45
|
-
* @param {String} setting type of supported setting.
|
|
46
|
-
* @returns {Object} settingsFile
|
|
47
|
-
* @description Returns settings object as defined per YAML files in
|
|
48
|
-
* `/content/settings` directory.
|
|
49
|
-
*/
|
|
50
|
-
get: function get(setting) {
|
|
51
|
-
const knownSettings = this.knownSettings();
|
|
52
|
-
|
|
53
|
-
// CASE: this should be an edge case and only if internal usage of the
|
|
54
|
-
// getter is incorrect.
|
|
55
|
-
if (!setting || _.indexOf(knownSettings, setting) < 0) {
|
|
56
|
-
throw new errors.IncorrectUsageError({
|
|
57
|
-
message: `Requested setting is not supported: '${setting}'.`,
|
|
58
|
-
help: `Please use only the supported settings: ${knownSettings}.`
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return SettingsLoader(setting);
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Getter for all YAML settings.
|
|
67
|
-
* Example: `settings.getAll().then(...)`
|
|
68
|
-
* will return an Object like this (assuming we're supporting `routes`
|
|
69
|
-
* and `globals`):
|
|
70
|
-
* {
|
|
71
|
-
* routes: {
|
|
72
|
-
* routes: null,
|
|
73
|
-
* collections: { '/': [Object] },
|
|
74
|
-
* resources: { tag: '/tag/{slug}/', author: '/author/{slug}/' }
|
|
75
|
-
* },
|
|
76
|
-
* globals: {
|
|
77
|
-
* config: { url: 'testblog.com' }
|
|
78
|
-
* }
|
|
79
|
-
* }
|
|
80
|
-
* @returns {Object} settingsObject
|
|
81
|
-
* @description Returns all settings object as defined per YAML files in
|
|
82
|
-
* `/content/settings` directory.
|
|
83
|
-
*/
|
|
84
|
-
getAll: function getAll() {
|
|
85
|
-
const knownSettings = this.knownSettings();
|
|
86
|
-
const settingsToReturn = {};
|
|
87
|
-
|
|
88
|
-
_.each(knownSettings, function (setting) {
|
|
89
|
-
settingsToReturn[setting] = SettingsLoader(setting);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
return settingsToReturn;
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
getDefaulHash: (setting) => {
|
|
96
|
-
return defaultHashes[setting];
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
getCurrentHash: async (setting) => {
|
|
100
|
-
const data = await SettingsLoader.loadSettings(setting);
|
|
101
|
-
|
|
102
|
-
return calculateHash(JSON.stringify(data));
|
|
103
|
-
}
|
|
104
|
-
};
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const debug = require('@tryghost/debug')('frontend:services:settings:settings-loader');
|
|
4
|
-
const {i18n} = require('../proxy');
|
|
5
|
-
const errors = require('@tryghost/errors');
|
|
6
|
-
const config = require('../../../shared/config');
|
|
7
|
-
const yamlParser = require('./yaml-parser');
|
|
8
|
-
const validate = require('./validate');
|
|
9
|
-
|
|
10
|
-
const getSettingFilePath = (setting) => {
|
|
11
|
-
// we only support the `yaml` file extension. `yml` will be ignored.
|
|
12
|
-
const fileName = `${setting}.yaml`;
|
|
13
|
-
const contentPath = config.getContentPath('settings');
|
|
14
|
-
const filePath = path.join(contentPath, fileName);
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
fileName,
|
|
18
|
-
contentPath,
|
|
19
|
-
filePath
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Functionally same as loadSettingsSync with exception of loading
|
|
25
|
-
* settigs asyncronously. This method is used at new places to read settings
|
|
26
|
-
* to prevent blocking the eventloop
|
|
27
|
-
*
|
|
28
|
-
* @param {String} setting the requested settings as defined in setting knownSettings
|
|
29
|
-
* @returns {Object} settingsFile
|
|
30
|
-
*/
|
|
31
|
-
const loadSettings = async (setting) => {
|
|
32
|
-
const {fileName, contentPath, filePath} = getSettingFilePath(setting);
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
const file = await fs.readFile(filePath, 'utf8');
|
|
36
|
-
debug('settings file found for', setting);
|
|
37
|
-
|
|
38
|
-
const object = yamlParser(file, fileName);
|
|
39
|
-
return validate(object);
|
|
40
|
-
} catch (err) {
|
|
41
|
-
if (errors.utils.isIgnitionError(err)) {
|
|
42
|
-
throw err;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
throw new errors.GhostError({
|
|
46
|
-
message: i18n.t('errors.services.settings.loader', {
|
|
47
|
-
setting: setting,
|
|
48
|
-
path: contentPath
|
|
49
|
-
}),
|
|
50
|
-
context: filePath,
|
|
51
|
-
err: err
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Reads the desired settings YAML file and passes the
|
|
58
|
-
* file to the YAML parser which then returns a JSON object.
|
|
59
|
-
* NOTE: loading happens syncronously
|
|
60
|
-
*
|
|
61
|
-
* @param {String} setting the requested settings as defined in setting knownSettings
|
|
62
|
-
* @returns {Object} settingsFile
|
|
63
|
-
*/
|
|
64
|
-
module.exports = function loadSettingsSync(setting) {
|
|
65
|
-
const {fileName, contentPath, filePath} = getSettingFilePath(setting);
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const file = fs.readFileSync(filePath, 'utf8');
|
|
69
|
-
debug('settings file found for', setting);
|
|
70
|
-
|
|
71
|
-
const object = yamlParser(file, fileName);
|
|
72
|
-
return validate(object);
|
|
73
|
-
} catch (err) {
|
|
74
|
-
if (errors.utils.isIgnitionError(err)) {
|
|
75
|
-
throw err;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
throw new errors.GhostError({
|
|
79
|
-
message: i18n.t('errors.services.settings.loader', {
|
|
80
|
-
setting: setting,
|
|
81
|
-
path: contentPath
|
|
82
|
-
}),
|
|
83
|
-
context: filePath,
|
|
84
|
-
err: err
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
module.exports.loadSettings = loadSettings;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const yaml = require('js-yaml');
|
|
2
|
-
const debug = require('@tryghost/debug')('frontend:services:settings:yaml-parser');
|
|
3
|
-
const {i18n} = require('../proxy');
|
|
4
|
-
const errors = require('@tryghost/errors');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Takes a YAML file, parses it and returns a JSON Object
|
|
8
|
-
* @param {YAML} file the YAML file utf8 encoded
|
|
9
|
-
* @param {String} fileName the name of the file incl. extension
|
|
10
|
-
* @returns {Object} parsed
|
|
11
|
-
*/
|
|
12
|
-
module.exports = function parseYaml(file, fileName) {
|
|
13
|
-
try {
|
|
14
|
-
const parsed = yaml.load(file);
|
|
15
|
-
|
|
16
|
-
debug('YAML settings file parsed:', fileName);
|
|
17
|
-
|
|
18
|
-
return parsed;
|
|
19
|
-
} catch (error) {
|
|
20
|
-
// CASE: parsing failed, `js-yaml` tells us exactly what and where in the
|
|
21
|
-
// `reason` property as well as in the message.
|
|
22
|
-
// As the file uploaded is invalid, the person uploading must fix this - it's a 4xx error
|
|
23
|
-
throw new errors.IncorrectUsageError({
|
|
24
|
-
message: i18n.t('errors.services.settings.yaml.error', {file: fileName, context: error.reason}),
|
|
25
|
-
code: 'YAML_PARSER_ERROR',
|
|
26
|
-
context: error.message,
|
|
27
|
-
err: error,
|
|
28
|
-
help: i18n.t('errors.services.settings.yaml.help', {file: fileName})
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
};
|