ghost 4.22.3 → 4.25.0
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/.c8rc.json +24 -0
- package/.eslintrc.js +39 -0
- package/Gruntfile.js +0 -1
- package/content/public/README.md +3 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/casper.js.map +1 -1
- package/content/themes/casper/assets/built/global.css +1 -1
- package/content/themes/casper/assets/built/global.css.map +1 -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/global.css +6 -1
- package/content/themes/casper/assets/css/screen.css +32 -216
- package/content/themes/casper/default.hbs +2 -2
- package/content/themes/casper/package.json +3 -2
- package/content/themes/casper/post.hbs +1 -1
- package/content/themes/casper/yarn.lock +173 -123
- package/core/app.js +12 -1
- package/core/boot.js +47 -28
- package/core/bridge.js +10 -10
- package/core/built/assets/{chunk.3.324fd0cc598c73650219.js → chunk.3.8f95b516d88ff4eec64c.js} +18 -18
- package/core/built/assets/ghost-dark-d690e732e17ffc794e2e59c1467ca282.css +1 -0
- package/core/built/assets/ghost.min-043bb7480a0810109b130f13b2a4235e.css +1 -0
- package/core/built/assets/{ghost.min-7da921f6c6cac3fe10da1ba104575440.js → ghost.min-bc72f685c1c9adc9885925c1412435a5.js} +563 -605
- package/core/built/assets/icons/audio-upload.svg +8 -0
- package/core/built/assets/icons/powered-by-tenor.svg +35 -0
- package/core/built/assets/icons/tenor.svg +7 -0
- package/core/built/assets/{vendor.min-413f887176a041e6dbf88214ca9a7481.js → vendor.min-d1234c632a54502777c34e50752fa3fc.js} +4622 -3631
- package/core/frontend/apps/amp/lib/helpers/amp_content.js +2 -2
- package/core/frontend/apps/amp/lib/views/amp.hbs +112 -0
- package/core/frontend/apps/private-blogging/index.js +1 -1
- package/core/frontend/apps/private-blogging/lib/router.js +1 -1
- package/core/frontend/services/apps/index.js +1 -1
- package/core/frontend/services/apps/loader.js +3 -3
- package/core/frontend/services/card-assets/index.js +0 -12
- package/core/frontend/services/card-assets/service.js +29 -28
- package/core/frontend/services/helpers/handlebars.js +1 -1
- package/core/frontend/services/routing/CollectionRouter.js +4 -5
- package/core/frontend/services/routing/EmailRouter.js +1 -1
- package/core/frontend/services/routing/ParentRouter.js +0 -8
- package/core/frontend/services/routing/PreviewRouter.js +1 -1
- package/core/frontend/services/routing/StaticPagesRouter.js +1 -1
- package/core/frontend/services/routing/StaticRoutesRouter.js +4 -4
- package/core/frontend/services/routing/TaxonomyRouter.js +3 -3
- package/core/frontend/services/routing/{middlewares → middleware}/index.js +0 -0
- package/core/frontend/services/routing/{middlewares → middleware}/page-param.js +0 -0
- package/core/frontend/services/routing/router-manager.js +7 -2
- package/core/frontend/services/rss/generate-feed.js +2 -1
- package/core/frontend/services/theme-engine/middleware/ensure-active-theme.js +34 -0
- package/core/frontend/services/theme-engine/middleware/index.js +6 -0
- package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +116 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-data.js +9 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +57 -0
- package/core/frontend/src/cards/css/bookmark.css +72 -47
- package/core/frontend/src/cards/css/button.css +4 -0
- package/core/frontend/src/cards/css/callout.css +40 -3
- package/core/frontend/src/cards/css/gallery.css +15 -10
- package/core/frontend/src/cards/css/nft.css +20 -11
- package/core/frontend/src/cards/css/toggle.css +58 -0
- package/core/frontend/src/cards/js/toggle.js +16 -0
- package/core/frontend/web/middleware/error-handler.js +93 -0
- package/core/frontend/web/middleware/handle-image-sizes.js +3 -6
- package/core/frontend/web/middleware/index.js +1 -0
- package/core/frontend/web/middleware/serve-public-file.js +39 -16
- package/core/frontend/web/site.js +11 -14
- package/core/server/adapters/scheduling/SchedulingDefault.js +2 -2
- package/core/server/adapters/storage/LocalStorageBase.js +2 -2
- package/core/server/api/canary/authentication.js +1 -1
- package/core/server/api/canary/db.js +2 -2
- package/core/server/api/canary/media.js +3 -2
- package/core/server/api/canary/oembed.js +16 -1
- package/core/server/api/canary/session.js +1 -1
- package/core/server/api/canary/slugs.js +1 -1
- package/core/server/api/canary/utils/permissions.js +2 -2
- package/core/server/api/canary/utils/serializers/output/config.js +2 -6
- package/core/server/api/v2/authentication.js +1 -1
- package/core/server/api/v2/db.js +2 -2
- package/core/server/api/v2/session.js +1 -1
- package/core/server/api/v2/slugs.js +1 -1
- package/core/server/api/v2/utils/permissions.js +2 -2
- package/core/server/api/v3/authentication.js +1 -1
- package/core/server/api/v3/db.js +2 -2
- package/core/server/api/v3/session.js +1 -1
- package/core/server/api/v3/slugs.js +1 -1
- package/core/server/api/v3/utils/permissions.js +2 -2
- package/core/server/data/db/connection.js +7 -0
- package/core/server/data/db/state-manager.js +4 -4
- package/core/server/data/exporter/export-filename.js +1 -1
- package/core/server/data/importer/handlers/json.js +1 -1
- package/core/server/data/importer/import-manager.js +1 -1
- package/core/server/data/importer/importers/data/base.js +1 -1
- package/core/server/data/importer/importers/data/data-importer.js +3 -3
- package/core/server/data/migrations/init/2-create-fixtures.js +3 -20
- package/core/server/data/migrations/utils.js +2 -2
- package/core/server/data/migrations/versions/1.21/1-add-contributor-role.js +5 -5
- package/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +1 -0
- package/core/server/data/migrations/versions/2.15/2-insert-zapier-integration.js +3 -3
- package/core/server/data/migrations/versions/2.2/3-insert-admin-integration-role.js +5 -5
- package/core/server/data/migrations/versions/2.27/1-insert-ghost-db-backup-role.js +5 -6
- package/core/server/data/migrations/versions/2.27/2-insert-db-backup-integration.js +3 -4
- package/core/server/data/migrations/versions/2.28/3-insert-ghost-scheduler-role.js +7 -7
- package/core/server/data/migrations/versions/2.28/4-insert-scheduler-integration.js +3 -3
- package/core/server/data/migrations/versions/3.1/08-add-uuid-values-to-members.js +1 -0
- package/core/server/data/migrations/versions/3.22/02-settings-key-renames.js +2 -0
- package/core/server/data/migrations/versions/3.22/05-migrate-members-subscription-settings.js +3 -0
- package/core/server/data/migrations/versions/3.22/06-migrate-stripe-connect-settings.js +2 -0
- package/core/server/data/migrations/versions/3.23/01-migrate-bulk-email-settings.js +1 -0
- package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -0
- package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -0
- package/core/server/data/migrations/versions/3.38/04-populate-recipient-filter-column.js +2 -0
- package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +2 -0
- package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +4 -0
- package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +5 -0
- package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -0
- package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -0
- package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +2 -1
- package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +1 -0
- package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +3 -0
- package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +1 -0
- package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +2 -0
- package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +59 -0
- package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -0
- package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +1 -0
- package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -0
- package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -0
- package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +1 -0
- package/core/server/data/schema/commands.js +2 -2
- package/core/server/data/schema/fixtures/fixture-manager.js +340 -0
- package/core/server/data/schema/fixtures/index.js +8 -2
- package/core/server/ghost-server.js +2 -2
- package/core/server/lib/image/image-size.js +2 -2
- package/core/server/models/base/listeners.js +2 -2
- package/core/server/models/member-email-change-event.js +2 -2
- package/core/server/models/member-login-event.js +2 -2
- package/core/server/models/member-paid-subscription-event.js +3 -3
- package/core/server/models/member-payment-event.js +3 -3
- package/core/server/models/member-product-event.js +6 -6
- package/core/server/models/member-status-event.js +5 -3
- package/core/server/models/member-subscribe-event.js +9 -3
- package/core/server/models/relations/authors.js +1 -1
- package/core/server/models/settings.js +1 -1
- package/core/server/services/auth/passwordreset.js +1 -1
- package/core/server/services/auth/setup.js +1 -1
- package/core/server/services/email-analytics/jobs/index.js +1 -1
- package/core/server/services/mega/mega.js +6 -4
- package/core/server/services/mega/post-email-serializer.js +5 -1
- package/core/server/services/mega/segment-parser.js +1 -2
- package/core/server/services/mega/template.js +52 -37
- package/core/server/services/members/api.js +22 -0
- package/core/server/services/members/config.js +1 -1
- package/core/server/services/members/emails/signup-paid.js +168 -0
- package/core/server/services/members/service.js +6 -2
- package/core/server/services/members/stripe-connect.js +4 -2
- package/core/server/services/nft-oembed.js +13 -22
- package/core/server/services/oembed.js +28 -24
- package/core/server/services/permissions/can-this.js +1 -1
- package/core/server/services/public-config/config.js +1 -1
- package/core/server/services/redirects/api.js +20 -25
- package/core/server/services/redirects/index.js +18 -10
- package/core/server/services/redirects/utils.js +14 -0
- package/core/server/services/redirects/validation.js +10 -0
- package/core/server/services/route-settings/default-settings-manager.js +1 -1
- package/core/server/services/route-settings/index.js +40 -17
- package/core/server/services/route-settings/route-settings.js +120 -115
- package/core/server/services/route-settings/settings-loader.js +18 -36
- package/core/server/services/route-settings/yaml-parser.js +1 -1
- package/core/server/services/slack.js +1 -1
- package/core/server/services/themes/activation-bridge.js +3 -3
- package/core/server/services/themes/storage.js +2 -2
- package/core/server/services/twitter-embed.js +80 -0
- package/core/server/services/url/LocalFileCache.js +75 -0
- package/core/server/services/url/Resources.js +8 -2
- package/core/server/services/url/UrlGenerator.js +23 -20
- package/core/server/services/url/UrlService.js +75 -63
- package/core/server/services/url/index.js +17 -3
- package/core/server/services/xmlrpc.js +2 -2
- package/core/server/web/admin/app.js +7 -10
- package/core/server/web/admin/controller.js +35 -12
- package/core/server/web/admin/middleware/redirect-admin-urls.js +15 -0
- 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/app.js +1 -1
- package/core/server/web/api/canary/admin/app.js +3 -6
- package/core/server/web/api/canary/admin/middleware.js +7 -7
- package/core/server/web/api/canary/admin/routes.js +5 -5
- package/core/server/web/api/canary/content/app.js +3 -6
- package/core/server/web/api/canary/content/middleware.js +3 -3
- package/core/server/web/api/v2/admin/app.js +3 -6
- package/core/server/web/api/v2/admin/middleware.js +7 -7
- package/core/server/web/api/v2/admin/routes.js +5 -5
- package/core/server/web/api/v2/content/app.js +3 -6
- package/core/server/web/api/v2/content/middleware.js +3 -3
- package/core/server/web/api/v3/admin/app.js +3 -6
- package/core/server/web/api/v3/admin/middleware.js +7 -7
- package/core/server/web/api/v3/admin/routes.js +5 -5
- package/core/server/web/api/v3/content/app.js +3 -6
- package/core/server/web/api/v3/content/middleware.js +3 -3
- package/core/server/web/members/app.js +6 -9
- package/core/server/web/oauth/app.js +0 -4
- package/core/server/web/parent/app.js +17 -9
- package/core/server/web/parent/frontend.js +1 -1
- package/core/server/web/shared/index.js +2 -2
- package/core/server/web/shared/{middlewares → middleware}/api/index.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/api/spam-prevention.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/brute.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/cache-control.js +0 -0
- package/core/server/web/shared/middleware/error-handler.js +224 -0
- package/core/server/web/shared/{middlewares → middleware}/index.js +0 -4
- package/core/server/web/shared/{middlewares → middleware}/pretty-urls.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/uncapitalise.js +0 -0
- package/core/server/web/shared/{middlewares → middleware}/url-redirects.js +0 -0
- package/core/shared/config/defaults.json +13 -1
- package/core/shared/config/helpers.js +42 -0
- package/core/shared/config/loader.js +1 -1
- package/core/shared/labs.js +9 -5
- package/core/shared/sentry.js +1 -1
- package/loggingrc.js +19 -20
- package/package.json +38 -37
- package/yarn.lock +1064 -892
- package/content/themes/casper/assets/js/gallery-card.js +0 -24
- package/core/built/assets/ghost-dark-39fb496d051565531062d7e047d1c0b1.css +0 -1
- package/core/built/assets/ghost.min-4207edfc1ae0a3f9f6505ca00d20b0c0.css +0 -1
- package/core/frontend/services/theme-engine/middleware.js +0 -209
- package/core/server/data/schema/fixtures/utils.js +0 -321
- package/core/server/web/parent/vhost-utils.js +0 -39
- package/core/server/web/shared/middlewares/error-handler.js +0 -329
- package/core/server/web/shared/middlewares/maintenance.js +0 -25
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const hbs = require('./engine');
|
|
3
|
-
const urlUtils = require('../../../shared/url-utils');
|
|
4
|
-
const {api} = require('../proxy');
|
|
5
|
-
const errors = require('@tryghost/errors');
|
|
6
|
-
const tpl = require('@tryghost/tpl');
|
|
7
|
-
const settingsCache = require('../../../shared/settings-cache');
|
|
8
|
-
const customThemeSettingsCache = require('../../../shared/custom-theme-settings-cache');
|
|
9
|
-
const labs = require('../../../shared/labs');
|
|
10
|
-
const activeTheme = require('./active');
|
|
11
|
-
const preview = require('./preview');
|
|
12
|
-
|
|
13
|
-
const messages = {
|
|
14
|
-
missingTheme: 'The currently active theme "{theme}" is missing.'
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// ### Ensure Active Theme
|
|
18
|
-
// Ensure there's a properly set & mounted active theme before attempting to serve a site request
|
|
19
|
-
// If there is no active theme, throw an error
|
|
20
|
-
// Else, ensure the active theme is mounted
|
|
21
|
-
function ensureActiveTheme(req, res, next) {
|
|
22
|
-
// CASE: this means that the theme hasn't been loaded yet i.e. there is no active theme
|
|
23
|
-
if (!activeTheme.get()) {
|
|
24
|
-
// This is the one place we ACTUALLY throw an error for a missing theme as it's a request we cannot serve
|
|
25
|
-
return next(new errors.InternalServerError({
|
|
26
|
-
// We use the settingsCache here, because the setting will be set,
|
|
27
|
-
// even if the theme itself is not usable because it is invalid or missing.
|
|
28
|
-
message: tpl(messages.missingTheme, {theme: settingsCache.get('active_theme')})
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// If the active theme has not yet been mounted, mount it into express
|
|
33
|
-
if (!activeTheme.get().mounted) {
|
|
34
|
-
activeTheme.get().mount(req.app);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
next();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function calculateLegacyPriceData(products) {
|
|
41
|
-
const defaultPrice = {
|
|
42
|
-
amount: 0,
|
|
43
|
-
currency: 'usd',
|
|
44
|
-
interval: 'year',
|
|
45
|
-
nickname: ''
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
function makePriceObject(price) {
|
|
49
|
-
const numberAmount = 0 + price.amount;
|
|
50
|
-
const dollarAmount = numberAmount ? Math.round(numberAmount / 100) : 0;
|
|
51
|
-
return {
|
|
52
|
-
valueOf() {
|
|
53
|
-
return dollarAmount;
|
|
54
|
-
},
|
|
55
|
-
amount: numberAmount,
|
|
56
|
-
currency: price.currency,
|
|
57
|
-
nickname: price.name,
|
|
58
|
-
interval: price.interval
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const defaultProduct = products[0] || {};
|
|
63
|
-
|
|
64
|
-
const monthlyPrice = makePriceObject(defaultProduct.monthly_price || defaultPrice);
|
|
65
|
-
|
|
66
|
-
const yearlyPrice = makePriceObject(defaultProduct.yearly_price || defaultPrice);
|
|
67
|
-
|
|
68
|
-
const priceData = {
|
|
69
|
-
monthly: monthlyPrice,
|
|
70
|
-
yearly: yearlyPrice,
|
|
71
|
-
currency: monthlyPrice ? monthlyPrice.currency : defaultPrice.currency
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
return priceData;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function getProductAndPricesData() {
|
|
78
|
-
try {
|
|
79
|
-
const page = await api.canary.productsPublic.browse({
|
|
80
|
-
include: ['monthly_price', 'yearly_price'],
|
|
81
|
-
limit: 'all'
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
return page.products;
|
|
85
|
-
} catch (err) {
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function getSiteData() {
|
|
91
|
-
let siteData = settingsCache.getPublic();
|
|
92
|
-
|
|
93
|
-
// theme-only computed property added to @site
|
|
94
|
-
if (settingsCache.get('members_signup_access') === 'none') {
|
|
95
|
-
const escapedUrl = encodeURIComponent(urlUtils.urlFor({relativeUrl: '/rss/'}, true));
|
|
96
|
-
siteData.signup_url = `https://feedly.com/i/subscription/feed/${escapedUrl}`;
|
|
97
|
-
} else {
|
|
98
|
-
siteData.signup_url = '#/portal';
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return siteData;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function updateGlobalTemplateOptions(req, res, next) {
|
|
105
|
-
// Static information, same for every request unless the settings change
|
|
106
|
-
// @TODO: bind this once and then update based on events?
|
|
107
|
-
// @TODO: decouple theme layer from settings cache using the Content API
|
|
108
|
-
const siteData = getSiteData();
|
|
109
|
-
const labsData = labs.getAll();
|
|
110
|
-
|
|
111
|
-
const themeData = {
|
|
112
|
-
posts_per_page: activeTheme.get().config('posts_per_page'),
|
|
113
|
-
image_sizes: activeTheme.get().config('image_sizes')
|
|
114
|
-
};
|
|
115
|
-
const themeSettingsData = customThemeSettingsCache.getAll();
|
|
116
|
-
const productData = await getProductAndPricesData();
|
|
117
|
-
const priceData = calculateLegacyPriceData(productData);
|
|
118
|
-
|
|
119
|
-
let products = null;
|
|
120
|
-
let product = null;
|
|
121
|
-
if (productData.length === 1) {
|
|
122
|
-
product = productData[0];
|
|
123
|
-
} else {
|
|
124
|
-
products = productData;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// @TODO: only do this if something changed?
|
|
128
|
-
// @TODO: remove blog in a major where we are happy to break more themes
|
|
129
|
-
{
|
|
130
|
-
hbs.updateTemplateOptions({
|
|
131
|
-
data: {
|
|
132
|
-
blog: siteData,
|
|
133
|
-
site: siteData,
|
|
134
|
-
labs: labsData,
|
|
135
|
-
config: themeData,
|
|
136
|
-
price: priceData,
|
|
137
|
-
product,
|
|
138
|
-
products,
|
|
139
|
-
custom: themeSettingsData
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
next();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function updateLocalTemplateData(req, res, next) {
|
|
148
|
-
// Pass 'secure' flag to the view engine
|
|
149
|
-
// so that templates can choose to render https or http 'url', see url utility
|
|
150
|
-
res.locals.secure = req.secure;
|
|
151
|
-
|
|
152
|
-
next();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function updateLocalTemplateOptions(req, res, next) {
|
|
156
|
-
const localTemplateOptions = hbs.getLocalTemplateOptions(res.locals);
|
|
157
|
-
|
|
158
|
-
// adjust @site.url for http/https based on the incoming request
|
|
159
|
-
const siteData = {
|
|
160
|
-
url: urlUtils.urlFor('home', {secure: req.secure, trailingSlash: false}, true)
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// @TODO: it would be nicer if this was proper middleware somehow...
|
|
164
|
-
const previewData = preview.handle(req, Object.keys(customThemeSettingsCache.getAll()));
|
|
165
|
-
|
|
166
|
-
// strip custom off of preview data so it doesn't get merged into @site
|
|
167
|
-
const customThemeSettingsPreviewData = previewData.custom;
|
|
168
|
-
delete previewData.custom;
|
|
169
|
-
let customData = {};
|
|
170
|
-
if (labs.isSet('customThemeSettings')) {
|
|
171
|
-
customData = customThemeSettingsPreviewData;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// update site data with any preview values from the request
|
|
175
|
-
Object.assign(siteData, previewData);
|
|
176
|
-
|
|
177
|
-
const member = req.member ? {
|
|
178
|
-
uuid: req.member.uuid,
|
|
179
|
-
email: req.member.email,
|
|
180
|
-
name: req.member.name,
|
|
181
|
-
firstname: req.member.name && req.member.name.split(' ')[0],
|
|
182
|
-
avatar_image: req.member.avatar_image,
|
|
183
|
-
subscriptions: req.member.subscriptions && req.member.subscriptions.map((sub) => {
|
|
184
|
-
return Object.assign({}, sub, {
|
|
185
|
-
default_payment_card_last4: sub.default_payment_card_last4 || '****'
|
|
186
|
-
});
|
|
187
|
-
}),
|
|
188
|
-
paid: req.member.status !== 'free'
|
|
189
|
-
} : null;
|
|
190
|
-
|
|
191
|
-
hbs.updateLocalTemplateOptions(res.locals, _.merge({}, localTemplateOptions, {
|
|
192
|
-
data: {
|
|
193
|
-
member: member,
|
|
194
|
-
site: siteData,
|
|
195
|
-
custom: customData,
|
|
196
|
-
// @deprecated: a gscan warning for @blog was added before 3.0 which replaced it with @site
|
|
197
|
-
blog: siteData
|
|
198
|
-
}
|
|
199
|
-
}));
|
|
200
|
-
|
|
201
|
-
next();
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
module.exports = [
|
|
205
|
-
ensureActiveTheme,
|
|
206
|
-
updateGlobalTemplateOptions,
|
|
207
|
-
updateLocalTemplateData,
|
|
208
|
-
updateLocalTemplateOptions
|
|
209
|
-
];
|
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
// # Fixture Utils
|
|
2
|
-
// Standalone file which can be required to help with advanced operations on the fixtures.json file
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
|
|
5
|
-
const Promise = require('bluebird');
|
|
6
|
-
const logging = require('@tryghost/logging');
|
|
7
|
-
const models = require('../../../models');
|
|
8
|
-
const baseUtils = require('../../../models/base/utils');
|
|
9
|
-
const {sequence} = require('@tryghost/promise');
|
|
10
|
-
const moment = require('moment');
|
|
11
|
-
const fixtures = require('./fixtures');
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* ### Match Func
|
|
15
|
-
* Figures out how to match across various combinations of keys and values.
|
|
16
|
-
* Match can be a string or an array containing 2 strings
|
|
17
|
-
* Key and Value are the values to be found
|
|
18
|
-
* Value can also be an array, in which case we look for a match in the array.
|
|
19
|
-
* @api private
|
|
20
|
-
* @param {String|Array} match
|
|
21
|
-
* @param {String|Integer} key
|
|
22
|
-
* @param {String|Array} [value]
|
|
23
|
-
* @returns {Function} matching function
|
|
24
|
-
*/
|
|
25
|
-
const matchFunc = function matchFunc(match, key, value) {
|
|
26
|
-
if (_.isArray(match)) {
|
|
27
|
-
return function (item) {
|
|
28
|
-
let valueTest = true;
|
|
29
|
-
|
|
30
|
-
if (_.isArray(value)) {
|
|
31
|
-
valueTest = value.indexOf(item.get(match[1])) > -1;
|
|
32
|
-
} else if (value !== 'all') {
|
|
33
|
-
valueTest = item.get(match[1]) === value;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return item.get(match[0]) === key && valueTest;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return function (item) {
|
|
41
|
-
key = key === 0 && value ? value : key;
|
|
42
|
-
return item.get(match) === key;
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const matchObj = function matchObj(match, item) {
|
|
47
|
-
const matchedObj = {};
|
|
48
|
-
|
|
49
|
-
if (_.isArray(match)) {
|
|
50
|
-
_.each(match, (matchProp) => {
|
|
51
|
-
matchedObj[matchProp] = item.get(matchProp);
|
|
52
|
-
});
|
|
53
|
-
} else {
|
|
54
|
-
matchedObj[match] = item.get(match);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return matchedObj;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* ### Fetch Relation Data
|
|
62
|
-
* Before we build relations we need to fetch all of the models from both sides so that we can
|
|
63
|
-
* use filter and find to quickly locate the correct models.
|
|
64
|
-
* @api private
|
|
65
|
-
* @param {{from, to, entries}} relation
|
|
66
|
-
* @returns {Promise<*>}
|
|
67
|
-
*/
|
|
68
|
-
const fetchRelationData = function fetchRelationData(relation, options) {
|
|
69
|
-
const fromOptions = _.extend({}, options, {withRelated: [relation.from.relation]});
|
|
70
|
-
|
|
71
|
-
const props = {
|
|
72
|
-
from: models[relation.from.model].findAll(fromOptions),
|
|
73
|
-
to: models[relation.to.model].findAll(options)
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
return Promise.props(props);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/*
|
|
80
|
-
* Find methods - use the local fixtures
|
|
81
|
-
*/
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* ### Find Model Fixture
|
|
85
|
-
* Finds a model fixture based on model name
|
|
86
|
-
* @api private
|
|
87
|
-
* @param {String} modelName
|
|
88
|
-
* @returns {Object} model fixture
|
|
89
|
-
*/
|
|
90
|
-
const findModelFixture = function findModelFixture(modelName) {
|
|
91
|
-
return _.find(fixtures.models, (modelFixture) => {
|
|
92
|
-
return modelFixture.name === modelName;
|
|
93
|
-
});
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* ### Find Model Fixture Entry
|
|
98
|
-
* Find a single model fixture entry by model name & a matching expression for the FIND function
|
|
99
|
-
* @param {String} modelName
|
|
100
|
-
* @param {String|Object|Function} matchExpr
|
|
101
|
-
* @returns {Object} model fixture entry
|
|
102
|
-
*/
|
|
103
|
-
const findModelFixtureEntry = function findModelFixtureEntry(modelName, matchExpr) {
|
|
104
|
-
return _.find(findModelFixture(modelName).entries, matchExpr);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* ### Find Model Fixtures
|
|
109
|
-
* Find a model fixture name & a matching expression for the FILTER function
|
|
110
|
-
* @param {String} modelName
|
|
111
|
-
* @param {String|Object|Function} matchExpr
|
|
112
|
-
* @returns {Object} model fixture
|
|
113
|
-
*/
|
|
114
|
-
const findModelFixtures = function findModelFixtures(modelName, matchExpr) {
|
|
115
|
-
const foundModel = _.cloneDeep(findModelFixture(modelName));
|
|
116
|
-
foundModel.entries = _.filter(foundModel.entries, matchExpr);
|
|
117
|
-
return foundModel;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* ### Find Relation Fixture
|
|
122
|
-
* Find a relation fixture by from & to models
|
|
123
|
-
* @api private
|
|
124
|
-
* @param {String} from
|
|
125
|
-
* @param {String} to
|
|
126
|
-
* @returns {Object} relation fixture
|
|
127
|
-
*/
|
|
128
|
-
const findRelationFixture = function findRelationFixture(from, to) {
|
|
129
|
-
return _.find(fixtures.relations, (relation) => {
|
|
130
|
-
return relation.from.model === from && relation.to.model === to;
|
|
131
|
-
});
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* ### Find Permission Relations For Object
|
|
136
|
-
* Specialist function can return the permission relation fixture with only entries for a particular object.model
|
|
137
|
-
* @param {String} objName
|
|
138
|
-
* @returns {Object} fixture relation
|
|
139
|
-
*/
|
|
140
|
-
const findPermissionRelationsForObject = function findPermissionRelationsForObject(objName, role) {
|
|
141
|
-
// Make a copy and delete any entries we don't want
|
|
142
|
-
const foundRelation = _.cloneDeep(findRelationFixture('Role', 'Permission'));
|
|
143
|
-
|
|
144
|
-
_.each(foundRelation.entries, (entry, key) => {
|
|
145
|
-
_.each(entry, (perm, obj) => {
|
|
146
|
-
if (obj !== objName) {
|
|
147
|
-
delete entry[obj];
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (_.isEmpty(entry) || (role && role !== key)) {
|
|
152
|
-
delete foundRelation.entries[key];
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
return foundRelation;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
/*
|
|
160
|
-
* Add and Remove Functions, require access to models
|
|
161
|
-
*/
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* ### Add Fixtures for Model
|
|
165
|
-
* Takes a model fixture, with a name and some entries and processes these
|
|
166
|
-
* into a sequence of promises to get each fixture added.
|
|
167
|
-
*
|
|
168
|
-
* @param {{name, entries}} modelFixture
|
|
169
|
-
* @returns {Promise<any>}
|
|
170
|
-
*/
|
|
171
|
-
const addFixturesForModel = async function addFixturesForModel(modelFixture, options = {}) {
|
|
172
|
-
// Clone the fixtures as they get changed in this function.
|
|
173
|
-
// The initial blog posts will be added a `published_at` property, which
|
|
174
|
-
// would change the fixturesHash.
|
|
175
|
-
modelFixture = _.cloneDeep(modelFixture);
|
|
176
|
-
// The Post model fixtures need a `published_at` date, where at least the seconds
|
|
177
|
-
// are different, otherwise `prev_post` and `next_post` helpers won't workd with
|
|
178
|
-
// them.
|
|
179
|
-
if (modelFixture.name === 'Post') {
|
|
180
|
-
_.forEach(modelFixture.entries, (post, index) => {
|
|
181
|
-
if (!post.published_at) {
|
|
182
|
-
post.published_at = moment().add(index, 'seconds');
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const results = await Promise.mapSeries(modelFixture.entries, async (entry) => {
|
|
188
|
-
let data = {};
|
|
189
|
-
|
|
190
|
-
// CASE: if id is specified, only query by id
|
|
191
|
-
if (entry.id) {
|
|
192
|
-
data.id = entry.id;
|
|
193
|
-
} else if (entry.slug) {
|
|
194
|
-
data.slug = entry.slug;
|
|
195
|
-
} else {
|
|
196
|
-
data = _.cloneDeep(entry);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (modelFixture.name === 'Post') {
|
|
200
|
-
data.status = 'all';
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const found = await models[modelFixture.name].findOne(data, options);
|
|
204
|
-
if (!found) {
|
|
205
|
-
return models[modelFixture.name].add(entry, options);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
return {expected: modelFixture.entries.length, done: _.compact(results).length};
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* ## Add Fixtures for Relation
|
|
214
|
-
* Takes a relation fixtures object, with a from, to and some entries and processes these
|
|
215
|
-
* into a sequence of promises, to get each fixture added.
|
|
216
|
-
*
|
|
217
|
-
* @param {{from, to, entries}} relationFixture
|
|
218
|
-
* @returns {Promise<any>}
|
|
219
|
-
*/
|
|
220
|
-
const addFixturesForRelation = async function addFixturesForRelation(relationFixture, options) {
|
|
221
|
-
const ops = [];
|
|
222
|
-
let max = 0;
|
|
223
|
-
|
|
224
|
-
const data = await fetchRelationData(relationFixture, options);
|
|
225
|
-
|
|
226
|
-
_.each(relationFixture.entries, (entry, key) => {
|
|
227
|
-
const fromItem = data.from.find(matchFunc(relationFixture.from.match, key));
|
|
228
|
-
|
|
229
|
-
// CASE: You add new fixtures e.g. a new role in a new release.
|
|
230
|
-
// As soon as an **older** migration script wants to add permissions for any resource, it iterates over the
|
|
231
|
-
// permissions for each role. But if the role does not exist yet, it won't find the matching db entry and breaks.
|
|
232
|
-
if (!fromItem) {
|
|
233
|
-
logging.warn('Skip: Target database entry not found for key: ' + key);
|
|
234
|
-
return Promise.resolve();
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
_.each(entry, (value, entryKey) => {
|
|
238
|
-
let toItems = data.to.filter(matchFunc(relationFixture.to.match, entryKey, value));
|
|
239
|
-
max += toItems.length;
|
|
240
|
-
|
|
241
|
-
// Remove any duplicates that already exist in the collection
|
|
242
|
-
toItems = _.reject(toItems, (item) => {
|
|
243
|
-
return fromItem
|
|
244
|
-
.related(relationFixture.from.relation)
|
|
245
|
-
.find((model) => {
|
|
246
|
-
const objectToMatch = matchObj(relationFixture.to.match, item);
|
|
247
|
-
return Object.keys(objectToMatch).every((keyToCheck) => {
|
|
248
|
-
return model.get(keyToCheck) === objectToMatch[keyToCheck];
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
if (toItems && toItems.length > 0) {
|
|
254
|
-
ops.push(function addRelationItems() {
|
|
255
|
-
return baseUtils.attach(
|
|
256
|
-
models[relationFixture.from.Model || relationFixture.from.model],
|
|
257
|
-
fromItem.id,
|
|
258
|
-
relationFixture.from.relation,
|
|
259
|
-
toItems,
|
|
260
|
-
options
|
|
261
|
-
);
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
const result = await sequence(ops);
|
|
268
|
-
return {expected: max, done: _(result).map('length').sum()};
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
const removeFixturesForModel = async function removeFixturesForModel(modelFixture, options) {
|
|
272
|
-
const results = await Promise.mapSeries(modelFixture.entries, async (entry) => {
|
|
273
|
-
const found = models[modelFixture.name].findOne(entry.id ? {id: entry.id} : entry, options);
|
|
274
|
-
if (found) {
|
|
275
|
-
return models[modelFixture.name].destroy(_.extend(options, {id: found.id}));
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
return {expected: modelFixture.entries.length, done: results.length};
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
const removeFixturesForRelation = async function removeFixturesForRelation(relationFixture, options) {
|
|
283
|
-
const data = await fetchRelationData(relationFixture, options);
|
|
284
|
-
const ops = [];
|
|
285
|
-
|
|
286
|
-
_.each(relationFixture.entries, (entry, key) => {
|
|
287
|
-
const fromItem = data.from.find(matchFunc(relationFixture.from.match, key));
|
|
288
|
-
|
|
289
|
-
_.each(entry, (value, entryKey) => {
|
|
290
|
-
const toItems = data.to.filter(matchFunc(relationFixture.to.match, entryKey, value));
|
|
291
|
-
|
|
292
|
-
if (toItems && toItems.length > 0) {
|
|
293
|
-
ops.push(function detachRelation() {
|
|
294
|
-
return baseUtils.detach(
|
|
295
|
-
models[relationFixture.from.Model || relationFixture.from.model],
|
|
296
|
-
fromItem.id,
|
|
297
|
-
relationFixture.from.relation,
|
|
298
|
-
toItems,
|
|
299
|
-
options
|
|
300
|
-
);
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
return await sequence(ops);
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
module.exports = {
|
|
310
|
-
// find
|
|
311
|
-
findModelFixtureEntry: findModelFixtureEntry,
|
|
312
|
-
findModelFixtures: findModelFixtures,
|
|
313
|
-
findRelationFixture: findRelationFixture,
|
|
314
|
-
findPermissionRelationsForObject: findPermissionRelationsForObject,
|
|
315
|
-
|
|
316
|
-
// add / remove
|
|
317
|
-
addFixturesForModel: addFixturesForModel,
|
|
318
|
-
addFixturesForRelation: addFixturesForRelation,
|
|
319
|
-
removeFixturesForModel: removeFixturesForModel,
|
|
320
|
-
removeFixturesForRelation: removeFixturesForRelation
|
|
321
|
-
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
const config = require('../../../shared/config');
|
|
2
|
-
const escapeRegExp = require('lodash/escapeRegExp');
|
|
3
|
-
const {URL} = require('url');
|
|
4
|
-
|
|
5
|
-
const DEFAULT_HOST_ARG = /.*/;
|
|
6
|
-
|
|
7
|
-
const getHostsFromConfig = () => {
|
|
8
|
-
const frontendHost = new URL(config.getSiteUrl()).hostname;
|
|
9
|
-
|
|
10
|
-
const backendHost = config.getAdminUrl() ? (new URL(config.getAdminUrl()).hostname) : '';
|
|
11
|
-
const hasSeparateBackendHost = backendHost && backendHost !== frontendHost;
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
backendHost,
|
|
15
|
-
hasSeparateBackendHost
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
21
|
-
* @returns {string|RegExp}
|
|
22
|
-
*/
|
|
23
|
-
module.exports.getBackendHostArg = () => {
|
|
24
|
-
const {backendHost, hasSeparateBackendHost} = getHostsFromConfig();
|
|
25
|
-
|
|
26
|
-
// with a separate admin url only serve on that host, otherwise serve on all hosts
|
|
27
|
-
return (hasSeparateBackendHost) && backendHost ? backendHost : DEFAULT_HOST_ARG;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
32
|
-
* @returns {string|RegExp}
|
|
33
|
-
*/
|
|
34
|
-
module.exports.getFrontendHostArg = () => {
|
|
35
|
-
const {backendHost, hasSeparateBackendHost} = getHostsFromConfig();
|
|
36
|
-
|
|
37
|
-
// with a separate admin url we adjust the frontend vhost to exclude requests to that host, otherwise serve on all hosts
|
|
38
|
-
return (hasSeparateBackendHost && backendHost) ? new RegExp(`^(?!${escapeRegExp(backendHost)}).*`) : DEFAULT_HOST_ARG;
|
|
39
|
-
};
|