ghost 5.16.2 → 5.17.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/components/tryghost-adapter-manager-5.17.0.tgz +0 -0
- package/components/tryghost-api-framework-5.17.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.17.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.17.0.tgz +0 -0
- package/components/tryghost-constants-5.17.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.17.0.tgz +0 -0
- package/components/tryghost-domain-events-5.17.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.17.0.tgz +0 -0
- package/components/{tryghost-email-analytics-service-5.16.2.tgz → tryghost-email-analytics-service-5.17.0.tgz} +0 -0
- package/components/tryghost-email-content-generator-5.17.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.17.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.17.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.17.0.tgz +0 -0
- package/components/tryghost-job-manager-5.17.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.17.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.17.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.17.0.tgz +0 -0
- package/components/tryghost-magic-link-5.17.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.17.0.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.17.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.17.0.tgz +0 -0
- package/components/tryghost-member-events-5.17.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.17.0.tgz +0 -0
- package/components/tryghost-members-api-5.17.0.tgz +0 -0
- package/components/tryghost-members-csv-5.17.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.17.0.tgz +0 -0
- package/components/tryghost-members-importer-5.17.0.tgz +0 -0
- package/components/tryghost-members-offers-5.17.0.tgz +0 -0
- package/components/{tryghost-members-payments-5.16.2.tgz → tryghost-members-payments-5.17.0.tgz} +0 -0
- package/components/{tryghost-members-ssr-5.16.2.tgz → tryghost-members-ssr-5.17.0.tgz} +0 -0
- package/components/tryghost-members-stripe-service-5.17.0.tgz +0 -0
- package/components/tryghost-minifier-5.17.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.17.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.17.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.17.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.17.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.17.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.17.0.tgz +0 -0
- package/components/{tryghost-oembed-service-5.16.2.tgz → tryghost-oembed-service-5.17.0.tgz} +0 -0
- package/components/{tryghost-package-json-5.16.2.tgz → tryghost-package-json-5.17.0.tgz} +0 -0
- package/components/tryghost-referrers-5.17.0.tgz +0 -0
- package/components/{tryghost-security-5.16.2.tgz → tryghost-security-5.17.0.tgz} +0 -0
- package/components/tryghost-session-service-5.17.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.17.0.tgz +0 -0
- package/components/tryghost-staff-service-5.17.0.tgz +0 -0
- package/components/tryghost-stats-service-5.17.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.17.0.tgz +0 -0
- package/components/{tryghost-verification-trigger-5.16.2.tgz → tryghost-verification-trigger-5.17.0.tgz} +0 -0
- package/components/{tryghost-version-notifications-data-service-5.16.2.tgz → tryghost-version-notifications-data-service-5.17.0.tgz} +0 -0
- package/core/built/admin/assets/{chunk.143.4e48bf4e6c3d13258bb1.js → chunk.143.4ce1dcfd5c372638114d.js} +6 -6
- package/core/built/admin/assets/{chunk.174.e1e89637eab79fdd5c5d.js → chunk.174.37fefc669899f0fd0064.js} +153 -153
- package/core/built/admin/assets/{chunk.178.9c6e85adf674bd785cf1.js → chunk.178.824dedc0f9c31a8f64a7.js} +4 -4
- package/core/built/admin/assets/{chunk.579.d14c3688558f34afeb3e.js → chunk.579.a9bccec4d650a7be727a.js} +142 -88
- package/core/built/admin/assets/{chunk.579.d14c3688558f34afeb3e.js.LICENSE.txt → chunk.579.a9bccec4d650a7be727a.js.LICENSE.txt} +0 -0
- package/core/built/admin/assets/ghost-597fb8e8b1b91dd0ac4d9f2d75bd67fb.css +1 -0
- package/core/built/admin/assets/ghost-dark-e4ccecd9903d35d360d71fe859cbb3bf.css +1 -0
- package/core/built/admin/assets/{ghost-5064a384fad1061ca77a8604e479da90.js → ghost-fa7772f8aa2522b3935efed71fa75619.js} +390 -342
- package/core/built/admin/assets/vendor-325e038b8609f0979f6578cae7a87f9e.js +13270 -0
- package/core/built/admin/index.html +6 -6
- package/core/frontend/helpers/ghost_head.js +1 -1
- package/core/frontend/src/member-attribution/member-attribution.js +14 -14
- package/core/frontend/web/middleware/error-handler.js +3 -1
- package/core/frontend/web/site.js +9 -9
- package/core/server/api/endpoints/pages.js +1 -1
- package/core/server/api/endpoints/posts.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/pages.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/posts.js +2 -2
- package/core/server/api/endpoints/utils/serializers/output/mappers/activity-feed-events.js +23 -0
- package/core/server/api/endpoints/utils/serializers/output/members.js +20 -3
- package/core/server/api/endpoints/utils/validators/input/pages.js +5 -1
- package/core/server/api/endpoints/utils/validators/input/posts.js +5 -1
- package/core/server/data/exporter/table-lists.js +2 -2
- package/core/server/data/migrations/utils/tables.js +1 -1
- package/core/server/data/migrations/versions/5.16/2022-09-19-09-04-add-link-redirects-table.js +9 -10
- package/core/server/data/migrations/versions/5.16/2022-09-19-09-05-add-members-link-click-events-table.js +9 -8
- package/core/server/data/migrations/versions/5.17/2022-09-27-13-53-remove-click-tracking-tables.js +28 -0
- package/core/server/data/migrations/versions/5.17/2022-09-27-13-55-add-redirects-table.js +10 -0
- package/core/server/data/migrations/versions/5.17/2022-09-27-13-56-add-members-click-events-table.js +8 -0
- package/core/server/data/migrations/versions/5.17/2022-09-27-16-49-set-track-clicks-based-on-opens.js +31 -0
- package/core/server/data/migrations/versions/5.17/2022-09-29-12-39-add-track-clicks-column-to-emails.js +7 -0
- package/core/server/data/schema/schema.js +4 -3
- package/core/server/models/email.js +1 -0
- package/core/server/models/{member-link-click-event.js → member-click-event.js} +6 -6
- package/core/server/models/member.js +22 -0
- package/core/server/models/post.js +6 -6
- package/core/server/models/{link-redirect.js → redirect.js} +7 -7
- package/core/server/services/api-version-compatibility/index.js +6 -1
- package/core/server/services/link-redirection/index.js +1 -1
- package/core/server/services/link-tracking/LinkClickRepository.js +2 -2
- package/core/server/services/link-tracking/index.js +4 -4
- package/core/server/services/mega/mega.js +1 -0
- package/core/server/services/mega/post-email-serializer.js +17 -28
- package/core/server/services/member-attribution/index.js +2 -1
- package/core/server/services/members/api.js +1 -1
- package/core/server/web/api/endpoints/content/app.js +5 -2
- package/core/server/web/well-known.js +5 -1
- package/core/shared/config/defaults.json +14 -2
- package/core/shared/labs.js +2 -2
- package/package.json +106 -105
- package/yarn.lock +311 -299
- package/components/tryghost-adapter-manager-5.16.2.tgz +0 -0
- package/components/tryghost-api-framework-5.16.2.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.16.2.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.16.2.tgz +0 -0
- package/components/tryghost-constants-5.16.2.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.16.2.tgz +0 -0
- package/components/tryghost-domain-events-5.16.2.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.16.2.tgz +0 -0
- package/components/tryghost-email-content-generator-5.16.2.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.16.2.tgz +0 -0
- package/components/tryghost-extract-api-key-5.16.2.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.16.2.tgz +0 -0
- package/components/tryghost-job-manager-5.16.2.tgz +0 -0
- package/components/tryghost-link-redirects-5.16.2.tgz +0 -0
- package/components/tryghost-link-replacer-5.16.2.tgz +0 -0
- package/components/tryghost-link-tracking-5.16.2.tgz +0 -0
- package/components/tryghost-magic-link-5.16.2.tgz +0 -0
- package/components/tryghost-mailgun-client-5.16.2.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.16.2.tgz +0 -0
- package/components/tryghost-member-attribution-5.16.2.tgz +0 -0
- package/components/tryghost-member-events-5.16.2.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.16.2.tgz +0 -0
- package/components/tryghost-members-api-5.16.2.tgz +0 -0
- package/components/tryghost-members-csv-5.16.2.tgz +0 -0
- package/components/tryghost-members-events-service-5.16.2.tgz +0 -0
- package/components/tryghost-members-importer-5.16.2.tgz +0 -0
- package/components/tryghost-members-offers-5.16.2.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.16.2.tgz +0 -0
- package/components/tryghost-minifier-5.16.2.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.16.2.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.16.2.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.16.2.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.16.2.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.16.2.tgz +0 -0
- package/components/tryghost-mw-vhost-5.16.2.tgz +0 -0
- package/components/tryghost-referrers-5.16.2.tgz +0 -0
- package/components/tryghost-session-service-5.16.2.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.16.2.tgz +0 -0
- package/components/tryghost-staff-service-5.16.2.tgz +0 -0
- package/components/tryghost-stats-service-5.16.2.tgz +0 -0
- package/components/tryghost-update-check-service-5.16.2.tgz +0 -0
- package/core/built/admin/assets/ghost-6491d134c450ca676911ea17e16cd7d4.css +0 -1
- package/core/built/admin/assets/ghost-dark-297ab2fcf4cadd1c950b84089a38c5e2.css +0 -1
- package/core/built/admin/assets/vendor-b2375e2f383cbc3fd73340c4b656c993.js +0 -13654
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Ghost Admin</title>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.17%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
<link integrity="" rel="stylesheet" href="assets/vendor-733135cd6cbca8126c6fa223d63a5bf3.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-597fb8e8b1b91dd0ac4d9f2d75bd67fb.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
|
|
57
57
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
58
58
|
|
|
59
|
-
<script src="assets/vendor-
|
|
60
|
-
<script src="assets/chunk.579.
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
59
|
+
<script src="assets/vendor-325e038b8609f0979f6578cae7a87f9e.js"></script>
|
|
60
|
+
<script src="assets/chunk.579.a9bccec4d650a7be727a.js"></script>
|
|
61
|
+
<script src="assets/chunk.143.4ce1dcfd5c372638114d.js"></script>
|
|
62
|
+
<script src="assets/ghost-fa7772f8aa2522b3935efed71fa75619.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -234,7 +234,7 @@ module.exports = async function ghost_head(options) { // eslint-disable-line cam
|
|
|
234
234
|
head.push(`<script defer src="${getAssetUrl('public/comment-counts.min.js')}" data-ghost-comments-counts-api="${urlUtils.getSiteUrl(true)}members/api/comments/counts/"></script>`);
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
if (
|
|
237
|
+
if (settingsCache.get('members_enabled')) {
|
|
238
238
|
head.push(`<script defer src="${getAssetUrl('public/member-attribution.min.js')}"></script>`);
|
|
239
239
|
}
|
|
240
240
|
|
|
@@ -87,9 +87,9 @@ const LIMIT = 15;
|
|
|
87
87
|
console.error('[Member Attribution] Parsing referrer from querystring failed', e);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
90
|
+
const referrerSource = refParam || sourceParam || utmSourceParam || null;
|
|
91
|
+
const referrerMedium = utmMediumParam || null;
|
|
92
|
+
const referrerUrl = window.document.referrer || null;
|
|
93
93
|
|
|
94
94
|
// Do we have attributions in the query string?
|
|
95
95
|
try {
|
|
@@ -101,9 +101,9 @@ const LIMIT = 15;
|
|
|
101
101
|
time: currentTime,
|
|
102
102
|
id: params.get('attribution_id'),
|
|
103
103
|
type: params.get('attribution_type'),
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
referrerSource,
|
|
105
|
+
referrerMedium,
|
|
106
|
+
referrerUrl
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
// Remove attribution from query string
|
|
@@ -122,19 +122,19 @@ const LIMIT = 15;
|
|
|
122
122
|
history.push({
|
|
123
123
|
path: currentPath,
|
|
124
124
|
time: currentTime,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
referrerSource,
|
|
126
|
+
referrerMedium,
|
|
127
|
+
referrerUrl
|
|
128
128
|
});
|
|
129
129
|
} else if (history.length > 0) {
|
|
130
130
|
history[history.length - 1].time = currentTime;
|
|
131
131
|
// Update referrer information for same path if available (e.g. when opening a link on same path via external referrer)
|
|
132
|
-
if (
|
|
133
|
-
history[history.length - 1].
|
|
134
|
-
history[history.length - 1].
|
|
132
|
+
if (referrerSource) {
|
|
133
|
+
history[history.length - 1].referrerSource = referrerSource;
|
|
134
|
+
history[history.length - 1].referrerMedium = referrerMedium;
|
|
135
135
|
}
|
|
136
|
-
if (
|
|
137
|
-
history[history.length - 1].
|
|
136
|
+
if (referrerUrl) {
|
|
137
|
+
history[history.length - 1].referrerUrl = referrerUrl;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -7,7 +7,7 @@ const config = require('../../../shared/config');
|
|
|
7
7
|
const renderer = require('../../services/rendering');
|
|
8
8
|
|
|
9
9
|
// @TODO: make this properly shared code
|
|
10
|
-
const {prepareError, prepareStack} = require('@tryghost/mw-error-handler');
|
|
10
|
+
const {prepareError, prepareErrorCacheControl, prepareStack} = require('@tryghost/mw-error-handler');
|
|
11
11
|
|
|
12
12
|
const messages = {
|
|
13
13
|
oopsErrorTemplateHasError: 'Oops, seems there is an error in the error template.',
|
|
@@ -86,6 +86,8 @@ const themeErrorRenderer = (err, req, res, next) => {
|
|
|
86
86
|
module.exports.handleThemeResponse = [
|
|
87
87
|
// Make sure the error can be served
|
|
88
88
|
prepareError,
|
|
89
|
+
// Add cache-control header
|
|
90
|
+
prepareErrorCacheControl(),
|
|
89
91
|
// Handle the error in Sentry
|
|
90
92
|
sentry.errorHandler,
|
|
91
93
|
// Format the stack for the user
|
|
@@ -65,21 +65,21 @@ module.exports = function setupSiteApp(routerConfig) {
|
|
|
65
65
|
siteApp.use(mw.serveFavicon());
|
|
66
66
|
|
|
67
67
|
// Serve sitemap.xsl file
|
|
68
|
-
siteApp.use(mw.servePublicFile('static', 'sitemap.xsl', 'text/xsl',
|
|
68
|
+
siteApp.use(mw.servePublicFile('static', 'sitemap.xsl', 'text/xsl', config.get('caching:sitemapXSL:maxAge')));
|
|
69
69
|
|
|
70
70
|
// Serve stylesheets for default templates
|
|
71
|
-
siteApp.use(mw.servePublicFile('static', 'public/ghost.css', 'text/css',
|
|
72
|
-
siteApp.use(mw.servePublicFile('static', 'public/ghost.min.css', 'text/css',
|
|
71
|
+
siteApp.use(mw.servePublicFile('static', 'public/ghost.css', 'text/css', config.get('caching:publicAssets:maxAge')));
|
|
72
|
+
siteApp.use(mw.servePublicFile('static', 'public/ghost.min.css', 'text/css', config.get('caching:publicAssets:maxAge')));
|
|
73
73
|
|
|
74
74
|
// Card assets
|
|
75
|
-
siteApp.use(mw.servePublicFile('built', 'public/cards.min.css', 'text/css',
|
|
76
|
-
siteApp.use(mw.servePublicFile('built', 'public/cards.min.js', 'application/javascript',
|
|
75
|
+
siteApp.use(mw.servePublicFile('built', 'public/cards.min.css', 'text/css', config.get('caching:publicAssets:maxAge')));
|
|
76
|
+
siteApp.use(mw.servePublicFile('built', 'public/cards.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
|
|
77
77
|
|
|
78
78
|
// Comment counts
|
|
79
|
-
siteApp.use(mw.servePublicFile('built', 'public/comment-counts.min.js', 'application/javascript',
|
|
79
|
+
siteApp.use(mw.servePublicFile('built', 'public/comment-counts.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
|
|
80
80
|
|
|
81
81
|
// Member attribution
|
|
82
|
-
siteApp.use(mw.servePublicFile('built', 'public/member-attribution.min.js', 'application/javascript',
|
|
82
|
+
siteApp.use(mw.servePublicFile('built', 'public/member-attribution.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
|
|
83
83
|
|
|
84
84
|
// Serve site images using the storage adapter
|
|
85
85
|
siteApp.use(STATIC_IMAGE_URL_PREFIX, mw.handleImageSizes, storage.getStorage('images').serve());
|
|
@@ -94,7 +94,7 @@ module.exports = function setupSiteApp(routerConfig) {
|
|
|
94
94
|
// /member/.well-known/* serves files (e.g. jwks.json) so it needs to be mounted before the prettyUrl mw to avoid trailing slashes
|
|
95
95
|
siteApp.use(
|
|
96
96
|
'/members/.well-known',
|
|
97
|
-
shared.middleware.cacheControl('public', {maxAge:
|
|
97
|
+
shared.middleware.cacheControl('public', {maxAge: config.get('caching:wellKnown:maxAge')}),
|
|
98
98
|
(req, res, next) => membersService.api.middleware.wellKnown(req, res, next)
|
|
99
99
|
);
|
|
100
100
|
|
|
@@ -118,7 +118,7 @@ module.exports = function setupSiteApp(routerConfig) {
|
|
|
118
118
|
debug('Themes done');
|
|
119
119
|
|
|
120
120
|
// Serve robots.txt if not found in theme
|
|
121
|
-
siteApp.use(mw.servePublicFile('static', 'robots.txt', 'text/plain',
|
|
121
|
+
siteApp.use(mw.servePublicFile('static', 'robots.txt', 'text/plain', config.get('caching:robotstxt:maxAge')));
|
|
122
122
|
|
|
123
123
|
// site map - this should probably be refactored to be an internal app
|
|
124
124
|
sitemapHandler(siteApp);
|
|
@@ -2,7 +2,7 @@ const models = require('../../models');
|
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
4
|
const getPostServiceInstance = require('../../services/posts/posts-service');
|
|
5
|
-
const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.
|
|
5
|
+
const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.paid_conversions'];
|
|
6
6
|
const UNSAFE_ATTRS = ['status', 'authors', 'visibility'];
|
|
7
7
|
|
|
8
8
|
const messages = {
|
|
@@ -24,7 +24,7 @@ function defaultRelations(frame) {
|
|
|
24
24
|
return false;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.
|
|
27
|
+
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.paid_conversions'];
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function setDefaultOrder(frame) {
|
|
@@ -26,9 +26,9 @@ function defaultRelations(frame) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
if (labs.isSet('emailClicks')) {
|
|
29
|
-
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.
|
|
29
|
+
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.paid_conversions', 'count.clicks'];
|
|
30
30
|
} else {
|
|
31
|
-
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.
|
|
31
|
+
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.paid_conversions'];
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -49,12 +49,32 @@ const clickEventMapper = (json, frame) => {
|
|
|
49
49
|
response.member = null;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
if (data.created_at) {
|
|
53
|
+
response.created_at = data.created_at;
|
|
54
|
+
}
|
|
55
|
+
|
|
52
56
|
return {
|
|
53
57
|
...json,
|
|
54
58
|
data: response
|
|
55
59
|
};
|
|
56
60
|
};
|
|
57
61
|
|
|
62
|
+
function serializeAttribution(attribution) {
|
|
63
|
+
if (!attribution) {
|
|
64
|
+
return attribution;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
id: attribution?.id,
|
|
69
|
+
type: attribution?.type,
|
|
70
|
+
url: attribution?.url,
|
|
71
|
+
title: attribution?.title,
|
|
72
|
+
referrer_source: attribution?.referrerSource,
|
|
73
|
+
referrer_medium: attribution?.referrerMedium,
|
|
74
|
+
referrer_url: attribution.referrerUrl
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
58
78
|
const activityFeedMapper = (event, frame) => {
|
|
59
79
|
if (event.type === 'comment_event') {
|
|
60
80
|
return commentEventMapper(event, frame);
|
|
@@ -62,6 +82,9 @@ const activityFeedMapper = (event, frame) => {
|
|
|
62
82
|
if (event.type === 'click_event') {
|
|
63
83
|
return clickEventMapper(event, frame);
|
|
64
84
|
}
|
|
85
|
+
if (event.data?.attribution) {
|
|
86
|
+
event.data.attribution = serializeAttribution(event.data.attribution);
|
|
87
|
+
}
|
|
65
88
|
return event;
|
|
66
89
|
};
|
|
67
90
|
|
|
@@ -96,6 +96,22 @@ function exportCSV(data) {
|
|
|
96
96
|
return unparse(data.data);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
function serializeAttribution(attribution) {
|
|
100
|
+
if (!attribution) {
|
|
101
|
+
return attribution;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
id: attribution?.id,
|
|
106
|
+
type: attribution?.type,
|
|
107
|
+
url: attribution?.url,
|
|
108
|
+
title: attribution?.title,
|
|
109
|
+
referrer_source: attribution?.referrerSource,
|
|
110
|
+
referrer_medium: attribution?.referrerMedium,
|
|
111
|
+
referrer_url: attribution.referrerUrl
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
99
115
|
/**
|
|
100
116
|
* @param {import('bookshelf').Model} member
|
|
101
117
|
* @param {object} options
|
|
@@ -129,7 +145,7 @@ function serializeMember(member, options) {
|
|
|
129
145
|
email_recipients: json.email_recipients,
|
|
130
146
|
status: json.status,
|
|
131
147
|
last_seen_at: json.last_seen_at,
|
|
132
|
-
attribution: json.attribution
|
|
148
|
+
attribution: serializeAttribution(json.attribution)
|
|
133
149
|
};
|
|
134
150
|
|
|
135
151
|
if (json.products) {
|
|
@@ -141,15 +157,16 @@ function serializeMember(member, options) {
|
|
|
141
157
|
if (!subscription.price) {
|
|
142
158
|
continue;
|
|
143
159
|
}
|
|
144
|
-
|
|
160
|
+
|
|
145
161
|
if (!subscription.price.tier && subscription.price.product) {
|
|
146
162
|
subscription.price.tier = subscription.price.product;
|
|
147
|
-
|
|
163
|
+
|
|
148
164
|
if (!subscription.price.tier.tier_id) {
|
|
149
165
|
subscription.price.tier.tier_id = subscription.price.tier.product_id;
|
|
150
166
|
}
|
|
151
167
|
delete subscription.price.tier.product_id;
|
|
152
168
|
}
|
|
169
|
+
subscription.attribution = serializeAttribution(subscription.attribution);
|
|
153
170
|
delete subscription.price.product;
|
|
154
171
|
}
|
|
155
172
|
|
|
@@ -5,7 +5,9 @@ const tpl = require('@tryghost/tpl');
|
|
|
5
5
|
|
|
6
6
|
const messages = {
|
|
7
7
|
invalidVisibilityFilter: 'Invalid filter in visibility_filter property',
|
|
8
|
-
onlySingleContentSource: '
|
|
8
|
+
onlySingleContentSource: 'Pages can have either a mobiledoc or a lexical property, never both.',
|
|
9
|
+
onlySingleContentSourceContext: 'Both the mobiledoc and lexical properties are set, one must be null',
|
|
10
|
+
onlySingleContentSourceHelp: 'https://ghost.org/docs/admin-api/#the-post-object'
|
|
9
11
|
};
|
|
10
12
|
|
|
11
13
|
const validateVisibility = async function (frame) {
|
|
@@ -43,6 +45,8 @@ const validateSingleContentSource = async function (frame) {
|
|
|
43
45
|
if (page.mobiledoc && page.lexical) {
|
|
44
46
|
return Promise.reject(new ValidationError({
|
|
45
47
|
message: tpl(messages.onlySingleContentSource),
|
|
48
|
+
context: tpl(messages.onlySingleContentSourceContext),
|
|
49
|
+
help: tpl(messages.onlySingleContentSourceHelp),
|
|
46
50
|
property: 'lexical'
|
|
47
51
|
}));
|
|
48
52
|
}
|
|
@@ -5,7 +5,9 @@ const tpl = require('@tryghost/tpl');
|
|
|
5
5
|
|
|
6
6
|
const messages = {
|
|
7
7
|
invalidVisibilityFilter: 'Invalid filter in visibility_filter property',
|
|
8
|
-
onlySingleContentSource: '
|
|
8
|
+
onlySingleContentSource: 'Posts can have either a mobiledoc or a lexical property, never both.',
|
|
9
|
+
onlySingleContentSourceContext: 'Both the mobiledoc and lexical properties are set, one must be null',
|
|
10
|
+
onlySingleContentSourceHelp: 'https://ghost.org/docs/admin-api/#the-post-object'
|
|
9
11
|
};
|
|
10
12
|
|
|
11
13
|
const validateVisibility = async function (frame) {
|
|
@@ -43,6 +45,8 @@ const validateSingleContentSource = async function (frame) {
|
|
|
43
45
|
if (post.mobiledoc && post.lexical) {
|
|
44
46
|
return Promise.reject(new ValidationError({
|
|
45
47
|
message: tpl(messages.onlySingleContentSource),
|
|
48
|
+
context: tpl(messages.onlySingleContentSourceContext),
|
|
49
|
+
help: tpl(messages.onlySingleContentSourceHelp),
|
|
46
50
|
property: 'lexical'
|
|
47
51
|
}));
|
|
48
52
|
}
|
|
@@ -39,8 +39,8 @@ const BACKUP_TABLES = [
|
|
|
39
39
|
'comment_likes',
|
|
40
40
|
'comment_reports',
|
|
41
41
|
'jobs',
|
|
42
|
-
'
|
|
43
|
-
'
|
|
42
|
+
'redirects',
|
|
43
|
+
'members_click_events'
|
|
44
44
|
];
|
|
45
45
|
|
|
46
46
|
// NOTE: exposing only tables which are going to be included in a "default" export file
|
|
@@ -38,7 +38,7 @@ function addTable(name, tableSpec) {
|
|
|
38
38
|
/**
|
|
39
39
|
* Creates migration which will drop a table
|
|
40
40
|
*
|
|
41
|
-
* @param {[
|
|
41
|
+
* @param {string[]} names - names of the tables to drop
|
|
42
42
|
*/
|
|
43
43
|
function dropTables(names) {
|
|
44
44
|
return createIrreversibleMigration(
|
package/core/server/data/migrations/versions/5.16/2022-09-19-09-04-add-link-redirects-table.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
});
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
module.exports = {
|
|
3
|
+
async up() {
|
|
4
|
+
logging.warn('Skipping migration - noop');
|
|
5
|
+
},
|
|
6
|
+
async down() {
|
|
7
|
+
logging.warn('Skipping migration - noop');
|
|
8
|
+
}
|
|
9
|
+
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
module.exports = {
|
|
3
|
+
async up() {
|
|
4
|
+
logging.warn('Skipping migration - noop');
|
|
5
|
+
},
|
|
6
|
+
async down() {
|
|
7
|
+
logging.warn('Skipping migration - noop');
|
|
8
|
+
}
|
|
9
|
+
};
|
package/core/server/data/migrations/versions/5.17/2022-09-27-13-53-remove-click-tracking-tables.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const {addTable, combineNonTransactionalMigrations} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
function reverseMigration({up, down, config}) {
|
|
4
|
+
return {
|
|
5
|
+
config,
|
|
6
|
+
up: down,
|
|
7
|
+
down: up
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = reverseMigration(
|
|
12
|
+
combineNonTransactionalMigrations(
|
|
13
|
+
addTable('link_redirects', {
|
|
14
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
15
|
+
from: {type: 'string', maxlength: 2000, nullable: false},
|
|
16
|
+
to: {type: 'string', maxlength: 2000, nullable: false},
|
|
17
|
+
post_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'posts.id', setNullDelete: true},
|
|
18
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
19
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
20
|
+
}),
|
|
21
|
+
addTable('members_link_click_events', {
|
|
22
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
23
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
24
|
+
link_id: {type: 'string', maxlength: 24, nullable: false, references: 'link_redirects.id', cascadeDelete: true},
|
|
25
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
26
|
+
})
|
|
27
|
+
)
|
|
28
|
+
);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('redirects', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
from: {type: 'string', maxlength: 2000, nullable: false},
|
|
6
|
+
to: {type: 'string', maxlength: 2000, nullable: false},
|
|
7
|
+
post_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'posts.id', setNullDelete: true},
|
|
8
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
9
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
10
|
+
});
|
package/core/server/data/migrations/versions/5.17/2022-09-27-13-56-add-members-click-events-table.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('members_click_events', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
6
|
+
redirect_id: {type: 'string', maxlength: 24, nullable: false, references: 'redirects.id', cascadeDelete: true},
|
|
7
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
8
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
const {createTransactionalMigration} = require('../../utils');
|
|
3
|
+
|
|
4
|
+
// Set email_track_clicks to the current value of email_track_opens
|
|
5
|
+
module.exports = createTransactionalMigration(
|
|
6
|
+
async function up(connection) {
|
|
7
|
+
const reuseValueOfSetting = await connection('settings')
|
|
8
|
+
.where('key', '=', 'email_track_opens')
|
|
9
|
+
.first();
|
|
10
|
+
|
|
11
|
+
if (!reuseValueOfSetting) {
|
|
12
|
+
logging.warn(`Skipped setting email_track_clicks to current value of email_track_opens - email_track_opens not found`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const affectedRows = await connection('settings')
|
|
17
|
+
.update({
|
|
18
|
+
value: reuseValueOfSetting.value
|
|
19
|
+
})
|
|
20
|
+
.where('key', '=', 'email_track_clicks');
|
|
21
|
+
|
|
22
|
+
if (affectedRows === 1) {
|
|
23
|
+
logging.info(`Set email_track_clicks to ${reuseValueOfSetting.value} (current email_track_opens value)`);
|
|
24
|
+
} else {
|
|
25
|
+
logging.warn(`Tried setting email_track_clicks to ${reuseValueOfSetting.value} — ${affectedRows} changes`);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
async function down() {
|
|
29
|
+
// no-op: we don't need to change it back
|
|
30
|
+
}
|
|
31
|
+
);
|
|
@@ -723,6 +723,7 @@ module.exports = {
|
|
|
723
723
|
html: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
724
724
|
plaintext: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
725
725
|
track_opens: {type: 'bool', nullable: false, defaultTo: false},
|
|
726
|
+
track_clicks: {type: 'bool', nullable: false, defaultTo: false},
|
|
726
727
|
submitted_at: {type: 'dateTime', nullable: false},
|
|
727
728
|
newsletter_id: {type: 'string', maxlength: 24, nullable: true, references: 'newsletters.id'},
|
|
728
729
|
created_at: {type: 'dateTime', nullable: false},
|
|
@@ -836,7 +837,7 @@ module.exports = {
|
|
|
836
837
|
created_at: {type: 'dateTime', nullable: false},
|
|
837
838
|
updated_at: {type: 'dateTime', nullable: true}
|
|
838
839
|
},
|
|
839
|
-
|
|
840
|
+
redirects: {
|
|
840
841
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
841
842
|
from: {type: 'string', maxlength: 2000, nullable: false},
|
|
842
843
|
to: {type: 'string', maxlength: 2000, nullable: false},
|
|
@@ -844,10 +845,10 @@ module.exports = {
|
|
|
844
845
|
created_at: {type: 'dateTime', nullable: false},
|
|
845
846
|
updated_at: {type: 'dateTime', nullable: true}
|
|
846
847
|
},
|
|
847
|
-
|
|
848
|
+
members_click_events: {
|
|
848
849
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
849
850
|
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
850
|
-
|
|
851
|
+
redirect_id: {type: 'string', maxlength: 24, nullable: false, references: 'redirects.id', cascadeDelete: true},
|
|
851
852
|
created_at: {type: 'dateTime', nullable: false}
|
|
852
853
|
}
|
|
853
854
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const errors = require('@tryghost/errors');
|
|
2
2
|
const ghostBookshelf = require('./base');
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
tableName: '
|
|
4
|
+
const MemberClickEvent = ghostBookshelf.Model.extend({
|
|
5
|
+
tableName: 'members_click_events',
|
|
6
6
|
|
|
7
7
|
link() {
|
|
8
|
-
return this.belongsTo('
|
|
8
|
+
return this.belongsTo('Redirect', 'link_id');
|
|
9
9
|
},
|
|
10
10
|
|
|
11
11
|
member() {
|
|
@@ -13,14 +13,14 @@ const MemberLinkClickEvent = ghostBookshelf.Model.extend({
|
|
|
13
13
|
}
|
|
14
14
|
}, {
|
|
15
15
|
async edit() {
|
|
16
|
-
throw new errors.IncorrectUsageError({message: 'Cannot edit
|
|
16
|
+
throw new errors.IncorrectUsageError({message: 'Cannot edit MemberClickEvent'});
|
|
17
17
|
},
|
|
18
18
|
|
|
19
19
|
async destroy() {
|
|
20
|
-
throw new errors.IncorrectUsageError({message: 'Cannot destroy
|
|
20
|
+
throw new errors.IncorrectUsageError({message: 'Cannot destroy MemberClickEvent'});
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
module.exports = {
|
|
25
|
-
|
|
25
|
+
MemberClickEvent: ghostBookshelf.model('MemberClickEvent', MemberClickEvent)
|
|
26
26
|
};
|
|
@@ -46,6 +46,12 @@ const Member = ghostBookshelf.Model.extend({
|
|
|
46
46
|
}, {
|
|
47
47
|
key: 'conversion',
|
|
48
48
|
replacement: 'conversions.attribution_id'
|
|
49
|
+
}, {
|
|
50
|
+
key: 'opened_emails.post_id',
|
|
51
|
+
replacement: 'emails.post_id',
|
|
52
|
+
// Currently we cannot expand on values such as null or a string in mongo-knex
|
|
53
|
+
// But the line below is essentially the same as: `email_recipients.opened_at:-null`
|
|
54
|
+
expansion: 'email_recipients.opened_at:>=0'
|
|
49
55
|
}];
|
|
50
56
|
},
|
|
51
57
|
|
|
@@ -92,6 +98,22 @@ const Member = ghostBookshelf.Model.extend({
|
|
|
92
98
|
tableNameAs: 'conversions',
|
|
93
99
|
type: 'oneToOne',
|
|
94
100
|
joinFrom: 'member_id'
|
|
101
|
+
},
|
|
102
|
+
clicked_links: {
|
|
103
|
+
tableName: 'link_redirects',
|
|
104
|
+
tableNameAs: 'clicked_links',
|
|
105
|
+
type: 'manyToMany',
|
|
106
|
+
joinTable: 'members_link_click_events',
|
|
107
|
+
joinFrom: 'member_id',
|
|
108
|
+
joinTo: 'link_id'
|
|
109
|
+
},
|
|
110
|
+
emails: {
|
|
111
|
+
tableName: 'emails',
|
|
112
|
+
tableNameAs: 'emails',
|
|
113
|
+
type: 'manyToMany',
|
|
114
|
+
joinTable: 'email_recipients',
|
|
115
|
+
joinFrom: 'member_id',
|
|
116
|
+
joinTo: 'email_id'
|
|
95
117
|
}
|
|
96
118
|
};
|
|
97
119
|
},
|
|
@@ -1338,20 +1338,20 @@ Post = ghostBookshelf.Model.extend({
|
|
|
1338
1338
|
.as('count__signups');
|
|
1339
1339
|
});
|
|
1340
1340
|
},
|
|
1341
|
-
|
|
1341
|
+
paid_conversions(modelOrCollection) {
|
|
1342
1342
|
modelOrCollection.query('columns', 'posts.*', (qb) => {
|
|
1343
1343
|
qb.count('members_subscription_created_events.id')
|
|
1344
1344
|
.from('members_subscription_created_events')
|
|
1345
1345
|
.whereRaw('posts.id = members_subscription_created_events.attribution_id')
|
|
1346
|
-
.as('
|
|
1346
|
+
.as('count__paid_conversions');
|
|
1347
1347
|
});
|
|
1348
1348
|
},
|
|
1349
1349
|
clicks(modelOrCollection) {
|
|
1350
1350
|
modelOrCollection.query('columns', 'posts.*', (qb) => {
|
|
1351
|
-
qb.countDistinct('
|
|
1352
|
-
.from('
|
|
1353
|
-
.join('
|
|
1354
|
-
.whereRaw('posts.id =
|
|
1351
|
+
qb.countDistinct('members_click_events.member_id')
|
|
1352
|
+
.from('members_click_events')
|
|
1353
|
+
.join('redirects', 'members_click_events.redirect_id', 'redirects.id')
|
|
1354
|
+
.whereRaw('posts.id = redirects.post_id')
|
|
1355
1355
|
.as('count__clicks');
|
|
1356
1356
|
});
|
|
1357
1357
|
}
|