ghost 5.81.1 → 5.82.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/{tryghost-adapter-cache-memory-ttl-5.81.1.tgz → tryghost-adapter-cache-memory-ttl-5.82.1.tgz} +0 -0
- package/components/{tryghost-adapter-cache-redis-5.81.1.tgz → tryghost-adapter-cache-redis-5.82.1.tgz} +0 -0
- package/components/{tryghost-adapter-manager-5.81.1.tgz → tryghost-adapter-manager-5.82.1.tgz} +0 -0
- package/components/tryghost-announcement-bar-settings-5.82.1.tgz +0 -0
- package/components/{tryghost-api-framework-5.81.1.tgz → tryghost-api-framework-5.82.1.tgz} +0 -0
- package/components/tryghost-api-version-compatibility-service-5.82.1.tgz +0 -0
- package/components/{tryghost-audience-feedback-5.81.1.tgz → tryghost-audience-feedback-5.82.1.tgz} +0 -0
- package/components/tryghost-bookshelf-repository-5.82.1.tgz +0 -0
- package/components/{tryghost-bootstrap-socket-5.81.1.tgz → tryghost-bootstrap-socket-5.82.1.tgz} +0 -0
- package/components/tryghost-collections-5.82.1.tgz +0 -0
- package/components/{tryghost-constants-5.81.1.tgz → tryghost-constants-5.82.1.tgz} +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.81.1.tgz → tryghost-custom-theme-settings-service-5.82.1.tgz} +0 -0
- package/components/{tryghost-data-generator-5.81.1.tgz → tryghost-data-generator-5.82.1.tgz} +0 -0
- package/components/tryghost-domain-events-5.82.1.tgz +0 -0
- package/components/{tryghost-donations-5.81.1.tgz → tryghost-donations-5.82.1.tgz} +0 -0
- package/components/tryghost-dynamic-routing-events-5.82.1.tgz +0 -0
- package/components/{tryghost-email-addresses-5.81.1.tgz → tryghost-email-addresses-5.82.1.tgz} +0 -0
- package/components/{tryghost-email-analytics-provider-mailgun-5.81.1.tgz → tryghost-email-analytics-provider-mailgun-5.82.1.tgz} +0 -0
- package/components/tryghost-email-analytics-service-5.82.1.tgz +0 -0
- package/components/{tryghost-email-content-generator-5.81.1.tgz → tryghost-email-content-generator-5.82.1.tgz} +0 -0
- package/components/{tryghost-email-events-5.81.1.tgz → tryghost-email-events-5.82.1.tgz} +0 -0
- package/components/{tryghost-email-service-5.81.1.tgz → tryghost-email-service-5.82.1.tgz} +0 -0
- package/components/tryghost-email-suppression-list-5.82.1.tgz +0 -0
- package/components/{tryghost-express-dynamic-redirects-5.81.1.tgz → tryghost-express-dynamic-redirects-5.82.1.tgz} +0 -0
- package/components/tryghost-external-media-inliner-5.82.1.tgz +0 -0
- package/components/tryghost-extract-api-key-5.82.1.tgz +0 -0
- package/components/tryghost-ghost-5.82.1.tgz +0 -0
- package/components/{tryghost-html-to-plaintext-5.81.1.tgz → tryghost-html-to-plaintext-5.82.1.tgz} +0 -0
- package/components/tryghost-i18n-5.82.1.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.82.1.tgz +0 -0
- package/components/{tryghost-importer-revue-5.81.1.tgz → tryghost-importer-revue-5.82.1.tgz} +0 -0
- package/components/{tryghost-in-memory-repository-5.81.1.tgz → tryghost-in-memory-repository-5.82.1.tgz} +0 -0
- package/components/{tryghost-job-manager-5.81.1.tgz → tryghost-job-manager-5.82.1.tgz} +0 -0
- package/components/{tryghost-link-redirects-5.81.1.tgz → tryghost-link-redirects-5.82.1.tgz} +0 -0
- package/components/{tryghost-link-replacer-5.81.1.tgz → tryghost-link-replacer-5.82.1.tgz} +0 -0
- package/components/{tryghost-link-tracking-5.81.1.tgz → tryghost-link-tracking-5.82.1.tgz} +0 -0
- package/components/{tryghost-magic-link-5.81.1.tgz → tryghost-magic-link-5.82.1.tgz} +0 -0
- package/components/{tryghost-mail-events-5.81.1.tgz → tryghost-mail-events-5.82.1.tgz} +0 -0
- package/components/tryghost-mailgun-client-5.82.1.tgz +0 -0
- package/components/{tryghost-member-attribution-5.81.1.tgz → tryghost-member-attribution-5.82.1.tgz} +0 -0
- package/components/{tryghost-member-events-5.81.1.tgz → tryghost-member-events-5.82.1.tgz} +0 -0
- package/components/tryghost-members-api-5.82.1.tgz +0 -0
- package/components/tryghost-members-csv-5.82.1.tgz +0 -0
- package/components/{tryghost-members-events-service-5.81.1.tgz → tryghost-members-events-service-5.82.1.tgz} +0 -0
- package/components/{tryghost-members-importer-5.81.1.tgz → tryghost-members-importer-5.82.1.tgz} +0 -0
- package/components/{tryghost-members-offers-5.81.1.tgz → tryghost-members-offers-5.82.1.tgz} +0 -0
- package/components/tryghost-members-payments-5.82.1.tgz +0 -0
- package/components/tryghost-members-ssr-5.82.1.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.82.1.tgz +0 -0
- package/components/{tryghost-mentions-email-report-5.81.1.tgz → tryghost-mentions-email-report-5.82.1.tgz} +0 -0
- package/components/tryghost-milestones-5.82.1.tgz +0 -0
- package/components/{tryghost-minifier-5.81.1.tgz → tryghost-minifier-5.82.1.tgz} +0 -0
- package/components/{tryghost-model-to-domain-event-interceptor-5.81.1.tgz → tryghost-model-to-domain-event-interceptor-5.82.1.tgz} +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.82.1.tgz +0 -0
- package/components/{tryghost-mw-cache-control-5.81.1.tgz → tryghost-mw-cache-control-5.82.1.tgz} +0 -0
- package/components/{tryghost-mw-error-handler-5.81.1.tgz → tryghost-mw-error-handler-5.82.1.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.82.1.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.82.1.tgz +0 -0
- package/components/{tryghost-mw-version-match-5.81.1.tgz → tryghost-mw-version-match-5.82.1.tgz} +0 -0
- package/components/tryghost-mw-vhost-5.82.1.tgz +0 -0
- package/components/{tryghost-nql-filter-expansions-5.81.1.tgz → tryghost-nql-filter-expansions-5.82.1.tgz} +0 -0
- package/components/{tryghost-oembed-service-5.81.1.tgz → tryghost-oembed-service-5.82.1.tgz} +0 -0
- package/components/{tryghost-package-json-5.81.1.tgz → tryghost-package-json-5.82.1.tgz} +0 -0
- package/components/{tryghost-post-events-5.81.1.tgz → tryghost-post-events-5.82.1.tgz} +0 -0
- package/components/tryghost-post-revisions-5.82.1.tgz +0 -0
- package/components/{tryghost-posts-service-5.81.1.tgz → tryghost-posts-service-5.82.1.tgz} +0 -0
- package/components/tryghost-recommendations-5.82.1.tgz +0 -0
- package/components/tryghost-referrers-5.82.1.tgz +0 -0
- package/components/{tryghost-security-5.81.1.tgz → tryghost-security-5.82.1.tgz} +0 -0
- package/components/tryghost-session-service-5.82.1.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.82.1.tgz +0 -0
- package/components/tryghost-slack-notifications-5.82.1.tgz +0 -0
- package/components/{tryghost-staff-service-5.81.1.tgz → tryghost-staff-service-5.82.1.tgz} +0 -0
- package/components/{tryghost-stats-service-5.81.1.tgz → tryghost-stats-service-5.82.1.tgz} +0 -0
- package/components/{tryghost-tiers-5.81.1.tgz → tryghost-tiers-5.82.1.tgz} +0 -0
- package/components/{tryghost-update-check-service-5.81.1.tgz → tryghost-update-check-service-5.82.1.tgz} +0 -0
- package/components/tryghost-verification-trigger-5.82.1.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.82.1.tgz +0 -0
- package/components/tryghost-webmentions-5.82.1.tgz +0 -0
- package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +1 -1
- package/core/built/admin/assets/admin-x-demo/{index-c81d9844.mjs → index-d851aa59.mjs} +3 -3
- package/core/built/admin/assets/admin-x-demo/{modals-b9180276.mjs → modals-e82d1de3.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-eef74c9c.mjs → CodeEditorView-2c507362.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
- package/core/built/admin/assets/admin-x-settings/{index-5f812a27.mjs → index-6e1dd00b.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-e986b71a.mjs → index-e75a9553.mjs} +1202 -1191
- package/core/built/admin/assets/admin-x-settings/{modals-24778772.mjs → modals-2a4dc4c5.mjs} +926 -916
- package/core/built/admin/assets/{chunk.524.a8bf57854398790b354d.js → chunk.524.326ea641bd5fcb846f80.js} +6 -6
- package/core/built/admin/assets/{chunk.582.6fc384a7dc0eb0bcdd3a.js → chunk.582.93b7d492a9b57a70a3cf.js} +4 -4
- package/core/built/admin/assets/{chunk.682.597be201dde4abdd4e68.js → chunk.682.d7ae35ab7054f9997ce6.js} +13 -11
- package/core/built/admin/assets/{ghost-dark-6385e71bb2c2fe3231b529c0e4eabbe6.css → ghost-dark-7488b47ac1849513fde7b7c8e51b3a70.css} +1 -1
- package/core/built/admin/assets/{ghost-c3ba0161decd868a3a79d6f4264c3286.js → ghost-de0cb83d4074a8874313e5f81872406b.js} +200 -195
- package/core/built/admin/assets/{ghost-85d66247f3eb43a3d462b89c9280e805.css → ghost-f84b3f229da06680df6c13ebd46ab671.css} +1 -1
- package/core/built/admin/index.html +5 -5
- package/core/frontend/services/routing/RouterManager.js +5 -0
- package/core/frontend/services/routing/SubscribeRouter.js +27 -0
- package/core/frontend/services/routing/controllers/index.js +4 -0
- package/core/frontend/services/routing/controllers/subscribe.js +35 -0
- package/core/frontend/views/subscribe.hbs +36 -0
- package/core/server/data/migrations/versions/5.82/2024-03-25-16-46-10-add-email-recipients-email-id-indexes.js +17 -0
- package/core/server/data/migrations/versions/5.82/2024-03-25-16-51-29-drop-email-recipients-non-email-id-indexes.js +17 -0
- package/core/server/data/schema/schema.js +7 -4
- package/core/server/services/email-analytics/lib/queries.js +15 -8
- package/core/server/services/members/MembersConfigProvider.js +3 -1
- package/core/server/services/stripe/service.js +2 -0
- package/core/server/web/members/app.js +3 -0
- package/core/shared/labs.js +4 -2
- package/package.json +149 -149
- package/yarn.lock +160 -160
- package/components/tryghost-announcement-bar-settings-5.81.1.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.81.1.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.81.1.tgz +0 -0
- package/components/tryghost-collections-5.81.1.tgz +0 -0
- package/components/tryghost-domain-events-5.81.1.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.81.1.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.81.1.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.81.1.tgz +0 -0
- package/components/tryghost-external-media-inliner-5.81.1.tgz +0 -0
- package/components/tryghost-extract-api-key-5.81.1.tgz +0 -0
- package/components/tryghost-ghost-5.81.1.tgz +0 -0
- package/components/tryghost-i18n-5.81.1.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.81.1.tgz +0 -0
- package/components/tryghost-mailgun-client-5.81.1.tgz +0 -0
- package/components/tryghost-members-api-5.81.1.tgz +0 -0
- package/components/tryghost-members-csv-5.81.1.tgz +0 -0
- package/components/tryghost-members-payments-5.81.1.tgz +0 -0
- package/components/tryghost-members-ssr-5.81.1.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.81.1.tgz +0 -0
- package/components/tryghost-milestones-5.81.1.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.81.1.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.81.1.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.81.1.tgz +0 -0
- package/components/tryghost-mw-vhost-5.81.1.tgz +0 -0
- package/components/tryghost-post-revisions-5.81.1.tgz +0 -0
- package/components/tryghost-recommendations-5.81.1.tgz +0 -0
- package/components/tryghost-referrers-5.81.1.tgz +0 -0
- package/components/tryghost-session-service-5.81.1.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.81.1.tgz +0 -0
- package/components/tryghost-slack-notifications-5.81.1.tgz +0 -0
- package/components/tryghost-verification-trigger-5.81.1.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.81.1.tgz +0 -0
- package/components/tryghost-webmentions-5.81.1.tgz +0 -0
- /package/core/built/admin/assets/{chunk.682.597be201dde4abdd4e68.js.LICENSE.txt → chunk.682.d7ae35ab7054f9997ce6.js.LICENSE.txt} +0 -0
|
@@ -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%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%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%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%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.82%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22ember-websockets%22%3A%7B%22socketIO%22%3Atrue%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%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%22a4b7e8c0f1%22%2C%22adminXDemoFilename%22%3A%22admin-x-demo.js%22%2C%22adminXDemoHash%22%3A%22c95f5407a8%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%22d20f997405%22%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-0ede59da8efb5e28fa929557f7ff7154.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-f84b3f229da06680df6c13ebd46ab671.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
58
58
|
|
|
59
59
|
<script src="assets/vendor-f9bad0b8c0149699f84c0bd9d325068e.js"></script>
|
|
60
|
-
<script src="assets/chunk.682.
|
|
61
|
-
<script src="assets/chunk.524.
|
|
62
|
-
<script src="assets/ghost-
|
|
60
|
+
<script src="assets/chunk.682.d7ae35ab7054f9997ce6.js"></script>
|
|
61
|
+
<script src="assets/chunk.524.326ea641bd5fcb846f80.js"></script>
|
|
62
|
+
<script src="assets/ghost-de0cb83d4074a8874313e5f81872406b.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -8,6 +8,7 @@ const PreviewRouter = require('./PreviewRouter');
|
|
|
8
8
|
const ParentRouter = require('./ParentRouter');
|
|
9
9
|
const EmailRouter = require('./EmailRouter');
|
|
10
10
|
const UnsubscribeRouter = require('./UnsubscribeRouter');
|
|
11
|
+
const SubscribeRouter = require('./SubscribeRouter');
|
|
11
12
|
|
|
12
13
|
// This emits its own routing events
|
|
13
14
|
const events = require('../../../server/lib/common/events');
|
|
@@ -109,6 +110,10 @@ class RouterManager {
|
|
|
109
110
|
this.siteRouter.mountRouter(unsubscribeRouter.router());
|
|
110
111
|
this.registry.setRouter('unsubscribeRouter', unsubscribeRouter);
|
|
111
112
|
|
|
113
|
+
const subscribeRouter = new SubscribeRouter();
|
|
114
|
+
this.siteRouter.mountRouter(subscribeRouter.router());
|
|
115
|
+
this.registry.setRouter('subscribeRouter', subscribeRouter);
|
|
116
|
+
|
|
112
117
|
if (RESOURCE_CONFIG.QUERY.email) {
|
|
113
118
|
const emailRouter = new EmailRouter(RESOURCE_CONFIG);
|
|
114
119
|
this.siteRouter.mountRouter(emailRouter.router());
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const ParentRouter = require('./ParentRouter');
|
|
2
|
+
const controllers = require('./controllers');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @description Subscribe Router.
|
|
6
|
+
*
|
|
7
|
+
* "/subscribe/" -> Subscribe Router
|
|
8
|
+
*/
|
|
9
|
+
class SubscribeRouter extends ParentRouter {
|
|
10
|
+
constructor() {
|
|
11
|
+
super('SubscribeRouter');
|
|
12
|
+
|
|
13
|
+
// @NOTE: hardcoded, not configurable
|
|
14
|
+
this.route = {value: '/confirm_signup/'};
|
|
15
|
+
this._registerRoutes();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @description Register all routes of this router.
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
_registerRoutes() {
|
|
23
|
+
this.mountRoute(this.route.value, controllers.subscribe);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = SubscribeRouter;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const debug = require('@tryghost/debug')('services:routing:controllers:subscribe');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const handlebars = require('handlebars');
|
|
5
|
+
const assetHelper = require('../../../helpers/asset');
|
|
6
|
+
const {settingsCache} = require('../../../services/proxy');
|
|
7
|
+
|
|
8
|
+
handlebars.registerHelper('asset', assetHelper);
|
|
9
|
+
|
|
10
|
+
module.exports = async function subscribeController(req, res) {
|
|
11
|
+
debug('subscribeController');
|
|
12
|
+
|
|
13
|
+
// Get the query params
|
|
14
|
+
const {query} = req;
|
|
15
|
+
const token = query.token || null;
|
|
16
|
+
const action = query.action || null;
|
|
17
|
+
const ref = query.r || null;
|
|
18
|
+
|
|
19
|
+
if (!token || !action) {
|
|
20
|
+
return res.send(404);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Prepare context for rendering template
|
|
24
|
+
const context = {
|
|
25
|
+
token,
|
|
26
|
+
action,
|
|
27
|
+
r: ref,
|
|
28
|
+
meta_title: settingsCache.get('title'),
|
|
29
|
+
accent_color: settingsCache.get('accent_color')
|
|
30
|
+
};
|
|
31
|
+
// Compile and render the template
|
|
32
|
+
const rawTemplate = fs.readFileSync(path.resolve(path.join(__dirname, '../../../views/subscribe.hbs'))).toString();
|
|
33
|
+
const template = handlebars.compile(rawTemplate);
|
|
34
|
+
return res.send(template(context));
|
|
35
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
|
+
<title>{{title}}</title>
|
|
8
|
+
<link rel="stylesheet" href="{{asset "public/ghost.css" hasMinFile="true"}}" />
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div class="gh-app">
|
|
12
|
+
<div class="gh-viewport">
|
|
13
|
+
<main class="gh-main" role="main" id="main">
|
|
14
|
+
<div class="gh-flow">
|
|
15
|
+
<div class="gh-flow-content-wrap">
|
|
16
|
+
<h1>Subscribing to {{meta_title}}</h1>
|
|
17
|
+
<p>If you are not redirected automatically, please click the "Subscribe" button below.</p>
|
|
18
|
+
<form id="gh-subscribe-form" action="/members/api/member" method="POST">
|
|
19
|
+
<input type="hidden" name="token" value="{{token}}" />
|
|
20
|
+
<input type="hidden" name="action" value="{{action}}" />
|
|
21
|
+
<input type="hidden" name="r" value="{{r}}" />
|
|
22
|
+
<button class="gh-btn" style="background-color: {{accent_color}}; color: #FFFFFF" type="submit"><span>Subscribe</span></button>
|
|
23
|
+
</form>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</main>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
<script>
|
|
30
|
+
const main = document.getElementById('main');
|
|
31
|
+
main.style.display = 'none';
|
|
32
|
+
const form = document.getElementById('gh-subscribe-form');
|
|
33
|
+
form.submit();
|
|
34
|
+
</script>
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// For information on writing migrations, see https://www.notion.so/ghost/Database-migrations-eb5b78c435d741d2b34a582d57c24253
|
|
2
|
+
|
|
3
|
+
const {createNonTransactionalMigration} = require('../../utils');
|
|
4
|
+
const commands = require('../../../schema/commands');
|
|
5
|
+
|
|
6
|
+
module.exports = createNonTransactionalMigration(
|
|
7
|
+
async function up(knex) {
|
|
8
|
+
await commands.addIndex('email_recipients', ['email_id', 'delivered_at'], knex);
|
|
9
|
+
await commands.addIndex('email_recipients', ['email_id', 'opened_at'], knex);
|
|
10
|
+
await commands.addIndex('email_recipients', ['email_id', 'failed_at'], knex);
|
|
11
|
+
},
|
|
12
|
+
async function down(knex) {
|
|
13
|
+
await commands.dropIndex('email_recipients', ['email_id', 'delivered_at'], knex);
|
|
14
|
+
await commands.dropIndex('email_recipients', ['email_id', 'opened_at'], knex);
|
|
15
|
+
await commands.dropIndex('email_recipients', ['email_id', 'failed_at'], knex);
|
|
16
|
+
}
|
|
17
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// For information on writing migrations, see https://www.notion.so/ghost/Database-migrations-eb5b78c435d741d2b34a582d57c24253
|
|
2
|
+
|
|
3
|
+
const {createNonTransactionalMigration} = require('../../utils');
|
|
4
|
+
const commands = require('../../../schema/commands');
|
|
5
|
+
|
|
6
|
+
module.exports = createNonTransactionalMigration(
|
|
7
|
+
async function up(knex) {
|
|
8
|
+
await commands.dropIndex('email_recipients', ['delivered_at'], knex);
|
|
9
|
+
await commands.dropIndex('email_recipients', ['opened_at'], knex);
|
|
10
|
+
await commands.dropIndex('email_recipients', ['failed_at'], knex);
|
|
11
|
+
},
|
|
12
|
+
async function down(knex) {
|
|
13
|
+
await commands.addIndex('email_recipients', ['delivered_at'], knex);
|
|
14
|
+
await commands.addIndex('email_recipients', ['opened_at'], knex);
|
|
15
|
+
await commands.addIndex('email_recipients', ['failed_at'], knex);
|
|
16
|
+
}
|
|
17
|
+
);
|
|
@@ -865,14 +865,17 @@ module.exports = {
|
|
|
865
865
|
member_id: {type: 'string', maxlength: 24, nullable: false, index: true},
|
|
866
866
|
batch_id: {type: 'string', maxlength: 24, nullable: false, references: 'email_batches.id'},
|
|
867
867
|
processed_at: {type: 'dateTime', nullable: true},
|
|
868
|
-
delivered_at: {type: 'dateTime', nullable: true
|
|
869
|
-
opened_at: {type: 'dateTime', nullable: true
|
|
870
|
-
failed_at: {type: 'dateTime', nullable: true
|
|
868
|
+
delivered_at: {type: 'dateTime', nullable: true},
|
|
869
|
+
opened_at: {type: 'dateTime', nullable: true},
|
|
870
|
+
failed_at: {type: 'dateTime', nullable: true},
|
|
871
871
|
member_uuid: {type: 'string', maxlength: 36, nullable: false},
|
|
872
872
|
member_email: {type: 'string', maxlength: 191, nullable: false},
|
|
873
873
|
member_name: {type: 'string', maxlength: 191, nullable: true},
|
|
874
874
|
'@@INDEXES@@': [
|
|
875
|
-
['email_id', 'member_email']
|
|
875
|
+
['email_id', 'member_email'],
|
|
876
|
+
['email_id', 'delivered_at'],
|
|
877
|
+
['email_id', 'opened_at'],
|
|
878
|
+
['email_id', 'failed_at']
|
|
876
879
|
]
|
|
877
880
|
},
|
|
878
881
|
email_recipient_failures: {
|
|
@@ -41,10 +41,16 @@ module.exports = {
|
|
|
41
41
|
},
|
|
42
42
|
|
|
43
43
|
async aggregateEmailStats(emailId) {
|
|
44
|
+
const {totalCount} = await db.knex('emails').select(db.knex.raw('email_count as totalCount')).where('id', emailId).first() || {totalCount: 0};
|
|
45
|
+
// use IS NULL here because that will typically match far fewer rows than IS NOT NULL making the query faster
|
|
46
|
+
const [undeliveredCount] = await db.knex('email_recipients').count('id as count').whereRaw('email_id = ? AND delivered_at IS NULL', [emailId]);
|
|
47
|
+
const [openedCount] = await db.knex('email_recipients').count('id as count').whereRaw('email_id = ? AND opened_at IS NOT NULL', [emailId]);
|
|
48
|
+
const [failedCount] = await db.knex('email_recipients').count('id as count').whereRaw('email_id = ? AND failed_at IS NOT NULL', [emailId]);
|
|
49
|
+
|
|
44
50
|
await db.knex('emails').update({
|
|
45
|
-
delivered_count:
|
|
46
|
-
opened_count:
|
|
47
|
-
failed_count:
|
|
51
|
+
delivered_count: totalCount - undeliveredCount.count,
|
|
52
|
+
opened_count: openedCount.count,
|
|
53
|
+
failed_count: failedCount.count
|
|
48
54
|
}).where('id', emailId);
|
|
49
55
|
},
|
|
50
56
|
|
|
@@ -56,15 +62,16 @@ module.exports = {
|
|
|
56
62
|
.where('emails.track_opens', true)
|
|
57
63
|
.first() || {};
|
|
58
64
|
|
|
65
|
+
const [emailCount] = await db.knex('email_recipients').count('id as count').whereRaw('member_id = ?', [memberId]);
|
|
66
|
+
const [emailOpenedCount] = await db.knex('email_recipients').count('id as count').whereRaw('member_id = ? AND opened_at IS NOT NULL', [memberId]);
|
|
67
|
+
|
|
59
68
|
const updateQuery = {
|
|
60
|
-
email_count:
|
|
61
|
-
email_opened_count:
|
|
69
|
+
email_count: emailCount.count,
|
|
70
|
+
email_opened_count: emailOpenedCount.count
|
|
62
71
|
};
|
|
63
72
|
|
|
64
73
|
if (trackedEmailCount >= MIN_EMAIL_COUNT_FOR_OPEN_RATE) {
|
|
65
|
-
updateQuery.email_open_rate =
|
|
66
|
-
ROUND(((SELECT COUNT(id) FROM email_recipients WHERE member_id = ? AND opened_at IS NOT NULL) * 1.0 / ? * 100), 0)
|
|
67
|
-
`, [memberId, trackedEmailCount]);
|
|
74
|
+
updateQuery.email_open_rate = Math.round(emailOpenedCount.count / trackedEmailCount * 100);
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
await db.knex('members')
|
|
@@ -2,6 +2,7 @@ const logging = require('@tryghost/logging');
|
|
|
2
2
|
const {URL} = require('url');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const createKeypair = require('keypair');
|
|
5
|
+
const labs = require('../../../shared/labs');
|
|
5
6
|
|
|
6
7
|
class MembersConfigProvider {
|
|
7
8
|
/**
|
|
@@ -87,7 +88,8 @@ class MembersConfigProvider {
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
getSigninURL(token, type, referrer) {
|
|
90
|
-
const
|
|
91
|
+
const relativeUrl = ['signup', 'subscribe'].includes(type) && labs.isSet('membersSpamPrevention') ? '/confirm_signup/' : '/members/';
|
|
92
|
+
const siteUrl = this._urlUtils.urlFor({relativeUrl}, true);
|
|
91
93
|
const signinURL = new URL(siteUrl);
|
|
92
94
|
signinURL.searchParams.set('token', token);
|
|
93
95
|
signinURL.searchParams.set('action', type);
|
|
@@ -11,6 +11,7 @@ const {getConfig} = require('./config');
|
|
|
11
11
|
const settingsHelpers = require('../settings-helpers');
|
|
12
12
|
const donationService = require('../donations');
|
|
13
13
|
const staffService = require('../staff');
|
|
14
|
+
const labs = require('../../../shared/labs');
|
|
14
15
|
|
|
15
16
|
async function configureApi() {
|
|
16
17
|
const cfg = getConfig({settingsHelpers, config, urlUtils});
|
|
@@ -30,6 +31,7 @@ const debouncedConfigureApi = _.debounce(() => {
|
|
|
30
31
|
}, 600);
|
|
31
32
|
|
|
32
33
|
module.exports = new StripeService({
|
|
34
|
+
labs,
|
|
33
35
|
membersService,
|
|
34
36
|
models: _.pick(models, [
|
|
35
37
|
'Product',
|
|
@@ -37,6 +37,9 @@ module.exports = function setupMembersApp() {
|
|
|
37
37
|
// Initializes members specific routes as well as assigns members specific data to the req/res objects
|
|
38
38
|
// We don't want to add global bodyParser middleware as that interferes with stripe webhook requests on - `/webhooks`.
|
|
39
39
|
|
|
40
|
+
// Double opt-in subscription handling
|
|
41
|
+
membersApp.post('/api/member', membersService.api.middleware.createMemberFromToken);
|
|
42
|
+
|
|
40
43
|
// Manage newsletter subscription via unsubscribe link
|
|
41
44
|
membersApp.get('/api/member/newsletters', middleware.getMemberNewsletters);
|
|
42
45
|
membersApp.put('/api/member/newsletters', bodyParser.json({limit: '50mb'}), middleware.updateMemberNewsletters);
|
package/core/shared/labs.js
CHANGED
|
@@ -24,12 +24,14 @@ const GA_FEATURES = [
|
|
|
24
24
|
'listUnsubscribeHeader',
|
|
25
25
|
'filterEmailDisabled',
|
|
26
26
|
'newEmailAddresses',
|
|
27
|
-
'portalImprovements'
|
|
27
|
+
'portalImprovements',
|
|
28
|
+
'onboardingChecklist'
|
|
28
29
|
];
|
|
29
30
|
|
|
30
31
|
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
|
31
32
|
// input for the "labs" setting value
|
|
32
33
|
const BETA_FEATURES = [
|
|
34
|
+
'additionalPaymentMethods',
|
|
33
35
|
'i18n',
|
|
34
36
|
'activitypub',
|
|
35
37
|
'webmentions'
|
|
@@ -49,7 +51,7 @@ const ALPHA_FEATURES = [
|
|
|
49
51
|
'lexicalIndicators',
|
|
50
52
|
// 'adminXOffers',
|
|
51
53
|
'adminXDemo',
|
|
52
|
-
'
|
|
54
|
+
'membersSpamPrevention'
|
|
53
55
|
];
|
|
54
56
|
|
|
55
57
|
module.exports.GA_KEYS = [...GA_FEATURES];
|