ghost 4.22.4 → 4.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +39 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/casper.js.map +1 -1
- package/content/themes/casper/assets/built/global.css +1 -1
- package/content/themes/casper/assets/built/global.css.map +1 -1
- 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/global.css +6 -1
- package/content/themes/casper/assets/css/screen.css +32 -216
- package/content/themes/casper/default.hbs +2 -2
- package/content/themes/casper/package.json +3 -2
- package/content/themes/casper/post.hbs +1 -1
- package/content/themes/casper/yarn.lock +173 -123
- package/core/app.js +12 -1
- package/core/boot.js +33 -19
- package/core/bridge.js +10 -10
- package/core/built/assets/ghost-dark-f67240a9636407594be38571c615629c.css +1 -0
- package/core/built/assets/{ghost.min-2e3e64eb258cf424c59c3e308b4bc6e6.js → ghost.min-3441c3282e390002626a2dc1d7586185.js} +544 -619
- package/core/built/assets/ghost.min-ee5bd95a831378b4c8ccefb37d26eac0.css +1 -0
- package/core/built/assets/icons/audio-upload.svg +8 -0
- package/core/built/assets/{vendor.min-c9002845b6c30ac978abdadde9f33d7c.js → vendor.min-6fc912d1248c906f95efad2cb3eebb7d.js} +2656 -2118
- package/core/frontend/apps/amp/lib/helpers/amp_content.js +2 -2
- package/core/frontend/apps/amp/lib/views/amp.hbs +70 -0
- package/core/frontend/apps/private-blogging/index.js +1 -1
- package/core/frontend/helpers/url.js +18 -1
- package/core/frontend/services/apps/index.js +1 -1
- package/core/frontend/services/apps/loader.js +3 -3
- package/core/frontend/services/card-assets/index.js +0 -12
- package/core/frontend/services/card-assets/service.js +22 -21
- package/core/frontend/services/helpers/handlebars.js +1 -1
- package/core/frontend/services/theme-engine/middleware/ensure-active-theme.js +34 -0
- package/core/frontend/services/theme-engine/middleware/index.js +6 -0
- package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +116 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-data.js +9 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +57 -0
- package/core/frontend/src/cards/css/blockquote.css +29 -0
- package/core/frontend/src/cards/css/bookmark.css +7 -0
- package/core/frontend/src/cards/css/button.css +4 -0
- package/core/frontend/src/cards/css/callout.css +23 -15
- package/core/frontend/src/cards/css/gallery.css +13 -3
- package/core/frontend/src/cards/css/toggle.css +36 -16
- package/core/frontend/web/middleware/error-handler.js +93 -0
- package/core/frontend/web/middleware/handle-image-sizes.js +3 -6
- package/core/frontend/web/middleware/index.js +1 -0
- package/core/frontend/web/middleware/serve-public-file.js +25 -8
- package/core/frontend/web/site.js +2 -5
- package/core/server/adapters/scheduling/SchedulingDefault.js +2 -2
- package/core/server/adapters/storage/LocalStorageBase.js +2 -2
- package/core/server/api/canary/db.js +2 -2
- package/core/server/api/canary/media.js +3 -2
- package/core/server/api/canary/oembed.js +16 -1
- package/core/server/api/canary/session.js +1 -1
- package/core/server/api/canary/slugs.js +1 -1
- package/core/server/api/canary/utils/permissions.js +2 -2
- package/core/server/api/canary/utils/serializers/output/config.js +2 -6
- package/core/server/api/v2/db.js +2 -2
- package/core/server/api/v2/session.js +1 -1
- package/core/server/api/v2/slugs.js +1 -1
- package/core/server/api/v2/utils/permissions.js +2 -2
- package/core/server/api/v3/db.js +2 -2
- package/core/server/api/v3/session.js +1 -1
- package/core/server/api/v3/slugs.js +1 -1
- package/core/server/api/v3/utils/permissions.js +2 -2
- package/core/server/data/db/state-manager.js +4 -4
- package/core/server/data/exporter/export-filename.js +1 -1
- package/core/server/data/importer/handlers/json.js +1 -1
- package/core/server/data/importer/import-manager.js +1 -1
- package/core/server/data/importer/importers/data/base.js +1 -1
- package/core/server/data/migrations/utils.js +2 -2
- package/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +1 -0
- package/core/server/data/migrations/versions/3.1/08-add-uuid-values-to-members.js +1 -0
- package/core/server/data/migrations/versions/3.22/02-settings-key-renames.js +2 -0
- package/core/server/data/migrations/versions/3.22/05-migrate-members-subscription-settings.js +3 -0
- package/core/server/data/migrations/versions/3.22/06-migrate-stripe-connect-settings.js +2 -0
- package/core/server/data/migrations/versions/3.23/01-migrate-bulk-email-settings.js +1 -0
- package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -0
- package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -0
- package/core/server/data/migrations/versions/3.38/04-populate-recipient-filter-column.js +2 -0
- package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +2 -0
- package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +4 -0
- package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +5 -0
- package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -0
- package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -0
- package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +2 -1
- package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +1 -0
- package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +3 -0
- package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +1 -0
- package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +2 -0
- package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +59 -0
- package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -0
- package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +1 -0
- package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -0
- package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -0
- package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +1 -0
- package/core/server/data/schema/commands.js +2 -2
- package/core/server/ghost-server.js +2 -2
- package/core/server/lib/image/image-size.js +2 -2
- package/core/server/models/base/listeners.js +2 -2
- package/core/server/models/member-email-change-event.js +2 -2
- package/core/server/models/member-login-event.js +2 -2
- package/core/server/models/member-paid-subscription-event.js +3 -3
- package/core/server/models/member-payment-event.js +3 -3
- package/core/server/models/member-product-event.js +6 -6
- package/core/server/models/member-status-event.js +5 -3
- package/core/server/models/member-subscribe-event.js +9 -3
- package/core/server/models/relations/authors.js +1 -1
- package/core/server/models/settings.js +1 -1
- package/core/server/services/auth/passwordreset.js +1 -1
- package/core/server/services/auth/setup.js +1 -1
- package/core/server/services/email-analytics/jobs/index.js +1 -1
- package/core/server/services/mega/mega.js +6 -4
- package/core/server/services/mega/template.js +31 -12
- package/core/server/services/members/api.js +22 -0
- package/core/server/services/members/config.js +1 -1
- package/core/server/services/members/emails/signup-paid.js +168 -0
- package/core/server/services/members/service.js +6 -2
- package/core/server/services/members/stripe-connect.js +4 -2
- package/core/server/services/nft-oembed.js +6 -1
- package/core/server/services/oembed.js +36 -28
- package/core/server/services/permissions/can-this.js +1 -1
- package/core/server/services/redirects/api.js +20 -25
- package/core/server/services/redirects/index.js +18 -10
- package/core/server/services/redirects/utils.js +14 -0
- package/core/server/services/redirects/validation.js +10 -0
- package/core/server/services/route-settings/default-settings-manager.js +1 -1
- package/core/server/services/route-settings/index.js +40 -17
- package/core/server/services/route-settings/route-settings.js +120 -115
- package/core/server/services/route-settings/settings-loader.js +18 -36
- package/core/server/services/route-settings/yaml-parser.js +1 -1
- package/core/server/services/slack.js +1 -1
- package/core/server/services/themes/activation-bridge.js +3 -3
- package/core/server/services/themes/storage.js +2 -2
- package/core/server/services/twitter-embed.js +80 -0
- package/core/server/services/url/LocalFileCache.js +75 -0
- package/core/server/services/url/UrlService.js +15 -47
- package/core/server/services/url/index.js +17 -4
- package/core/server/services/xmlrpc.js +2 -2
- package/core/server/web/admin/app.js +2 -5
- package/core/server/web/admin/controller.js +35 -12
- package/core/server/web/admin/middleware/redirect-admin-urls.js +15 -0
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/api/canary/admin/app.js +0 -3
- package/core/server/web/api/canary/admin/middleware.js +1 -1
- package/core/server/web/api/canary/content/app.js +0 -3
- package/core/server/web/api/v2/admin/app.js +0 -3
- package/core/server/web/api/v2/admin/middleware.js +1 -1
- package/core/server/web/api/v2/content/app.js +0 -3
- package/core/server/web/api/v3/admin/app.js +0 -3
- package/core/server/web/api/v3/admin/middleware.js +1 -1
- package/core/server/web/api/v3/content/app.js +0 -3
- package/core/server/web/members/app.js +0 -3
- package/core/server/web/oauth/app.js +0 -4
- package/core/server/web/parent/app.js +17 -8
- package/core/server/web/shared/middleware/error-handler.js +57 -162
- package/core/server/web/shared/middleware/index.js +0 -4
- package/core/shared/config/defaults.json +7 -1
- package/core/shared/labs.js +10 -5
- package/core/shared/sentry.js +1 -1
- package/package.json +43 -42
- package/yarn.lock +802 -923
- package/content/themes/casper/assets/js/gallery-card.js +0 -24
- package/core/built/assets/ghost-dark-42cf6e0c730578940ec069bda45aea41.css +0 -1
- package/core/built/assets/ghost.min-fcf6a0738421f86c47c55f20d00c5ba9.css +0 -1
- package/core/frontend/services/theme-engine/middleware.js +0 -209
- package/core/server/web/shared/middleware/maintenance.js +0 -25
|
@@ -11,7 +11,9 @@ const MemberStatusEvent = ghostBookshelf.Model.extend({
|
|
|
11
11
|
customQuery(qb, options) {
|
|
12
12
|
if (options.aggregateStatusCounts) {
|
|
13
13
|
if (options.limit || options.filter) {
|
|
14
|
-
throw new errors.IncorrectUsageError(
|
|
14
|
+
throw new errors.IncorrectUsageError({
|
|
15
|
+
message: 'aggregateStatusCounts does not work when passed a filter or limit'
|
|
16
|
+
});
|
|
15
17
|
}
|
|
16
18
|
const knex = ghostBookshelf.knex;
|
|
17
19
|
return qb.clear('select')
|
|
@@ -46,11 +48,11 @@ const MemberStatusEvent = ghostBookshelf.Model.extend({
|
|
|
46
48
|
return options;
|
|
47
49
|
},
|
|
48
50
|
async edit() {
|
|
49
|
-
throw new errors.IncorrectUsageError('Cannot edit MemberStatusEvent');
|
|
51
|
+
throw new errors.IncorrectUsageError({message: 'Cannot edit MemberStatusEvent'});
|
|
50
52
|
},
|
|
51
53
|
|
|
52
54
|
async destroy() {
|
|
53
|
-
throw new errors.IncorrectUsageError('Cannot destroy MemberStatusEvent');
|
|
55
|
+
throw new errors.IncorrectUsageError({message: 'Cannot destroy MemberStatusEvent'});
|
|
54
56
|
}
|
|
55
57
|
});
|
|
56
58
|
|
|
@@ -11,7 +11,9 @@ const MemberSubscribeEvent = ghostBookshelf.Model.extend({
|
|
|
11
11
|
customQuery(qb, options) {
|
|
12
12
|
if (options.aggregateSubscriptionDeltas) {
|
|
13
13
|
if (options.limit || options.filter) {
|
|
14
|
-
throw new errors.IncorrectUsageError(
|
|
14
|
+
throw new errors.IncorrectUsageError({
|
|
15
|
+
message: 'aggregateSubscriptionDeltas does not work when passed a filter or limit'
|
|
16
|
+
});
|
|
15
17
|
}
|
|
16
18
|
const knex = ghostBookshelf.knex;
|
|
17
19
|
return qb.clear('select')
|
|
@@ -32,11 +34,15 @@ const MemberSubscribeEvent = ghostBookshelf.Model.extend({
|
|
|
32
34
|
return options;
|
|
33
35
|
},
|
|
34
36
|
async edit() {
|
|
35
|
-
throw new errors.IncorrectUsageError(
|
|
37
|
+
throw new errors.IncorrectUsageError({
|
|
38
|
+
message: 'Cannot edit MemberSubscribeEvent'
|
|
39
|
+
});
|
|
36
40
|
},
|
|
37
41
|
|
|
38
42
|
async destroy() {
|
|
39
|
-
throw new errors.IncorrectUsageError(
|
|
43
|
+
throw new errors.IncorrectUsageError({
|
|
44
|
+
message: 'Cannot destroy MemberSubscribeEvent'
|
|
45
|
+
});
|
|
40
46
|
}
|
|
41
47
|
});
|
|
42
48
|
|
|
@@ -324,7 +324,7 @@ module.exports.extendModel = function extendModel(Post, Posts, ghostBookshelf) {
|
|
|
324
324
|
.then(() => response);
|
|
325
325
|
})
|
|
326
326
|
.catch((err) => {
|
|
327
|
-
throw new errors.
|
|
327
|
+
throw new errors.InternalServerError({err: err});
|
|
328
328
|
});
|
|
329
329
|
});
|
|
330
330
|
|
|
@@ -346,7 +346,7 @@ Settings = ghostBookshelf.Model.extend({
|
|
|
346
346
|
);
|
|
347
347
|
|
|
348
348
|
if (validationErrors.length) {
|
|
349
|
-
throw new errors.ValidationError(validationErrors.join('\n'));
|
|
349
|
+
throw new errors.ValidationError({message: validationErrors.join('\n')});
|
|
350
350
|
}
|
|
351
351
|
},
|
|
352
352
|
async labs(model) {
|
|
@@ -148,7 +148,7 @@ function doReset(options, tokenParts, settingsAPI) {
|
|
|
148
148
|
return Promise.reject(err);
|
|
149
149
|
})
|
|
150
150
|
.catch((err) => {
|
|
151
|
-
if (errors.utils.
|
|
151
|
+
if (errors.utils.isGhostError(err)) {
|
|
152
152
|
return Promise.reject(err);
|
|
153
153
|
}
|
|
154
154
|
return Promise.reject(new errors.UnauthorizedError({err: err}));
|
|
@@ -59,7 +59,7 @@ async function setupUser(userData) {
|
|
|
59
59
|
const owner = await models.User.findOne({role: 'Owner', status: 'all'});
|
|
60
60
|
|
|
61
61
|
if (!owner) {
|
|
62
|
-
throw new errors.
|
|
62
|
+
throw new errors.InternalServerError({
|
|
63
63
|
message: tpl(messages.setupUnableToRun)
|
|
64
64
|
});
|
|
65
65
|
}
|
|
@@ -12,7 +12,7 @@ module.exports = {
|
|
|
12
12
|
!hasScheduled &&
|
|
13
13
|
config.get('emailAnalytics') &&
|
|
14
14
|
config.get('backgroundJobs:emailAnalytics') &&
|
|
15
|
-
!process.env.NODE_ENV.
|
|
15
|
+
!process.env.NODE_ENV.startsWith('test')
|
|
16
16
|
) {
|
|
17
17
|
// Don't register email analytics job if we have no emails,
|
|
18
18
|
// processor usage from many sites spinning up threads can be high.
|
|
@@ -131,7 +131,7 @@ const transformEmailRecipientFilter = (emailRecipientFilter, {errorProperty = 'e
|
|
|
131
131
|
// `paid` and `free` were swapped out for NQL filters in 4.5.0, we shouldn't see them here now
|
|
132
132
|
case 'paid':
|
|
133
133
|
case 'free':
|
|
134
|
-
throw new errors.
|
|
134
|
+
throw new errors.InternalServerError({
|
|
135
135
|
message: tpl(messages.unexpectedFilterError, {
|
|
136
136
|
property: errorProperty,
|
|
137
137
|
value: emailRecipientFilter
|
|
@@ -140,7 +140,7 @@ const transformEmailRecipientFilter = (emailRecipientFilter, {errorProperty = 'e
|
|
|
140
140
|
case 'all':
|
|
141
141
|
return 'subscribed:true';
|
|
142
142
|
case 'none':
|
|
143
|
-
throw new errors.
|
|
143
|
+
throw new errors.InternalServerError({
|
|
144
144
|
message: tpl(messages.noneFilterError, {
|
|
145
145
|
property: errorProperty
|
|
146
146
|
})
|
|
@@ -352,7 +352,7 @@ async function sendEmailJob({emailModel, options}) {
|
|
|
352
352
|
error: errorMessage
|
|
353
353
|
}, {patch: true});
|
|
354
354
|
|
|
355
|
-
throw new errors.
|
|
355
|
+
throw new errors.InternalServerError({
|
|
356
356
|
err: error,
|
|
357
357
|
context: tpl(messages.sendEmailRequestFailed)
|
|
358
358
|
});
|
|
@@ -416,7 +416,9 @@ function partitionMembersBySegment(memberRows, segments) {
|
|
|
416
416
|
segmentedMemberRows = memberRows.filter(member => member.status !== 'free');
|
|
417
417
|
memberRows = memberRows.filter(member => member.status === 'free');
|
|
418
418
|
} else {
|
|
419
|
-
throw new errors.ValidationError(
|
|
419
|
+
throw new errors.ValidationError({
|
|
420
|
+
message: tpl(messages.invalidSegment)
|
|
421
|
+
});
|
|
420
422
|
}
|
|
421
423
|
|
|
422
424
|
partitions[memberSegment] = segmentedMemberRows;
|
|
@@ -157,6 +157,16 @@ blockquote {
|
|
|
157
157
|
letter-spacing: -0.2px;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
blockquote.kg-blockquote-alt {
|
|
161
|
+
border-left: 0 none;
|
|
162
|
+
padding-left: 50px;
|
|
163
|
+
padding-right: 50px;
|
|
164
|
+
margin-bottom: 2.5em;
|
|
165
|
+
text-align: center;
|
|
166
|
+
font-size: 1.2em;
|
|
167
|
+
color: #999999;
|
|
168
|
+
}
|
|
169
|
+
|
|
160
170
|
blockquote p {
|
|
161
171
|
margin: 0.8em 0;
|
|
162
172
|
font-size: 1em;
|
|
@@ -557,52 +567,56 @@ figure blockquote p {
|
|
|
557
567
|
padding-bottom: 4px;
|
|
558
568
|
}
|
|
559
569
|
|
|
560
|
-
.kg-card
|
|
570
|
+
.kg-callout-card {
|
|
561
571
|
display: flex;
|
|
562
572
|
margin: 0 0 1.5em 0;
|
|
563
573
|
padding: 20px 28px;
|
|
564
574
|
border-radius: 3px;
|
|
565
575
|
}
|
|
566
576
|
|
|
567
|
-
.kg-card
|
|
577
|
+
.kg-callout-card p {
|
|
568
578
|
margin: 0
|
|
569
579
|
}
|
|
570
580
|
|
|
571
|
-
.kg-card-
|
|
581
|
+
.kg-callout-card-grey {
|
|
572
582
|
background: #eef0f2;
|
|
573
583
|
}
|
|
574
584
|
|
|
575
|
-
.kg-card-
|
|
585
|
+
.kg-callout-card-white {
|
|
576
586
|
background: #fff;
|
|
577
587
|
box-shadow: inset 0 0 0 1px #dddedf;
|
|
578
588
|
}
|
|
579
589
|
|
|
580
|
-
.kg-card-
|
|
590
|
+
.kg-callout-card-blue {
|
|
581
591
|
background: #E9F6FB;
|
|
582
592
|
}
|
|
583
593
|
|
|
584
|
-
.kg-card-
|
|
594
|
+
.kg-callout-card-green {
|
|
585
595
|
background: #E8F8EA;
|
|
586
596
|
}
|
|
587
597
|
|
|
588
|
-
.kg-card-
|
|
598
|
+
.kg-callout-card-yellow {
|
|
589
599
|
background: #FCF4E3;
|
|
590
600
|
}
|
|
591
601
|
|
|
592
|
-
.kg-card-
|
|
602
|
+
.kg-callout-card-red {
|
|
593
603
|
background: #FBE9E9;
|
|
594
604
|
}
|
|
595
605
|
|
|
596
|
-
.kg-card-
|
|
606
|
+
.kg-callout-card-pink {
|
|
597
607
|
background: #FCEEF8;
|
|
598
608
|
}
|
|
599
609
|
|
|
600
|
-
.kg-card-
|
|
610
|
+
.kg-callout-card-purple {
|
|
601
611
|
background: #F2EDFC;
|
|
602
612
|
}
|
|
603
613
|
|
|
604
|
-
.kg-card-
|
|
605
|
-
background:
|
|
614
|
+
.kg-callout-card-accent {
|
|
615
|
+
background: ${templateSettings.accentColor || '#15212A'};
|
|
616
|
+
color: #fff;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.kg-callout-card-accent a {
|
|
606
620
|
color: #fff;
|
|
607
621
|
}
|
|
608
622
|
|
|
@@ -890,6 +904,11 @@ figure blockquote p {
|
|
|
890
904
|
padding-left: 15px !important;
|
|
891
905
|
}
|
|
892
906
|
|
|
907
|
+
table.body blockquote.kg-blockquote-alt {
|
|
908
|
+
padding-left: 50px !important;
|
|
909
|
+
margin-bottom: 2.5em !important;
|
|
910
|
+
}
|
|
911
|
+
|
|
893
912
|
table.body blockquote + * {
|
|
894
913
|
margin-top: 1.5em !important;
|
|
895
914
|
}
|
|
@@ -6,6 +6,7 @@ const mail = require('../mail');
|
|
|
6
6
|
const models = require('../../models');
|
|
7
7
|
const signinEmail = require('./emails/signin');
|
|
8
8
|
const signupEmail = require('./emails/signup');
|
|
9
|
+
const signupPaidEmail = require('./emails/signup-paid');
|
|
9
10
|
const subscribeEmail = require('./emails/subscribe');
|
|
10
11
|
const updateEmail = require('./emails/updateEmail');
|
|
11
12
|
const SingleUseTokenProvider = require('./SingleUseTokenProvider');
|
|
@@ -49,6 +50,8 @@ function createApiInstance(config) {
|
|
|
49
50
|
return `📫 Confirm your subscription to ${siteTitle}`;
|
|
50
51
|
case 'signup':
|
|
51
52
|
return `🙌 Complete your sign up to ${siteTitle}!`;
|
|
53
|
+
case 'signup-paid':
|
|
54
|
+
return `🙌 Thank you for signing up to ${siteTitle}!`;
|
|
52
55
|
case 'updateEmail':
|
|
53
56
|
return `📫 Confirm your email update for ${siteTitle}!`;
|
|
54
57
|
case 'signin':
|
|
@@ -93,6 +96,23 @@ function createApiInstance(config) {
|
|
|
93
96
|
Sent to ${email}
|
|
94
97
|
If you did not make this request, you can simply delete this message. You will not be signed up, and no account will be created for you.
|
|
95
98
|
`;
|
|
99
|
+
case 'signup-paid':
|
|
100
|
+
return `
|
|
101
|
+
Thank you for subscribing to ${siteTitle}!
|
|
102
|
+
|
|
103
|
+
Tap the link below to be automatically signed in:
|
|
104
|
+
|
|
105
|
+
${url}
|
|
106
|
+
|
|
107
|
+
For your security, the link will expire in 24 hours time.
|
|
108
|
+
|
|
109
|
+
See you soon!
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
Sent to ${email}
|
|
114
|
+
Thank you for subscribing to ${siteTitle}!
|
|
115
|
+
`;
|
|
96
116
|
case 'updateEmail':
|
|
97
117
|
return `
|
|
98
118
|
Hey there,
|
|
@@ -139,6 +159,8 @@ function createApiInstance(config) {
|
|
|
139
159
|
return subscribeEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
|
|
140
160
|
case 'signup':
|
|
141
161
|
return signupEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
|
|
162
|
+
case 'signup-paid':
|
|
163
|
+
return signupPaidEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
|
|
142
164
|
case 'updateEmail':
|
|
143
165
|
return updateEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
|
|
144
166
|
case 'signin':
|
|
@@ -98,7 +98,7 @@ class MembersConfigProvider {
|
|
|
98
98
|
*/
|
|
99
99
|
getStripeKeys(type) {
|
|
100
100
|
if (type !== 'direct' && type !== 'connect') {
|
|
101
|
-
throw new errors.IncorrectUsageError(tpl(messages.incorrectKeyType));
|
|
101
|
+
throw new errors.IncorrectUsageError({message: tpl(messages.incorrectKeyType)});
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const secretKey = this._settingsCache.get(`stripe_${type === 'connect' ? 'connect_' : ''}secret_key`);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
module.exports = ({siteTitle, email, url, accentColor = '#15212A', siteDomain, siteUrl}) => `
|
|
2
|
+
<!doctype html>
|
|
3
|
+
<html>
|
|
4
|
+
<head>
|
|
5
|
+
<meta name="viewport" content="width=device-width">
|
|
6
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
7
|
+
<title>🙌 Thank you for signing up to ${siteTitle}!</title>
|
|
8
|
+
<style>
|
|
9
|
+
/* -------------------------------------
|
|
10
|
+
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
11
|
+
------------------------------------- */
|
|
12
|
+
@media only screen and (max-width: 620px) {
|
|
13
|
+
table[class=body] h1 {
|
|
14
|
+
font-size: 28px !important;
|
|
15
|
+
margin-bottom: 10px !important;
|
|
16
|
+
}
|
|
17
|
+
table[class=body] p,
|
|
18
|
+
table[class=body] ul,
|
|
19
|
+
table[class=body] ol,
|
|
20
|
+
table[class=body] td,
|
|
21
|
+
table[class=body] span,
|
|
22
|
+
table[class=body] a {
|
|
23
|
+
font-size: 16px !important;
|
|
24
|
+
}
|
|
25
|
+
table[class=body] .wrapper,
|
|
26
|
+
table[class=body] .article {
|
|
27
|
+
padding: 10px !important;
|
|
28
|
+
}
|
|
29
|
+
table[class=body] .content {
|
|
30
|
+
padding: 0 !important;
|
|
31
|
+
}
|
|
32
|
+
table[class=body] .container {
|
|
33
|
+
padding: 0 !important;
|
|
34
|
+
width: 100% !important;
|
|
35
|
+
}
|
|
36
|
+
table[class=body] .main {
|
|
37
|
+
border-left-width: 0 !important;
|
|
38
|
+
border-radius: 0 !important;
|
|
39
|
+
border-right-width: 0 !important;
|
|
40
|
+
}
|
|
41
|
+
table[class=body] .btn table {
|
|
42
|
+
width: 100% !important;
|
|
43
|
+
}
|
|
44
|
+
table[class=body] .btn a {
|
|
45
|
+
width: 100% !important;
|
|
46
|
+
}
|
|
47
|
+
table[class=body] .img-responsive {
|
|
48
|
+
height: auto !important;
|
|
49
|
+
max-width: 100% !important;
|
|
50
|
+
width: auto !important;
|
|
51
|
+
}
|
|
52
|
+
table[class=body] p[class=small],
|
|
53
|
+
table[class=body] a[class=small] {
|
|
54
|
+
font-size: 11px !important;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/* -------------------------------------
|
|
58
|
+
PRESERVE THESE STYLES IN THE HEAD
|
|
59
|
+
------------------------------------- */
|
|
60
|
+
@media all {
|
|
61
|
+
.ExternalClass {
|
|
62
|
+
width: 100%;
|
|
63
|
+
}
|
|
64
|
+
.ExternalClass,
|
|
65
|
+
.ExternalClass p,
|
|
66
|
+
.ExternalClass span,
|
|
67
|
+
.ExternalClass font,
|
|
68
|
+
.ExternalClass td,
|
|
69
|
+
.ExternalClass div {
|
|
70
|
+
line-height: 100%;
|
|
71
|
+
}
|
|
72
|
+
.recipient-link a {
|
|
73
|
+
color: inherit !important;
|
|
74
|
+
font-family: inherit !important;
|
|
75
|
+
font-size: inherit !important;
|
|
76
|
+
font-weight: inherit !important;
|
|
77
|
+
line-height: inherit !important;
|
|
78
|
+
text-decoration: none !important;
|
|
79
|
+
}
|
|
80
|
+
#MessageViewBody a {
|
|
81
|
+
color: inherit;
|
|
82
|
+
text-decoration: none;
|
|
83
|
+
font-size: inherit;
|
|
84
|
+
font-family: inherit;
|
|
85
|
+
font-weight: inherit;
|
|
86
|
+
line-height: inherit;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
hr {
|
|
90
|
+
border-width: 0;
|
|
91
|
+
height: 0;
|
|
92
|
+
margin-top: 34px;
|
|
93
|
+
margin-bottom: 34px;
|
|
94
|
+
border-bottom-width: 1px;
|
|
95
|
+
border-bottom-color: #EEF5F8;
|
|
96
|
+
}
|
|
97
|
+
a {
|
|
98
|
+
color: #3A464C;
|
|
99
|
+
}
|
|
100
|
+
</style>
|
|
101
|
+
</head>
|
|
102
|
+
<body class="" style="background-color: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
|
|
103
|
+
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #FFFFFF;">
|
|
104
|
+
<tr>
|
|
105
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;"> </td>
|
|
106
|
+
<td class="container" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; display: block; margin: 0 auto; max-width: 540px; padding: 10px; width: 540px;">
|
|
107
|
+
<div class="content" style="box-sizing: border-box; display: block; margin: 0 auto; max-width: 600px; padding: 30px 20px;">
|
|
108
|
+
|
|
109
|
+
<!-- START CENTERED WHITE CONTAINER -->
|
|
110
|
+
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Complete signup for ${siteTitle}!</span>
|
|
111
|
+
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
|
|
112
|
+
|
|
113
|
+
<!-- START MAIN CONTENT AREA -->
|
|
114
|
+
<tr>
|
|
115
|
+
<td class="wrapper" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; box-sizing: border-box;">
|
|
116
|
+
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
|
117
|
+
<tr>
|
|
118
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
|
|
119
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 25px; margin: 0; margin-bottom: 15px;">Thank you for subscribing to ${siteTitle}</p>
|
|
120
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 25px; margin: 0; margin-bottom: 32px;">Tap the link below to be automatically signed in:</p>
|
|
121
|
+
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
|
122
|
+
<tbody>
|
|
123
|
+
<tr>
|
|
124
|
+
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; padding-bottom: 35px;">
|
|
125
|
+
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
|
126
|
+
<tbody>
|
|
127
|
+
<tr>
|
|
128
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; background-color: ${accentColor}; border-radius: 5px; text-align: center;"> <a href="${url}" target="_blank" style="display: inline-block; color: #ffffff; background-color: ${accentColor}; border: solid 1px ${accentColor}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: ${accentColor};">Confirm signup</a> </td>
|
|
129
|
+
</tr>
|
|
130
|
+
</tbody>
|
|
131
|
+
</table>
|
|
132
|
+
</td>
|
|
133
|
+
</tr>
|
|
134
|
+
</tbody>
|
|
135
|
+
</table>
|
|
136
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 25px; margin: 0; margin-bottom: 25px;">For your security, the link will expire in 24 hours time.</p>
|
|
137
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 25px; margin: 0; margin-bottom: 30px;">See you soon!</p>
|
|
138
|
+
<hr/>
|
|
139
|
+
<p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; color: #3A464C; font-weight: normal; line-height: 25px; margin: 0; margin-bottom: 5px;">You can also copy & paste this URL into your browser:</p>
|
|
140
|
+
<p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top: 0; color: #3A464C;">${url}</p>
|
|
141
|
+
</td>
|
|
142
|
+
</tr>
|
|
143
|
+
</table>
|
|
144
|
+
</td>
|
|
145
|
+
</tr>
|
|
146
|
+
|
|
147
|
+
<!-- START FOOTER -->
|
|
148
|
+
<tr>
|
|
149
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 2px;">
|
|
150
|
+
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 11px; color: #738A94; font-weight: normal; margin: 0; margin-bottom: 2px;">This message was sent from <a class="small" href="${siteUrl}" style="text-decoration: underline; color: #738A94; font-size: 11px;">${siteDomain}</a> to <a class="small" href="mailto:${email}" style="text-decoration: underline; color: #738A94; font-size: 11px;">${email}</a></p>
|
|
151
|
+
</td>
|
|
152
|
+
</tr>
|
|
153
|
+
|
|
154
|
+
<!-- END FOOTER -->
|
|
155
|
+
|
|
156
|
+
<!-- END MAIN CONTENT AREA -->
|
|
157
|
+
</table>
|
|
158
|
+
|
|
159
|
+
<!-- END CENTERED WHITE CONTAINER -->
|
|
160
|
+
</div>
|
|
161
|
+
</td>
|
|
162
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;"> </td>
|
|
163
|
+
</tr>
|
|
164
|
+
</table>
|
|
165
|
+
</body>
|
|
166
|
+
</html>
|
|
167
|
+
`;
|
|
168
|
+
|
|
@@ -159,12 +159,16 @@ const membersService = {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
if (stripeService.api.configured && stripeService.api.mode === 'live') {
|
|
162
|
-
throw new errors.IncorrectUsageError(
|
|
162
|
+
throw new errors.IncorrectUsageError({
|
|
163
|
+
message: tpl(messages.noLiveKeysInDevelopment)
|
|
164
|
+
});
|
|
163
165
|
}
|
|
164
166
|
} else {
|
|
165
167
|
const siteUrl = urlUtils.getSiteUrl();
|
|
166
168
|
if (!/^https/.test(siteUrl) && stripeService.api.configured) {
|
|
167
|
-
throw new errors.IncorrectUsageError(
|
|
169
|
+
throw new errors.IncorrectUsageError({
|
|
170
|
+
message: tpl(messages.sslRequiredForStripe)
|
|
171
|
+
});
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
if (!membersApi) {
|
|
@@ -63,7 +63,7 @@ async function getStripeConnectTokenData(encodedData, getSessionProp) {
|
|
|
63
63
|
const state = await getSessionProp(STATE_PROP);
|
|
64
64
|
|
|
65
65
|
if (state !== data.s) {
|
|
66
|
-
throw new errors.NoPermissionError(tpl(messages.incorrectState));
|
|
66
|
+
throw new errors.NoPermissionError({message: tpl(messages.incorrectState)});
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
return {
|
|
@@ -81,7 +81,9 @@ function checkCanConnect() {
|
|
|
81
81
|
const siteUrlUsingSSL = /^https/.test(siteUrl);
|
|
82
82
|
const cannotConnectToStripe = productionMode && !siteUrlUsingSSL;
|
|
83
83
|
if (cannotConnectToStripe) {
|
|
84
|
-
throw new errors.BadRequestError(
|
|
84
|
+
throw new errors.BadRequestError({
|
|
85
|
+
message: 'Cannot connect to stripe unless site is using https://'
|
|
86
|
+
});
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -35,8 +35,13 @@ class NFTOEmbedProvider {
|
|
|
35
35
|
if (!match) {
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
|
+
const headers = {};
|
|
39
|
+
if (this.dependencies.config.apiKey) {
|
|
40
|
+
headers['X-API-KEY'] = this.dependencies.config.apiKey;
|
|
41
|
+
}
|
|
38
42
|
const result = await externalRequest(`https://api.opensea.io/api/v1/asset/${transaction}/${asset}/?format=json`, {
|
|
39
|
-
json: true
|
|
43
|
+
json: true,
|
|
44
|
+
headers
|
|
40
45
|
});
|
|
41
46
|
return {
|
|
42
47
|
version: '1.0',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const errors = require('@tryghost/errors');
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
3
|
const logging = require('@tryghost/logging');
|
|
4
|
+
const sentry = require('../../shared/sentry');
|
|
4
5
|
const {extract, hasProvider} = require('oembed-parser');
|
|
5
6
|
const cheerio = require('cheerio');
|
|
6
7
|
const _ = require('lodash');
|
|
@@ -63,6 +64,7 @@ class OEmbed {
|
|
|
63
64
|
*/
|
|
64
65
|
constructor({config, externalRequest}) {
|
|
65
66
|
this.config = config;
|
|
67
|
+
|
|
66
68
|
/** @type {IExternalRequest} */
|
|
67
69
|
this.externalRequest = async (url, requestConfig) => {
|
|
68
70
|
if (this.isIpOrLocalhost(url)) {
|
|
@@ -74,6 +76,7 @@ class OEmbed {
|
|
|
74
76
|
}
|
|
75
77
|
return response;
|
|
76
78
|
};
|
|
79
|
+
|
|
77
80
|
/** @type {ICustomProvider[]} */
|
|
78
81
|
this.customProviders = [];
|
|
79
82
|
}
|
|
@@ -108,27 +111,6 @@ class OEmbed {
|
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
/**
|
|
112
|
-
* @param {string} url
|
|
113
|
-
*/
|
|
114
|
-
errorHandler(url) {
|
|
115
|
-
/**
|
|
116
|
-
* @param {Error|errors.GhostError} err
|
|
117
|
-
*/
|
|
118
|
-
return async (err) => {
|
|
119
|
-
// allow specific validation errors through for better error messages
|
|
120
|
-
if (errors.utils.isIgnitionError(err) && err.errorType === 'ValidationError') {
|
|
121
|
-
throw err;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// log the real error because we're going to throw a generic "Unknown provider" error
|
|
125
|
-
logging.error(new errors.GhostError({err}));
|
|
126
|
-
|
|
127
|
-
// default to unknown provider to avoid leaking any app specifics
|
|
128
|
-
return this.unknownProvider(url);
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
114
|
async fetchBookmarkData(url) {
|
|
133
115
|
const metascraper = require('metascraper')([
|
|
134
116
|
require('metascraper-url')(),
|
|
@@ -147,7 +129,14 @@ class OEmbed {
|
|
|
147
129
|
const response = await this.externalRequest(url, {cookieJar});
|
|
148
130
|
|
|
149
131
|
const html = response.body;
|
|
150
|
-
|
|
132
|
+
try {
|
|
133
|
+
scraperResponse = await metascraper({html, url});
|
|
134
|
+
} catch (err) {
|
|
135
|
+
// Log to avoid being blind to errors happenning in metascraper
|
|
136
|
+
sentry.captureException(err);
|
|
137
|
+
logging.error(err);
|
|
138
|
+
return this.unknownProvider(url);
|
|
139
|
+
}
|
|
151
140
|
|
|
152
141
|
const metadata = Object.assign({}, scraperResponse, {
|
|
153
142
|
thumbnail: scraperResponse.image,
|
|
@@ -300,10 +289,13 @@ class OEmbed {
|
|
|
300
289
|
* @returns {Promise<Object>}
|
|
301
290
|
*/
|
|
302
291
|
async fetchOembedDataFromUrl(url, type) {
|
|
303
|
-
let data;
|
|
304
|
-
|
|
305
292
|
try {
|
|
306
293
|
const urlObject = new URL(url);
|
|
294
|
+
|
|
295
|
+
// Trimming solves the difference of url validation between `new URL(url)`
|
|
296
|
+
// and metascraper.
|
|
297
|
+
url = url.trim();
|
|
298
|
+
|
|
307
299
|
for (const provider of this.customProviders) {
|
|
308
300
|
if (await provider.canSupportRequest(urlObject)) {
|
|
309
301
|
const result = await provider.getOEmbedData(urlObject, this.externalRequest);
|
|
@@ -313,23 +305,39 @@ class OEmbed {
|
|
|
313
305
|
}
|
|
314
306
|
}
|
|
315
307
|
|
|
308
|
+
// fetch only bookmark when explicitly requested
|
|
316
309
|
if (type === 'bookmark') {
|
|
317
310
|
return this.fetchBookmarkData(url);
|
|
318
311
|
}
|
|
319
312
|
|
|
320
|
-
|
|
313
|
+
// attempt to fetch oembed
|
|
314
|
+
let data = await this.fetchOembedData(url);
|
|
321
315
|
|
|
316
|
+
// fallback to bookmark when we can't get oembed
|
|
322
317
|
if (!data && !type) {
|
|
323
318
|
data = await this.fetchBookmarkData(url);
|
|
324
319
|
}
|
|
325
320
|
|
|
321
|
+
// couldn't get anything, throw a validation error
|
|
326
322
|
if (!data) {
|
|
327
|
-
|
|
323
|
+
return this.unknownProvider(url);
|
|
328
324
|
}
|
|
329
325
|
|
|
330
326
|
return data;
|
|
331
|
-
} catch (
|
|
332
|
-
|
|
327
|
+
} catch (err) {
|
|
328
|
+
// allow specific validation errors through for better error messages
|
|
329
|
+
if (errors.utils.isGhostError(err) && err.errorType === 'ValidationError') {
|
|
330
|
+
throw err;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// log the real error because we're going to throw a generic "Unknown provider" error
|
|
334
|
+
logging.error(new errors.InternalServerError({
|
|
335
|
+
message: 'Encountered error when fetching oembed',
|
|
336
|
+
err
|
|
337
|
+
}));
|
|
338
|
+
|
|
339
|
+
// default to unknown provider to avoid leaking any app specifics
|
|
340
|
+
return this.unknownProvider(url);
|
|
333
341
|
}
|
|
334
342
|
}
|
|
335
343
|
}
|
|
@@ -128,7 +128,7 @@ CanThisResult.prototype.beginCheck = function (context) {
|
|
|
128
128
|
context = parseContext(context);
|
|
129
129
|
|
|
130
130
|
if (actionsMap.empty()) {
|
|
131
|
-
throw new errors.
|
|
131
|
+
throw new errors.InternalServerError({message: tpl(messages.noActionsMapFoundError)});
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
// Kick off loading of user permissions if necessary
|