ghost 4.19.1 → 4.20.3
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 +9 -8
- package/Gruntfile.js +1 -1
- package/PRIVACY.md +3 -0
- package/content/adapters/README.md +2 -2
- package/core/boot.js +4 -4
- package/core/bridge.js +9 -1
- package/core/built/assets/{chunk.3.0778d8e4d707d2a625f1.js → chunk.3.777d43e2ce954ba8b2f5.js} +1 -1
- package/core/built/assets/codemirror/{codemirror-21a09582262987037db73b152fb35f7c.js → codemirror-d25c379b87ec8b33d54ac7149bc0b6ae.js} +14 -14
- package/core/built/assets/ghost-dark-20e2892d4f30d0d1183c9ac725ea37d0.css +1 -0
- package/core/built/assets/{ghost.min-102753ec485602c8fe80d60a1750bf84.js → ghost.min-07b6a50c54b3e2e190332c28c7255d2f.js} +525 -340
- package/core/built/assets/ghost.min-57e46fd3b1145ecf2cbd185a13611f3b.css +1 -0
- package/core/built/assets/icons/arrow-left-small.svg +0 -4
- package/core/built/assets/img/footer-marketplace-bg-572b6c6486a7e26316954d599eaa9f30.png +0 -0
- package/core/built/assets/img/marketing/offers-1-f2e1b653c4d5bb90eea9d7a2862530f9.jpg +0 -0
- package/core/built/assets/img/marketing/offers-2-28a225d34cc39d133748431536961d00.jpg +0 -0
- package/core/built/assets/img/marketing/offers-3-2094c91ab21a16c37fbe6ec16c140160.jpg +0 -0
- package/core/built/assets/img/themes/Casper-c7e784d7188cc5d7f097d9b6c97b0263.jpg +0 -0
- package/core/built/assets/simplemde/{simplemde-232f69d126310434489071a1891e6d8b.js → simplemde-3ffc0ec9e9fecf29b9a499db678c9e65.js} +14 -14
- package/core/built/assets/{vendor.min-0916203b598271a795909e8e0b1c16c2.js → vendor.min-af502ac4142871500fc424f6a5a254ec.js} +1046 -1043
- package/core/frontend/apps/amp/lib/router.js +1 -1
- package/core/frontend/meta/author-url.js +1 -1
- package/core/frontend/meta/url.js +1 -1
- package/core/{server → frontend}/public/favicon.ico +0 -0
- package/core/{server → frontend}/public/ghost.css +0 -0
- package/core/{server → frontend}/public/ghost.min.css +0 -0
- package/core/{server → frontend}/public/robots.txt +0 -0
- package/core/{server → frontend}/public/sitemap.xsl +0 -0
- package/core/frontend/services/proxy.js +1 -1
- package/core/frontend/services/routing/CollectionRouter.js +3 -49
- package/core/frontend/services/routing/ParentRouter.js +1 -4
- package/core/frontend/services/routing/StaticPagesRouter.js +3 -5
- package/core/frontend/services/routing/StaticRoutesRouter.js +4 -6
- package/core/frontend/services/routing/TaxonomyRouter.js +4 -5
- package/core/frontend/services/routing/controllers/collection.js +2 -2
- package/core/frontend/services/routing/controllers/email-post.js +2 -2
- package/core/frontend/services/routing/controllers/entry.js +2 -2
- package/core/frontend/services/routing/controllers/preview.js +2 -2
- package/core/frontend/services/routing/index.js +6 -12
- package/core/frontend/services/routing/registry.js +13 -0
- package/core/frontend/services/routing/router-manager.js +185 -0
- package/core/frontend/services/rss/generate-feed.js +2 -2
- package/core/frontend/services/theme-engine/i18n/i18n.js +267 -28
- package/core/frontend/services/theme-engine/i18n/index.js +1 -1
- package/core/frontend/services/theme-engine/i18n/theme-i18n.js +73 -0
- package/core/frontend/web/index.js +1 -0
- package/core/{server/web/site → frontend/web}/middleware/handle-image-sizes.js +4 -4
- package/core/{server/web/site → frontend/web}/middleware/index.js +0 -0
- package/core/{server/web/site → frontend/web}/middleware/redirect-ghost-to-admin.js +3 -3
- package/core/{server/web/site → frontend/web}/middleware/serve-favicon.js +6 -6
- package/core/{server/web/site → frontend/web}/middleware/serve-public-file.js +2 -2
- package/core/{server/web/site → frontend/web}/middleware/static-theme.js +3 -3
- package/core/{server/web/site → frontend/web}/routes.js +5 -4
- package/core/{server/web/site/app.js → frontend/web/site.js} +12 -16
- package/core/server/adapters/storage/LocalFileStorage.js +35 -39
- package/core/server/adapters/storage/index.js +12 -2
- package/core/server/api/canary/images.js +1 -1
- package/core/server/api/canary/offers.js +19 -0
- package/core/server/api/canary/utils/serializers/output/settings.js +2 -3
- package/core/server/api/canary/utils/serializers/output/utils/url.js +1 -1
- package/core/server/api/v2/images.js +1 -1
- package/core/server/api/v2/utils/serializers/output/utils/url.js +1 -1
- package/core/server/api/v3/images.js +1 -1
- package/core/server/api/v3/utils/serializers/output/settings.js +2 -3
- package/core/server/api/v3/utils/serializers/output/utils/url.js +1 -1
- package/core/server/data/importer/handlers/image.js +1 -1
- package/core/server/data/importer/importers/image.js +1 -1
- package/core/server/data/migrations/init/1-create-tables.js +7 -8
- package/core/server/data/migrations/init/2-create-fixtures.js +8 -8
- package/core/server/data/migrations/versions/4.20/01-remove-offer-redemptions-table.js +19 -0
- package/core/server/data/migrations/versions/4.20/02-remove-offers-table.js +30 -0
- package/core/server/data/migrations/versions/4.20/03-add-offers-table.js +21 -0
- package/core/server/data/migrations/versions/4.20/04-add-offer-redemptions-table.js +9 -0
- package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +41 -0
- package/core/server/data/schema/fixtures/utils.js +150 -143
- package/core/server/data/schema/schema.js +4 -3
- package/core/server/frontend/ghost.min.css +1 -0
- package/core/server/lib/image/image-size.js +2 -2
- package/core/server/lib/mobiledoc.js +3 -2
- package/core/server/models/action.js +7 -4
- package/core/server/models/base/plugins/overrides.js +19 -6
- package/core/server/models/index.js +4 -46
- package/core/server/models/member.js +5 -0
- package/core/server/models/user.js +2 -1
- package/core/server/overrides.js +6 -2
- package/core/server/services/adapter-manager/config.js +1 -0
- package/core/server/services/adapter-manager/index.js +9 -5
- package/core/server/services/adapter-manager/options-resolver.js +18 -0
- package/core/server/services/bulk-email/mailgun.js +1 -1
- package/core/server/services/mega/post-email-serializer.js +2 -2
- package/core/server/services/members/api.js +1 -3
- package/core/server/services/members/emails/signin.js +1 -1
- package/core/server/services/members/emails/signup.js +1 -1
- package/core/server/services/members/emails/subscribe.js +1 -1
- package/core/server/services/members/service.js +2 -1
- package/core/server/services/offers/service.js +1 -1
- package/core/server/services/route-settings/route-settings.js +1 -1
- package/core/server/services/settings/index.js +3 -1
- package/core/server/services/settings/settings-bread-service.js +42 -20
- package/core/server/services/slack.js +1 -1
- package/core/server/services/themes/activate.js +2 -2
- package/core/server/services/themes/activation-bridge.js +6 -6
- package/core/server/services/themes/storage.js +1 -1
- package/core/{frontend → server}/services/url/Queue.js +0 -0
- package/core/{frontend → server}/services/url/Resource.js +0 -0
- package/core/{frontend → server}/services/url/Resources.js +2 -2
- package/core/{frontend → server}/services/url/UrlGenerator.js +14 -14
- package/core/{frontend → server}/services/url/UrlService.js +12 -15
- package/core/{frontend → server}/services/url/Urls.js +1 -1
- package/core/{frontend → server}/services/url/configs/canary.js +0 -0
- package/core/{frontend → server}/services/url/configs/v2.js +0 -0
- package/core/{frontend → server}/services/url/configs/v3.js +0 -0
- package/core/{frontend → server}/services/url/configs/v4.js +0 -0
- package/core/{frontend → server}/services/url/index.js +0 -0
- package/core/server/services/xmlrpc.js +1 -1
- package/core/server/update-check.js +3 -3
- package/core/server/web/admin/controller.js +11 -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 +8 -9
- package/core/server/web/oauth/app.js +4 -2
- package/core/server/web/parent/backend.js +3 -3
- package/core/server/web/parent/frontend.js +2 -2
- package/core/server/web/shared/middlewares/custom-redirects.js +0 -8
- package/core/server/web/shared/middlewares/maintenance.js +1 -1
- package/core/server/web/well-known.js +10 -10
- package/core/shared/config/overrides.json +1 -1
- package/core/shared/express.js +10 -0
- package/core/shared/html-to-plaintext.js +2 -2
- package/core/shared/labs.js +14 -5
- package/package.json +45 -43
- package/yarn.lock +649 -284
- package/core/built/assets/ghost-dark-da8e8eba130fb52f97494e51850d1045.css +0 -1
- package/core/built/assets/ghost.min-0d8f19623e9f077351bce453034daf4d.css +0 -1
- package/core/frontend/services/routing/bootstrap.js +0 -134
- package/core/server/public/404-ghost.png +0 -0
- package/core/server/public/404-ghost@2x.png +0 -0
- package/core/server/web/site/index.js +0 -1
- package/core/shared/i18n/i18n.js +0 -312
- package/core/shared/i18n/index.js +0 -6
|
@@ -6,7 +6,7 @@ const tpl = require('@tryghost/tpl');
|
|
|
6
6
|
const errors = require('@tryghost/errors');
|
|
7
7
|
|
|
8
8
|
// Dirty requires
|
|
9
|
-
const urlService = require('
|
|
9
|
+
const urlService = require('../../../../server/services/url');
|
|
10
10
|
const helpers = require('../../../services/routing/helpers');
|
|
11
11
|
const templateName = 'amp';
|
|
12
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const schema = require('../../server/data/schema').checks;
|
|
2
2
|
const urlUtils = require('../../shared/url-utils');
|
|
3
|
-
const urlService = require('
|
|
3
|
+
const urlService = require('../../server/services/url');
|
|
4
4
|
|
|
5
5
|
// This cleans the url from any `/amp` postfixes, so we'll never
|
|
6
6
|
// output a url with `/amp` in the end, except for the needed `amphtml`
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -46,6 +46,6 @@ module.exports = {
|
|
|
46
46
|
// Labs utils for enabling/disabling helpers
|
|
47
47
|
labs: require('../../shared/labs'),
|
|
48
48
|
// URGH... Yuk (unhelpful comment :D)
|
|
49
|
-
urlService: require('
|
|
49
|
+
urlService: require('../../server/services/url'),
|
|
50
50
|
urlUtils: require('../../shared/url-utils')
|
|
51
51
|
};
|
|
@@ -6,16 +6,13 @@ const controllers = require('./controllers');
|
|
|
6
6
|
const middlewares = require('./middlewares');
|
|
7
7
|
const RSSRouter = require('./RSSRouter');
|
|
8
8
|
|
|
9
|
-
// This emits its own routing events AND listens to settings.timezone.edited :(
|
|
10
|
-
const events = require('../../../server/lib/common/events');
|
|
11
|
-
|
|
12
9
|
/**
|
|
13
10
|
* @description Collection Router for post resource.
|
|
14
11
|
*
|
|
15
12
|
* Fundamental router to define where resources live and how their url structure is.
|
|
16
13
|
*/
|
|
17
14
|
class CollectionRouter extends ParentRouter {
|
|
18
|
-
constructor(mainRoute, object, RESOURCE_CONFIG) {
|
|
15
|
+
constructor(mainRoute, object, RESOURCE_CONFIG, routerCreated) {
|
|
19
16
|
super('CollectionRouter');
|
|
20
17
|
|
|
21
18
|
this.RESOURCE_CONFIG = RESOURCE_CONFIG.QUERY.post;
|
|
@@ -55,11 +52,11 @@ class CollectionRouter extends ParentRouter {
|
|
|
55
52
|
};
|
|
56
53
|
|
|
57
54
|
this.context = [this.routerName];
|
|
55
|
+
this.routerCreated = routerCreated;
|
|
58
56
|
|
|
59
57
|
debug(this.name, this.route, this.permalinks);
|
|
60
58
|
|
|
61
59
|
this._registerRoutes();
|
|
62
|
-
this._listeners();
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
/**
|
|
@@ -92,7 +89,7 @@ class CollectionRouter extends ParentRouter {
|
|
|
92
89
|
// REGISTER: permalinks e.g. /:slug/, /podcast/:slug
|
|
93
90
|
this.mountRoute(this.permalinks.getValue({withUrlOptions: true}), controllers.entry);
|
|
94
91
|
|
|
95
|
-
|
|
92
|
+
this.routerCreated(this);
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
/**
|
|
@@ -127,43 +124,6 @@ class CollectionRouter extends ParentRouter {
|
|
|
127
124
|
next();
|
|
128
125
|
}
|
|
129
126
|
|
|
130
|
-
/**
|
|
131
|
-
* @description This router has listeners to react on changes which happen in Ghost.
|
|
132
|
-
* @private
|
|
133
|
-
*/
|
|
134
|
-
_listeners() {
|
|
135
|
-
/**
|
|
136
|
-
* CASE: timezone changes
|
|
137
|
-
*
|
|
138
|
-
* If your permalink contains a date reference, we have to regenerate the urls.
|
|
139
|
-
*
|
|
140
|
-
* e.g. /:year/:month/:day/:slug/ or /:day/:slug/
|
|
141
|
-
*/
|
|
142
|
-
this._onTimezoneEditedListener = this._onTimezoneEdited.bind(this);
|
|
143
|
-
events.on('settings.timezone.edited', this._onTimezoneEditedListener);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* @description Helper function to handle a timezone change.
|
|
148
|
-
* @param settingModel
|
|
149
|
-
* @private
|
|
150
|
-
*/
|
|
151
|
-
_onTimezoneEdited(settingModel) {
|
|
152
|
-
const newTimezone = settingModel.attributes.value;
|
|
153
|
-
const previousTimezone = settingModel._previousAttributes.value;
|
|
154
|
-
|
|
155
|
-
if (newTimezone === previousTimezone) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (this.getPermalinks().getValue().match(/:year|:month|:day/)) {
|
|
160
|
-
debug('_onTimezoneEdited: trigger regeneration');
|
|
161
|
-
|
|
162
|
-
// @NOTE: The connected url generator will listen on this event and regenerate urls.
|
|
163
|
-
this.emit('updated');
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
127
|
/**
|
|
168
128
|
* @description Get resource type of this router (always "posts")
|
|
169
129
|
* @returns {string}
|
|
@@ -196,12 +156,6 @@ class CollectionRouter extends ParentRouter {
|
|
|
196
156
|
|
|
197
157
|
return urlUtils.createUrl(urlUtils.urlJoin(this.route.value, this.rssRouter.route.value), options.absolute, options.secure);
|
|
198
158
|
}
|
|
199
|
-
|
|
200
|
-
reset() {
|
|
201
|
-
if (this._onTimezoneEditedListener) {
|
|
202
|
-
events.removeListener('settings.timezone.edited', this._onTimezoneEditedListener);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
159
|
}
|
|
206
160
|
|
|
207
161
|
module.exports = CollectionRouter;
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
const debug = require('@tryghost/debug')('routing:parent-router');
|
|
11
11
|
|
|
12
|
-
const EventEmitter = require('events').EventEmitter;
|
|
13
12
|
const express = require('../../../shared/express');
|
|
14
13
|
const _ = require('lodash');
|
|
15
14
|
const url = require('url');
|
|
@@ -46,10 +45,8 @@ function GhostRouter(options) {
|
|
|
46
45
|
return innerRouter;
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
class ParentRouter
|
|
48
|
+
class ParentRouter {
|
|
50
49
|
constructor(name) {
|
|
51
|
-
super();
|
|
52
|
-
|
|
53
50
|
this.identifier = security.identifier.uid(10);
|
|
54
51
|
|
|
55
52
|
this.name = name;
|
|
@@ -3,17 +3,15 @@ const urlUtils = require('../../../shared/url-utils');
|
|
|
3
3
|
const ParentRouter = require('./ParentRouter');
|
|
4
4
|
const controllers = require('./controllers');
|
|
5
5
|
|
|
6
|
-
// This emits its own routing events
|
|
7
|
-
const events = require('../../../server/lib/common/events');
|
|
8
|
-
|
|
9
6
|
/**
|
|
10
7
|
* @description Resource: pages
|
|
11
8
|
*/
|
|
12
9
|
class StaticPagesRouter extends ParentRouter {
|
|
13
|
-
constructor(RESOURCE_CONFIG) {
|
|
10
|
+
constructor(RESOURCE_CONFIG, routerCreated) {
|
|
14
11
|
super('StaticPagesRouter');
|
|
15
12
|
|
|
16
13
|
this.RESOURCE_CONFIG = RESOURCE_CONFIG.QUERY.page;
|
|
14
|
+
this.routerCreated = routerCreated;
|
|
17
15
|
|
|
18
16
|
// @NOTE: Permalink is always /:slug, not configure able
|
|
19
17
|
this.permalinks = {
|
|
@@ -50,7 +48,7 @@ class StaticPagesRouter extends ParentRouter {
|
|
|
50
48
|
// REGISTER: permalink for static pages
|
|
51
49
|
this.mountRoute(this.permalinks.getValue({withUrlOptions: true}), controllers.entry);
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
this.routerCreated(this);
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
/**
|
|
@@ -6,20 +6,18 @@ const controllers = require('./controllers');
|
|
|
6
6
|
const middlewares = require('./middlewares');
|
|
7
7
|
const ParentRouter = require('./ParentRouter');
|
|
8
8
|
|
|
9
|
-
// This emits its own routing events
|
|
10
|
-
const events = require('../../../server/lib/common/events');
|
|
11
|
-
|
|
12
9
|
/**
|
|
13
10
|
* @description Template routes allow you to map individual URLs to specific template files within a Ghost theme
|
|
14
11
|
*/
|
|
15
12
|
class StaticRoutesRouter extends ParentRouter {
|
|
16
|
-
constructor(mainRoute, object) {
|
|
13
|
+
constructor(mainRoute, object, routerCreated) {
|
|
17
14
|
super('StaticRoutesRouter');
|
|
18
15
|
|
|
19
16
|
this.route = {value: mainRoute};
|
|
20
17
|
this.templates = object.templates || [];
|
|
21
18
|
this.data = object.data || {query: {}, router: {}};
|
|
22
19
|
this.routerName = mainRoute === '/' ? 'index' : mainRoute.replace(/\//g, '');
|
|
20
|
+
this.routerCreated = routerCreated;
|
|
23
21
|
|
|
24
22
|
debug(this.route.value, this.templates);
|
|
25
23
|
|
|
@@ -64,7 +62,7 @@ class StaticRoutesRouter extends ParentRouter {
|
|
|
64
62
|
this.router().param('page', middlewares.pageParam);
|
|
65
63
|
this.mountRoute(urlUtils.urlJoin(this.route.value, 'page', ':page(\\d+)'), controllers[this.controller]);
|
|
66
64
|
|
|
67
|
-
|
|
65
|
+
this.routerCreated(this);
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
/**
|
|
@@ -100,7 +98,7 @@ class StaticRoutesRouter extends ParentRouter {
|
|
|
100
98
|
// REGISTER: static route
|
|
101
99
|
this.mountRoute(this.route.value, controllers.static);
|
|
102
100
|
|
|
103
|
-
|
|
101
|
+
this.routerCreated(this);
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
/**
|
|
@@ -6,15 +6,12 @@ const urlUtils = require('../../../shared/url-utils');
|
|
|
6
6
|
const controllers = require('./controllers');
|
|
7
7
|
const middlewares = require('./middlewares');
|
|
8
8
|
|
|
9
|
-
// This emits its own routing events
|
|
10
|
-
const events = require('../../../server/lib/common/events');
|
|
11
|
-
|
|
12
9
|
/**
|
|
13
10
|
* @description Taxonomies are groupings of posts based on a common relation.
|
|
14
11
|
* Taxonomies do not change the url of a resource.
|
|
15
12
|
*/
|
|
16
13
|
class TaxonomyRouter extends ParentRouter {
|
|
17
|
-
constructor(key, permalinks, RESOURCE_CONFIG) {
|
|
14
|
+
constructor(key, permalinks, RESOURCE_CONFIG, routerCreated) {
|
|
18
15
|
super('Taxonomy');
|
|
19
16
|
|
|
20
17
|
this.taxonomyKey = key;
|
|
@@ -28,6 +25,8 @@ class TaxonomyRouter extends ParentRouter {
|
|
|
28
25
|
return this.permalinks.value;
|
|
29
26
|
};
|
|
30
27
|
|
|
28
|
+
this.routerCreated = routerCreated;
|
|
29
|
+
|
|
31
30
|
debug(this.permalinks);
|
|
32
31
|
|
|
33
32
|
this._registerRoutes();
|
|
@@ -60,7 +59,7 @@ class TaxonomyRouter extends ParentRouter {
|
|
|
60
59
|
this.mountRoute(urlUtils.urlJoin(this.permalinks.value, 'edit'), this._redirectEditOption.bind(this));
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
|
|
62
|
+
this.routerCreated(this);
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
/**
|
|
@@ -3,7 +3,7 @@ const debug = require('@tryghost/debug')('services:routing:controllers:collectio
|
|
|
3
3
|
const tpl = require('@tryghost/tpl');
|
|
4
4
|
const errors = require('@tryghost/errors');
|
|
5
5
|
const security = require('@tryghost/security');
|
|
6
|
-
const
|
|
6
|
+
const {routerManager} = require('../');
|
|
7
7
|
const themeEngine = require('../../theme-engine');
|
|
8
8
|
const helpers = require('../helpers');
|
|
9
9
|
|
|
@@ -72,7 +72,7 @@ module.exports = function collectionController(req, res, next) {
|
|
|
72
72
|
* People should always invert their filters to ensure that the database query loads unique posts per collection.
|
|
73
73
|
*/
|
|
74
74
|
result.posts = _.filter(result.posts, (post) => {
|
|
75
|
-
if (
|
|
75
|
+
if (routerManager.owns(res.routerOptions.identifier, post.id)) {
|
|
76
76
|
return post;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('services:routing:controllers:emailpost');
|
|
2
2
|
const config = require('../../../../shared/config');
|
|
3
|
-
const
|
|
3
|
+
const {routerManager} = require('../');
|
|
4
4
|
const urlUtils = require('../../../../shared/url-utils');
|
|
5
5
|
const helpers = require('../helpers');
|
|
6
6
|
|
|
@@ -48,7 +48,7 @@ module.exports = function emailPostController(req, res, next) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
if (post.status === 'published') {
|
|
51
|
-
return urlUtils.redirect301(res,
|
|
51
|
+
return urlUtils.redirect301(res, routerManager.getUrlByResourceId(post.id, {withSubdirectory: true}));
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
if (res.locals.apiVersion !== 'v0.1' && res.locals.apiVersion !== 'v2') {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('services:routing:controllers:entry');
|
|
2
2
|
const url = require('url');
|
|
3
3
|
const config = require('../../../../shared/config');
|
|
4
|
-
const
|
|
4
|
+
const {routerManager} = require('../');
|
|
5
5
|
const urlUtils = require('../../../../shared/url-utils');
|
|
6
6
|
const helpers = require('../helpers');
|
|
7
7
|
|
|
@@ -60,7 +60,7 @@ module.exports = function entryController(req, res, next) {
|
|
|
60
60
|
*
|
|
61
61
|
* That's why we have to check against the router type.
|
|
62
62
|
*/
|
|
63
|
-
if (
|
|
63
|
+
if (routerManager.getResourceById(entry.id).config.type !== res.routerOptions.resourceType) {
|
|
64
64
|
debug('not my resource type');
|
|
65
65
|
return next();
|
|
66
66
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('services:routing:controllers:preview');
|
|
2
2
|
const config = require('../../../../shared/config');
|
|
3
|
-
const
|
|
3
|
+
const {routerManager} = require('../');
|
|
4
4
|
const urlUtils = require('../../../../shared/url-utils');
|
|
5
5
|
const helpers = require('../helpers');
|
|
6
6
|
|
|
@@ -50,7 +50,7 @@ module.exports = function previewController(req, res, next) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
if (post.status === 'published') {
|
|
53
|
-
return urlUtils.redirect301(res,
|
|
53
|
+
return urlUtils.redirect301(res, routerManager.getUrlByResourceId(post.id, {withSubdirectory: true}));
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
if (res.locals.apiVersion !== 'v0.1' && res.locals.apiVersion !== 'v2') {
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
+
const registry = require('./registry');
|
|
2
|
+
const RouterManager = require('./router-manager');
|
|
3
|
+
const routerManager = new RouterManager({registry});
|
|
4
|
+
|
|
1
5
|
module.exports = {
|
|
2
|
-
|
|
3
|
-
return require('./bootstrap');
|
|
4
|
-
},
|
|
6
|
+
routerManager: routerManager,
|
|
5
7
|
|
|
6
8
|
get registry() {
|
|
7
|
-
return
|
|
9
|
+
return registry;
|
|
8
10
|
},
|
|
9
11
|
|
|
10
12
|
get helpers() {
|
|
11
13
|
return require('./helpers');
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
get CollectionRouter() {
|
|
15
|
-
return require('./CollectionRouter');
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
get TaxonomyRouter() {
|
|
19
|
-
return require('./TaxonomyRouter');
|
|
20
14
|
}
|
|
21
15
|
};
|
|
@@ -41,6 +41,19 @@ module.exports = {
|
|
|
41
41
|
return routers[name];
|
|
42
42
|
},
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Gets a router by it's internal router name
|
|
46
|
+
* @param {String} name internal router name
|
|
47
|
+
* @returns {Express-Router}
|
|
48
|
+
*/
|
|
49
|
+
getRouterByName(name) {
|
|
50
|
+
for (let routerKey in routers) {
|
|
51
|
+
if (routers[routerKey].name === name) {
|
|
52
|
+
return routers[routerKey];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
|
|
44
57
|
/**
|
|
45
58
|
*
|
|
46
59
|
*
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
const debug = require('@tryghost/debug')('routing');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const StaticRoutesRouter = require('./StaticRoutesRouter');
|
|
4
|
+
const StaticPagesRouter = require('./StaticPagesRouter');
|
|
5
|
+
const CollectionRouter = require('./CollectionRouter');
|
|
6
|
+
const TaxonomyRouter = require('./TaxonomyRouter');
|
|
7
|
+
const PreviewRouter = require('./PreviewRouter');
|
|
8
|
+
const ParentRouter = require('./ParentRouter');
|
|
9
|
+
const EmailRouter = require('./EmailRouter');
|
|
10
|
+
const UnsubscribeRouter = require('./UnsubscribeRouter');
|
|
11
|
+
|
|
12
|
+
// This emits its own routing events
|
|
13
|
+
const events = require('../../../server/lib/common/events');
|
|
14
|
+
|
|
15
|
+
class RouterManager {
|
|
16
|
+
constructor({registry, defaultApiVersion = 'v4'}) {
|
|
17
|
+
this.registry = registry;
|
|
18
|
+
this.defaultApiVersion = defaultApiVersion;
|
|
19
|
+
this.siteRouter = null;
|
|
20
|
+
this.urlService = null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
owns(routerId, id) {
|
|
24
|
+
return this.urlService.owns(routerId, id);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getUrlByResourceId(id, options) {
|
|
28
|
+
return this.urlService.getUrlByResourceId(id, options);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getResourceById(resourceId) {
|
|
32
|
+
return this.urlService.getResourceById(resourceId);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
routerCreated(router) {
|
|
36
|
+
// NOTE: this event should be become an "internal frontend even"
|
|
37
|
+
// and should not be consumed by the modules outside the frontend
|
|
38
|
+
events.emit('router.created', this);
|
|
39
|
+
|
|
40
|
+
// CASE: there are router types which do not generate resource urls
|
|
41
|
+
// e.g. static route router, in this case we don't want ot notify the URL service
|
|
42
|
+
if (!router || !router.getPermalinks()) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.urlService.onRouterAddedType(router);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @description The `init` function will return the wrapped parent express router and will start creating all
|
|
51
|
+
* routers if you pass the option "start: true".
|
|
52
|
+
*
|
|
53
|
+
* CASES:
|
|
54
|
+
* - if Ghost starts, it will first init the site app with the wrapper router and then call `start`
|
|
55
|
+
* separately, because it could be that your blog goes into maintenance mode
|
|
56
|
+
* - if you change your route settings, we will re-initialize routing
|
|
57
|
+
*
|
|
58
|
+
* @param {Object} options
|
|
59
|
+
* @param {Boolean} [options.start] - flag controlling if the frontend Routes should be reinitialized
|
|
60
|
+
* @param {String} options.apiVersion - API version frontend Routes should communicate through
|
|
61
|
+
* @param {Object} options.routerSettings - JSON configuration to build frontend Routes
|
|
62
|
+
* @param {Object} options.urlService - service providing resource URL utility functions such as owns, getUrlByResourceId, and getResourceById
|
|
63
|
+
* @returns {ExpressRouter}
|
|
64
|
+
*/
|
|
65
|
+
init({start = false, routerSettings, apiVersion, urlService}) {
|
|
66
|
+
this.urlService = urlService;
|
|
67
|
+
debug('routing init', start, apiVersion, routerSettings);
|
|
68
|
+
|
|
69
|
+
this.registry.resetAllRouters();
|
|
70
|
+
this.registry.resetAllRoutes();
|
|
71
|
+
|
|
72
|
+
// NOTE: this event could become an "internal frontend" in the future, it's used has been kept to prevent
|
|
73
|
+
// from tying up this module with sitemaps
|
|
74
|
+
events.emit('routers.reset');
|
|
75
|
+
|
|
76
|
+
this.siteRouter = new ParentRouter('SiteRouter');
|
|
77
|
+
this.registry.setRouter('siteRouter', this.siteRouter);
|
|
78
|
+
|
|
79
|
+
if (start) {
|
|
80
|
+
apiVersion = apiVersion || this.defaultApiVersion;
|
|
81
|
+
this.start(apiVersion, routerSettings);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return this.siteRouter.router();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @description This function will create the routers based on the route settings
|
|
89
|
+
*
|
|
90
|
+
* The routers are created in a specific order. This order defines who can get a resource first or
|
|
91
|
+
* who can dominant other routers.
|
|
92
|
+
*
|
|
93
|
+
* 1. Preview + Unsubscribe Routers: Strongest inbuilt features, which you can never override.
|
|
94
|
+
* 2. Static Routes: Very strong, because you can override any urls and redirect to a static route.
|
|
95
|
+
* 3. Taxonomies: Stronger than collections, because it's an inbuilt feature.
|
|
96
|
+
* 4. Collections
|
|
97
|
+
* 5. Static Pages: Weaker than collections, because we first try to find a post slug and fallback to lookup a static page.
|
|
98
|
+
* 6. Internal Apps: Weakest
|
|
99
|
+
*
|
|
100
|
+
* @param {string} apiVersion
|
|
101
|
+
* @param {object} routerSettings
|
|
102
|
+
*/
|
|
103
|
+
start(apiVersion, routerSettings) {
|
|
104
|
+
debug('routing start', apiVersion, routerSettings);
|
|
105
|
+
const RESOURCE_CONFIG = require(`./config/${apiVersion}`);
|
|
106
|
+
|
|
107
|
+
const unsubscribeRouter = new UnsubscribeRouter();
|
|
108
|
+
this.siteRouter.mountRouter(unsubscribeRouter.router());
|
|
109
|
+
this.registry.setRouter('unsubscribeRouter', unsubscribeRouter);
|
|
110
|
+
|
|
111
|
+
const emailRouter = new EmailRouter(RESOURCE_CONFIG);
|
|
112
|
+
this.siteRouter.mountRouter(emailRouter.router());
|
|
113
|
+
this.registry.setRouter('emailRouter', emailRouter);
|
|
114
|
+
|
|
115
|
+
const previewRouter = new PreviewRouter(RESOURCE_CONFIG);
|
|
116
|
+
this.siteRouter.mountRouter(previewRouter.router());
|
|
117
|
+
this.registry.setRouter('previewRouter', previewRouter);
|
|
118
|
+
|
|
119
|
+
_.each(routerSettings.routes, (value, key) => {
|
|
120
|
+
const staticRoutesRouter = new StaticRoutesRouter(key, value, this.routerCreated.bind(this));
|
|
121
|
+
this.siteRouter.mountRouter(staticRoutesRouter.router());
|
|
122
|
+
|
|
123
|
+
this.registry.setRouter(staticRoutesRouter.identifier, staticRoutesRouter);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
_.each(routerSettings.collections, (value, key) => {
|
|
127
|
+
const collectionRouter = new CollectionRouter(key, value, RESOURCE_CONFIG, this.routerCreated.bind(this));
|
|
128
|
+
this.siteRouter.mountRouter(collectionRouter.router());
|
|
129
|
+
this.registry.setRouter(collectionRouter.identifier, collectionRouter);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const staticPagesRouter = new StaticPagesRouter(RESOURCE_CONFIG, this.routerCreated.bind(this));
|
|
133
|
+
this.siteRouter.mountRouter(staticPagesRouter.router());
|
|
134
|
+
|
|
135
|
+
this.registry.setRouter('staticPagesRouter', staticPagesRouter);
|
|
136
|
+
|
|
137
|
+
_.each(routerSettings.taxonomies, (value, key) => {
|
|
138
|
+
const taxonomyRouter = new TaxonomyRouter(key, value, RESOURCE_CONFIG, this.routerCreated.bind(this));
|
|
139
|
+
this.siteRouter.mountRouter(taxonomyRouter.router());
|
|
140
|
+
|
|
141
|
+
this.registry.setRouter(taxonomyRouter.identifier, taxonomyRouter);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const appRouter = new ParentRouter('AppsRouter');
|
|
145
|
+
this.siteRouter.mountRouter(appRouter.router());
|
|
146
|
+
|
|
147
|
+
this.registry.setRouter('appRouter', appRouter);
|
|
148
|
+
|
|
149
|
+
debug('Routes:', this.registry.getAllRoutes());
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* This is a glue code to keep the implementation of routers away from
|
|
154
|
+
* this sort of logic. Ideally this method should not be ever called
|
|
155
|
+
* and handled completely on the URL Service layer without touching the frontend
|
|
156
|
+
* @param {Object} settingModel instance of the settings model
|
|
157
|
+
* @returns {void}
|
|
158
|
+
*/
|
|
159
|
+
handleTimezoneEdit(settingModel) {
|
|
160
|
+
const newTimezone = settingModel.attributes.value;
|
|
161
|
+
const previousTimezone = settingModel._previousAttributes.value;
|
|
162
|
+
|
|
163
|
+
if (newTimezone === previousTimezone) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* CASE: timezone changes
|
|
169
|
+
*
|
|
170
|
+
* If your permalink contains a date reference, we have to regenerate the urls.
|
|
171
|
+
*
|
|
172
|
+
* e.g. /:year/:month/:day/:slug/ or /:day/:slug/
|
|
173
|
+
*/
|
|
174
|
+
|
|
175
|
+
// NOTE: timezone change only affects the collection router with dated permalinks
|
|
176
|
+
const collectionRouter = this.registry.getRouterByName('CollectionRouter');
|
|
177
|
+
if (collectionRouter.getPermalinks().getValue().match(/:year|:month|:day/)) {
|
|
178
|
+
debug('handleTimezoneEdit: trigger regeneration');
|
|
179
|
+
|
|
180
|
+
this.urlService.onRouterUpdated(collectionRouter);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
module.exports = RouterManager;
|
|
@@ -3,7 +3,7 @@ const Promise = require('bluebird');
|
|
|
3
3
|
const cheerio = require('cheerio');
|
|
4
4
|
const RSS = require('rss');
|
|
5
5
|
const urlUtils = require('../../../shared/url-utils');
|
|
6
|
-
const
|
|
6
|
+
const {routerManager} = require('../routing');
|
|
7
7
|
|
|
8
8
|
const generateTags = function generateTags(data) {
|
|
9
9
|
if (data.tags) {
|
|
@@ -19,7 +19,7 @@ const generateTags = function generateTags(data) {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
const generateItem = function generateItem(post, secure) {
|
|
22
|
-
const itemUrl =
|
|
22
|
+
const itemUrl = routerManager.getUrlByResourceId(post.id, {secure, absolute: true});
|
|
23
23
|
const htmlContent = cheerio.load(post.html || '');
|
|
24
24
|
const item = {
|
|
25
25
|
title: post.title,
|