ghost 5.12.0 → 5.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-adapter-manager-5.12.3.tgz +0 -0
- package/components/{tryghost-api-framework-5.12.0.tgz → tryghost-api-framework-5.12.3.tgz} +0 -0
- package/components/{tryghost-api-version-compatibility-service-5.12.0.tgz → tryghost-api-version-compatibility-service-5.12.3.tgz} +0 -0
- package/components/tryghost-bootstrap-socket-5.12.3.tgz +0 -0
- package/components/tryghost-constants-5.12.3.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.12.0.tgz → tryghost-custom-theme-settings-service-5.12.3.tgz} +0 -0
- package/components/tryghost-domain-events-5.12.3.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.12.3.tgz +0 -0
- package/components/{tryghost-email-analytics-service-5.12.0.tgz → tryghost-email-analytics-service-5.12.3.tgz} +0 -0
- package/components/tryghost-email-content-generator-5.12.3.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.12.3.tgz +0 -0
- package/components/{tryghost-extract-api-key-5.12.0.tgz → tryghost-extract-api-key-5.12.3.tgz} +0 -0
- package/components/{tryghost-html-to-plaintext-5.12.0.tgz → tryghost-html-to-plaintext-5.12.3.tgz} +0 -0
- package/components/{tryghost-job-manager-5.12.0.tgz → tryghost-job-manager-5.12.3.tgz} +0 -0
- package/components/{tryghost-magic-link-5.12.0.tgz → tryghost-magic-link-5.12.3.tgz} +0 -0
- package/components/{tryghost-mailgun-client-5.12.0.tgz → tryghost-mailgun-client-5.12.3.tgz} +0 -0
- package/components/{tryghost-member-analytics-service-5.12.0.tgz → tryghost-member-analytics-service-5.12.3.tgz} +0 -0
- package/components/{tryghost-member-attribution-5.12.0.tgz → tryghost-member-attribution-5.12.3.tgz} +0 -0
- package/components/tryghost-member-events-5.12.3.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.12.3.tgz +0 -0
- package/components/tryghost-members-api-5.12.3.tgz +0 -0
- package/components/{tryghost-members-csv-5.12.0.tgz → tryghost-members-csv-5.12.3.tgz} +0 -0
- package/components/tryghost-members-events-service-5.12.3.tgz +0 -0
- package/components/{tryghost-members-importer-5.12.0.tgz → tryghost-members-importer-5.12.3.tgz} +0 -0
- package/components/{tryghost-members-offers-5.12.0.tgz → tryghost-members-offers-5.12.3.tgz} +0 -0
- package/components/tryghost-members-payments-5.12.3.tgz +0 -0
- package/components/tryghost-members-ssr-5.12.3.tgz +0 -0
- package/components/{tryghost-members-stripe-service-5.12.0.tgz → tryghost-members-stripe-service-5.12.3.tgz} +0 -0
- package/components/tryghost-minifier-5.12.3.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.12.3.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.12.3.tgz +0 -0
- package/components/{tryghost-mw-error-handler-5.12.0.tgz → tryghost-mw-error-handler-5.12.3.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.12.3.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.12.3.tgz +0 -0
- package/components/tryghost-mw-vhost-5.12.3.tgz +0 -0
- package/components/{tryghost-oembed-service-5.12.0.tgz → tryghost-oembed-service-5.12.3.tgz} +0 -0
- package/components/tryghost-package-json-5.12.3.tgz +0 -0
- package/components/tryghost-security-5.12.3.tgz +0 -0
- package/components/{tryghost-session-service-5.12.0.tgz → tryghost-session-service-5.12.3.tgz} +0 -0
- package/components/{tryghost-settings-path-manager-5.12.0.tgz → tryghost-settings-path-manager-5.12.3.tgz} +0 -0
- package/components/{tryghost-staff-service-5.12.0.tgz → tryghost-staff-service-5.12.3.tgz} +0 -0
- package/components/{tryghost-update-check-service-5.12.0.tgz → tryghost-update-check-service-5.12.3.tgz} +0 -0
- package/components/tryghost-verification-trigger-5.12.3.tgz +0 -0
- package/components/{tryghost-version-notifications-data-service-5.12.0.tgz → tryghost-version-notifications-data-service-5.12.3.tgz} +0 -0
- package/core/built/admin/assets/{chunk.143.da85cf08f47cfe520bf6.js → chunk.143.926fba90e07af88ce0ed.js} +5 -5
- package/core/built/admin/assets/{chunk.178.29d3fb3dc6811b673a00.js → chunk.178.99b5208c35b2fa3fe348.js} +4 -4
- package/core/built/admin/assets/ghost-0526c96b20843697927c1d06a9010197.js +1 -1
- package/core/built/admin/assets/{ghost-f62b0e78ddcd947273873bdeba4abc3c.css → ghost-0bbbef127e5dc0c0651fc442c4fdba8e.css} +1 -1
- package/core/built/admin/assets/{ghost-dark-e4d6987343ee26af534a06c1b9639d97.css → ghost-dark-27e002e66fbfdfaf3efcde63d5429c38.css} +1 -1
- package/core/built/admin/index.html +2 -2
- package/core/server/api/endpoints/settings.js +0 -94
- package/core/server/api/endpoints/utils/validators/input/settings.js +1 -20
- package/core/server/services/comments/service.js +8 -4
- package/core/server/services/mega/mega.js +7 -4
- package/core/server/services/mega/template.js +44 -16
- package/core/server/services/members/middleware.js +6 -2
- package/core/server/web/api/endpoints/admin/middleware.js +1 -3
- package/core/server/web/api/endpoints/admin/routes.js +0 -7
- package/core/server/web/shared/middleware/brute.js +2 -15
- package/package.json +85 -85
- package/yarn.lock +37 -62
- package/components/tryghost-adapter-manager-5.12.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.12.0.tgz +0 -0
- package/components/tryghost-constants-5.12.0.tgz +0 -0
- package/components/tryghost-domain-events-5.12.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.12.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.12.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.12.0.tgz +0 -0
- package/components/tryghost-member-events-5.12.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.12.0.tgz +0 -0
- package/components/tryghost-members-api-5.12.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.12.0.tgz +0 -0
- package/components/tryghost-members-payments-5.12.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.12.0.tgz +0 -0
- package/components/tryghost-minifier-5.12.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.12.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.12.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.12.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.12.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.12.0.tgz +0 -0
- package/components/tryghost-package-json-5.12.0.tgz +0 -0
- package/components/tryghost-security-5.12.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.12.0.tgz +0 -0
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
<link integrity="" rel="stylesheet" href="assets/vendor-bc9d2c9e5c8a33f0c92e81189d48e04c.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-0bbbef127e5dc0c0651fc442c4fdba8e.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
|
|
56
56
|
<script src="assets/vendor-52613f40d62355e9ac64cbfa211169bb.js"></script>
|
|
57
57
|
<script src="assets/chunk.579.65e09dd89eec70d059a0.js"></script>
|
|
58
|
-
<script src="assets/chunk.143.
|
|
58
|
+
<script src="assets/chunk.143.926fba90e07af88ce0ed.js"></script>
|
|
59
59
|
<script src="assets/ghost-0526c96b20843697927c1d06a9010197.js"></script>
|
|
60
60
|
</body>
|
|
61
61
|
</html>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const Promise = require('bluebird');
|
|
2
1
|
const _ = require('lodash');
|
|
3
2
|
const models = require('../../models');
|
|
4
3
|
const routeSettings = require('../../services/route-settings');
|
|
@@ -6,13 +5,8 @@ const {BadRequestError} = require('@tryghost/errors');
|
|
|
6
5
|
const settingsService = require('../../services/settings/settings-service');
|
|
7
6
|
const membersService = require('../../services/members');
|
|
8
7
|
const stripeService = require('../../services/stripe');
|
|
9
|
-
const tpl = require('@tryghost/tpl');
|
|
10
8
|
const settingsBREADService = settingsService.getSettingsBREADServiceInstance();
|
|
11
9
|
|
|
12
|
-
const messages = {
|
|
13
|
-
failedSendingEmail: 'Failed Sending Email'
|
|
14
|
-
};
|
|
15
|
-
|
|
16
10
|
async function getStripeConnectData(frame) {
|
|
17
11
|
const stripeConnectIntegrationToken = frame.data.settings.find(setting => setting.key === 'stripe_connect_integration_token');
|
|
18
12
|
|
|
@@ -77,94 +71,6 @@ module.exports = {
|
|
|
77
71
|
}
|
|
78
72
|
},
|
|
79
73
|
|
|
80
|
-
/**
|
|
81
|
-
* @deprecated
|
|
82
|
-
*/
|
|
83
|
-
updateMembersEmail: {
|
|
84
|
-
statusCode: 204,
|
|
85
|
-
permissions: {
|
|
86
|
-
method: 'edit'
|
|
87
|
-
},
|
|
88
|
-
data: [
|
|
89
|
-
'email',
|
|
90
|
-
'type'
|
|
91
|
-
],
|
|
92
|
-
async query(frame) {
|
|
93
|
-
const {email, type} = frame.data;
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
// Mapped internally to the newer method of changing emails
|
|
97
|
-
const actionToKeyMapping = {
|
|
98
|
-
supportAddressUpdate: 'members_support_address'
|
|
99
|
-
};
|
|
100
|
-
const edit = {
|
|
101
|
-
key: actionToKeyMapping[type],
|
|
102
|
-
value: email
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
await settingsBREADService.edit([edit], frame.options, null);
|
|
106
|
-
} catch (err) {
|
|
107
|
-
throw new BadRequestError({
|
|
108
|
-
err,
|
|
109
|
-
message: tpl(messages.failedSendingEmail)
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* @todo can get removed, since this is moved to verifyKeyUpdate
|
|
117
|
-
* @deprecated: keep to not break existing email verification links, but remove after 1 - 2 releases
|
|
118
|
-
*/
|
|
119
|
-
validateMembersEmailUpdate: {
|
|
120
|
-
options: [
|
|
121
|
-
'token',
|
|
122
|
-
'action'
|
|
123
|
-
],
|
|
124
|
-
permissions: false,
|
|
125
|
-
validation: {
|
|
126
|
-
options: {
|
|
127
|
-
token: {
|
|
128
|
-
required: true
|
|
129
|
-
},
|
|
130
|
-
action: {
|
|
131
|
-
values: ['supportaddressupdate']
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
async query(frame) {
|
|
136
|
-
// This is something you have to do if you want to use the "framework" with access to the raw req/res
|
|
137
|
-
frame.response = async function (req, res) {
|
|
138
|
-
try {
|
|
139
|
-
const {token, action} = frame.options;
|
|
140
|
-
const updatedEmailAddress = await membersService.settings.getEmailFromToken({token});
|
|
141
|
-
const actionToKeyMapping = {
|
|
142
|
-
supportAddressUpdate: 'members_support_address'
|
|
143
|
-
};
|
|
144
|
-
if (updatedEmailAddress) {
|
|
145
|
-
return models.Settings.edit({
|
|
146
|
-
key: actionToKeyMapping[action],
|
|
147
|
-
value: updatedEmailAddress
|
|
148
|
-
}).then(() => {
|
|
149
|
-
// Redirect to Ghost-Admin settings page
|
|
150
|
-
const adminLink = membersService.settings.getAdminRedirectLink({type: action});
|
|
151
|
-
res.redirect(adminLink);
|
|
152
|
-
});
|
|
153
|
-
} else {
|
|
154
|
-
return Promise.reject(new BadRequestError({
|
|
155
|
-
message: 'Invalid token!'
|
|
156
|
-
}));
|
|
157
|
-
}
|
|
158
|
-
} catch (err) {
|
|
159
|
-
return Promise.reject(new BadRequestError({
|
|
160
|
-
err,
|
|
161
|
-
message: 'Invalid token!'
|
|
162
|
-
}));
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
|
|
168
74
|
disconnectStripeConnectIntegration: {
|
|
169
75
|
statusCode: 204,
|
|
170
76
|
permissions: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const Promise = require('bluebird');
|
|
2
2
|
const _ = require('lodash');
|
|
3
|
-
const {ValidationError
|
|
3
|
+
const {ValidationError} = require('@tryghost/errors');
|
|
4
4
|
const validator = require('@tryghost/validator');
|
|
5
5
|
const tpl = require('@tryghost/tpl');
|
|
6
6
|
|
|
@@ -71,24 +71,5 @@ module.exports = {
|
|
|
71
71
|
if (errors.length) {
|
|
72
72
|
return Promise.reject(errors[0]);
|
|
73
73
|
}
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* @deprecated
|
|
78
|
-
*/
|
|
79
|
-
updateMembersEmail(apiConfig, frame) {
|
|
80
|
-
const {email, type} = frame.data;
|
|
81
|
-
|
|
82
|
-
if (typeof email !== 'string' || !validator.isEmail(email)) {
|
|
83
|
-
throw new BadRequestError({
|
|
84
|
-
message: messages.invalidEmailReceived
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (!type || !['supportAddressUpdate'].includes(type)) {
|
|
89
|
-
throw new BadRequestError({
|
|
90
|
-
message: messages.invalidEmailTypeReceived
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
74
|
}
|
|
94
75
|
};
|
|
@@ -160,7 +160,8 @@ class CommentsService {
|
|
|
160
160
|
id: member
|
|
161
161
|
}, {
|
|
162
162
|
require: true,
|
|
163
|
-
...options
|
|
163
|
+
...options,
|
|
164
|
+
withRelated: ['products']
|
|
164
165
|
});
|
|
165
166
|
|
|
166
167
|
this.checkCommentAccess(memberModel);
|
|
@@ -169,7 +170,8 @@ class CommentsService {
|
|
|
169
170
|
id: post
|
|
170
171
|
}, {
|
|
171
172
|
require: true,
|
|
172
|
-
...options
|
|
173
|
+
...options,
|
|
174
|
+
withRelated: ['tiers']
|
|
173
175
|
});
|
|
174
176
|
|
|
175
177
|
this.checkPostAccess(postModel, memberModel);
|
|
@@ -208,7 +210,8 @@ class CommentsService {
|
|
|
208
210
|
id: member
|
|
209
211
|
}, {
|
|
210
212
|
require: true,
|
|
211
|
-
...options
|
|
213
|
+
...options,
|
|
214
|
+
withRelated: ['products']
|
|
212
215
|
});
|
|
213
216
|
|
|
214
217
|
this.checkCommentAccess(memberModel);
|
|
@@ -229,7 +232,8 @@ class CommentsService {
|
|
|
229
232
|
id: parentComment.get('post_id')
|
|
230
233
|
}, {
|
|
231
234
|
require: true,
|
|
232
|
-
...options
|
|
235
|
+
...options,
|
|
236
|
+
withRelated: ['tiers']
|
|
233
237
|
});
|
|
234
238
|
|
|
235
239
|
this.checkPostAccess(postModel, memberModel);
|
|
@@ -306,10 +306,11 @@ async function sendEmailJob({emailModel, options}) {
|
|
|
306
306
|
const existingBatchCount = await emailModel.related('emailBatches').count('id');
|
|
307
307
|
|
|
308
308
|
if (existingBatchCount === 0) {
|
|
309
|
-
let newBatchCount;
|
|
309
|
+
let newBatchCount = 0;
|
|
310
310
|
|
|
311
311
|
await models.Base.transaction(async (transacting) => {
|
|
312
|
-
|
|
312
|
+
const emailBatches = await createSegmentedEmailBatches({emailModel, options: {transacting}});
|
|
313
|
+
newBatchCount = emailBatches.length;
|
|
313
314
|
});
|
|
314
315
|
|
|
315
316
|
if (newBatchCount === 0) {
|
|
@@ -423,6 +424,8 @@ function partitionMembersBySegment(memberRows, segments) {
|
|
|
423
424
|
* @param {Object} options
|
|
424
425
|
* @param {Object} options.emailModel - instance of Email model
|
|
425
426
|
* @param {Object} options.options - knex options
|
|
427
|
+
*
|
|
428
|
+
* @returns {Promise<string[]>}
|
|
426
429
|
*/
|
|
427
430
|
async function createSegmentedEmailBatches({emailModel, options}) {
|
|
428
431
|
let memberRows = await getEmailMemberRows({emailModel, options});
|
|
@@ -444,11 +447,11 @@ async function createSegmentedEmailBatches({emailModel, options}) {
|
|
|
444
447
|
memberSegment: partition === 'unsegmented' ? null : partition,
|
|
445
448
|
options
|
|
446
449
|
});
|
|
447
|
-
batchIds.push(emailBatchIds);
|
|
450
|
+
batchIds.push(...emailBatchIds);
|
|
448
451
|
}
|
|
449
452
|
} else {
|
|
450
453
|
const emailBatchIds = await createEmailBatches({emailModel, memberRows, options});
|
|
451
|
-
batchIds.push(emailBatchIds);
|
|
454
|
+
batchIds.push(...emailBatchIds);
|
|
452
455
|
}
|
|
453
456
|
|
|
454
457
|
return batchIds;
|
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
/* eslint indent: warn, no-irregular-whitespace: warn */
|
|
2
2
|
const iff = (cond, yes, no) => (cond ? yes : no);
|
|
3
|
+
const sanitizeHtml = require('sanitize-html');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @template {Object.<string, any>} Input
|
|
7
|
+
* @param {Input} obj
|
|
8
|
+
* @param {string[]} [keys]
|
|
9
|
+
* @returns {Input}
|
|
10
|
+
*/
|
|
11
|
+
const sanitizeKeys = (obj, keys) => {
|
|
12
|
+
const sanitized = Object.assign({}, obj);
|
|
13
|
+
const keysToSanitize = keys || Object.keys(obj);
|
|
14
|
+
|
|
15
|
+
for (const key of keysToSanitize) {
|
|
16
|
+
if (typeof sanitized[key] === 'string') {
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
sanitized[key] = sanitizeHtml(sanitized[key], {
|
|
19
|
+
allowedTags: false,
|
|
20
|
+
allowedAttributes: false
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return sanitized;
|
|
26
|
+
};
|
|
27
|
+
|
|
3
28
|
module.exports = ({post, site, newsletter, templateSettings}) => {
|
|
4
29
|
const date = new Date();
|
|
5
30
|
const hasFeatureImageCaption = templateSettings.showFeatureImage && post.feature_image && post.feature_image_caption;
|
|
31
|
+
const cleanPost = sanitizeKeys(post, ['title', 'excerpt', 'html', 'feature_image_alt', 'feature_image_caption']);
|
|
32
|
+
const cleanSite = sanitizeKeys(site, ['title']);
|
|
33
|
+
const cleanNewsletter = sanitizeKeys(newsletter, ['name']);
|
|
6
34
|
return `<!doctype html>
|
|
7
35
|
<html>
|
|
8
36
|
|
|
9
37
|
<head>
|
|
10
38
|
<meta name="viewport" content="width=device-width" />
|
|
11
39
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
12
|
-
<title>${
|
|
40
|
+
<title>${cleanPost.title}</title>
|
|
13
41
|
<style>
|
|
14
42
|
/* -------------------------------------
|
|
15
43
|
GLOBAL RESETS
|
|
@@ -1137,7 +1165,7 @@ ${ templateSettings.showBadge ? `
|
|
|
1137
1165
|
</head>
|
|
1138
1166
|
|
|
1139
1167
|
<body>
|
|
1140
|
-
<span class="preheader">${
|
|
1168
|
+
<span class="preheader">${ cleanPost.excerpt ? cleanPost.excerpt : `${cleanPost.title} – ` }</span>
|
|
1141
1169
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" width="100%">
|
|
1142
1170
|
|
|
1143
1171
|
<!-- Outlook doesn't respect max-width so we need an extra centered table -->
|
|
@@ -1172,24 +1200,24 @@ ${ templateSettings.showBadge ? `
|
|
|
1172
1200
|
<tr>
|
|
1173
1201
|
<td class="${templateSettings.showHeaderTitle ? `site-info-bordered` : `site-info`}" width="100%" align="center">
|
|
1174
1202
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
|
1175
|
-
${ templateSettings.showHeaderIcon &&
|
|
1203
|
+
${ templateSettings.showHeaderIcon && cleanSite.iconUrl ? `
|
|
1176
1204
|
<tr>
|
|
1177
|
-
<td class="site-icon"><a href="${
|
|
1205
|
+
<td class="site-icon"><a href="${cleanSite.url}"><img src="${cleanSite.iconUrl}" alt="${cleanSite.title}" border="0"></a></td>
|
|
1178
1206
|
</tr>
|
|
1179
1207
|
` : ``}
|
|
1180
1208
|
${ templateSettings.showHeaderTitle ? `
|
|
1181
1209
|
<tr>
|
|
1182
|
-
<td class="site-url ${!templateSettings.showHeaderName ? 'site-url-bottom-padding' : ''}"><div style="width: 100% !important;"><a href="${
|
|
1210
|
+
<td class="site-url ${!templateSettings.showHeaderName ? 'site-url-bottom-padding' : ''}"><div style="width: 100% !important;"><a href="${cleanSite.url}" class="site-title">${cleanSite.title}</a></div></td>
|
|
1183
1211
|
</tr>
|
|
1184
1212
|
` : ``}
|
|
1185
1213
|
${ templateSettings.showHeaderName && templateSettings.showHeaderTitle ? `
|
|
1186
1214
|
<tr>
|
|
1187
|
-
<td class="site-url site-url-bottom-padding"><div style="width: 100% !important;"><a href="${
|
|
1215
|
+
<td class="site-url site-url-bottom-padding"><div style="width: 100% !important;"><a href="${cleanSite.url}" class="site-subtitle">${cleanNewsletter.name}</a></div></td>
|
|
1188
1216
|
</tr>
|
|
1189
1217
|
` : ``}
|
|
1190
1218
|
${ templateSettings.showHeaderName && !templateSettings.showHeaderTitle ? `
|
|
1191
1219
|
<tr>
|
|
1192
|
-
<td class="site-url site-url-bottom-padding"><div style="width: 100% !important;"><a href="${
|
|
1220
|
+
<td class="site-url site-url-bottom-padding"><div style="width: 100% !important;"><a href="${cleanSite.url}" class="site-title">${cleanNewsletter.name}</a></div></td>
|
|
1193
1221
|
</tr>
|
|
1194
1222
|
` : ``}
|
|
1195
1223
|
|
|
@@ -1201,7 +1229,7 @@ ${ templateSettings.showBadge ? `
|
|
|
1201
1229
|
|
|
1202
1230
|
<tr>
|
|
1203
1231
|
<td class="post-title ${templateSettings.titleFontCategory === 'serif' ? `post-title-serif` : `` } ${templateSettings.titleAlignment === 'left' ? `post-title-left` : ``}">
|
|
1204
|
-
<a href="${
|
|
1232
|
+
<a href="${cleanPost.url}" class="post-title-link ${templateSettings.titleAlignment === 'left' ? `post-title-link-left` : ``}">${cleanPost.title}</a>
|
|
1205
1233
|
</td>
|
|
1206
1234
|
</tr>
|
|
1207
1235
|
<tr>
|
|
@@ -1209,28 +1237,28 @@ ${ templateSettings.showBadge ? `
|
|
|
1209
1237
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
|
|
1210
1238
|
<tr>
|
|
1211
1239
|
<td class="post-meta ${templateSettings.titleAlignment === 'left' ? `post-meta-left` : ``}">
|
|
1212
|
-
By ${
|
|
1213
|
-
${
|
|
1214
|
-
<a href="${
|
|
1240
|
+
By ${cleanPost.authors} –
|
|
1241
|
+
${cleanPost.published_at} –
|
|
1242
|
+
<a href="${cleanPost.url}" class="view-online-link">View online →</a>
|
|
1215
1243
|
</td>
|
|
1216
1244
|
</tr>
|
|
1217
1245
|
</table>
|
|
1218
1246
|
</td>
|
|
1219
1247
|
</tr>
|
|
1220
|
-
${ templateSettings.showFeatureImage &&
|
|
1248
|
+
${ templateSettings.showFeatureImage && cleanPost.feature_image ? `
|
|
1221
1249
|
<tr>
|
|
1222
|
-
<td class="feature-image ${hasFeatureImageCaption ? 'feature-image-with-caption' : ''}"><img src="${
|
|
1250
|
+
<td class="feature-image ${hasFeatureImageCaption ? 'feature-image-with-caption' : ''}"><img src="${cleanPost.feature_image}"${cleanPost.feature_image_width ? ` width="${cleanPost.feature_image_width}"` : ''}${cleanPost.feature_image_alt ? ` alt="${cleanPost.feature_image_alt}"` : ''}></td>
|
|
1223
1251
|
</tr>
|
|
1224
1252
|
` : ``}
|
|
1225
1253
|
${ hasFeatureImageCaption ? `
|
|
1226
1254
|
<tr>
|
|
1227
|
-
<td class="feature-image-caption" align="center">${
|
|
1255
|
+
<td class="feature-image-caption" align="center">${cleanPost.feature_image_caption}</td>
|
|
1228
1256
|
</tr>
|
|
1229
1257
|
` : ``}
|
|
1230
1258
|
<tr>
|
|
1231
1259
|
<td class="${(templateSettings.bodyFontCategory === 'sans_serif') ? `post-content-sans-serif` : `post-content` }">
|
|
1232
1260
|
<!-- POST CONTENT START -->
|
|
1233
|
-
${
|
|
1261
|
+
${cleanPost.html}
|
|
1234
1262
|
<!-- POST CONTENT END -->
|
|
1235
1263
|
</td>
|
|
1236
1264
|
</tr>
|
|
@@ -1245,7 +1273,7 @@ ${ templateSettings.showBadge ? `
|
|
|
1245
1273
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-top: 40px; padding-bottom: 30px;">
|
|
1246
1274
|
${iff(!!templateSettings.footerContent, `<tr><td class="footer">${templateSettings.footerContent}</td></tr>`, '')}
|
|
1247
1275
|
<tr>
|
|
1248
|
-
<td class="footer">${
|
|
1276
|
+
<td class="footer">${cleanSite.title} © ${date.getFullYear()} – <a href="%recipient.unsubscribe_url%">Unsubscribe</a></td>
|
|
1249
1277
|
</tr>
|
|
1250
1278
|
|
|
1251
1279
|
${ templateSettings.showBadge ? `
|
|
@@ -36,7 +36,9 @@ const deleteSession = async function (req, res) {
|
|
|
36
36
|
res.writeHead(204);
|
|
37
37
|
res.end();
|
|
38
38
|
} catch (err) {
|
|
39
|
-
res.writeHead(err.statusCode
|
|
39
|
+
res.writeHead(err.statusCode, {
|
|
40
|
+
'Content-Type': 'text/plain;charset=UTF-8'
|
|
41
|
+
});
|
|
40
42
|
res.end(err.message);
|
|
41
43
|
}
|
|
42
44
|
};
|
|
@@ -130,7 +132,9 @@ const updateMemberData = async function (req, res) {
|
|
|
130
132
|
res.json(null);
|
|
131
133
|
}
|
|
132
134
|
} catch (err) {
|
|
133
|
-
res.writeHead(err.statusCode
|
|
135
|
+
res.writeHead(err.statusCode, {
|
|
136
|
+
'Content-Type': 'text/plain;charset=UTF-8'
|
|
137
|
+
});
|
|
134
138
|
res.end(err.message);
|
|
135
139
|
}
|
|
136
140
|
};
|
|
@@ -14,15 +14,13 @@ const notImplemented = function (req, res, next) {
|
|
|
14
14
|
return next();
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// @NOTE: integrations have limited access
|
|
17
|
+
// @NOTE: integrations & staff tokens have limited access to the API
|
|
18
18
|
const allowlisted = {
|
|
19
|
-
// @NOTE: stable
|
|
20
19
|
site: ['GET'],
|
|
21
20
|
posts: ['GET', 'PUT', 'DELETE', 'POST'],
|
|
22
21
|
pages: ['GET', 'PUT', 'DELETE', 'POST'],
|
|
23
22
|
images: ['POST'],
|
|
24
23
|
webhooks: ['POST', 'PUT', 'DELETE'],
|
|
25
|
-
// @NOTE: experimental
|
|
26
24
|
actions: ['GET'],
|
|
27
25
|
tags: ['GET', 'PUT', 'DELETE', 'POST'],
|
|
28
26
|
labels: ['GET', 'PUT', 'DELETE', 'POST'],
|
|
@@ -65,13 +65,6 @@ module.exports = function apiRoutes() {
|
|
|
65
65
|
router.get('/settings', mw.authAdminApi, http(api.settings.browse));
|
|
66
66
|
router.put('/settings', mw.authAdminApi, http(api.settings.edit));
|
|
67
67
|
router.put('/settings/verifications/', mw.authAdminApi, http(api.settings.verifyKeyUpdate));
|
|
68
|
-
|
|
69
|
-
/** @deprecated This endpoint is part of the old email verification flow for the support email */
|
|
70
|
-
router.get('/settings/members/email', http(api.settings.validateMembersEmailUpdate));
|
|
71
|
-
|
|
72
|
-
/** @deprecated This endpoint is part of the old email verification flow for the support email */
|
|
73
|
-
router.post('/settings/members/email', mw.authAdminApi, http(api.settings.updateMembersEmail));
|
|
74
|
-
|
|
75
68
|
router.del('/settings/stripe/connect', mw.authAdminApi, http(api.settings.disconnectStripeConnectIntegration));
|
|
76
69
|
|
|
77
70
|
// ## Users
|
|
@@ -29,26 +29,13 @@ module.exports = {
|
|
|
29
29
|
})(req, res, next);
|
|
30
30
|
},
|
|
31
31
|
/**
|
|
32
|
-
* block per
|
|
33
|
-
* username === email!
|
|
32
|
+
* block per ip
|
|
34
33
|
*/
|
|
35
34
|
userLogin(req, res, next) {
|
|
36
35
|
return spamPrevention.userLogin().getMiddleware({
|
|
37
36
|
ignoreIP: false,
|
|
38
37
|
key(_req, _res, _next) {
|
|
39
|
-
|
|
40
|
-
return _next(`${_req.body.username}login`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (_req.body.authorizationCode) {
|
|
44
|
-
return _next(`${_req.body.authorizationCode}login`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (_req.body.refresh_token) {
|
|
48
|
-
return _next(`${_req.body.refresh_token}login`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return _next();
|
|
38
|
+
return _next('user_login');
|
|
52
39
|
}
|
|
53
40
|
})(req, res, next);
|
|
54
41
|
},
|