ghost 5.14.2 → 5.16.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-adapter-manager-5.16.0.tgz +0 -0
- package/components/tryghost-api-framework-5.16.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.16.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.16.0.tgz +0 -0
- package/components/tryghost-constants-5.16.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.16.0.tgz +0 -0
- package/components/tryghost-domain-events-5.16.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.16.0.tgz +0 -0
- package/components/{tryghost-email-analytics-service-5.14.2.tgz → tryghost-email-analytics-service-5.16.0.tgz} +0 -0
- package/components/tryghost-email-content-generator-5.16.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.16.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.16.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.16.0.tgz +0 -0
- package/components/{tryghost-job-manager-5.14.2.tgz → tryghost-job-manager-5.16.0.tgz} +0 -0
- package/components/tryghost-link-redirects-5.16.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.16.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.16.0.tgz +0 -0
- package/components/tryghost-magic-link-5.16.0.tgz +0 -0
- package/components/{tryghost-mailgun-client-5.14.2.tgz → tryghost-mailgun-client-5.16.0.tgz} +0 -0
- package/components/tryghost-member-analytics-service-5.16.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.16.0.tgz +0 -0
- package/components/tryghost-member-events-5.16.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.16.0.tgz +0 -0
- package/components/tryghost-members-api-5.16.0.tgz +0 -0
- package/components/tryghost-members-csv-5.16.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.16.0.tgz +0 -0
- package/components/tryghost-members-importer-5.16.0.tgz +0 -0
- package/components/tryghost-members-offers-5.16.0.tgz +0 -0
- package/components/{tryghost-members-payments-5.14.2.tgz → tryghost-members-payments-5.16.0.tgz} +0 -0
- package/components/{tryghost-members-ssr-5.14.2.tgz → tryghost-members-ssr-5.16.0.tgz} +0 -0
- package/components/tryghost-members-stripe-service-5.16.0.tgz +0 -0
- package/components/tryghost-minifier-5.16.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.16.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.16.0.tgz +0 -0
- package/components/{tryghost-mw-error-handler-5.14.2.tgz → tryghost-mw-error-handler-5.16.0.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.16.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.16.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.16.0.tgz +0 -0
- package/components/{tryghost-oembed-service-5.14.2.tgz → tryghost-oembed-service-5.16.0.tgz} +0 -0
- package/components/{tryghost-package-json-5.14.2.tgz → tryghost-package-json-5.16.0.tgz} +0 -0
- package/components/tryghost-referrers-5.16.0.tgz +0 -0
- package/components/{tryghost-security-5.14.2.tgz → tryghost-security-5.16.0.tgz} +0 -0
- package/components/tryghost-session-service-5.16.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.16.0.tgz +0 -0
- package/components/tryghost-staff-service-5.16.0.tgz +0 -0
- package/components/tryghost-stats-service-5.16.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.16.0.tgz +0 -0
- package/components/{tryghost-verification-trigger-5.14.2.tgz → tryghost-verification-trigger-5.16.0.tgz} +0 -0
- package/components/{tryghost-version-notifications-data-service-5.14.2.tgz → tryghost-version-notifications-data-service-5.16.0.tgz} +0 -0
- package/content/themes/casper/default.hbs +2 -2
- package/core/boot.js +10 -3
- package/core/built/admin/assets/{chunk.143.a5ef705453da0d58b75a.js → chunk.143.a281d460e6059cd0210a.js} +21 -21
- package/core/built/admin/assets/{chunk.174.2edaa0869bfc2d88cf90.js → chunk.174.e1e89637eab79fdd5c5d.js} +68 -68
- package/core/built/admin/assets/{chunk.178.579a6edabc75a2d7378f.js → chunk.178.68eca2346b6f343991e7.js} +4 -4
- package/core/built/admin/assets/{chunk.579.2de3f4300baf25f9a0db.js → chunk.579.d14c3688558f34afeb3e.js} +8872 -7851
- package/core/built/admin/assets/{chunk.579.2de3f4300baf25f9a0db.js.LICENSE.txt → chunk.579.d14c3688558f34afeb3e.js.LICENSE.txt} +45 -0
- package/core/built/admin/assets/fonts/{Inter.ttf → Inter-e19174fb2c0e19b1fa67492a07886c75.ttf} +0 -0
- package/core/built/admin/assets/ghost-6491d134c450ca676911ea17e16cd7d4.css +1 -0
- package/core/built/admin/assets/ghost-dark-297ab2fcf4cadd1c950b84089a38c5e2.css +1 -0
- package/core/built/admin/assets/{ghost-8919656440ad4617a07bb31069b1f71b.js → ghost-f2bf99b26aee662cf37fe59f87b1ceb5.js} +593 -511
- package/core/built/admin/assets/img/{amp.svg → amp-d7b72aae3315fda95921fb575dfca100.svg} +0 -0
- package/core/built/admin/assets/img/{disqus.svg → disqus-43503a3fa4f38dc8c61c7358b811f343.svg} +0 -0
- package/core/built/admin/assets/img/{favicon.ico → favicon-a9c6dbdcdc3ae568f4e0dad92149a0e3.ico} +0 -0
- package/core/built/admin/assets/img/{github.svg → github-c3a739c59df26fed12c10ffb00b33bd4.svg} +0 -0
- package/core/built/admin/assets/img/{google-docs.svg → google-docs-1e42cc272fc088da49e4b0ddfb01b006.svg} +0 -0
- package/core/built/admin/assets/img/{mailchimp.svg → mailchimp-f22b1e130aac764965b9306d7265a6b2.svg} +0 -0
- package/core/built/admin/assets/img/marketing/analytics-1-aa2d72c4e7347a3cb5666d07916b92aa.jpg +0 -0
- package/core/built/admin/assets/img/marketing/analytics-2-389d53f80041ff98111cce79facf66b8.jpg +0 -0
- package/core/built/admin/assets/img/{patreon.svg → patreon-b19a5e6418a72977a16b30039d374d04.svg} +0 -0
- package/core/built/admin/assets/img/{paypal.svg → paypal-38e9448ce7549ea4caf8e7753ae661d6.svg} +0 -0
- package/core/built/admin/assets/img/{twitter.svg → twitter-7a7a0ba12d9b5bfb8a2058764a827c31.svg} +0 -0
- package/core/built/admin/assets/img/{typeform.svg → typeform-9f23f8712d776a7515594676285266f5.svg} +0 -0
- package/core/built/admin/assets/img/{unsplash.svg → unsplash-5b329eef0b11447b4117eaf817ebad6f.svg} +0 -0
- package/core/built/admin/assets/img/{zapier.svg → zapier-bf93bc440a3fd43b73489a63c215cdc7.svg} +0 -0
- package/core/built/admin/assets/img/{zapier-logo.svg → zapier-logo-a125f24313dfe01ef49af01fc90061fb.svg} +0 -0
- package/core/built/admin/assets/{vendor-eb76d0236a09b8b6f44675dba45b1fc6.js → vendor-b2375e2f383cbc3fd73340c4b656c993.js} +59 -47
- package/core/built/admin/assets/videos/logo-loader.mp4 +0 -0
- package/core/built/admin/index.html +11 -8
- package/core/frontend/helpers/search.js +1 -15
- package/core/frontend/src/member-attribution/member-attribution.js +64 -3
- package/core/frontend/web/site.js +10 -7
- package/core/server/api/endpoints/index.js +4 -0
- package/core/server/api/endpoints/links.js +25 -0
- package/core/server/api/endpoints/posts.js +2 -1
- package/core/server/api/endpoints/redirects.js +6 -8
- package/core/server/api/endpoints/stats.js +24 -0
- package/core/server/api/endpoints/utils/permissions.js +2 -16
- package/core/server/api/endpoints/utils/serializers/input/pages.js +5 -5
- package/core/server/api/endpoints/utils/serializers/input/posts.js +13 -8
- package/core/server/api/endpoints/utils/serializers/input/settings.js +1 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/activity-feed-events.js +51 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +10 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +1 -1
- package/core/server/api/endpoints/utils/validators/input/pages.js +24 -9
- package/core/server/api/endpoints/utils/validators/input/posts.js +24 -9
- package/core/server/data/exporter/table-lists.js +4 -1
- package/core/server/data/migrations/utils/settings.js +1 -3
- package/core/server/data/migrations/versions/5.15/2022-09-12-16-10-add-posts-lexical-column.js +8 -0
- package/core/server/data/migrations/versions/5.15/2022-09-14-12-46-add-email-track-clicks-setting.js +8 -0
- package/core/server/data/migrations/versions/5.15/2022-09-16-08-22-add-post-revisions-table.js +9 -0
- package/core/server/data/migrations/versions/5.16/2022-09-19-09-04-add-link-redirects-table.js +10 -0
- package/core/server/data/migrations/versions/5.16/2022-09-19-09-05-add-members-link-click-events-table.js +8 -0
- package/core/server/data/migrations/versions/5.16/2022-09-19-17-44-add-referrer-columns-to-member-events-table.js +21 -0
- package/core/server/data/migrations/versions/5.16/2022-09-19-17-44-add-referrer-columns-to-subscription-events-table.js +21 -0
- package/core/server/data/schema/default-settings/default-settings.json +8 -0
- package/core/server/data/schema/schema.js +29 -1
- package/core/server/lib/lexical.js +12 -0
- package/core/server/models/base/plugins/user-type.js +4 -6
- package/core/server/models/link-redirect.js +65 -0
- package/core/server/models/member-link-click-event.js +26 -0
- package/core/server/models/post-revision.js +35 -0
- package/core/server/models/post.js +90 -9
- package/core/server/services/bulk-email/bulk-email-processor.js +9 -10
- package/core/server/services/{redirects → custom-redirects}/api.js +0 -0
- package/core/server/services/{redirects → custom-redirects}/index.js +0 -0
- package/core/server/services/{redirects → custom-redirects}/utils.js +0 -0
- package/core/server/services/{redirects → custom-redirects}/validation.js +0 -0
- package/core/server/services/explore/service.js +5 -3
- package/core/server/services/link-redirection/LinkRedirectRepository.js +88 -0
- package/core/server/services/link-redirection/index.js +31 -0
- package/core/server/services/link-tracking/LinkClickRepository.js +69 -0
- package/core/server/services/link-tracking/PostLinkRepository.js +62 -0
- package/core/server/services/link-tracking/index.js +48 -0
- package/core/server/services/mega/email-preview.js +7 -0
- package/core/server/services/mega/mega.js +1 -1
- package/core/server/services/mega/post-email-serializer.js +101 -27
- package/core/server/services/member-attribution/index.js +12 -5
- package/core/server/services/members/api.js +1 -2
- package/core/server/services/permissions/index.js +1 -2
- package/core/server/services/posts/posts-service.js +7 -16
- package/core/server/services/posts/stats/post-stats.js +35 -0
- package/core/server/services/staff/index.js +10 -1
- package/core/server/services/url/config.js +2 -0
- package/core/server/web/admin/app.js +8 -2
- package/core/server/web/api/endpoints/admin/routes.js +5 -0
- package/core/shared/config/defaults.json +7 -7
- package/core/shared/config/overrides.json +3 -2
- package/core/shared/labs.js +4 -2
- package/package.json +115 -107
- package/yarn.lock +828 -414
- package/components/tryghost-adapter-manager-5.14.2.tgz +0 -0
- package/components/tryghost-api-framework-5.14.2.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.14.2.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.14.2.tgz +0 -0
- package/components/tryghost-constants-5.14.2.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.14.2.tgz +0 -0
- package/components/tryghost-domain-events-5.14.2.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.14.2.tgz +0 -0
- package/components/tryghost-email-content-generator-5.14.2.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.14.2.tgz +0 -0
- package/components/tryghost-extract-api-key-5.14.2.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.14.2.tgz +0 -0
- package/components/tryghost-magic-link-5.14.2.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.14.2.tgz +0 -0
- package/components/tryghost-member-attribution-5.14.2.tgz +0 -0
- package/components/tryghost-member-events-5.14.2.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.14.2.tgz +0 -0
- package/components/tryghost-members-api-5.14.2.tgz +0 -0
- package/components/tryghost-members-csv-5.14.2.tgz +0 -0
- package/components/tryghost-members-events-service-5.14.2.tgz +0 -0
- package/components/tryghost-members-importer-5.14.2.tgz +0 -0
- package/components/tryghost-members-offers-5.14.2.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.14.2.tgz +0 -0
- package/components/tryghost-minifier-5.14.2.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.14.2.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.14.2.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.14.2.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.14.2.tgz +0 -0
- package/components/tryghost-mw-vhost-5.14.2.tgz +0 -0
- package/components/tryghost-session-service-5.14.2.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.14.2.tgz +0 -0
- package/components/tryghost-staff-service-5.14.2.tgz +0 -0
- package/components/tryghost-update-check-service-5.14.2.tgz +0 -0
- package/core/built/admin/assets/ghost-40adc8310dcdd0be163cbf7b9d89c59a.css +0 -1
- package/core/built/admin/assets/ghost-dark-13b669d50f494edf24d832b32ece2177.css +0 -1
- package/core/server/services/permissions/public.js +0 -76
|
@@ -4,7 +4,8 @@ const {ValidationError} = require('@tryghost/errors');
|
|
|
4
4
|
const tpl = require('@tryghost/tpl');
|
|
5
5
|
|
|
6
6
|
const messages = {
|
|
7
|
-
invalidVisibilityFilter: 'Invalid filter in visibility_filter property'
|
|
7
|
+
invalidVisibilityFilter: 'Invalid filter in visibility_filter property',
|
|
8
|
+
onlySingleContentSource: 'It\'s only possible to save mobiledoc or lexical properties, not both'
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
const validateVisibility = async function (frame) {
|
|
@@ -33,15 +34,29 @@ const validateVisibility = async function (frame) {
|
|
|
33
34
|
}
|
|
34
35
|
};
|
|
35
36
|
|
|
37
|
+
const validateSingleContentSource = async function (frame) {
|
|
38
|
+
if (!frame.data.posts?.[0]) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const [post] = frame.data.posts;
|
|
43
|
+
if (post.mobiledoc && post.lexical) {
|
|
44
|
+
return Promise.reject(new ValidationError({
|
|
45
|
+
message: tpl(messages.onlySingleContentSource),
|
|
46
|
+
property: 'lexical'
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
36
51
|
module.exports = {
|
|
37
|
-
add(apiConfig, frame) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
async add(apiConfig, frame) {
|
|
53
|
+
await jsonSchema.validate(...arguments);
|
|
54
|
+
await validateVisibility(frame);
|
|
55
|
+
await validateSingleContentSource(frame);
|
|
41
56
|
},
|
|
42
|
-
edit(apiConfig, frame) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
57
|
+
async edit(apiConfig, frame) {
|
|
58
|
+
await jsonSchema.validate(...arguments);
|
|
59
|
+
await validateVisibility(frame);
|
|
60
|
+
await validateSingleContentSource(frame);
|
|
46
61
|
}
|
|
47
62
|
};
|
|
@@ -21,6 +21,7 @@ const BACKUP_TABLES = [
|
|
|
21
21
|
'tokens',
|
|
22
22
|
'sessions',
|
|
23
23
|
'mobiledoc_revisions',
|
|
24
|
+
'post_revisions',
|
|
24
25
|
'email_batches',
|
|
25
26
|
'email_recipients',
|
|
26
27
|
'members_cancel_events',
|
|
@@ -37,7 +38,9 @@ const BACKUP_TABLES = [
|
|
|
37
38
|
'comments',
|
|
38
39
|
'comment_likes',
|
|
39
40
|
'comment_reports',
|
|
40
|
-
'jobs'
|
|
41
|
+
'jobs',
|
|
42
|
+
'link_redirects',
|
|
43
|
+
'members_link_click_events'
|
|
41
44
|
];
|
|
42
45
|
|
|
43
46
|
// NOTE: exposing only tables which are going to be included in a "default" export file
|
package/core/server/data/migrations/versions/5.15/2022-09-16-08-22-add-post-revisions-table.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('post_revisions', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
post_id: {type: 'string', maxlength: 24, nullable: false, index: true},
|
|
6
|
+
lexical: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
7
|
+
created_at_ts: {type: 'bigInteger', nullable: false},
|
|
8
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
9
|
+
});
|
package/core/server/data/migrations/versions/5.16/2022-09-19-09-04-add-link-redirects-table.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('link_redirects', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
from: {type: 'string', maxlength: 2000, nullable: false},
|
|
6
|
+
to: {type: 'string', maxlength: 2000, nullable: false},
|
|
7
|
+
post_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'posts.id', setNullDelete: true},
|
|
8
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
9
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
10
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('members_link_click_events', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
6
|
+
link_id: {type: 'string', maxlength: 24, nullable: false, references: 'link_redirects.id', cascadeDelete: true},
|
|
7
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
8
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const {createAddColumnMigration, combineNonTransactionalMigrations} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = combineNonTransactionalMigrations(
|
|
4
|
+
createAddColumnMigration('members_created_events', 'referrer_source', {
|
|
5
|
+
type: 'string',
|
|
6
|
+
maxlength: 191,
|
|
7
|
+
nullable: true
|
|
8
|
+
}),
|
|
9
|
+
|
|
10
|
+
createAddColumnMigration('members_created_events', 'referrer_medium', {
|
|
11
|
+
type: 'string',
|
|
12
|
+
maxlength: 191,
|
|
13
|
+
nullable: true
|
|
14
|
+
}),
|
|
15
|
+
|
|
16
|
+
createAddColumnMigration('members_created_events', 'referrer_url', {
|
|
17
|
+
type: 'string',
|
|
18
|
+
maxlength: 2000,
|
|
19
|
+
nullable: true
|
|
20
|
+
})
|
|
21
|
+
);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const {createAddColumnMigration, combineNonTransactionalMigrations} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = combineNonTransactionalMigrations(
|
|
4
|
+
createAddColumnMigration('members_subscription_created_events', 'referrer_source', {
|
|
5
|
+
type: 'string',
|
|
6
|
+
maxlength: 191,
|
|
7
|
+
nullable: true
|
|
8
|
+
}),
|
|
9
|
+
|
|
10
|
+
createAddColumnMigration('members_subscription_created_events', 'referrer_medium', {
|
|
11
|
+
type: 'string',
|
|
12
|
+
maxlength: 191,
|
|
13
|
+
nullable: true
|
|
14
|
+
}),
|
|
15
|
+
|
|
16
|
+
createAddColumnMigration('members_subscription_created_events', 'referrer_url', {
|
|
17
|
+
type: 'string',
|
|
18
|
+
maxlength: 2000,
|
|
19
|
+
nullable: true
|
|
20
|
+
})
|
|
21
|
+
);
|
|
@@ -360,6 +360,14 @@
|
|
|
360
360
|
},
|
|
361
361
|
"type": "boolean"
|
|
362
362
|
},
|
|
363
|
+
"email_track_clicks": {
|
|
364
|
+
"defaultValue": "true",
|
|
365
|
+
"validations": {
|
|
366
|
+
"isEmpty": false,
|
|
367
|
+
"isIn": [["true", "false"]]
|
|
368
|
+
},
|
|
369
|
+
"type": "boolean"
|
|
370
|
+
},
|
|
363
371
|
"email_verification_required": {
|
|
364
372
|
"defaultValue": "false",
|
|
365
373
|
"validations": {
|
|
@@ -45,6 +45,7 @@ module.exports = {
|
|
|
45
45
|
title: {type: 'string', maxlength: 2000, nullable: false, validations: {isLength: {max: 255}}},
|
|
46
46
|
slug: {type: 'string', maxlength: 191, nullable: false},
|
|
47
47
|
mobiledoc: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
48
|
+
lexical: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
48
49
|
html: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
49
50
|
comment_id: {type: 'string', maxlength: 50, nullable: true},
|
|
50
51
|
plaintext: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
@@ -386,6 +387,13 @@ module.exports = {
|
|
|
386
387
|
created_at_ts: {type: 'bigInteger', nullable: false},
|
|
387
388
|
created_at: {type: 'dateTime', nullable: false}
|
|
388
389
|
},
|
|
390
|
+
post_revisions: {
|
|
391
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
392
|
+
post_id: {type: 'string', maxlength: 24, nullable: false, index: true},
|
|
393
|
+
lexical: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
394
|
+
created_at_ts: {type: 'bigInteger', nullable: false},
|
|
395
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
396
|
+
},
|
|
389
397
|
members: {
|
|
390
398
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
391
399
|
uuid: {type: 'string', maxlength: 36, nullable: true, unique: true, validations: {isUUID: true}},
|
|
@@ -489,6 +497,9 @@ module.exports = {
|
|
|
489
497
|
}
|
|
490
498
|
},
|
|
491
499
|
attribution_url: {type: 'string', maxlength: 2000, nullable: true},
|
|
500
|
+
referrer_source: {type: 'string', maxlength: 191, nullable: true},
|
|
501
|
+
referrer_medium: {type: 'string', maxlength: 191, nullable: true},
|
|
502
|
+
referrer_url: {type: 'string', maxlength: 2000, nullable: true},
|
|
492
503
|
source: {
|
|
493
504
|
type: 'string', maxlength: 50, nullable: false, validations: {
|
|
494
505
|
isIn: [['member', 'import', 'system', 'api', 'admin']]
|
|
@@ -626,7 +637,10 @@ module.exports = {
|
|
|
626
637
|
isIn: [['url', 'post', 'page', 'author', 'tag']]
|
|
627
638
|
}
|
|
628
639
|
},
|
|
629
|
-
attribution_url: {type: 'string', maxlength: 2000, nullable: true}
|
|
640
|
+
attribution_url: {type: 'string', maxlength: 2000, nullable: true},
|
|
641
|
+
referrer_source: {type: 'string', maxlength: 191, nullable: true},
|
|
642
|
+
referrer_medium: {type: 'string', maxlength: 191, nullable: true},
|
|
643
|
+
referrer_url: {type: 'string', maxlength: 2000, nullable: true}
|
|
630
644
|
},
|
|
631
645
|
offer_redemptions: {
|
|
632
646
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
@@ -821,5 +835,19 @@ module.exports = {
|
|
|
821
835
|
finished_at: {type: 'dateTime', nullable: true},
|
|
822
836
|
created_at: {type: 'dateTime', nullable: false},
|
|
823
837
|
updated_at: {type: 'dateTime', nullable: true}
|
|
838
|
+
},
|
|
839
|
+
link_redirects: {
|
|
840
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
841
|
+
from: {type: 'string', maxlength: 2000, nullable: false},
|
|
842
|
+
to: {type: 'string', maxlength: 2000, nullable: false},
|
|
843
|
+
post_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'posts.id', setNullDelete: true},
|
|
844
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
845
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
846
|
+
},
|
|
847
|
+
members_link_click_events: {
|
|
848
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
849
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
850
|
+
link_id: {type: 'string', maxlength: 24, nullable: false, references: 'link_redirects.id', cascadeDelete: true},
|
|
851
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
824
852
|
}
|
|
825
853
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
let lexicalHtmlRenderer;
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
get lexicalHtmlRenderer() {
|
|
5
|
+
if (!lexicalHtmlRenderer) {
|
|
6
|
+
const LexicalHtmlRenderer = require('@tryghost/kg-lexical-html-renderer');
|
|
7
|
+
lexicalHtmlRenderer = new LexicalHtmlRenderer();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return lexicalHtmlRenderer;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -39,13 +39,11 @@ module.exports = function (Bookshelf) {
|
|
|
39
39
|
/**
|
|
40
40
|
* @NOTE:
|
|
41
41
|
*
|
|
42
|
-
* This is a dirty
|
|
43
|
-
*
|
|
42
|
+
* This is a dirty fix until we get rid of all the x_by columns
|
|
43
|
+
* @deprecated x_by columns are deprecated as of v1.0 - instead we should use the actions table
|
|
44
|
+
* see https://github.com/TryGhost/Ghost/issues/10286.
|
|
44
45
|
*
|
|
45
|
-
* We return the owner ID '1' in case an integration updates or creates
|
|
46
|
-
* resources. v0.1 will continue to use the `x_by` columns. v0.1 does not support integrations.
|
|
47
|
-
* API v2 will introduce a new feature to solve inserting/updating resources
|
|
48
|
-
* from users or integrations. API v2 won't expose `x_by` columns anymore.
|
|
46
|
+
* We return the owner ID '1' in case an integration updates or creates resources.
|
|
49
47
|
*
|
|
50
48
|
* ---
|
|
51
49
|
*
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const ghostBookshelf = require('./base');
|
|
2
|
+
const urlUtils = require('../../shared/url-utils');
|
|
3
|
+
|
|
4
|
+
const LinkRedirect = ghostBookshelf.Model.extend({
|
|
5
|
+
tableName: 'link_redirects',
|
|
6
|
+
|
|
7
|
+
post() {
|
|
8
|
+
return this.belongsTo('Post', 'post_id');
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
formatOnWrite(attrs) {
|
|
12
|
+
if (attrs.to) {
|
|
13
|
+
attrs.to = urlUtils.absoluteToTransformReady(attrs.to);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return attrs;
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
parse() {
|
|
20
|
+
const attrs = ghostBookshelf.Model.prototype.parse.apply(this, arguments);
|
|
21
|
+
|
|
22
|
+
if (attrs.to) {
|
|
23
|
+
attrs.to = urlUtils.transformReadyToAbsolute(attrs.to);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return attrs;
|
|
27
|
+
}
|
|
28
|
+
}, {
|
|
29
|
+
orderDefaultRaw(options) {
|
|
30
|
+
if (options.withRelated && options.withRelated.includes('count.clicks')) {
|
|
31
|
+
return '`count__clicks` DESC, `to` DESC';
|
|
32
|
+
}
|
|
33
|
+
return '`to` DESC';
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
permittedOptions(methodName) {
|
|
37
|
+
let options = ghostBookshelf.Model.permittedOptions.call(this, methodName);
|
|
38
|
+
const validOptions = {
|
|
39
|
+
findAll: ['filter', 'columns', 'withRelated']
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (validOptions[methodName]) {
|
|
43
|
+
options = options.concat(validOptions[methodName]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return options;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
countRelations() {
|
|
50
|
+
return {
|
|
51
|
+
clicks(modelOrCollection) {
|
|
52
|
+
modelOrCollection.query('columns', 'link_redirects.*', (qb) => {
|
|
53
|
+
qb.countDistinct('members_link_click_events.member_id')
|
|
54
|
+
.from('members_link_click_events')
|
|
55
|
+
.whereRaw('link_redirects.id = members_link_click_events.link_id')
|
|
56
|
+
.as('count__clicks');
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
module.exports = {
|
|
64
|
+
LinkRedirect: ghostBookshelf.model('LinkRedirect', LinkRedirect)
|
|
65
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const errors = require('@tryghost/errors');
|
|
2
|
+
const ghostBookshelf = require('./base');
|
|
3
|
+
|
|
4
|
+
const MemberLinkClickEvent = ghostBookshelf.Model.extend({
|
|
5
|
+
tableName: 'members_link_click_events',
|
|
6
|
+
|
|
7
|
+
link() {
|
|
8
|
+
return this.belongsTo('LinkRedirect', 'link_id');
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
member() {
|
|
12
|
+
return this.belongsTo('Member', 'member_id', 'id');
|
|
13
|
+
}
|
|
14
|
+
}, {
|
|
15
|
+
async edit() {
|
|
16
|
+
throw new errors.IncorrectUsageError({message: 'Cannot edit MemberLinkClickEvent'});
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
async destroy() {
|
|
20
|
+
throw new errors.IncorrectUsageError({message: 'Cannot destroy MemberLinkClickEvent'});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
MemberLinkClickEvent: ghostBookshelf.model('MemberLinkClickEvent', MemberLinkClickEvent)
|
|
26
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const ghostBookshelf = require('./base');
|
|
2
|
+
|
|
3
|
+
const PostRevision = ghostBookshelf.Model.extend({
|
|
4
|
+
tableName: 'post_revisions'
|
|
5
|
+
}, {
|
|
6
|
+
permittedOptions(methodName) {
|
|
7
|
+
let options = ghostBookshelf.Model.permittedOptions.call(this, methodName);
|
|
8
|
+
const validOptions = {
|
|
9
|
+
findAll: ['filter', 'columns']
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
if (validOptions[methodName]) {
|
|
13
|
+
options = options.concat(validOptions[methodName]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return options;
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
orderDefaultRaw() {
|
|
20
|
+
return 'created_at_ts DESC';
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
toJSON(unfilteredOptions) {
|
|
24
|
+
const options = PostRevision.filterOptions(unfilteredOptions, 'toJSON');
|
|
25
|
+
const attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);
|
|
26
|
+
|
|
27
|
+
// CASE: only for internal accuracy
|
|
28
|
+
delete attrs.created_at_ts;
|
|
29
|
+
return attrs;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
PostRevision: ghostBookshelf.model('PostRevision', PostRevision)
|
|
35
|
+
};
|
|
@@ -13,6 +13,7 @@ const config = require('../../shared/config');
|
|
|
13
13
|
const settingsCache = require('../../shared/settings-cache');
|
|
14
14
|
const limitService = require('../services/limits');
|
|
15
15
|
const mobiledocLib = require('../lib/mobiledoc');
|
|
16
|
+
const lexicalLib = require('../lib/lexical');
|
|
16
17
|
const relations = require('./relations');
|
|
17
18
|
const urlUtils = require('../../shared/url-utils');
|
|
18
19
|
const {Tag} = require('./tag');
|
|
@@ -25,10 +26,15 @@ const messages = {
|
|
|
25
26
|
expectedPublishedAtInFuture: 'Date must be at least {cannotScheduleAPostBeforeInMinutes} minutes in the future.',
|
|
26
27
|
untitled: '(Untitled)',
|
|
27
28
|
notEnoughPermission: 'You do not have permission to perform this action',
|
|
28
|
-
invalidNewsletter: 'The newsletter parameter doesn\'t match any active newsletter.'
|
|
29
|
+
invalidNewsletter: 'The newsletter parameter doesn\'t match any active newsletter.',
|
|
30
|
+
invalidMobiledocStructure: 'Invalid mobiledoc structure.',
|
|
31
|
+
invalidMobiledocStructureHelp: 'https://ghost.org/docs/publishing/',
|
|
32
|
+
invalidLexicalStructure: 'Invalid lexical structure.',
|
|
33
|
+
invalidLexicalStructureHelp: 'https://ghost.org/docs/publishing/'
|
|
29
34
|
};
|
|
30
35
|
|
|
31
36
|
const MOBILEDOC_REVISIONS_COUNT = 10;
|
|
37
|
+
const POST_REVISIONS_COUNT = 10;
|
|
32
38
|
const ALL_STATUSES = ['published', 'draft', 'scheduled', 'sent'];
|
|
33
39
|
|
|
34
40
|
let Post;
|
|
@@ -90,7 +96,7 @@ Post = ghostBookshelf.Model.extend({
|
|
|
90
96
|
};
|
|
91
97
|
},
|
|
92
98
|
|
|
93
|
-
relationships: ['tags', 'authors', 'mobiledoc_revisions', 'posts_meta', 'tiers'],
|
|
99
|
+
relationships: ['tags', 'authors', 'mobiledoc_revisions', 'post_revisions', 'posts_meta', 'tiers'],
|
|
94
100
|
|
|
95
101
|
// NOTE: look up object, not super nice, but was easy to implement
|
|
96
102
|
relationshipBelongsTo: {
|
|
@@ -128,6 +134,7 @@ Post = ghostBookshelf.Model.extend({
|
|
|
128
134
|
// transform URLs from __GHOST_URL__ to absolute
|
|
129
135
|
[
|
|
130
136
|
'mobiledoc',
|
|
137
|
+
'lexical',
|
|
131
138
|
'html',
|
|
132
139
|
'plaintext',
|
|
133
140
|
'custom_excerpt',
|
|
@@ -156,6 +163,7 @@ Post = ghostBookshelf.Model.extend({
|
|
|
156
163
|
cardTransformers: mobiledocLib.cards
|
|
157
164
|
}
|
|
158
165
|
},
|
|
166
|
+
lexical: 'lexicalToTransformReady',
|
|
159
167
|
html: 'htmlToTransformReady',
|
|
160
168
|
plaintext: 'plaintextToTransformReady',
|
|
161
169
|
custom_excerpt: 'htmlToTransformReady',
|
|
@@ -596,7 +604,7 @@ Post = ghostBookshelf.Model.extend({
|
|
|
596
604
|
});
|
|
597
605
|
}
|
|
598
606
|
|
|
599
|
-
if (!this.get('mobiledoc')) {
|
|
607
|
+
if (!this.get('mobiledoc') && !this.get('lexical')) {
|
|
600
608
|
this.set('mobiledoc', JSON.stringify(mobiledocLib.blankDocument));
|
|
601
609
|
}
|
|
602
610
|
|
|
@@ -610,20 +618,46 @@ Post = ghostBookshelf.Model.extend({
|
|
|
610
618
|
// CASE: ?force_rerender=true passed via Admin API
|
|
611
619
|
// CASE: html is null, but mobiledoc exists (only important for migrations & importing)
|
|
612
620
|
if (
|
|
613
|
-
this.
|
|
614
|
-
|
|
615
|
-
|
|
621
|
+
!this.get('lexical') &&
|
|
622
|
+
(
|
|
623
|
+
this.hasChanged('mobiledoc')
|
|
624
|
+
|| options.force_rerender
|
|
625
|
+
|| (!this.get('html') && (options.migrating || options.importing))
|
|
626
|
+
)
|
|
616
627
|
) {
|
|
617
628
|
try {
|
|
618
629
|
this.set('html', mobiledocLib.mobiledocHtmlRenderer.render(JSON.parse(this.get('mobiledoc'))));
|
|
619
630
|
} catch (err) {
|
|
620
631
|
throw new errors.ValidationError({
|
|
621
|
-
message:
|
|
632
|
+
message: tpl(messages.invalidMobiledocStructure),
|
|
622
633
|
help: 'https://ghost.org/docs/publishing/'
|
|
623
634
|
});
|
|
624
635
|
}
|
|
625
636
|
}
|
|
626
637
|
|
|
638
|
+
// CASE: lexical has changed, generate html
|
|
639
|
+
// CASE: ?force_rerender=true passed via Admin API
|
|
640
|
+
// CASE: html is null, but lexical exists (only important for migrations & importing)
|
|
641
|
+
if (
|
|
642
|
+
!this.get('mobiledoc') &&
|
|
643
|
+
(
|
|
644
|
+
this.hasChanged('lexical')
|
|
645
|
+
|| options.force_rerender
|
|
646
|
+
|| (!this.get('html') && (options.migrating || options.importing))
|
|
647
|
+
)
|
|
648
|
+
) {
|
|
649
|
+
try {
|
|
650
|
+
this.set('html', lexicalLib.lexicalHtmlRenderer.render(this.get('lexical')));
|
|
651
|
+
} catch (err) {
|
|
652
|
+
throw new errors.ValidationError({
|
|
653
|
+
message: tpl(messages.invalidLexicalStructure),
|
|
654
|
+
context: err.message,
|
|
655
|
+
property: 'lexical',
|
|
656
|
+
help: tpl(messages.invalidLexicalStructureHelp)
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
627
661
|
if (this.hasChanged('html') || !this.get('plaintext')) {
|
|
628
662
|
let plaintext;
|
|
629
663
|
|
|
@@ -750,7 +784,7 @@ Post = ghostBookshelf.Model.extend({
|
|
|
750
784
|
}
|
|
751
785
|
|
|
752
786
|
// CASE: Handle mobiledoc backups/revisions. This is a pure database feature.
|
|
753
|
-
if (model.hasChanged('mobiledoc') && !options.importing && !options.migrating) {
|
|
787
|
+
if (model.hasChanged('mobiledoc') && !model.get('lexical') && !options.importing && !options.migrating) {
|
|
754
788
|
ops.push(function updateRevisions() {
|
|
755
789
|
return ghostBookshelf.model('MobiledocRevision')
|
|
756
790
|
.findAll(Object.assign({
|
|
@@ -794,6 +828,39 @@ Post = ghostBookshelf.Model.extend({
|
|
|
794
828
|
});
|
|
795
829
|
}
|
|
796
830
|
|
|
831
|
+
// CASE: Handle post backups/revisions. This is a pure database feature.
|
|
832
|
+
if (model.hasChanged('lexical') && !model.get('mobiledoc') && !options.importing && !options.migrating) {
|
|
833
|
+
ops.push(function updateRevisions() {
|
|
834
|
+
return ghostBookshelf.model('PostRevision')
|
|
835
|
+
.findAll(Object.assign({
|
|
836
|
+
filter: `post_id:${model.id}`,
|
|
837
|
+
columns: ['id']
|
|
838
|
+
}, _.pick(options, 'transacting')))
|
|
839
|
+
.then((revisions) => {
|
|
840
|
+
// Store previous + latest lexical content
|
|
841
|
+
if (!revisions.length && options.method !== 'insert') {
|
|
842
|
+
model.set('post_revisions', [{
|
|
843
|
+
post_id: model.id,
|
|
844
|
+
lexical: model.previous('lexical'),
|
|
845
|
+
created_at_ts: Date.now() - 1
|
|
846
|
+
}, {
|
|
847
|
+
post_id: model.id,
|
|
848
|
+
lexical: model.get('lexical'),
|
|
849
|
+
created_at_ts: Date.now()
|
|
850
|
+
}]);
|
|
851
|
+
} else {
|
|
852
|
+
const revisionsJSON = revisions.toJSON().slice(0, POST_REVISIONS_COUNT - 1);
|
|
853
|
+
|
|
854
|
+
model.set('post_revisions', revisionsJSON.concat([{
|
|
855
|
+
post_id: model.id,
|
|
856
|
+
lexical: model.get('lexical'),
|
|
857
|
+
created_at_ts: Date.now()
|
|
858
|
+
}]));
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
|
|
797
864
|
if (this.get('tiers')) {
|
|
798
865
|
this.set('tiers', this.get('tiers').map(t => ({
|
|
799
866
|
id: t.id
|
|
@@ -831,6 +898,10 @@ Post = ghostBookshelf.Model.extend({
|
|
|
831
898
|
return this.hasMany('MobiledocRevision', 'post_id');
|
|
832
899
|
},
|
|
833
900
|
|
|
901
|
+
post_revisions() {
|
|
902
|
+
return this.hasMany('PostRevision', 'post_id');
|
|
903
|
+
},
|
|
904
|
+
|
|
834
905
|
posts_meta: function postsMeta() {
|
|
835
906
|
return this.hasOne('PostsMeta', 'post_id');
|
|
836
907
|
},
|
|
@@ -901,6 +972,7 @@ Post = ghostBookshelf.Model.extend({
|
|
|
901
972
|
|
|
902
973
|
// CASE: never expose the revisions
|
|
903
974
|
delete attrs.mobiledoc_revisions;
|
|
975
|
+
delete attrs.post_revisions;
|
|
904
976
|
|
|
905
977
|
// If the current column settings allow it...
|
|
906
978
|
if (!options.columns || (options.columns && options.columns.indexOf('primary_tag') > -1)) {
|
|
@@ -974,7 +1046,7 @@ Post = ghostBookshelf.Model.extend({
|
|
|
974
1046
|
return filter;
|
|
975
1047
|
}
|
|
976
1048
|
}, {
|
|
977
|
-
allowedFormats: ['mobiledoc', 'html', 'plaintext'],
|
|
1049
|
+
allowedFormats: ['mobiledoc', 'lexical', 'html', 'plaintext'],
|
|
978
1050
|
|
|
979
1051
|
orderDefaultOptions: function orderDefaultOptions() {
|
|
980
1052
|
return {
|
|
@@ -1273,6 +1345,15 @@ Post = ghostBookshelf.Model.extend({
|
|
|
1273
1345
|
.whereRaw('posts.id = members_subscription_created_events.attribution_id')
|
|
1274
1346
|
.as('count__conversions');
|
|
1275
1347
|
});
|
|
1348
|
+
},
|
|
1349
|
+
clicks(modelOrCollection) {
|
|
1350
|
+
modelOrCollection.query('columns', 'posts.*', (qb) => {
|
|
1351
|
+
qb.countDistinct('members_link_click_events.member_id')
|
|
1352
|
+
.from('members_link_click_events')
|
|
1353
|
+
.join('link_redirects', 'members_link_click_events.link_id', 'link_redirects.id')
|
|
1354
|
+
.whereRaw('posts.id = link_redirects.post_id')
|
|
1355
|
+
.as('count__clicks');
|
|
1356
|
+
});
|
|
1276
1357
|
}
|
|
1277
1358
|
};
|
|
1278
1359
|
}
|
|
@@ -7,14 +7,13 @@ const logging = require('@tryghost/logging');
|
|
|
7
7
|
const models = require('../../models');
|
|
8
8
|
const MailgunClient = require('@tryghost/mailgun-client');
|
|
9
9
|
const sentry = require('../../../shared/sentry');
|
|
10
|
-
const labs = require('../../../shared/labs');
|
|
11
10
|
const debug = require('@tryghost/debug')('mega');
|
|
12
11
|
const postEmailSerializer = require('../mega/post-email-serializer');
|
|
13
12
|
const configService = require('../../../shared/config');
|
|
14
13
|
const settingsCache = require('../../../shared/settings-cache');
|
|
15
14
|
|
|
16
15
|
const messages = {
|
|
17
|
-
error: 'The email service was unable to send
|
|
16
|
+
error: 'The email service received an error from mailgun and was unable to send.'
|
|
18
17
|
};
|
|
19
18
|
|
|
20
19
|
const mailgunClient = new MailgunClient({config: configService, settings: settingsCache});
|
|
@@ -173,10 +172,8 @@ module.exports = {
|
|
|
173
172
|
// Load newsletter data on email
|
|
174
173
|
await emailBatchModel.relations.email.getLazyRelation('newsletter', {require: false, ...knexOptions});
|
|
175
174
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
await emailBatchModel.relations.email.getLazyRelation('post', {require: false, ...knexOptions});
|
|
179
|
-
}
|
|
175
|
+
// Load post data on email - for content gating on paywall
|
|
176
|
+
await emailBatchModel.relations.email.getLazyRelation('post', {require: false, ...knexOptions});
|
|
180
177
|
|
|
181
178
|
// send the email
|
|
182
179
|
const sendResponse = await this.send(emailBatchModel.relations.email.toJSON(), recipientRows, memberSegment);
|
|
@@ -254,11 +251,13 @@ module.exports = {
|
|
|
254
251
|
const response = await mailgunClient.send(emailData, recipientData, replacements);
|
|
255
252
|
debug(`sent message (${Date.now() - startTime}ms)`);
|
|
256
253
|
return response;
|
|
257
|
-
} catch (
|
|
258
|
-
// REF: possible mailgun errors https://documentation.mailgun.com/en/latest/api-intro.html#errors
|
|
254
|
+
} catch (err) {
|
|
259
255
|
let ghostError = new errors.EmailError({
|
|
260
|
-
err
|
|
261
|
-
|
|
256
|
+
err,
|
|
257
|
+
message: tpl(messages.error),
|
|
258
|
+
context: `Mailgun Error ${err.error.status}: ${err.error.details}`,
|
|
259
|
+
// REF: possible mailgun errors https://documentation.mailgun.com/en/latest/api-intro.html#errors
|
|
260
|
+
help: `https://ghost.org/docs/newsletters/#bulk-email-configuration`,
|
|
262
261
|
code: 'BULK_EMAIL_SEND_FAILED'
|
|
263
262
|
});
|
|
264
263
|
|