ghost 5.119.2 → 5.120.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-i18n-5.120.0.tgz +0 -0
- package/core/boot.js +0 -2
- package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +7555 -7216
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-60ce658c.mjs → CodeEditorView-1c5b0683.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-8480baa8.mjs → index-14e518a7.mjs} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-a2648c61.mjs → index-fc9f985b.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{modals-6900c1d5.mjs → modals-15bc6a0f.mjs} +7192 -6656
- package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js → chunk.383.25fca2f09b4896656125.js} +76 -59
- package/core/built/admin/assets/chunk.524.1657b12c0ab25dd9fb79.js +28 -0
- package/core/built/admin/assets/{chunk.582.98a820cbc4bb65f2e685.js → chunk.582.09869b1f1a3cc0ab81f6.js} +19 -26
- package/core/built/admin/assets/{ghost-843572e9507d099162ae744d791daba1.js → ghost-b3b44421acca3b3eec76bfbb6ba0e81b.js} +3 -3
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +12578 -12352
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +423 -211
- package/core/built/admin/assets/posts/posts.js +13680 -13671
- package/core/built/admin/assets/stats/stats.js +16457 -16635
- package/core/built/admin/assets/{vendor-8f805740fee4db959a5b2119001a56b1.js → vendor-4ce6d282a2a00fe486a0951e0591da19.js} +11 -9
- package/core/built/admin/index.html +5 -5
- package/core/frontend/helpers/match.js +6 -0
- package/core/frontend/services/routing/ParentRouter.js +1 -1
- package/core/frontend/services/routing/controllers/email-post.js +0 -2
- package/core/frontend/services/routing/controllers/previews.js +0 -3
- package/core/frontend/web/middleware/frontend-caching.js +2 -2
- package/core/server/api/endpoints/authentication.js +37 -73
- package/core/server/api/endpoints/authors-public.js +8 -9
- package/core/server/api/endpoints/db.js +34 -35
- package/core/server/api/endpoints/emails.js +8 -10
- package/core/server/api/endpoints/integrations.js +20 -18
- package/core/server/api/endpoints/invites.js +8 -10
- package/core/server/api/endpoints/labels.js +19 -23
- package/core/server/api/endpoints/notifications.js +3 -4
- package/core/server/api/endpoints/pages-public.js +8 -10
- package/core/server/api/endpoints/pages.js +14 -18
- package/core/server/api/endpoints/posts-public.js +8 -10
- package/core/server/api/endpoints/posts.js +6 -8
- package/core/server/api/endpoints/previews.js +8 -10
- package/core/server/api/endpoints/redirects.js +7 -8
- package/core/server/api/endpoints/schedules.js +5 -7
- package/core/server/api/endpoints/slugs.js +7 -9
- package/core/server/api/endpoints/snippets.js +16 -20
- package/core/server/api/endpoints/tags-public.js +8 -10
- package/core/server/api/endpoints/tags.js +19 -23
- package/core/server/api/endpoints/themes.js +6 -8
- package/core/server/api/endpoints/users.js +31 -36
- package/core/server/api/endpoints/utils/permissions.js +10 -10
- package/core/server/api/endpoints/utils/serializers/output/roles.js +9 -10
- package/core/server/api/endpoints/utils/validators/input/images.js +43 -52
- package/core/server/api/endpoints/utils/validators/input/invites.js +6 -8
- package/core/server/api/endpoints/webhooks.js +38 -42
- package/core/server/data/migrations/versions/5.120/2025-05-07-14-57-38-add-newsletters-button-corners-column.js +8 -0
- package/core/server/data/migrations/versions/5.120/2025-05-13-17-36-56-add-newsletters-button-style-column.js +8 -0
- package/core/server/data/migrations/versions/5.120/2025-05-14-20-00-15-add-newsletters-setting-columns.js +22 -0
- package/core/server/data/schema/schema.js +6 -1
- package/core/server/lib/image/Gravatar.js +12 -13
- package/core/server/lib/lexical.js +3 -1
- package/core/server/models/newsletter.js +6 -1
- package/core/server/services/api-version-compatibility/index.js +1 -33
- package/core/server/services/auth/session/emails/signin.js +3 -3
- package/core/server/services/email-address/EmailAddressParser.js +52 -0
- package/core/server/services/email-address/EmailAddressParser.js.d.ts +13 -0
- package/core/server/services/email-address/EmailAddressService.js +142 -0
- package/core/server/services/email-address/EmailAddressService.ts +183 -0
- package/core/server/services/email-address/EmailAddressServiceWrapper.js +2 -4
- package/core/server/services/email-analytics/EmailAnalyticsService.js +1 -1
- package/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js +2 -1
- package/core/server/services/email-service/BatchSendingService.js +703 -0
- package/core/server/services/email-service/EmailBodyCache.js +20 -0
- package/core/server/services/email-service/EmailController.js +94 -0
- package/core/server/services/email-service/EmailEventProcessor.js +267 -0
- package/core/server/services/email-service/EmailEventStorage.js +187 -0
- package/core/server/services/email-service/EmailRenderer.js +1263 -0
- package/core/server/services/email-service/EmailSegmenter.js +74 -0
- package/core/server/services/email-service/EmailService.js +310 -0
- package/core/server/services/email-service/EmailServiceWrapper.js +9 -2
- package/core/server/services/email-service/MailgunEmailProvider.js +191 -0
- package/core/server/services/email-service/SendingService.js +173 -0
- package/core/server/services/email-service/email-templates/partials/feedback-button.hbs +7 -0
- package/core/server/services/email-service/email-templates/partials/latest-posts.hbs +39 -0
- package/core/server/services/email-service/email-templates/partials/paywall.hbs +20 -0
- package/core/server/services/email-service/email-templates/partials/styles.hbs +2348 -0
- package/core/server/services/email-service/email-templates/template.hbs +238 -0
- package/core/server/services/email-service/events/EmailBouncedEvent.js +63 -0
- package/core/server/services/email-service/events/EmailDeliveredEvent.js +49 -0
- package/core/server/services/email-service/events/EmailOpenedEvent.js +49 -0
- package/core/server/services/email-service/events/EmailTemporaryBouncedEvent.js +63 -0
- package/core/server/services/email-service/events/EmailUnsubscribedEvent.js +42 -0
- package/core/server/services/email-service/events/SpamComplaintEvent.js +42 -0
- package/core/server/services/email-service/helpers/register-helpers.js +59 -0
- package/core/server/services/email-suppression-list/MailgunEmailSuppressionList.js +2 -1
- package/core/server/services/explore-ping/index.js +2 -1
- package/core/server/services/mail/GhostMailer.js +1 -1
- package/core/server/services/media-inliner/ExternalMediaInliner.js +2 -1
- package/core/server/services/members/api.js +15 -15
- package/core/server/services/members/emails/signin.js +4 -4
- package/core/server/services/members/emails/signup-paid.js +3 -4
- package/core/server/services/members/emails/signup.js +3 -3
- package/core/server/services/members/emails/subscribe.js +3 -3
- package/core/server/services/members/members-api/controllers/RouterController.js +50 -36
- package/core/server/services/members/members-api/repositories/MemberRepository.js +92 -92
- package/core/server/services/members-events/LastSeenAtUpdater.js +1 -1
- package/core/server/services/settings-helpers/SettingsHelpers.js +1 -1
- package/core/server/services/staff/StaffServiceEmails.js +1 -1
- package/core/server/services/stats/PostsStatsService.js +28 -7
- package/core/server/web/api/app.js +0 -1
- package/core/server/web/api/endpoints/admin/app.js +0 -2
- package/core/server/web/api/endpoints/content/app.js +0 -2
- package/core/server/web/api/middleware/upload.js +2 -2
- package/core/shared/custom-theme-settings-cache/CustomThemeSettingsService.js +2 -1
- package/package.json +39 -97
- package/tsconfig.tsbuildinfo +1 -1
- package/yarn.lock +385 -517
- package/components/tryghost-api-framework-5.119.2.tgz +0 -0
- package/components/tryghost-custom-fonts-5.119.2.tgz +0 -0
- package/components/tryghost-domain-events-5.119.2.tgz +0 -0
- package/components/tryghost-email-addresses-5.119.2.tgz +0 -0
- package/components/tryghost-email-service-5.119.2.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.119.2.tgz +0 -0
- package/components/tryghost-i18n-5.119.2.tgz +0 -0
- package/components/tryghost-job-manager-5.119.2.tgz +0 -0
- package/components/tryghost-members-csv-5.119.2.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.119.2.tgz +0 -0
- package/components/tryghost-mw-vhost-5.119.2.tgz +0 -0
- package/components/tryghost-prometheus-metrics-5.119.2.tgz +0 -0
- package/components/tryghost-security-5.119.2.tgz +0 -0
- package/core/built/admin/assets/chunk.524.b8545af3bb714bc4f820.js +0 -35
- package/core/server/services/api-version-compatibility/APIVersionCompatibilityService.js +0 -99
- package/core/server/services/api-version-compatibility/VersionNotificationsDataService.js +0 -80
- package/core/server/services/api-version-compatibility/extract-api-key.js +0 -57
- package/core/server/services/api-version-compatibility/mw-api-version-mismatch.js +0 -31
- /package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js.LICENSE.txt → chunk.383.25fca2f09b4896656125.js.LICENSE.txt} +0 -0
|
@@ -79,16 +79,16 @@ function createApiInstance(config) {
|
|
|
79
79
|
const siteTitle = settingsCache.get('title');
|
|
80
80
|
switch (type) {
|
|
81
81
|
case 'subscribe':
|
|
82
|
-
return `📫 ${t(`Confirm your subscription to {
|
|
82
|
+
return `📫 ${t(`Confirm your subscription to {siteTitle}`, {siteTitle, interpolation: {escapeValue: false}})}`;
|
|
83
83
|
case 'signup':
|
|
84
|
-
return `🙌 ${t(`Complete your sign up to {
|
|
84
|
+
return `🙌 ${t(`Complete your sign up to {siteTitle}!`, {siteTitle, interpolation: {escapeValue: false}})}`;
|
|
85
85
|
case 'signup-paid':
|
|
86
|
-
return `🙌 ${t(`Thank you for signing up to {
|
|
86
|
+
return `🙌 ${t(`Thank you for signing up to {siteTitle}!`, {siteTitle, interpolation: {escapeValue: false}})}`;
|
|
87
87
|
case 'updateEmail':
|
|
88
|
-
return `📫 ${t(`Confirm your email update for {
|
|
88
|
+
return `📫 ${t(`Confirm your email update for {siteTitle}!`, {siteTitle, interpolation: {escapeValue: false}})}`;
|
|
89
89
|
case 'signin':
|
|
90
90
|
default:
|
|
91
|
-
return `🔑 ${t(`Secure sign in link for {
|
|
91
|
+
return `🔑 ${t(`Secure sign in link for {siteTitle}`, {siteTitle, interpolation: {escapeValue: false}})}`;
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
getText(url, type, email) {
|
|
@@ -98,7 +98,7 @@ function createApiInstance(config) {
|
|
|
98
98
|
return trimLeadingWhitespace`
|
|
99
99
|
${t(`Hey there,`)}
|
|
100
100
|
|
|
101
|
-
${t('You\'re one tap away from subscribing to {
|
|
101
|
+
${t('You\'re one tap away from subscribing to {siteTitle} — please confirm your email address with this link:', {siteTitle, interpolation: {escapeValue: false}})}
|
|
102
102
|
|
|
103
103
|
${url}
|
|
104
104
|
|
|
@@ -108,14 +108,14 @@ function createApiInstance(config) {
|
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
-
${t('Sent to {
|
|
111
|
+
${t('Sent to {email}', {email})}
|
|
112
112
|
${t('If you did not make this request, you can simply delete this message.')} ${t('You will not be subscribed.')}
|
|
113
113
|
`;
|
|
114
114
|
case 'signup':
|
|
115
115
|
return trimLeadingWhitespace`
|
|
116
116
|
${t(`Hey there,`)}
|
|
117
117
|
|
|
118
|
-
${t('Tap the link below to complete the signup process for {
|
|
118
|
+
${t('Tap the link below to complete the signup process for {siteTitle}, and be automatically signed in:', {siteTitle, interpolation: {escapeValue: false}})}
|
|
119
119
|
|
|
120
120
|
${url}
|
|
121
121
|
|
|
@@ -125,14 +125,14 @@ function createApiInstance(config) {
|
|
|
125
125
|
|
|
126
126
|
---
|
|
127
127
|
|
|
128
|
-
${t('Sent to {
|
|
128
|
+
${t('Sent to {email}', {email})}
|
|
129
129
|
${t('If you did not make this request, you can simply delete this message.')} ${t('You will not be signed up, and no account will be created for you.')}
|
|
130
130
|
`;
|
|
131
131
|
case 'signup-paid':
|
|
132
132
|
return trimLeadingWhitespace`
|
|
133
133
|
${t(`Hey there,`)}
|
|
134
134
|
|
|
135
|
-
${t('Thank you for subscribing to {
|
|
135
|
+
${t('Thank you for subscribing to {siteTitle}. Tap the link below to be automatically signed in:', {siteTitle, interpolation: {escapeValue: false}})}
|
|
136
136
|
|
|
137
137
|
${url}
|
|
138
138
|
|
|
@@ -142,8 +142,8 @@ function createApiInstance(config) {
|
|
|
142
142
|
|
|
143
143
|
---
|
|
144
144
|
|
|
145
|
-
${t('Sent to {
|
|
146
|
-
${t('Thank you for subscribing to {
|
|
145
|
+
${t('Sent to {email}', {email})}
|
|
146
|
+
${t('Thank you for subscribing to {siteTitle}!', {siteTitle, interpolation: {escapeValue: false}})}
|
|
147
147
|
`;
|
|
148
148
|
case 'updateEmail':
|
|
149
149
|
return trimLeadingWhitespace`
|
|
@@ -157,7 +157,7 @@ function createApiInstance(config) {
|
|
|
157
157
|
|
|
158
158
|
---
|
|
159
159
|
|
|
160
|
-
${t('Sent to {
|
|
160
|
+
${t('Sent to {email}', {email})}
|
|
161
161
|
${t('If you did not make this request, you can simply delete this message.')} ${t('This email address will not be used.')}
|
|
162
162
|
`;
|
|
163
163
|
case 'signin':
|
|
@@ -165,7 +165,7 @@ function createApiInstance(config) {
|
|
|
165
165
|
return trimLeadingWhitespace`
|
|
166
166
|
${t(`Hey there,`)}
|
|
167
167
|
|
|
168
|
-
${t('Welcome back! Use this link to securely sign in to your {
|
|
168
|
+
${t('Welcome back! Use this link to securely sign in to your {siteTitle} account:', {siteTitle, interpolation: {escapeValue: false}})}
|
|
169
169
|
|
|
170
170
|
${url}
|
|
171
171
|
|
|
@@ -175,7 +175,7 @@ function createApiInstance(config) {
|
|
|
175
175
|
|
|
176
176
|
---
|
|
177
177
|
|
|
178
|
-
${t('Sent to {
|
|
178
|
+
${t('Sent to {email}', {email})}
|
|
179
179
|
${t('If you did not make this request, you can safely ignore this email.')}
|
|
180
180
|
`;
|
|
181
181
|
}
|
|
@@ -4,7 +4,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta name="viewport" content="width=device-width">
|
|
6
6
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
7
|
-
<title>🔑 ${t('Secure sign in link for {
|
|
7
|
+
<title>🔑 ${t('Secure sign in link for {siteTitle}', {siteTitle, interpolation: {escapeValue: false}})}</title>
|
|
8
8
|
<style>
|
|
9
9
|
/* -------------------------------------
|
|
10
10
|
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
@@ -107,7 +107,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
107
107
|
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 600px; padding: 30px 20px;">
|
|
108
108
|
|
|
109
109
|
<!-- START CENTERED CONTAINER -->
|
|
110
|
-
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">${t('Welcome back to {
|
|
110
|
+
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">${t('Welcome back to {siteTitle}!', {siteTitle, interpolation: {escapeValue: false}})}</span>
|
|
111
111
|
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
|
|
112
112
|
|
|
113
113
|
<!-- START MAIN CONTENT AREA -->
|
|
@@ -117,7 +117,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
117
117
|
<tr>
|
|
118
118
|
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
|
|
119
119
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 24px; margin: 0; margin-bottom: 15px;">${t('Hey there,')}</p>
|
|
120
|
-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 24px; margin-bottom: 32px;">${t('Welcome back! Use this link to securely sign in to your {
|
|
120
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 24px; margin-bottom: 32px;">${t('Welcome back! Use this link to securely sign in to your {siteTitle} account:', {siteTitle, interpolation: {escapeValue: false}})}</p>
|
|
121
121
|
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
|
122
122
|
<tbody>
|
|
123
123
|
<tr>
|
|
@@ -125,7 +125,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
125
125
|
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
|
126
126
|
<tbody>
|
|
127
127
|
<tr>
|
|
128
|
-
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; background-color: ${accentColor}; border-radius: 5px; text-align: center;"> <a href="${url}" target="_blank" style="display: inline-block; color: #ffffff; background-color: ${accentColor}; border: solid 1px ${accentColor}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: ${accentColor};">${t('Sign in to {
|
|
128
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; background-color: ${accentColor}; border-radius: 5px; text-align: center;"> <a href="${url}" target="_blank" style="display: inline-block; color: #ffffff; background-color: ${accentColor}; border: solid 1px ${accentColor}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: ${accentColor};">${t('Sign in to {siteTitle}', {siteTitle, interpolation: {escapeValue: false}})}</a> </td>
|
|
129
129
|
</tr>
|
|
130
130
|
</tbody>
|
|
131
131
|
</table>
|
|
@@ -4,7 +4,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta name="viewport" content="width=device-width">
|
|
6
6
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
7
|
-
<title>🙌 ${t('Thank you for signing up to {
|
|
7
|
+
<title>🙌 ${t('Thank you for signing up to {siteTitle}!', {siteTitle, interpolation: {escapeValue: false}})}</title>
|
|
8
8
|
<style>
|
|
9
9
|
/* -------------------------------------
|
|
10
10
|
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
@@ -107,7 +107,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
107
107
|
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 600px; padding: 30px 20px;">
|
|
108
108
|
|
|
109
109
|
<!-- START CENTERED WHITE CONTAINER -->
|
|
110
|
-
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">${t('Thank you for subscribing to {
|
|
110
|
+
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">${t('Thank you for subscribing to {siteTitle}.', {siteTitle, interpolation: {escapeValue: false}})}</span>
|
|
111
111
|
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
|
|
112
112
|
|
|
113
113
|
<!-- START MAIN CONTENT AREA -->
|
|
@@ -117,7 +117,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
117
117
|
<tr>
|
|
118
118
|
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
|
|
119
119
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 24px; margin: 0; margin-bottom: 15px;">${t('Hey there!')}</p>
|
|
120
|
-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 24px; margin: 0; margin-bottom: 32px;">${t('Thank you for subscribing to {
|
|
120
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 24px; margin: 0; margin-bottom: 32px;">${t('Thank you for subscribing to {siteTitle}. Tap the link below to be automatically signed in:', {siteTitle, interpolation: {escapeValue: false}})}</p>
|
|
121
121
|
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
|
122
122
|
<tbody>
|
|
123
123
|
<tr>
|
|
@@ -165,4 +165,3 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
165
165
|
</body>
|
|
166
166
|
</html>
|
|
167
167
|
`;
|
|
168
|
-
|
|
@@ -4,7 +4,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta name="viewport" content="width=device-width">
|
|
6
6
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
7
|
-
<title>🙌 ${t('Complete your sign up to {
|
|
7
|
+
<title>🙌 ${t('Complete your sign up to {siteTitle}!', {siteTitle, interpolation: {escapeValue: false}})}</title>
|
|
8
8
|
<style>
|
|
9
9
|
/* -------------------------------------
|
|
10
10
|
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
@@ -107,7 +107,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
107
107
|
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 600px; padding: 30px 20px;">
|
|
108
108
|
|
|
109
109
|
<!-- START CENTERED WHITE CONTAINER -->
|
|
110
|
-
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">${t('Complete signup for {
|
|
110
|
+
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">${t('Complete signup for {siteTitle}!', {siteTitle, interpolation: {escapeValue: false}})}</span>
|
|
111
111
|
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
|
|
112
112
|
|
|
113
113
|
<!-- START MAIN CONTENT AREA -->
|
|
@@ -117,7 +117,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
117
117
|
<tr>
|
|
118
118
|
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
|
|
119
119
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 24px; margin: 0; margin-bottom: 15px;">${t('Hey there!')}</p>
|
|
120
|
-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 24px; margin: 0; margin-bottom: 32px;">${t('Tap the link below to complete the signup process for {
|
|
120
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 24px; margin: 0; margin-bottom: 32px;">${t('Tap the link below to complete the signup process for {siteTitle}, and be automatically signed in:', {siteTitle, interpolation: {escapeValue: false}})}</p>
|
|
121
121
|
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
|
122
122
|
<tbody>
|
|
123
123
|
<tr>
|
|
@@ -4,7 +4,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta name="viewport" content="width=device-width">
|
|
6
6
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
7
|
-
<title>📫 ${t('Confirm your subscription to {
|
|
7
|
+
<title>📫 ${t('Confirm your subscription to {siteTitle}', {siteTitle, interpolation: {escapeValue: false}})}</title>
|
|
8
8
|
<style>
|
|
9
9
|
/* -------------------------------------
|
|
10
10
|
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
@@ -107,7 +107,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
107
107
|
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 600px; padding: 30px 20px;">
|
|
108
108
|
|
|
109
109
|
<!-- START CENTERED WHITE CONTAINER -->
|
|
110
|
-
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">${t(`You're one tap away from subscribing to {
|
|
110
|
+
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">${t(`You're one tap away from subscribing to {siteTitle}!`, {siteTitle, interpolation: {escapeValue: false}})}</span>
|
|
111
111
|
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
|
|
112
112
|
|
|
113
113
|
<!-- START MAIN CONTENT AREA -->
|
|
@@ -117,7 +117,7 @@ module.exports = ({t, siteTitle, email, url, accentColor = '#15212A', siteDomain
|
|
|
117
117
|
<tr>
|
|
118
118
|
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
|
|
119
119
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 24px; margin: 0; margin-bottom: 15px;">${t('Hey there,')}</p>
|
|
120
|
-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 24px; margin-bottom: 32px;">${t(`You're one tap away from subscribing to {
|
|
120
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 24px; margin-bottom: 32px;">${t(`You're one tap away from subscribing to {siteTitle} — please confirm your email address with this link:`, {siteTitle, interpolation: {escapeValue: false}})}</p>
|
|
121
121
|
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
|
122
122
|
<tbody>
|
|
123
123
|
<tr>
|
|
@@ -369,9 +369,9 @@ module.exports = class RouterController {
|
|
|
369
369
|
|
|
370
370
|
if (member) {
|
|
371
371
|
options.successUrl = this._generateSuccessUrl(options.successUrl, tier.welcomePageURL);
|
|
372
|
-
|
|
372
|
+
|
|
373
373
|
const restrictCheckout = member.get('status') === 'paid';
|
|
374
|
-
|
|
374
|
+
|
|
375
375
|
if (restrictCheckout) {
|
|
376
376
|
// This member is already subscribed to a paid tier
|
|
377
377
|
// We don't want to create a duplicate subscription
|
|
@@ -413,17 +413,17 @@ module.exports = class RouterController {
|
|
|
413
413
|
try {
|
|
414
414
|
// Create URL objects
|
|
415
415
|
const siteUrl = this._urlUtils.getSiteUrl();
|
|
416
|
-
|
|
416
|
+
|
|
417
417
|
// This will throw if welcomePageURL is invalid
|
|
418
418
|
const welcomeUrl = new URL(
|
|
419
|
-
welcomePageURL.startsWith('http') ? welcomePageURL : welcomePageURL,
|
|
419
|
+
welcomePageURL.startsWith('http') ? welcomePageURL : welcomePageURL,
|
|
420
420
|
siteUrl
|
|
421
421
|
);
|
|
422
|
-
|
|
422
|
+
|
|
423
423
|
// Add success parameters
|
|
424
424
|
welcomeUrl.searchParams.set('success', 'true');
|
|
425
425
|
welcomeUrl.searchParams.set('action', 'signup');
|
|
426
|
-
|
|
426
|
+
|
|
427
427
|
return welcomeUrl.href;
|
|
428
428
|
} catch (err) {
|
|
429
429
|
logging.warn(`Invalid welcome page URL "${welcomePageURL}", using original success URL`, err);
|
|
@@ -509,6 +509,10 @@ module.exports = class RouterController {
|
|
|
509
509
|
// Store attribution data in the metadata
|
|
510
510
|
await this._setAttributionMetadata(metadata);
|
|
511
511
|
|
|
512
|
+
if (metadata.newsletters) {
|
|
513
|
+
metadata.newsletters = JSON.stringify(await this._validateNewsletters(JSON.parse(metadata.newsletters)));
|
|
514
|
+
}
|
|
515
|
+
|
|
512
516
|
// Build options
|
|
513
517
|
const options = {
|
|
514
518
|
successUrl: req.body.successUrl,
|
|
@@ -649,7 +653,7 @@ module.exports = class RouterController {
|
|
|
649
653
|
labels: req.body.labels,
|
|
650
654
|
name: req.body.name,
|
|
651
655
|
reqIp: req.ip ?? undefined,
|
|
652
|
-
newsletters: await this._validateNewsletters(req),
|
|
656
|
+
newsletters: await this._validateNewsletters(req.body?.newsletters ?? []),
|
|
653
657
|
attribution: await this._memberAttributionService.getAttribution(req.body.urlHistory)
|
|
654
658
|
};
|
|
655
659
|
|
|
@@ -671,42 +675,52 @@ module.exports = class RouterController {
|
|
|
671
675
|
return await this._sendEmailWithMagicLink({email, tokenData, requestedType: emailType, referrer});
|
|
672
676
|
}
|
|
673
677
|
|
|
674
|
-
|
|
675
|
-
|
|
678
|
+
/**
|
|
679
|
+
* Validates the newsletters in the request body
|
|
680
|
+
* @param {object[]} requestedNewsletters
|
|
681
|
+
* @param {string} requestedNewsletters[].name
|
|
682
|
+
* @returns {Promise<object[] | undefined>} The validated newsletters
|
|
683
|
+
*/
|
|
684
|
+
async _validateNewsletters(requestedNewsletters) {
|
|
685
|
+
if (!requestedNewsletters || requestedNewsletters.length === 0) {
|
|
686
|
+
return undefined;
|
|
687
|
+
}
|
|
676
688
|
|
|
677
|
-
if (requestedNewsletters
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
const newsletters = (await this._newslettersService.getAll({
|
|
681
|
-
filter: `name:[${newsletterNamesFilter}]`,
|
|
682
|
-
columns: ['id','name','status']
|
|
683
|
-
}));
|
|
689
|
+
if (requestedNewsletters.some(newsletter => !newsletter.name)) {
|
|
690
|
+
return undefined;
|
|
691
|
+
}
|
|
684
692
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
693
|
+
const requestedNewsletterNames = requestedNewsletters.map(newsletter => newsletter.name);
|
|
694
|
+
const requestedNewsletterNamesFilter = requestedNewsletterNames.map(newsletter => `'${newsletter.replace(/("|')/g, '\\$1')}'`);
|
|
695
|
+
const matchedNewsletters = (await this._newslettersService.getAll({
|
|
696
|
+
filter: `name:[${requestedNewsletterNamesFilter}]`,
|
|
697
|
+
columns: ['id','name','status']
|
|
698
|
+
}));
|
|
689
699
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
700
|
+
// Check for invalid newsletters
|
|
701
|
+
if (matchedNewsletters.length !== requestedNewsletterNames.length) {
|
|
702
|
+
const validNewsletterNames = matchedNewsletters.map(newsletter => newsletter.name);
|
|
703
|
+
const invalidNewsletterNames = requestedNewsletterNames.filter(newsletter => !validNewsletterNames.includes(newsletter));
|
|
694
704
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
705
|
+
throw new errors.BadRequestError({
|
|
706
|
+
message: tpl(messages.invalidNewsletters, {newsletters: invalidNewsletterNames})
|
|
707
|
+
});
|
|
708
|
+
}
|
|
699
709
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
}
|
|
710
|
+
// Check for archived newsletters
|
|
711
|
+
const requestedArchivedNewsletters = matchedNewsletters
|
|
712
|
+
.filter(newsletter => newsletter.status === 'archived')
|
|
713
|
+
.map(newsletter => newsletter.name);
|
|
705
714
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
.
|
|
715
|
+
if (requestedArchivedNewsletters && requestedArchivedNewsletters.length > 0) {
|
|
716
|
+
throw new errors.BadRequestError({
|
|
717
|
+
message: tpl(messages.archivedNewsletters, {newsletters: requestedArchivedNewsletters})
|
|
718
|
+
});
|
|
709
719
|
}
|
|
720
|
+
|
|
721
|
+
return matchedNewsletters
|
|
722
|
+
.filter(newsletter => newsletter.status === 'active')
|
|
723
|
+
.map(newsletter => ({id: newsletter.id}));
|
|
710
724
|
}
|
|
711
725
|
};
|
|
712
726
|
|