ghost 5.17.2 → 5.19.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/README.md +3 -3
- package/components/tryghost-adapter-manager-5.19.0.tgz +0 -0
- package/components/{tryghost-api-framework-5.17.2.tgz → tryghost-api-framework-5.19.0.tgz} +0 -0
- package/components/tryghost-api-version-compatibility-service-5.19.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.19.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.19.0.tgz +0 -0
- package/components/tryghost-constants-5.19.0.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.17.2.tgz → tryghost-custom-theme-settings-service-5.19.0.tgz} +0 -0
- package/components/tryghost-domain-events-5.19.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.19.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.19.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.19.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.19.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.19.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.19.0.tgz +0 -0
- package/components/tryghost-job-manager-5.19.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.19.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.19.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.19.0.tgz +0 -0
- package/components/tryghost-magic-link-5.19.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.19.0.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.19.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.19.0.tgz +0 -0
- package/components/tryghost-member-events-5.19.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.19.0.tgz +0 -0
- package/components/tryghost-members-api-5.19.0.tgz +0 -0
- package/components/tryghost-members-csv-5.19.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.19.0.tgz +0 -0
- package/components/tryghost-members-importer-5.19.0.tgz +0 -0
- package/components/tryghost-members-offers-5.19.0.tgz +0 -0
- package/components/tryghost-members-payments-5.19.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.19.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.19.0.tgz +0 -0
- package/components/tryghost-minifier-5.19.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.19.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.19.0.tgz +0 -0
- package/components/{tryghost-mw-error-handler-5.17.2.tgz → tryghost-mw-error-handler-5.19.0.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.19.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.19.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.19.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.19.0.tgz +0 -0
- package/components/tryghost-package-json-5.19.0.tgz +0 -0
- package/components/tryghost-referrers-5.19.0.tgz +0 -0
- package/components/tryghost-security-5.19.0.tgz +0 -0
- package/components/tryghost-session-service-5.19.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.19.0.tgz +0 -0
- package/components/tryghost-staff-service-5.19.0.tgz +0 -0
- package/components/tryghost-stats-service-5.19.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.19.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.19.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.19.0.tgz +0 -0
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/built/screen.css.map +1 -1
- package/content/themes/casper/assets/css/screen.css +18 -21
- package/content/themes/casper/package.json +1 -1
- package/core/boot.js +3 -1
- package/core/built/admin/assets/{chunk.143.3f2f5cbbd1ef4b1425d9.js → chunk.143.eaf838fbf1470f018bf3.js} +6 -6
- package/core/built/admin/assets/{chunk.174.37fefc669899f0fd0064.js → chunk.174.3a133d51d9b45097c101.js} +160 -159
- package/core/built/admin/assets/{chunk.178.8fa31be80e639cbd2df2.js → chunk.178.44dae8a74f7f9d606e06.js} +4 -4
- package/core/built/admin/assets/{chunk.579.a9bccec4d650a7be727a.js → chunk.613.f1d519ad47e7f9024263.js} +8962 -8890
- package/core/built/admin/assets/{chunk.579.a9bccec4d650a7be727a.js.LICENSE.txt → chunk.613.f1d519ad47e7f9024263.js.LICENSE.txt} +0 -0
- package/core/built/admin/assets/{ghost-3577dfa3f54651db5ae5b9fd3d9b2824.js → ghost-5ce6f5a730c83c91fc258b12c537ea35.js} +2905 -2866
- package/core/built/admin/assets/ghost-982146a4ada3a5af1981d1919ae01d08.css +1 -0
- package/core/built/admin/assets/ghost-dark-41929e4857de411a23597a9de49a4e4f.css +1 -0
- package/core/built/admin/assets/{vendor-733135cd6cbca8126c6fa223d63a5bf3.css → vendor-3e6947aa681f0fb82b193090e520dc73.css} +24 -92
- package/core/built/admin/assets/{vendor-325e038b8609f0979f6578cae7a87f9e.js → vendor-5c7d7063620bec13668c4370145cd4b4.js} +561 -555
- package/core/built/admin/index.html +7 -7
- package/core/frontend/helpers/t.js +12 -0
- package/core/frontend/helpers/tpl/content-cta.hbs +1 -1
- package/core/frontend/public/ghost.min.css +1 -1
- package/core/frontend/public/robots.txt +1 -0
- package/core/frontend/services/sitemap/handler.js +1 -1
- package/core/frontend/services/sitemap/index-generator.js +1 -3
- package/core/frontend/web/middleware/static-theme.js +2 -0
- package/core/server/api/endpoints/feedback-members.js +23 -0
- package/core/server/api/endpoints/index.js +5 -1
- package/core/server/api/endpoints/utils/serializers/input/posts.js +3 -3
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +14 -13
- package/core/server/data/exporter/table-lists.js +3 -1
- package/core/server/data/importer/handlers/json.js +21 -23
- package/core/server/data/importer/importers/data/base.js +1 -1
- package/core/server/data/migrations/versions/4.0/05-add-members-subscribe-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/11-add-members-paid-subscription-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/13-add-members-payment-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -1
- package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +1 -1
- package/core/server/data/migrations/versions/4.11/02-add-email-verification-required-setting.js +1 -1
- package/core/server/data/migrations/versions/4.12/01-add-email-only-column-to-posts-meta-table.js +1 -1
- package/core/server/data/migrations/versions/4.3/03-add-default-product.js +1 -1
- package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -1
- package/core/server/data/migrations/versions/4.3/06-add-stripe-prices-table.js +2 -2
- package/core/server/data/migrations/versions/4.3/08-migrate-members-signup-setting.js +1 -1
- package/core/server/data/migrations/versions/4.33/2022-01-14-11-51-add-default-free-tier.js +1 -1
- package/core/server/data/migrations/versions/4.42/2022-03-21-17-17-add.js +2 -2
- package/core/server/data/migrations/versions/4.43/2022-03-28-19-26-recreate-newsletter-table.js +5 -5
- package/core/server/data/migrations/versions/4.46/2022-04-13-13-00-add-default-newsletter.js +1 -1
- package/core/server/data/migrations/versions/4.46/2022-04-20-08-39-map-subscribers-to-default-newsletter.js +1 -1
- package/core/server/data/migrations/versions/4.7/03-add-labs-setting.js +1 -1
- package/core/server/data/migrations/versions/4.8/03-add-default-product-portal-products.js +1 -1
- package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -1
- package/core/server/data/migrations/versions/5.0/2022-05-06-13-22-add-frontend-integration.js +1 -1
- package/core/server/data/migrations/versions/5.17/2022-09-29-12-39-add-track-clicks-column-to-emails.js +2 -2
- package/core/server/data/migrations/versions/5.19/2022-09-02-20-25-add-columns-to-products-table.js +19 -0
- package/core/server/data/migrations/versions/5.19/2022-09-02-20-52-backfill-new-product-columns.js +37 -0
- package/core/server/data/migrations/versions/5.19/2022-10-10-06-58-add-subscriptions-table.js +19 -0
- package/core/server/data/migrations/versions/5.19/2022-10-10-10-05-add-members-feedback-table.js +10 -0
- package/core/server/data/migrations/versions/5.19/2022-10-11-10-38-add-feedback-enabled-column-to-newsletters.js +7 -0
- package/core/server/data/schema/commands.js +3 -3
- package/core/server/data/schema/fixtures/fixtures.json +4 -1
- package/core/server/data/schema/schema.js +90 -24
- package/core/server/data/schema/validator.js +1 -1
- package/core/server/models/base/bookshelf.js +3 -4
- package/core/server/models/base/plugins/data-manipulation.js +1 -1
- package/core/server/models/base/plugins/events.js +1 -1
- package/core/server/models/base/utils.js +1 -1
- package/core/server/models/member-feedback.js +22 -0
- package/core/server/models/newsletter.js +3 -2
- package/core/server/models/post.js +24 -0
- package/core/server/models/settings.js +1 -1
- package/core/server/models/user.js +1 -1
- package/core/server/services/audience-feedback/FeedbackRepository.js +67 -0
- package/core/server/services/audience-feedback/index.js +33 -0
- package/core/server/services/bulk-email/bulk-email-processor.js +7 -1
- package/core/server/services/mail/GhostMailer.js +17 -1
- package/core/server/services/mega/feedback-buttons.js +69 -0
- package/core/server/services/mega/mega.js +1 -1
- package/core/server/services/mega/post-email-serializer.js +24 -4
- package/core/server/services/mega/template.js +3 -0
- package/core/server/services/members/middleware.js +40 -0
- package/core/server/services/notifications/notifications.js +1 -1
- package/core/server/services/settings/settings-service.js +1 -1
- package/core/server/services/themes/storage.js +1 -1
- package/core/server/services/webhooks/serialize.js +1 -1
- package/core/server/web/admin/app.js +2 -0
- package/core/server/web/api/endpoints/admin/routes.js +1 -2
- package/core/server/web/members/app.js +12 -0
- package/core/shared/config/defaults.json +1 -1
- package/core/shared/labs.js +6 -4
- package/package.json +117 -117
- package/yarn.lock +4867 -1572
- package/components/tryghost-adapter-manager-5.17.2.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.17.2.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.17.2.tgz +0 -0
- package/components/tryghost-constants-5.17.2.tgz +0 -0
- package/components/tryghost-domain-events-5.17.2.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.17.2.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.17.2.tgz +0 -0
- package/components/tryghost-email-content-generator-5.17.2.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.17.2.tgz +0 -0
- package/components/tryghost-extract-api-key-5.17.2.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.17.2.tgz +0 -0
- package/components/tryghost-job-manager-5.17.2.tgz +0 -0
- package/components/tryghost-link-redirects-5.17.2.tgz +0 -0
- package/components/tryghost-link-replacer-5.17.2.tgz +0 -0
- package/components/tryghost-link-tracking-5.17.2.tgz +0 -0
- package/components/tryghost-magic-link-5.17.2.tgz +0 -0
- package/components/tryghost-mailgun-client-5.17.2.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.17.2.tgz +0 -0
- package/components/tryghost-member-attribution-5.17.2.tgz +0 -0
- package/components/tryghost-member-events-5.17.2.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.17.2.tgz +0 -0
- package/components/tryghost-members-api-5.17.2.tgz +0 -0
- package/components/tryghost-members-csv-5.17.2.tgz +0 -0
- package/components/tryghost-members-events-service-5.17.2.tgz +0 -0
- package/components/tryghost-members-importer-5.17.2.tgz +0 -0
- package/components/tryghost-members-offers-5.17.2.tgz +0 -0
- package/components/tryghost-members-payments-5.17.2.tgz +0 -0
- package/components/tryghost-members-ssr-5.17.2.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.17.2.tgz +0 -0
- package/components/tryghost-minifier-5.17.2.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.17.2.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.17.2.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.17.2.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.17.2.tgz +0 -0
- package/components/tryghost-mw-vhost-5.17.2.tgz +0 -0
- package/components/tryghost-oembed-service-5.17.2.tgz +0 -0
- package/components/tryghost-package-json-5.17.2.tgz +0 -0
- package/components/tryghost-referrers-5.17.2.tgz +0 -0
- package/components/tryghost-security-5.17.2.tgz +0 -0
- package/components/tryghost-session-service-5.17.2.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.17.2.tgz +0 -0
- package/components/tryghost-staff-service-5.17.2.tgz +0 -0
- package/components/tryghost-stats-service-5.17.2.tgz +0 -0
- package/components/tryghost-update-check-service-5.17.2.tgz +0 -0
- package/components/tryghost-verification-trigger-5.17.2.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.17.2.tgz +0 -0
- package/core/built/admin/assets/ghost-597fb8e8b1b91dd0ac4d9f2d75bd67fb.css +0 -1
- package/core/built/admin/assets/ghost-dark-e4ccecd9903d35d360d71fe859cbb3bf.css +0 -1
|
@@ -7,6 +7,7 @@ const errors = require('@tryghost/errors');
|
|
|
7
7
|
const tpl = require('@tryghost/tpl');
|
|
8
8
|
const settingsCache = require('../../../shared/settings-cache');
|
|
9
9
|
const urlUtils = require('../../../shared/url-utils');
|
|
10
|
+
const metrics = require('@tryghost/metrics');
|
|
10
11
|
const messages = {
|
|
11
12
|
title: 'Ghost at {domain}',
|
|
12
13
|
checkEmailConfigInstructions: 'Please see {url} for instructions on configuring email.',
|
|
@@ -83,7 +84,8 @@ module.exports = class GhostMailer {
|
|
|
83
84
|
const options = config.get('mail') && _.clone(config.get('mail').options) || {};
|
|
84
85
|
|
|
85
86
|
this.state = {
|
|
86
|
-
usingDirect: transport === 'direct'
|
|
87
|
+
usingDirect: transport === 'direct',
|
|
88
|
+
usingMailgun: transport === 'mailgun'
|
|
87
89
|
};
|
|
88
90
|
this.transport = nodemailer(transport, options);
|
|
89
91
|
}
|
|
@@ -121,10 +123,24 @@ module.exports = class GhostMailer {
|
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
async sendMail(message) {
|
|
126
|
+
const startTime = Date.now();
|
|
124
127
|
try {
|
|
125
128
|
const response = await this.transport.sendMail(message);
|
|
129
|
+
if (this.state.usingMailgun) {
|
|
130
|
+
metrics.metric('mailgun-send-transactional-mail', {
|
|
131
|
+
value: Date.now() - startTime,
|
|
132
|
+
statusCode: 200
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
126
136
|
return response;
|
|
127
137
|
} catch (err) {
|
|
138
|
+
if (this.state.usingMailgun) {
|
|
139
|
+
metrics.metric('mailgun-send-transactional-mail', {
|
|
140
|
+
value: Date.now() - startTime,
|
|
141
|
+
statusCode: err.status
|
|
142
|
+
});
|
|
143
|
+
}
|
|
128
144
|
throw createMailError({
|
|
129
145
|
message: tpl(messages.reason, {reason: err.message || err}),
|
|
130
146
|
err
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const {Color} = require('@tryghost/color-utils');
|
|
2
|
+
const audienceFeedback = require('../audience-feedback');
|
|
3
|
+
|
|
4
|
+
const templateStrings = {
|
|
5
|
+
like: '%{feedback_button_like}%',
|
|
6
|
+
dislike: '%{feedback_button_dislike}%'
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const generateLinks = (postId, uuid, html) => {
|
|
10
|
+
const positiveLink = audienceFeedback.service.buildLink(
|
|
11
|
+
uuid,
|
|
12
|
+
postId,
|
|
13
|
+
1
|
|
14
|
+
);
|
|
15
|
+
const negativeLink = audienceFeedback.service.buildLink(
|
|
16
|
+
uuid,
|
|
17
|
+
postId,
|
|
18
|
+
0
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
html = html.replace(templateStrings.like, positiveLink.href);
|
|
22
|
+
html = html.replace(templateStrings.dislike, negativeLink.href);
|
|
23
|
+
|
|
24
|
+
return html;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const getTemplate = (accentColor) => {
|
|
28
|
+
const likeButtonHtml = getButtonHtml(templateStrings.like, 'More like this', accentColor);
|
|
29
|
+
const dislikeButtonHtml = getButtonHtml(templateStrings.dislike, 'Less like this', accentColor);
|
|
30
|
+
|
|
31
|
+
return (`
|
|
32
|
+
<tr>
|
|
33
|
+
<td dir="ltr" width="100%" style="background-color: #ffffff; text-align: center; padding: 40px 4px; border-bottom: 1px solid #e5eff5" align="center">
|
|
34
|
+
<h3 style="text-align: center; margin-bottom: 22px; font-size: 17px; letter-spacing: -0.2px; margin-top: 0 !important;">What did you think of this post?</h3>
|
|
35
|
+
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="margin: auto; width: auto !important;">
|
|
36
|
+
<tr>
|
|
37
|
+
${likeButtonHtml}
|
|
38
|
+
${dislikeButtonHtml}
|
|
39
|
+
</tr>
|
|
40
|
+
</table>
|
|
41
|
+
</td>
|
|
42
|
+
</tr>
|
|
43
|
+
`);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function getButtonHtml(href, buttonText, accentColor) {
|
|
47
|
+
const color = new Color(accentColor);
|
|
48
|
+
const bgColor = `${accentColor}10`;
|
|
49
|
+
const textColor = color.darken(0.6).hex();
|
|
50
|
+
|
|
51
|
+
return (`
|
|
52
|
+
<td dir="ltr" valign="top" align="center" style="font-family: inherit; font-size: 14px; text-align: center;" nowrap>
|
|
53
|
+
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="width: auto !important;">
|
|
54
|
+
<tr>
|
|
55
|
+
<td style="padding: 0 6px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';">
|
|
56
|
+
<a href=${href} style="background-color: ${bgColor}; color: ${textColor}; border-radius: 22px; font-family: inherit; padding: 12px 20px; border: none; font-size: 14px; font-weight: bold; line-height: 100%; text-decoration: none; display: block;">
|
|
57
|
+
${buttonText}
|
|
58
|
+
</a>
|
|
59
|
+
</td>
|
|
60
|
+
</tr>
|
|
61
|
+
</table>
|
|
62
|
+
</td>
|
|
63
|
+
`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
generateLinks,
|
|
68
|
+
getTemplate
|
|
69
|
+
};
|
|
@@ -3,7 +3,7 @@ const Promise = require('bluebird');
|
|
|
3
3
|
const debug = require('@tryghost/debug')('mega');
|
|
4
4
|
const tpl = require('@tryghost/tpl');
|
|
5
5
|
const moment = require('moment');
|
|
6
|
-
const ObjectID = require('bson-objectid');
|
|
6
|
+
const ObjectID = require('bson-objectid').default;
|
|
7
7
|
const errors = require('@tryghost/errors');
|
|
8
8
|
const logging = require('@tryghost/logging');
|
|
9
9
|
const settingsCache = require('../../../shared/settings-cache');
|
|
@@ -16,11 +16,12 @@ const urlService = require('../../services/url');
|
|
|
16
16
|
const linkReplacer = require('@tryghost/link-replacer');
|
|
17
17
|
const linkTracking = require('../link-tracking');
|
|
18
18
|
const memberAttribution = require('../member-attribution');
|
|
19
|
+
const feedbackButtons = require('./feedback-buttons');
|
|
19
20
|
|
|
20
21
|
const ALLOWED_REPLACEMENTS = ['first_name', 'uuid'];
|
|
21
22
|
|
|
22
23
|
const PostEmailSerializer = {
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
// Format a full html document ready for email by inlining CSS, adjusting links,
|
|
25
26
|
// and performing any client-specific fixes
|
|
26
27
|
formatHtmlForEmail(html) {
|
|
@@ -107,6 +108,23 @@ const PostEmailSerializer = {
|
|
|
107
108
|
return signupUrl.href;
|
|
108
109
|
},
|
|
109
110
|
|
|
111
|
+
/**
|
|
112
|
+
* createUserLinks
|
|
113
|
+
*
|
|
114
|
+
* Generate personalised links for each user
|
|
115
|
+
*
|
|
116
|
+
* @param {string} memberUuid member uuid
|
|
117
|
+
* @param {Object} email
|
|
118
|
+
*/
|
|
119
|
+
createUserLinks(email, memberUuid) {
|
|
120
|
+
const result = {...email};
|
|
121
|
+
|
|
122
|
+
result.html = feedbackButtons.generateLinks(result.post.id, memberUuid, result.html);
|
|
123
|
+
result.plaintext = htmlToPlaintext.email(result.html);
|
|
124
|
+
|
|
125
|
+
return result;
|
|
126
|
+
},
|
|
127
|
+
|
|
110
128
|
// NOTE: serialization is needed to make sure we do post transformations such as image URL transformation from relative to absolute
|
|
111
129
|
async serializePostModel(model) {
|
|
112
130
|
// fetch mobiledoc rather than html and plaintext so we can render email-specific contents
|
|
@@ -206,6 +224,7 @@ const PostEmailSerializer = {
|
|
|
206
224
|
titleAlignment: newsletter.get('title_alignment'),
|
|
207
225
|
bodyFontCategory: newsletter.get('body_font_category'),
|
|
208
226
|
showBadge: newsletter.get('show_badge'),
|
|
227
|
+
feedbackEnabled: newsletter.get('feedback_enabled'),
|
|
209
228
|
footerContent: newsletter.get('footer_content'),
|
|
210
229
|
showHeaderName: newsletter.get('show_header_name'),
|
|
211
230
|
accentColor,
|
|
@@ -335,7 +354,7 @@ const PostEmailSerializer = {
|
|
|
335
354
|
plaintext: post.plaintext
|
|
336
355
|
};
|
|
337
356
|
|
|
338
|
-
/**
|
|
357
|
+
/**
|
|
339
358
|
* If a part of the email is members-only and the post is paid-only, add a paywall:
|
|
340
359
|
* - Just before sending the email, we'll hide the paywall or paid content depending on the member segment it is sent to.
|
|
341
360
|
* - We already need to do URL-replacement on the HTML here
|
|
@@ -369,7 +388,7 @@ const PostEmailSerializer = {
|
|
|
369
388
|
|
|
370
389
|
// Add link click tracking
|
|
371
390
|
url = await linkTracking.service.addTrackingToUrl(url, post, '--uuid--');
|
|
372
|
-
|
|
391
|
+
|
|
373
392
|
// We need to convert to a string at this point, because we need invalid string characters in the URL
|
|
374
393
|
const str = url.toString().replace(/--uuid--/g, '%%{uuid}%%');
|
|
375
394
|
return str;
|
|
@@ -490,7 +509,7 @@ const PostEmailSerializer = {
|
|
|
490
509
|
});
|
|
491
510
|
|
|
492
511
|
result.html = this.formatHtmlForEmail($.html());
|
|
493
|
-
result.plaintext = htmlToPlaintext.email(result.html);
|
|
512
|
+
result.plaintext = htmlToPlaintext.email(result.html);
|
|
494
513
|
delete result.post;
|
|
495
514
|
|
|
496
515
|
return result;
|
|
@@ -501,6 +520,7 @@ module.exports = {
|
|
|
501
520
|
serialize: PostEmailSerializer.serialize.bind(PostEmailSerializer),
|
|
502
521
|
createUnsubscribeUrl: PostEmailSerializer.createUnsubscribeUrl.bind(PostEmailSerializer),
|
|
503
522
|
createPostSignupUrl: PostEmailSerializer.createPostSignupUrl.bind(PostEmailSerializer),
|
|
523
|
+
createUserLinks: PostEmailSerializer.createUserLinks.bind(PostEmailSerializer),
|
|
504
524
|
renderEmailForSegment: PostEmailSerializer.renderEmailForSegment.bind(PostEmailSerializer),
|
|
505
525
|
parseReplacements: PostEmailSerializer.parseReplacements.bind(PostEmailSerializer),
|
|
506
526
|
// Export for tests
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const {escapeHtml: escape} = require('@tryghost/string');
|
|
2
|
+
const feedbackButtons = require('./feedback-buttons');
|
|
2
3
|
|
|
3
4
|
/* eslint indent: warn, no-irregular-whitespace: warn */
|
|
4
5
|
const iff = (cond, yes, no) => (cond ? yes : no);
|
|
@@ -1265,6 +1266,8 @@ ${ templateSettings.showBadge ? `
|
|
|
1265
1266
|
|
|
1266
1267
|
<!-- END MAIN CONTENT AREA -->
|
|
1267
1268
|
|
|
1269
|
+
${iff(templateSettings.feedbackEnabled, feedbackButtons.getTemplate(templateSettings.accentColor), '')}
|
|
1270
|
+
|
|
1268
1271
|
<tr>
|
|
1269
1272
|
<td class="wrapper" align="center">
|
|
1270
1273
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-top: 40px; padding-bottom: 30px;">
|
|
@@ -5,6 +5,13 @@ const models = require('../../models');
|
|
|
5
5
|
const urlUtils = require('../../../shared/url-utils');
|
|
6
6
|
const spamPrevention = require('../../web/shared/middleware/api/spam-prevention');
|
|
7
7
|
const {formattedMemberResponse} = require('./utils');
|
|
8
|
+
const errors = require('@tryghost/errors');
|
|
9
|
+
const tpl = require('@tryghost/tpl');
|
|
10
|
+
|
|
11
|
+
const messages = {
|
|
12
|
+
missingUuid: 'Missing uuid.',
|
|
13
|
+
invalidUuid: 'Invalid uuid.'
|
|
14
|
+
};
|
|
8
15
|
|
|
9
16
|
// @TODO: This piece of middleware actually belongs to the frontend, not to the member app
|
|
10
17
|
// Need to figure a way to separate these things (e.g. frontend actually talks to members API)
|
|
@@ -20,6 +27,38 @@ const loadMemberSession = async function (req, res, next) {
|
|
|
20
27
|
}
|
|
21
28
|
};
|
|
22
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Require member authentication, and make it possible to authenticate via uuid.
|
|
32
|
+
* You can chain this after loadMemberSession to make it possible to authetnicate via both the uuid and the session.
|
|
33
|
+
*/
|
|
34
|
+
const authMemberByUuid = async function (req, res, next) {
|
|
35
|
+
try {
|
|
36
|
+
const uuid = req.query.uuid;
|
|
37
|
+
if (!uuid) {
|
|
38
|
+
if (res.locals.member && req.member) {
|
|
39
|
+
// Already authenticated via session
|
|
40
|
+
return next();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
throw new errors.UnauthorizedError({
|
|
44
|
+
messsage: tpl(messages.missingUuid)
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const member = await membersService.api.memberBREADService.read({uuid});
|
|
49
|
+
if (!member) {
|
|
50
|
+
throw new errors.UnauthorizedError({
|
|
51
|
+
message: tpl(messages.invalidUuid)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
Object.assign(req, {member});
|
|
55
|
+
res.locals.member = req.member;
|
|
56
|
+
next();
|
|
57
|
+
} catch (err) {
|
|
58
|
+
next(err);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
23
62
|
const getIdentityToken = async function (req, res) {
|
|
24
63
|
try {
|
|
25
64
|
const token = await membersService.ssr.getIdentityTokenForMemberFromSession(req, res);
|
|
@@ -216,6 +255,7 @@ const createSessionFromMagicLink = async function (req, res, next) {
|
|
|
216
255
|
// Set req.member & res.locals.member if a cookie is set
|
|
217
256
|
module.exports = {
|
|
218
257
|
loadMemberSession,
|
|
258
|
+
authMemberByUuid,
|
|
219
259
|
createSessionFromMagicLink,
|
|
220
260
|
getIdentityToken,
|
|
221
261
|
getMemberNewsletters,
|
|
@@ -5,7 +5,7 @@ const _ = require('lodash');
|
|
|
5
5
|
const errors = require('@tryghost/errors');
|
|
6
6
|
const ghostVersion = require('@tryghost/version');
|
|
7
7
|
const tpl = require('@tryghost/tpl');
|
|
8
|
-
const ObjectId = require('bson-objectid');
|
|
8
|
+
const ObjectId = require('bson-objectid').default;
|
|
9
9
|
|
|
10
10
|
const messages = {
|
|
11
11
|
noPermissionToDismissNotif: 'You do not have permission to dismiss this notification.',
|
|
@@ -13,7 +13,7 @@ const mail = require('../mail');
|
|
|
13
13
|
const SingleUseTokenProvider = require('../members/SingleUseTokenProvider');
|
|
14
14
|
const urlUtils = require('../../../shared/url-utils');
|
|
15
15
|
|
|
16
|
-
const ObjectId = require('bson-objectid');
|
|
16
|
+
const ObjectId = require('bson-objectid').default;
|
|
17
17
|
const settingsHelpers = require('../settings-helpers');
|
|
18
18
|
|
|
19
19
|
const MAGIC_LINK_TOKEN_VALIDITY = 24 * 60 * 60 * 1000;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('themes');
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
|
-
const ObjectID = require('bson-objectid');
|
|
3
|
+
const ObjectID = require('bson-objectid').default;
|
|
4
4
|
|
|
5
5
|
const tpl = require('@tryghost/tpl');
|
|
6
6
|
const logging = require('@tryghost/logging');
|
|
@@ -13,7 +13,7 @@ module.exports = (event, model) => {
|
|
|
13
13
|
ops.push(() => {
|
|
14
14
|
let frame = {options: {previous: false, context: {user: true}}};
|
|
15
15
|
|
|
16
|
-
// NOTE: below options are lost
|
|
16
|
+
// @NOTE: below options are lost during event processing, a more holistic approach would be
|
|
17
17
|
// to pass them somehow along with the model
|
|
18
18
|
if (['posts', 'pages'].includes(docName)) {
|
|
19
19
|
frame.options.formats = ['mobiledoc', 'html', 'plaintext'];
|
|
@@ -20,6 +20,8 @@ module.exports = function setupAdminApp() {
|
|
|
20
20
|
// @NOTE: when we start working on HTTP/3 optimizations the immutable headers
|
|
21
21
|
// produced below should be split into separate 'Cache-Control' entry.
|
|
22
22
|
// For reference see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#validation_2
|
|
23
|
+
// @NOTE: the maxAge config passed below are in milliseconds and the config
|
|
24
|
+
// is specified in seconds. See https://github.com/expressjs/serve-static/issues/150 for more context
|
|
23
25
|
adminApp.use('/assets', serveStatic(
|
|
24
26
|
path.join(config.get('paths').adminAssets, 'assets'), {
|
|
25
27
|
maxAge: (configMaxAge || configMaxAge === 0) ? configMaxAge : constants.ONE_YEAR_MS,
|
|
@@ -3,7 +3,6 @@ const api = require('../../../../api').endpoints;
|
|
|
3
3
|
const {http} = require('@tryghost/api-framework');
|
|
4
4
|
const apiMw = require('../../middleware');
|
|
5
5
|
const mw = require('./middleware');
|
|
6
|
-
const labs = require('../../../../../shared/labs');
|
|
7
6
|
|
|
8
7
|
const shared = require('../../../shared');
|
|
9
8
|
|
|
@@ -310,7 +309,7 @@ module.exports = function apiRoutes() {
|
|
|
310
309
|
router.put('/newsletters/verifications/', mw.authAdminApi, http(api.newsletters.verifyPropertyUpdate));
|
|
311
310
|
router.put('/newsletters/:id', mw.authAdminApi, http(api.newsletters.edit));
|
|
312
311
|
|
|
313
|
-
router.get('/links',
|
|
312
|
+
router.get('/links', mw.authAdminApi, http(api.links.browse));
|
|
314
313
|
|
|
315
314
|
return router;
|
|
316
315
|
};
|
|
@@ -10,6 +10,8 @@ const shared = require('../shared');
|
|
|
10
10
|
const labs = require('../../../shared/labs');
|
|
11
11
|
const errorHandler = require('@tryghost/mw-error-handler');
|
|
12
12
|
const config = require('../../../shared/config');
|
|
13
|
+
const {http} = require('@tryghost/api-framework');
|
|
14
|
+
const api = require('../../api').endpoints;
|
|
13
15
|
|
|
14
16
|
const commentRouter = require('../comments');
|
|
15
17
|
|
|
@@ -65,6 +67,16 @@ module.exports = function setupMembersApp() {
|
|
|
65
67
|
// Comments
|
|
66
68
|
membersApp.use('/api/comments', commentRouter());
|
|
67
69
|
|
|
70
|
+
// Feedback
|
|
71
|
+
membersApp.post(
|
|
72
|
+
'/api/feedback',
|
|
73
|
+
labs.enabledMiddleware('audienceFeedback'),
|
|
74
|
+
bodyParser.json({limit: '50mb'}),
|
|
75
|
+
middleware.loadMemberSession,
|
|
76
|
+
middleware.authMemberByUuid,
|
|
77
|
+
http(api.feedbackMembers.add)
|
|
78
|
+
);
|
|
79
|
+
|
|
68
80
|
// API error handling
|
|
69
81
|
membersApp.use('/api', errorHandler.resourceNotFound);
|
|
70
82
|
membersApp.use('/api', errorHandler.handleJSONResponse(sentry));
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
"comments": {
|
|
169
169
|
"url": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/comments-ui.min.js",
|
|
170
170
|
"styles": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/main.css",
|
|
171
|
-
"version": "0.10
|
|
171
|
+
"version": "0.10"
|
|
172
172
|
},
|
|
173
173
|
"editor": {
|
|
174
174
|
"url": "https://unpkg.com/@tryghost/koenig-lexical@~{version}/dist/koenig-lexical.umd.js",
|
package/core/shared/labs.js
CHANGED
|
@@ -19,22 +19,24 @@ const GA_FEATURES = [
|
|
|
19
19
|
'freeTrial',
|
|
20
20
|
'compExpiring',
|
|
21
21
|
'searchHelper',
|
|
22
|
-
'emailAlerts'
|
|
23
|
-
'emailClicks'
|
|
22
|
+
'emailAlerts'
|
|
24
23
|
];
|
|
25
24
|
|
|
26
25
|
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
|
27
26
|
// input for the "labs" setting value
|
|
28
27
|
const BETA_FEATURES = [
|
|
29
28
|
'activitypub',
|
|
29
|
+
'sourceAttribution',
|
|
30
30
|
'memberAttribution'
|
|
31
31
|
];
|
|
32
32
|
|
|
33
33
|
const ALPHA_FEATURES = [
|
|
34
34
|
'urlCache',
|
|
35
35
|
'beforeAfterCard',
|
|
36
|
-
'
|
|
37
|
-
'
|
|
36
|
+
'lexicalEditor',
|
|
37
|
+
'exploreApp',
|
|
38
|
+
'audienceFeedback',
|
|
39
|
+
'fixNewsletterLinks'
|
|
38
40
|
];
|
|
39
41
|
|
|
40
42
|
module.exports.GA_KEYS = [...GA_FEATURES];
|