ghost 5.119.3 → 5.120.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.
Files changed (129) hide show
  1. package/components/tryghost-i18n-5.120.1.tgz +0 -0
  2. package/core/boot.js +0 -2
  3. package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +7555 -7216
  4. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-60ce658c.mjs → CodeEditorView-1c5b0683.mjs} +2 -2
  5. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
  6. package/core/built/admin/assets/admin-x-settings/{index-8480baa8.mjs → index-14e518a7.mjs} +3 -3
  7. package/core/built/admin/assets/admin-x-settings/{index-a2648c61.mjs → index-fc9f985b.mjs} +2 -2
  8. package/core/built/admin/assets/admin-x-settings/{modals-6900c1d5.mjs → modals-15bc6a0f.mjs} +7192 -6656
  9. package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js → chunk.383.25fca2f09b4896656125.js} +76 -59
  10. package/core/built/admin/assets/chunk.524.24ab802c6c20f2da3449.js +28 -0
  11. package/core/built/admin/assets/{chunk.582.2697b46a5652693fc674.js → chunk.582.ebfc460cd2d6864d2cc9.js} +20 -27
  12. package/core/built/admin/assets/{ghost-843572e9507d099162ae744d791daba1.js → ghost-b3b44421acca3b3eec76bfbb6ba0e81b.js} +3 -3
  13. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +12578 -12352
  14. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +423 -211
  15. package/core/built/admin/assets/posts/posts.js +13680 -13671
  16. package/core/built/admin/assets/stats/stats.js +16457 -16635
  17. package/core/built/admin/assets/{vendor-8f805740fee4db959a5b2119001a56b1.js → vendor-4ce6d282a2a00fe486a0951e0591da19.js} +11 -9
  18. package/core/built/admin/index.html +5 -5
  19. package/core/frontend/helpers/match.js +6 -0
  20. package/core/frontend/services/routing/ParentRouter.js +1 -1
  21. package/core/frontend/services/routing/controllers/email-post.js +0 -2
  22. package/core/frontend/services/routing/controllers/previews.js +0 -3
  23. package/core/frontend/web/middleware/frontend-caching.js +2 -2
  24. package/core/server/api/endpoints/authentication.js +37 -73
  25. package/core/server/api/endpoints/authors-public.js +8 -9
  26. package/core/server/api/endpoints/db.js +34 -35
  27. package/core/server/api/endpoints/emails.js +8 -10
  28. package/core/server/api/endpoints/integrations.js +20 -18
  29. package/core/server/api/endpoints/invites.js +8 -10
  30. package/core/server/api/endpoints/labels.js +19 -23
  31. package/core/server/api/endpoints/notifications.js +3 -4
  32. package/core/server/api/endpoints/pages-public.js +8 -10
  33. package/core/server/api/endpoints/pages.js +14 -18
  34. package/core/server/api/endpoints/posts-public.js +8 -10
  35. package/core/server/api/endpoints/posts.js +6 -8
  36. package/core/server/api/endpoints/previews.js +8 -10
  37. package/core/server/api/endpoints/redirects.js +7 -8
  38. package/core/server/api/endpoints/schedules.js +5 -7
  39. package/core/server/api/endpoints/slugs.js +7 -9
  40. package/core/server/api/endpoints/snippets.js +16 -20
  41. package/core/server/api/endpoints/tags-public.js +8 -10
  42. package/core/server/api/endpoints/tags.js +19 -23
  43. package/core/server/api/endpoints/themes.js +6 -8
  44. package/core/server/api/endpoints/users.js +31 -36
  45. package/core/server/api/endpoints/utils/permissions.js +10 -10
  46. package/core/server/api/endpoints/utils/serializers/output/roles.js +9 -10
  47. package/core/server/api/endpoints/utils/validators/input/images.js +43 -52
  48. package/core/server/api/endpoints/utils/validators/input/invites.js +6 -8
  49. package/core/server/api/endpoints/webhooks.js +38 -42
  50. package/core/server/data/migrations/versions/5.120/2025-05-07-14-57-38-add-newsletters-button-corners-column.js +8 -0
  51. package/core/server/data/migrations/versions/5.120/2025-05-13-17-36-56-add-newsletters-button-style-column.js +8 -0
  52. package/core/server/data/migrations/versions/5.120/2025-05-14-20-00-15-add-newsletters-setting-columns.js +22 -0
  53. package/core/server/data/schema/schema.js +6 -1
  54. package/core/server/lib/image/Gravatar.js +12 -13
  55. package/core/server/lib/lexical.js +3 -1
  56. package/core/server/models/newsletter.js +6 -1
  57. package/core/server/services/api-version-compatibility/index.js +1 -33
  58. package/core/server/services/auth/session/emails/signin.js +3 -3
  59. package/core/server/services/email-address/EmailAddressParser.js +70 -0
  60. package/core/server/services/email-address/EmailAddressParser.js.d.ts +13 -0
  61. package/core/server/services/email-address/EmailAddressService.js +142 -0
  62. package/core/server/services/email-address/EmailAddressService.ts +183 -0
  63. package/core/server/services/email-address/EmailAddressServiceWrapper.js +2 -4
  64. package/core/server/services/email-analytics/EmailAnalyticsService.js +1 -1
  65. package/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js +2 -1
  66. package/core/server/services/email-service/BatchSendingService.js +703 -0
  67. package/core/server/services/email-service/EmailBodyCache.js +20 -0
  68. package/core/server/services/email-service/EmailController.js +94 -0
  69. package/core/server/services/email-service/EmailEventProcessor.js +267 -0
  70. package/core/server/services/email-service/EmailEventStorage.js +187 -0
  71. package/core/server/services/email-service/EmailRenderer.js +1263 -0
  72. package/core/server/services/email-service/EmailSegmenter.js +74 -0
  73. package/core/server/services/email-service/EmailService.js +310 -0
  74. package/core/server/services/email-service/EmailServiceWrapper.js +9 -2
  75. package/core/server/services/email-service/MailgunEmailProvider.js +191 -0
  76. package/core/server/services/email-service/SendingService.js +173 -0
  77. package/core/server/services/email-service/email-templates/partials/feedback-button.hbs +7 -0
  78. package/core/server/services/email-service/email-templates/partials/latest-posts.hbs +39 -0
  79. package/core/server/services/email-service/email-templates/partials/paywall.hbs +20 -0
  80. package/core/server/services/email-service/email-templates/partials/styles.hbs +2348 -0
  81. package/core/server/services/email-service/email-templates/template.hbs +238 -0
  82. package/core/server/services/email-service/events/EmailBouncedEvent.js +63 -0
  83. package/core/server/services/email-service/events/EmailDeliveredEvent.js +49 -0
  84. package/core/server/services/email-service/events/EmailOpenedEvent.js +49 -0
  85. package/core/server/services/email-service/events/EmailTemporaryBouncedEvent.js +63 -0
  86. package/core/server/services/email-service/events/EmailUnsubscribedEvent.js +42 -0
  87. package/core/server/services/email-service/events/SpamComplaintEvent.js +42 -0
  88. package/core/server/services/email-service/helpers/register-helpers.js +59 -0
  89. package/core/server/services/email-suppression-list/MailgunEmailSuppressionList.js +2 -1
  90. package/core/server/services/explore-ping/index.js +2 -1
  91. package/core/server/services/mail/GhostMailer.js +1 -1
  92. package/core/server/services/media-inliner/ExternalMediaInliner.js +2 -1
  93. package/core/server/services/members/api.js +15 -15
  94. package/core/server/services/members/emails/signin.js +4 -4
  95. package/core/server/services/members/emails/signup-paid.js +3 -4
  96. package/core/server/services/members/emails/signup.js +3 -3
  97. package/core/server/services/members/emails/subscribe.js +3 -3
  98. package/core/server/services/members/members-api/repositories/MemberRepository.js +92 -92
  99. package/core/server/services/members-events/LastSeenAtUpdater.js +1 -1
  100. package/core/server/services/settings-helpers/SettingsHelpers.js +1 -1
  101. package/core/server/services/staff/StaffServiceEmails.js +1 -1
  102. package/core/server/services/stats/PostsStatsService.js +28 -7
  103. package/core/server/web/api/app.js +0 -1
  104. package/core/server/web/api/endpoints/admin/app.js +0 -2
  105. package/core/server/web/api/endpoints/content/app.js +0 -2
  106. package/core/server/web/api/middleware/upload.js +2 -2
  107. package/core/shared/custom-theme-settings-cache/CustomThemeSettingsService.js +2 -1
  108. package/package.json +39 -97
  109. package/tsconfig.tsbuildinfo +1 -1
  110. package/yarn.lock +385 -517
  111. package/components/tryghost-api-framework-5.119.3.tgz +0 -0
  112. package/components/tryghost-custom-fonts-5.119.3.tgz +0 -0
  113. package/components/tryghost-domain-events-5.119.3.tgz +0 -0
  114. package/components/tryghost-email-addresses-5.119.3.tgz +0 -0
  115. package/components/tryghost-email-service-5.119.3.tgz +0 -0
  116. package/components/tryghost-html-to-plaintext-5.119.3.tgz +0 -0
  117. package/components/tryghost-i18n-5.119.3.tgz +0 -0
  118. package/components/tryghost-job-manager-5.119.3.tgz +0 -0
  119. package/components/tryghost-members-csv-5.119.3.tgz +0 -0
  120. package/components/tryghost-mw-error-handler-5.119.3.tgz +0 -0
  121. package/components/tryghost-mw-vhost-5.119.3.tgz +0 -0
  122. package/components/tryghost-prometheus-metrics-5.119.3.tgz +0 -0
  123. package/components/tryghost-security-5.119.3.tgz +0 -0
  124. package/core/built/admin/assets/chunk.524.c86e2e1b3e94d7cb1e4c.js +0 -35
  125. package/core/server/services/api-version-compatibility/APIVersionCompatibilityService.js +0 -99
  126. package/core/server/services/api-version-compatibility/VersionNotificationsDataService.js +0 -80
  127. package/core/server/services/api-version-compatibility/extract-api-key.js +0 -57
  128. package/core/server/services/api-version-compatibility/mw-api-version-mismatch.js +0 -31
  129. /package/core/built/admin/assets/{chunk.137.c9bf40f01afeeadb4660.js.LICENSE.txt → chunk.383.25fca2f09b4896656125.js.LICENSE.txt} +0 -0
@@ -0,0 +1,173 @@
1
+ const validator = require('@tryghost/validator');
2
+ const logging = require('@tryghost/logging');
3
+
4
+ /**
5
+ * @typedef {object} EmailData
6
+ * @prop {string} html
7
+ * @prop {string} plaintext
8
+ * @prop {string} subject
9
+ * @prop {string} from
10
+ * @prop {string} emailId
11
+ * @prop {string} [replyTo]
12
+ * @prop {Recipient[]} recipients
13
+ * @prop {import("./EmailRenderer").ReplacementDefinition[]} replacementDefinitions
14
+ *
15
+ * @typedef {object} IEmailProviderService
16
+ * @prop {(emailData: EmailData, options: EmailSendingOptions) => Promise<EmailProviderSuccessResponse>} send
17
+ * @prop {() => number} getMaximumRecipients
18
+ * @prop {() => number} getTargetDeliveryWindow
19
+ *
20
+ * @typedef {object} Post
21
+ * @typedef {object} Newsletter
22
+ */
23
+
24
+ /**
25
+ * @typedef {import("./EmailRenderer")} EmailRenderer
26
+ * @typedef {import("./EmailRenderer").EmailBody} EmailBody
27
+ */
28
+
29
+ /**
30
+ * @typedef {object} EmailSendingOptions
31
+ * @prop {boolean} clickTrackingEnabled
32
+ * @prop {boolean} openTrackingEnabled
33
+ * @prop {Date} deliveryTime
34
+ * @prop {{get(id: string): EmailBody | null, set(id: string, body: EmailBody): void}} [emailBodyCache]
35
+ */
36
+
37
+ /**
38
+ * @typedef {import("./EmailRenderer").MemberLike} MemberLike
39
+ */
40
+
41
+ /**
42
+ * @typedef {object} Recipient
43
+ * @prop {string} email
44
+ * @prop {Replacement[]} replacements
45
+ */
46
+
47
+ /**
48
+ * @typedef {object} Replacement
49
+ * @prop {string} id
50
+ * @prop {RegExp} token
51
+ * @prop {string} value
52
+ */
53
+
54
+ /**
55
+ * @typedef {object} EmailProviderSuccessResponse
56
+ * @prop {string} id
57
+ */
58
+
59
+ class SendingService {
60
+ #emailProvider;
61
+ #emailRenderer;
62
+
63
+ /**
64
+ * @param {object} dependencies
65
+ * @param {IEmailProviderService} dependencies.emailProvider
66
+ * @param {EmailRenderer} dependencies.emailRenderer
67
+ */
68
+ constructor({
69
+ emailProvider,
70
+ emailRenderer
71
+ }) {
72
+ this.#emailProvider = emailProvider;
73
+ this.#emailRenderer = emailRenderer;
74
+ }
75
+
76
+ getMaximumRecipients() {
77
+ return this.#emailProvider.getMaximumRecipients();
78
+ }
79
+
80
+ /**
81
+ * Returns the configured target delivery window in seconds
82
+ *
83
+ * @returns {number}
84
+ */
85
+ getTargetDeliveryWindow() {
86
+ return this.#emailProvider.getTargetDeliveryWindow();
87
+ }
88
+
89
+ /**
90
+ * Send a given post, rendered for a given newsletter and segment to the members provided in the list
91
+ * @param {object} data
92
+ * @param {Post} data.post
93
+ * @param {Newsletter} data.newsletter
94
+ * @param {string|null} data.segment
95
+ * @param {string|null} data.emailId
96
+ * @param {MemberLike[]} data.members
97
+ * @param {EmailSendingOptions} options
98
+ * @returns {Promise<EmailProviderSuccessResponse>}
99
+ */
100
+ async send({post, newsletter, segment, members, emailId}, options) {
101
+ const cacheId = emailId + '-' + (segment ?? 'null');
102
+ const isTestEmail = options.isTestEmail ?? false;
103
+
104
+ /**
105
+ * @type {EmailBody | null}
106
+ */
107
+ let emailBody = null;
108
+
109
+ if (options.emailBodyCache) {
110
+ emailBody = options.emailBodyCache.get(cacheId);
111
+ }
112
+
113
+ if (!emailBody) {
114
+ emailBody = await this.#emailRenderer.renderBody(
115
+ post,
116
+ newsletter,
117
+ segment,
118
+ {
119
+ clickTrackingEnabled: !!options.clickTrackingEnabled
120
+ }
121
+ );
122
+ if (options.emailBodyCache) {
123
+ options.emailBodyCache.set(cacheId, emailBody);
124
+ }
125
+ }
126
+
127
+ const recipients = this.buildRecipients(members, emailBody.replacements);
128
+ return await this.#emailProvider.send({
129
+ subject: this.#emailRenderer.getSubject(post, isTestEmail),
130
+ from: this.#emailRenderer.getFromAddress(post, newsletter),
131
+ replyTo: this.#emailRenderer.getReplyToAddress(post, newsletter) ?? undefined,
132
+ html: emailBody.html,
133
+ plaintext: emailBody.plaintext,
134
+ recipients,
135
+ emailId: emailId,
136
+ replacementDefinitions: emailBody.replacements
137
+ }, {
138
+ clickTrackingEnabled: !!options.clickTrackingEnabled,
139
+ openTrackingEnabled: !!options.openTrackingEnabled,
140
+ ...(options.deliveryTime && {deliveryTime: options.deliveryTime})
141
+ });
142
+ }
143
+
144
+ /**
145
+ * @private
146
+ * @param {MemberLike[]} members
147
+ * @param {import("./EmailRenderer").ReplacementDefinition[]} replacementDefinitions
148
+ * @returns {Recipient[]}
149
+ */
150
+ buildRecipients(members, replacementDefinitions) {
151
+ return members.map((member) => {
152
+ return {
153
+ email: member.email?.trim(),
154
+ replacements: replacementDefinitions.map((def) => {
155
+ return {
156
+ id: def.id,
157
+ token: def.token,
158
+ value: def.getValue(member) || ''
159
+ };
160
+ })
161
+ };
162
+ }).filter((recipient) => {
163
+ // Remove invalid recipient email addresses
164
+ const isValidRecipient = validator.isEmail(recipient.email, {legacy: false});
165
+ if (!isValidRecipient) {
166
+ logging.warn(`Removed recipient ${recipient.email} from list because it is not a valid email address`);
167
+ }
168
+ return isValidRecipient;
169
+ });
170
+ }
171
+ }
172
+
173
+ module.exports = SendingService;
@@ -0,0 +1,7 @@
1
+ <td dir="ltr" valign="top" align="center" style="display: inline-block; vertical-align: top; font-family: inherit; text-align: center; padding: 0 4px 4px; cursor: pointer; width: 30%;">
2
+ <a href="{{href}}" target="_blank">
3
+ <img src={{iconUrl}} border="0" width={{width}} height={{height}} alt="{{buttonText}}">
4
+ <p class="feedback-button-text">{{t buttonText}}</p>
5
+ {{!-- Button text possible values: {{t 'More like this'}} {{t 'Less like this'}} {{t 'Comment'}} --}}
6
+ </a>
7
+ </td>
@@ -0,0 +1,39 @@
1
+ {{#each latestPosts}}
2
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
3
+ <tr>
4
+ <td class="latest-post">
5
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
6
+ <tr>
7
+ <td valign="top" align="left" class="latest-post-title">
8
+ <h4 class="{{#if ../latestPostsHasImages}}{{#if (not featureImage)}}no-image{{/if}}{{/if}}">
9
+ <a href="{{url}}">{{{title}}}</a>
10
+ </h4>
11
+ {{#if excerpt}}
12
+ <p class="latest-post-excerpt">
13
+ <a href="{{url}}">{{{excerpt}}}</a>
14
+ </p>
15
+ {{/if}}
16
+ </td>
17
+ {{#if ../latestPostsHasImages}}
18
+ {{#if featureImage}}
19
+ <td width="100" class="latest-post-img">
20
+ <a href="{{url}}">
21
+ <img
22
+ src="{{featureImage.src}}"
23
+ {{#if featureImage.width }}
24
+ width="{{featureImage.width}}"
25
+ {{/if}}
26
+ {{#if featureImage.height }}
27
+ height="{{featureImage.height}}"
28
+ {{/if}}
29
+ />
30
+ </a>
31
+ </td>
32
+ {{/if}}
33
+ {{/if}}
34
+ </tr>
35
+ </table>
36
+ </td>
37
+ </tr>
38
+ </table>
39
+ {{/each}}
@@ -0,0 +1,20 @@
1
+ <div class="align-center" style="text-align: center;">
2
+ <hr
3
+ style="position: relative; display: block; width: 100%; margin: 3em 0; padding: 0; height: 1px; border: 0; border-top: 1px solid #e0e7eb;">
4
+ <h3
5
+ style="margin-top: 0; font-family: -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; line-height: 1.11em; font-weight: 700; text-rendering: optimizeLegibility; margin: 1.5em 0 0.5em 0; text-wrap: pretty;">
6
+ {{t 'Upgrade to continue reading.'}}</h3>
7
+ <p style="margin: 0 auto 1.5em auto; line-height: 1.6em; max-width: 440px; text-wrap: pretty;">{{{t 'Become a paid member of {site} to get access to all premium content.' site=site.title}}}</p>
8
+ <div class="btn btn-accent" style="box-sizing: border-box; width: 100%; display: table;">
9
+ <table border="0" cellspacing="0" cellpadding="0" align="center" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
10
+ <tbody>
11
+ <tr>
12
+ <td align="center" valign="top" bgcolor="{{accentColor}}" style="vertical-align: top; text-align: center; border-radius: 5px;">
13
+ <a href="{{paywall.signupUrl}}" style="overflow-wrap: anywhere; border: solid 1px #3498db; border-radius: 5px; box-sizing: border-box; cursor: pointer; display: inline-block; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-decoration: none; background-color: {{accentColor}}; border-color: {{accentColor}}; color: #FFFFFF;" target="_blank">{{t 'Upgrade'}}</a>
14
+ </td>
15
+ </tr>
16
+ </tbody>
17
+ </table>
18
+ </div>
19
+ <p style="margin: 0 0 1.5em 0; line-height: 1.6em;"></p>
20
+ </div>