ghost 4.37.0 → 4.39.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/.c8rc.json +1 -1
- package/README.md +26 -18
- package/content/themes/casper/LICENSE +1 -1
- package/content/themes/casper/README.md +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 +14 -6
- package/content/themes/casper/assets/css/screen.css +9 -1
- package/content/themes/casper/package.json +2 -2
- package/content/themes/casper/partials/post-card.hbs +1 -1
- package/content/themes/casper/post.hbs +18 -19
- package/content/themes/casper/yarn.lock +186 -217
- package/core/built/assets/{chunk.3.4906cf0b01d6d8e33374.js → chunk.3.6e2ed2d00856e12bd81a.js} +19 -19
- package/core/built/assets/ghost-dark-498ff8339a89bb68c3f78f59bee4146e.css +1 -0
- package/core/built/assets/ghost.min-77b93478f83b0def6ddc5a4f23ce963e.css +1 -0
- package/core/built/assets/{ghost.min-c1938f6ee696bf08bd6bf93cac341ea2.js → ghost.min-e6559d901897066aa6a6d4145e3728ed.js} +466 -413
- package/core/built/assets/icons/{event-changed-subscription.svg → event-subscriptions.svg} +0 -1
- package/core/built/assets/icons/eye.svg +4 -1
- package/core/built/assets/icons/member-add.svg +3 -0
- package/core/built/assets/icons/member.svg +3 -0
- package/core/built/assets/icons/pin.svg +4 -1
- package/core/built/assets/{vendor.min-6dc30be68238b5c55df0cdc1f2dc8b8d.js → vendor.min-c39476bced9adb98ee2b292d01c7a8f4.js} +2303 -1372
- package/core/frontend/apps/private-blogging/lib/middleware.js +1 -1
- package/core/frontend/apps/private-blogging/lib/views/private.hbs +9 -10
- package/core/frontend/helpers/get.js +4 -0
- package/core/frontend/helpers/match.js +12 -0
- package/core/frontend/helpers/prev_post.js +11 -1
- package/core/frontend/helpers/tiers.js +59 -0
- package/core/frontend/helpers/tpl/content-cta.hbs +1 -1
- package/core/frontend/public/ghost.css +205 -143
- package/core/frontend/services/routing/router-manager.js +1 -1
- package/core/frontend/views/unsubscribe.hbs +28 -33
- package/core/frontend/web/middleware/error-handler.js +2 -2
- package/core/frontend/web/site.js +10 -0
- package/core/server/api/canary/authentication.js +7 -0
- package/core/server/api/canary/index.js +4 -0
- package/core/server/api/canary/members.js +9 -2
- package/core/server/api/canary/products.js +3 -6
- package/core/server/api/canary/tiers-public.js +34 -0
- package/core/server/api/canary/tiers.js +6 -7
- package/core/server/api/canary/utils/serializers/input/pages.js +1 -1
- package/core/server/api/canary/utils/serializers/input/posts.js +1 -1
- package/core/server/api/canary/utils/serializers/output/email-posts.js +7 -1
- package/core/server/api/canary/utils/serializers/output/pages.js +9 -2
- package/core/server/api/canary/utils/serializers/output/posts.js +8 -2
- package/core/server/api/canary/utils/serializers/output/preview.js +7 -1
- package/core/server/api/canary/utils/serializers/output/products.js +3 -1
- package/core/server/api/canary/utils/serializers/output/tiers.js +4 -2
- package/core/server/api/canary/utils/serializers/output/utils/mapper.js +17 -7
- package/core/server/api/shared/serializers/handle.js +2 -2
- package/core/server/api/v2/utils/serializers/input/pages.js +1 -1
- package/core/server/api/v2/utils/serializers/input/posts.js +1 -1
- package/core/server/api/v3/utils/serializers/input/pages.js +1 -1
- package/core/server/api/v3/utils/serializers/input/posts.js +1 -1
- package/core/server/data/db/connection.js +3 -2
- package/core/server/data/importer/import-manager.js +152 -113
- package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -1
- package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -1
- package/core/server/data/migrations/versions/3.29/03-remove-orphaned-customers.js +2 -1
- package/core/server/data/migrations/versions/3.29/04-remove-orphaned-subscriptions.js +2 -1
- package/core/server/data/migrations/versions/3.29/05-add-member-constraints.js +3 -2
- package/core/server/data/migrations/versions/3.39/06-add-email-recipient-index.js +4 -3
- package/core/server/data/migrations/versions/4.0/14-remove-orphaned-stripe-records.js +2 -1
- package/core/server/data/migrations/versions/4.0/26-add-cascade-on-delete.js +2 -1
- package/core/server/data/migrations/versions/4.0/29-fix-foreign-key-for-members-stripe-customers-subscriptions.js +2 -1
- package/core/server/data/migrations/versions/4.1/02-add-unique-constraint-for-member-stripe-tables.js +2 -1
- package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +3 -2
- package/core/server/data/migrations/versions/4.33/2022-01-14-11-51-add-default-free-tier.js +3 -0
- package/core/server/data/migrations/versions/4.33/2022-01-18-09-07-remove-duplicate-offer-redemptions.js +2 -2
- package/core/server/data/migrations/versions/4.35/2022-02-01-11-48-update-email-recipient-filter-column-type.js +2 -1
- package/core/server/data/migrations/versions/4.35/2022-02-01-12-03-update-recipient-filter-column-type.js +2 -1
- package/core/server/data/migrations/versions/4.37/2022-02-21-09-53-backfill-members-last-seen-at-column.js +3 -2
- package/core/server/data/migrations/versions/4.38/2022-03-01-08-46-add-visibility-to-tiers.js +11 -0
- package/core/server/data/migrations/versions/4.38/2022-03-03-16-12-add-visibility-to-tiers.js +8 -0
- package/core/server/data/migrations/versions/4.38/2022-03-03-16-17-drop-tiers-visible-column.js +7 -0
- package/core/server/data/migrations/versions/4.39/2022-03-07-10-57-update-free-products-visibility-column.js +66 -0
- package/core/server/data/migrations/versions/4.39/2022-03-07-10-57-update-products-visibility-column.js +36 -0
- package/core/server/data/schema/clients/index.js +1 -1
- package/core/server/data/schema/clients/mysql.js +4 -4
- package/core/server/data/schema/commands.js +42 -50
- package/core/server/data/schema/default-settings/default-settings.json +2 -2
- package/core/server/data/schema/fixtures/fixtures.json +18 -161
- package/core/server/data/schema/schema.js +7 -0
- package/core/server/frontend/ghost.min.css +1 -1
- package/core/server/lib/image/image-size.js +12 -4
- package/core/server/models/base/plugins/generate-slug.js +13 -1
- package/core/server/models/base/plugins/raw-knex.js +1 -1
- package/core/server/models/post.js +16 -6
- package/core/server/models/product.js +2 -1
- package/core/server/models/user.js +1 -1
- package/core/server/services/auth/api-key/admin.js +15 -6
- package/core/server/services/auth/setup.js +34 -13
- package/core/server/services/email-analytics/lib/event-processor.js +18 -1
- package/core/server/services/mega/mega.js +4 -4
- package/core/server/services/mega/template.js +1 -1
- package/core/server/services/members/content-gating.js +1 -1
- package/core/server/services/members/middleware.js +4 -0
- package/core/server/services/members/service.js +13 -1
- package/core/server/services/posts/posts-service.js +1 -1
- package/core/server/services/url/UrlGenerator.js +1 -1
- package/core/server/services/webhooks/webhooks-service.js +2 -0
- package/core/server/views/maintenance.html +2 -2
- 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/app.js +3 -0
- package/core/server/web/api/canary/admin/middleware.js +2 -0
- package/core/server/web/api/canary/content/routes.js +1 -0
- package/core/server/web/members/app.js +1 -1
- package/core/server/web/parent/backend.js +2 -1
- package/core/server/web/shared/middleware/uncapitalise.js +3 -2
- package/core/shared/config/defaults.json +2 -2
- package/core/shared/config/utils.js +5 -1
- package/core/shared/labs.js +8 -9
- package/core/shared/url-utils.js +4 -1
- package/package.json +56 -52
- package/yarn.lock +809 -607
- package/core/built/assets/ghost-dark-d54723f7267e66fa2595f897076e86c2.css +0 -1
- package/core/built/assets/ghost.min-02a5f8954bd85fe28817b8c8b111b8aa.css +0 -1
- package/core/built/assets/icons/event-started-subscription.svg +0 -6
- package/core/built/assets/icons/locked-email-back.svg +0 -1
- package/core/built/assets/icons/locked-email-front.svg +0 -1
- package/core/built/assets/icons/locked-email-lock.svg +0 -1
- package/core/built/assets/img/ghost-logo-de2acf283f53ba1fd1149928faeaaa74.png +0 -0
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
7
|
<title>
|
|
8
|
-
{{#if
|
|
9
|
-
|
|
8
|
+
{{#if error}}
|
|
9
|
+
Unsubscribe Failed
|
|
10
|
+
{{else}}
|
|
11
|
+
Successfully Unsubscribed
|
|
12
|
+
{{/if}}
|
|
10
13
|
</title>
|
|
11
14
|
<link rel="stylesheet" href="{{asset "public/ghost.css" hasMinFile="true"}}" />
|
|
12
15
|
</head>
|
|
@@ -15,42 +18,34 @@
|
|
|
15
18
|
<div class="gh-viewport">
|
|
16
19
|
<main class="gh-main" role="main">
|
|
17
20
|
<div class="gh-flow">
|
|
18
|
-
<header class="gh-flow-head gh-flow-head-unsubscribe">
|
|
19
|
-
<nav class="gh-flow-nav">
|
|
20
|
-
<a href="{{@site.url}}" class="gh-flow-back-plain">
|
|
21
|
-
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->
|
|
22
|
-
<svg width="17px" height="27px" viewBox="0 0 17 27" version="1.1"
|
|
23
|
-
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
24
|
-
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"
|
|
25
|
-
stroke-linecap="round" stroke-linejoin="round">
|
|
26
|
-
<g id="Stroke-42" transform="translate(2.000000, 2.000000)" stroke-width="3"
|
|
27
|
-
stroke="#7d878a">
|
|
28
|
-
<polyline points="13.5401 0.4256 0.3971 11.9256 13.5401 23.4256"></polyline>
|
|
29
|
-
</g>
|
|
30
|
-
</g>
|
|
31
|
-
</svg>
|
|
32
|
-
Back to {{@site.title}}
|
|
33
|
-
</a>
|
|
34
|
-
</nav>
|
|
35
|
-
</header>
|
|
36
|
-
|
|
37
21
|
<div class="gh-flow-content-wrap">
|
|
38
|
-
|
|
22
|
+
{{#if @site.icon}}
|
|
23
|
+
<img class="site-icon" src="{{img_url @site.icon absolute="true"}}" layout="fixed">
|
|
24
|
+
{{/if}}
|
|
25
|
+
<section class="gh-flow-content unsubscribe">
|
|
39
26
|
{{#if error}}
|
|
27
|
+
<h1>Unsubscribe failed.</h1>
|
|
40
28
|
<p>{{error}}</p>
|
|
41
|
-
{{else}}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
</p>
|
|
50
|
-
<p>Didn't mean to do this? Manage your account <a href="{{@site.url}}/#/portal/account">here</a>.</p>
|
|
51
|
-
{{/if}}
|
|
29
|
+
{{else if member}}
|
|
30
|
+
<h1>Successfully unsubscribed.</h1>
|
|
31
|
+
<p>
|
|
32
|
+
<span class="gh-flow-em">{{member.email}}</span> will no longer receive this newsletter.
|
|
33
|
+
{{#match member.status "!=" "free"}}
|
|
34
|
+
This will not cancel your paid subscription{{#if @site.title}} to {{@site.title}}{{/if}}.
|
|
35
|
+
{{/match}}
|
|
36
|
+
</p>
|
|
52
37
|
{{/if}}
|
|
53
38
|
</section>
|
|
39
|
+
<div class="unsubscribe-footer">
|
|
40
|
+
{{#unless error}}
|
|
41
|
+
<p>Didn't mean to do this? Manage your account <a href="{{@site.url}}/#/portal/account">here</a>.</p>
|
|
42
|
+
{{/unless}}
|
|
43
|
+
{{#if @site.title}}
|
|
44
|
+
<a href="{{@site.url}}">{{@site.title}}</a>
|
|
45
|
+
{{else}}
|
|
46
|
+
<a href="{{@site.url}}">Visit site</a>
|
|
47
|
+
{{/if}}
|
|
48
|
+
</div>
|
|
54
49
|
</div>
|
|
55
50
|
</div>
|
|
56
51
|
</main>
|
|
@@ -84,10 +84,10 @@ const themeErrorRenderer = (err, req, res, next) => {
|
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
module.exports.handleThemeResponse = [
|
|
87
|
-
// Make sure the error can be served
|
|
88
|
-
prepareError,
|
|
89
87
|
// Handle the error in Sentry
|
|
90
88
|
sentry.errorHandler,
|
|
89
|
+
// Make sure the error can be served
|
|
90
|
+
prepareError,
|
|
91
91
|
// Render the error using theme template
|
|
92
92
|
themeErrorRenderer
|
|
93
93
|
];
|
|
@@ -4,6 +4,8 @@ const express = require('../../shared/express');
|
|
|
4
4
|
const cors = require('cors');
|
|
5
5
|
const {URL} = require('url');
|
|
6
6
|
const errors = require('@tryghost/errors');
|
|
7
|
+
const DomainEvents = require('@tryghost/domain-events');
|
|
8
|
+
const {MemberPageViewEvent} = require('@tryghost/member-events');
|
|
7
9
|
|
|
8
10
|
// App requires
|
|
9
11
|
const config = require('../../shared/config');
|
|
@@ -171,6 +173,14 @@ module.exports = function setupSiteApp(options = {}) {
|
|
|
171
173
|
}
|
|
172
174
|
});
|
|
173
175
|
|
|
176
|
+
siteApp.use(function (req, res, next) {
|
|
177
|
+
if (req.member) {
|
|
178
|
+
// This event needs memberLastSeenAt to avoid doing un-necessary database queries when updating `last_seen_at`
|
|
179
|
+
DomainEvents.dispatch(MemberPageViewEvent.create({url: req.url, memberId: req.member.id, memberLastSeenAt: req.member.last_seen_at}, new Date()));
|
|
180
|
+
}
|
|
181
|
+
next();
|
|
182
|
+
});
|
|
183
|
+
|
|
174
184
|
debug('General middleware done');
|
|
175
185
|
|
|
176
186
|
router = siteRoutes(options);
|
|
@@ -49,6 +49,13 @@ module.exports = {
|
|
|
49
49
|
|
|
50
50
|
return auth.setup.setupUser(setupDetails);
|
|
51
51
|
})
|
|
52
|
+
.then((data) => {
|
|
53
|
+
try {
|
|
54
|
+
return auth.setup.doFixtures(data, api.products);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
return data;
|
|
57
|
+
}
|
|
58
|
+
})
|
|
52
59
|
.then((data) => {
|
|
53
60
|
try {
|
|
54
61
|
return auth.setup.doProduct(data, api.products);
|
|
@@ -207,5 +207,9 @@ module.exports = {
|
|
|
207
207
|
|
|
208
208
|
get productsPublic() {
|
|
209
209
|
return shared.pipeline(require('./products-public'), localUtils, 'content');
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
get tiersPublic() {
|
|
213
|
+
return shared.pipeline(require('./tiers-public'), localUtils, 'content');
|
|
210
214
|
}
|
|
211
215
|
};
|
|
@@ -54,10 +54,17 @@ module.exports = {
|
|
|
54
54
|
'order',
|
|
55
55
|
'debug',
|
|
56
56
|
'page',
|
|
57
|
-
'search'
|
|
57
|
+
'search',
|
|
58
|
+
'include'
|
|
58
59
|
],
|
|
59
60
|
permissions: true,
|
|
60
|
-
validation: {
|
|
61
|
+
validation: {
|
|
62
|
+
options: {
|
|
63
|
+
include: {
|
|
64
|
+
values: allowedIncludes
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
61
68
|
async query(frame) {
|
|
62
69
|
const page = await membersService.api.memberBREADService.browse(frame.options);
|
|
63
70
|
|
|
@@ -93,7 +93,9 @@ module.exports = {
|
|
|
93
93
|
options: [
|
|
94
94
|
'id'
|
|
95
95
|
],
|
|
96
|
-
headers: {
|
|
96
|
+
headers: {
|
|
97
|
+
cacheInvalidate: true
|
|
98
|
+
},
|
|
97
99
|
validation: {
|
|
98
100
|
options: {
|
|
99
101
|
id: {
|
|
@@ -108,11 +110,6 @@ module.exports = {
|
|
|
108
110
|
frame.options
|
|
109
111
|
);
|
|
110
112
|
|
|
111
|
-
if (model.wasChanged()) {
|
|
112
|
-
this.headers.cacheInvalidate = true;
|
|
113
|
-
} else {
|
|
114
|
-
this.headers.cacheInvalidate = false;
|
|
115
|
-
}
|
|
116
113
|
return model;
|
|
117
114
|
}
|
|
118
115
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// NOTE: We must not cache references to membersService.api
|
|
2
|
+
// as it is a getter and may change during runtime.
|
|
3
|
+
const membersService = require('../../services/members');
|
|
4
|
+
|
|
5
|
+
const allowedIncludes = ['monthly_price', 'yearly_price', 'benefits'];
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
docName: 'tiers',
|
|
9
|
+
|
|
10
|
+
browse: {
|
|
11
|
+
options: [
|
|
12
|
+
'limit',
|
|
13
|
+
'fields',
|
|
14
|
+
'include',
|
|
15
|
+
'filter',
|
|
16
|
+
'order',
|
|
17
|
+
'debug',
|
|
18
|
+
'page'
|
|
19
|
+
],
|
|
20
|
+
permissions: true,
|
|
21
|
+
validation: {
|
|
22
|
+
options: {
|
|
23
|
+
include: {
|
|
24
|
+
values: allowedIncludes
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
async query(frame) {
|
|
29
|
+
const page = await membersService.api.productRepository.list(frame.options);
|
|
30
|
+
|
|
31
|
+
return page;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -54,7 +54,9 @@ module.exports = {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
|
-
permissions:
|
|
57
|
+
permissions: {
|
|
58
|
+
docName: 'products'
|
|
59
|
+
},
|
|
58
60
|
async query(frame) {
|
|
59
61
|
const model = await membersService.api.productRepository.get(frame.data, frame.options);
|
|
60
62
|
|
|
@@ -95,7 +97,9 @@ module.exports = {
|
|
|
95
97
|
options: [
|
|
96
98
|
'id'
|
|
97
99
|
],
|
|
98
|
-
headers: {
|
|
100
|
+
headers: {
|
|
101
|
+
cacheInvalidate: true
|
|
102
|
+
},
|
|
99
103
|
validation: {
|
|
100
104
|
options: {
|
|
101
105
|
id: {
|
|
@@ -112,11 +116,6 @@ module.exports = {
|
|
|
112
116
|
frame.options
|
|
113
117
|
);
|
|
114
118
|
|
|
115
|
-
if (model.wasChanged()) {
|
|
116
|
-
this.headers.cacheInvalidate = true;
|
|
117
|
-
} else {
|
|
118
|
-
this.headers.cacheInvalidate = false;
|
|
119
|
-
}
|
|
120
119
|
return model;
|
|
121
120
|
}
|
|
122
121
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:input:pages');
|
|
3
|
-
const mapNQLKeyValues = require('@
|
|
3
|
+
const mapNQLKeyValues = require('@tryghost/nql').utils.mapKeyValues;
|
|
4
4
|
const mobiledoc = require('../../../../../lib/mobiledoc');
|
|
5
5
|
const url = require('./utils/url');
|
|
6
6
|
const slugFilterOrder = require('./utils/slug-filter-order');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:input:posts');
|
|
3
|
-
const mapNQLKeyValues = require('@
|
|
3
|
+
const mapNQLKeyValues = require('@tryghost/nql').utils.mapKeyValues;
|
|
4
4
|
const url = require('./utils/url');
|
|
5
5
|
const slugFilterOrder = require('./utils/slug-filter-order');
|
|
6
6
|
const localUtils = require('../../index');
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
const mapper = require('./utils/mapper');
|
|
2
2
|
const gating = require('./utils/post-gating');
|
|
3
|
+
const membersService = require('../../../../../services/members');
|
|
3
4
|
|
|
4
5
|
module.exports = {
|
|
5
6
|
async read(model, apiConfig, frame) {
|
|
6
|
-
const
|
|
7
|
+
const tiersModels = await membersService.api.productRepository.list({
|
|
8
|
+
withRelated: ['monthlyPrice', 'yearlyPrice']
|
|
9
|
+
});
|
|
10
|
+
const tiers = tiersModels.data && tiersModels.data.map(tierModel => tierModel.toJSON());
|
|
11
|
+
|
|
12
|
+
const emailPost = await mapper.mapPost(model, frame, {tiers});
|
|
7
13
|
gating.forPost(emailPost, frame);
|
|
8
14
|
|
|
9
15
|
frame.response = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:pages');
|
|
2
2
|
const mapper = require('./utils/mapper');
|
|
3
|
+
const membersService = require('../../../../../services/members');
|
|
3
4
|
|
|
4
5
|
module.exports = {
|
|
5
6
|
async all(models, apiConfig, frame) {
|
|
@@ -10,9 +11,15 @@ module.exports = {
|
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
12
13
|
let pages = [];
|
|
14
|
+
|
|
15
|
+
const tiersModels = await membersService.api.productRepository.list({
|
|
16
|
+
withRelated: ['monthlyPrice', 'yearlyPrice']
|
|
17
|
+
});
|
|
18
|
+
const tiers = tiersModels.data ? tiersModels.data.map(tierModel => tierModel.toJSON()) : [];
|
|
19
|
+
|
|
13
20
|
if (models.meta) {
|
|
14
21
|
for (let model of models.data) {
|
|
15
|
-
let page = await mapper.mapPage(model, frame);
|
|
22
|
+
let page = await mapper.mapPage(model, frame, {tiers});
|
|
16
23
|
pages.push(page);
|
|
17
24
|
}
|
|
18
25
|
frame.response = {
|
|
@@ -22,7 +29,7 @@ module.exports = {
|
|
|
22
29
|
|
|
23
30
|
return;
|
|
24
31
|
}
|
|
25
|
-
let page = await mapper.mapPage(models, frame);
|
|
32
|
+
let page = await mapper.mapPage(models, frame, {tiers});
|
|
26
33
|
frame.response = {
|
|
27
34
|
pages: [page]
|
|
28
35
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:posts');
|
|
2
2
|
const mapper = require('./utils/mapper');
|
|
3
|
+
const membersService = require('../../../../../services/members');
|
|
3
4
|
|
|
4
5
|
module.exports = {
|
|
5
6
|
async all(models, apiConfig, frame) {
|
|
@@ -10,9 +11,14 @@ module.exports = {
|
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
12
13
|
let posts = [];
|
|
14
|
+
|
|
15
|
+
const tiersModels = await membersService.api.productRepository.list({
|
|
16
|
+
withRelated: ['monthlyPrice', 'yearlyPrice']
|
|
17
|
+
});
|
|
18
|
+
const tiers = tiersModels.data ? tiersModels.data.map(tierModel => tierModel.toJSON()) : [];
|
|
13
19
|
if (models.meta) {
|
|
14
20
|
for (let model of models.data) {
|
|
15
|
-
let post = await mapper.mapPost(model, frame);
|
|
21
|
+
let post = await mapper.mapPost(model, frame, {tiers});
|
|
16
22
|
posts.push(post);
|
|
17
23
|
}
|
|
18
24
|
frame.response = {
|
|
@@ -22,7 +28,7 @@ module.exports = {
|
|
|
22
28
|
|
|
23
29
|
return;
|
|
24
30
|
}
|
|
25
|
-
let post = await mapper.mapPost(models, frame);
|
|
31
|
+
let post = await mapper.mapPost(models, frame, {tiers});
|
|
26
32
|
frame.response = {
|
|
27
33
|
posts: [post]
|
|
28
34
|
};
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
const mapper = require('./utils/mapper');
|
|
2
|
+
const membersService = require('../../../../../services/members');
|
|
2
3
|
|
|
3
4
|
module.exports = {
|
|
4
5
|
async all(model, apiConfig, frame) {
|
|
5
|
-
const
|
|
6
|
+
const tiersModels = await membersService.api.productRepository.list({
|
|
7
|
+
withRelated: ['monthlyPrice', 'yearlyPrice']
|
|
8
|
+
});
|
|
9
|
+
const tiers = tiersModels.data ? tiersModels.data.map(tierModel => tierModel.toJSON()) : [];
|
|
10
|
+
|
|
11
|
+
const data = await mapper.mapPost(model, frame, {tiers});
|
|
6
12
|
frame.response = {
|
|
7
13
|
preview: [data]
|
|
8
14
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//@ts-check
|
|
2
2
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:products');
|
|
3
3
|
const _ = require('lodash');
|
|
4
|
+
const utils = require('../../../../shared/utils');
|
|
4
5
|
|
|
5
6
|
const allowedIncludes = ['stripe_prices', 'monthly_price', 'yearly_price'];
|
|
6
7
|
|
|
@@ -22,7 +23,7 @@ module.exports = {
|
|
|
22
23
|
*/
|
|
23
24
|
function paginatedProducts(page, _apiConfig, frame) {
|
|
24
25
|
const requestedQueryIncludes = frame.original && frame.original.query && frame.original.query.include && frame.original.query.include.split(',') || [];
|
|
25
|
-
const requestedOptionsIncludes = frame.original && frame.original.options && frame.original.options.include || [];
|
|
26
|
+
const requestedOptionsIncludes = utils.options.trimAndLowerCase(frame.original && frame.original.options && frame.original.options.include || []);
|
|
26
27
|
return {
|
|
27
28
|
products: page.data.map((model) => {
|
|
28
29
|
return cleanIncludes(
|
|
@@ -74,6 +75,7 @@ function serializeProduct(product, options, apiType) {
|
|
|
74
75
|
description: json.description,
|
|
75
76
|
slug: json.slug,
|
|
76
77
|
active: json.active,
|
|
78
|
+
visibility: json.visibility,
|
|
77
79
|
type: json.type,
|
|
78
80
|
welcome_page_url: json.welcome_page_url,
|
|
79
81
|
created_at: json.created_at,
|
|
@@ -3,6 +3,7 @@ const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:ti
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
|
|
5
5
|
const allowedIncludes = ['monthly_price', 'yearly_price'];
|
|
6
|
+
const utils = require('../../../../shared/utils');
|
|
6
7
|
|
|
7
8
|
module.exports = {
|
|
8
9
|
browse: createSerializer('browse', paginatedTiers),
|
|
@@ -22,7 +23,7 @@ module.exports = {
|
|
|
22
23
|
*/
|
|
23
24
|
function paginatedTiers(page, _apiConfig, frame) {
|
|
24
25
|
const requestedQueryIncludes = frame.original && frame.original.query && frame.original.query.include && frame.original.query.include.split(',') || [];
|
|
25
|
-
const requestedOptionsIncludes = frame.original && frame.original.options && frame.original.options.include || [];
|
|
26
|
+
const requestedOptionsIncludes = utils.options.trimAndLowerCase(frame.original && frame.original.options && frame.original.options.include || []);
|
|
26
27
|
return {
|
|
27
28
|
tiers: page.data.map((model) => {
|
|
28
29
|
return cleanIncludes(
|
|
@@ -81,7 +82,8 @@ function serializeTier(tier, options, apiType) {
|
|
|
81
82
|
stripe_prices: json.stripePrices ? json.stripePrices.map(price => serializeStripePrice(price, hideStripeData)) : null,
|
|
82
83
|
monthly_price: serializeStripePrice(json.monthlyPrice, hideStripeData),
|
|
83
84
|
yearly_price: serializeStripePrice(json.yearlyPrice, hideStripeData),
|
|
84
|
-
benefits: json.benefits || null
|
|
85
|
+
benefits: json.benefits || null,
|
|
86
|
+
visibility: json.visibility
|
|
85
87
|
};
|
|
86
88
|
|
|
87
89
|
return serialized;
|
|
@@ -31,7 +31,8 @@ const mapTag = (model, frame) => {
|
|
|
31
31
|
return jsonModel;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
const mapPost = async (model, frame) => {
|
|
34
|
+
const mapPost = async (model, frame, options = {}) => {
|
|
35
|
+
const {tiers: tiersData} = options || {};
|
|
35
36
|
const extendedOptions = Object.assign(_.cloneDeep(frame.options), {
|
|
36
37
|
extraProperties: ['canonical_url']
|
|
37
38
|
});
|
|
@@ -45,12 +46,21 @@ const mapPost = async (model, frame) => {
|
|
|
45
46
|
// Attach tiers to custom nql visibility filter
|
|
46
47
|
if (labsService.isSet('multipleProducts')
|
|
47
48
|
&& jsonModel.visibility
|
|
48
|
-
&& !['members', 'public', 'paid', 'tiers'].includes(jsonModel.visibility)
|
|
49
49
|
) {
|
|
50
|
-
|
|
50
|
+
if (['members', 'public'].includes(jsonModel.visibility) && jsonModel.tiers) {
|
|
51
|
+
jsonModel.tiers = tiersData || [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (jsonModel.visibility === 'paid' && jsonModel.tiers) {
|
|
55
|
+
jsonModel.tiers = tiersData ? tiersData.filter(t => t.type === 'paid') : [];
|
|
56
|
+
}
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
if (!['members', 'public', 'paid', 'tiers'].includes(jsonModel.visibility)) {
|
|
59
|
+
const tiers = await postsService.getProductsFromVisibilityFilter(jsonModel.visibility);
|
|
60
|
+
|
|
61
|
+
jsonModel.visibility = 'tiers';
|
|
62
|
+
jsonModel.tiers = tiers;
|
|
63
|
+
}
|
|
54
64
|
}
|
|
55
65
|
|
|
56
66
|
if (utils.isContentAPI(frame)) {
|
|
@@ -103,8 +113,8 @@ const mapPost = async (model, frame) => {
|
|
|
103
113
|
return jsonModel;
|
|
104
114
|
};
|
|
105
115
|
|
|
106
|
-
const mapPage = async (model, frame) => {
|
|
107
|
-
const jsonModel = await mapPost(model, frame);
|
|
116
|
+
const mapPage = async (model, frame, options) => {
|
|
117
|
+
const jsonModel = await mapPost(model, frame, options);
|
|
108
118
|
|
|
109
119
|
delete jsonModel.email_subject;
|
|
110
120
|
delete jsonModel.email_recipient_filter;
|
|
@@ -21,11 +21,11 @@ module.exports.input = (apiConfig, apiSerializers, frame) => {
|
|
|
21
21
|
const tasks = [];
|
|
22
22
|
const sharedSerializers = require('./input');
|
|
23
23
|
|
|
24
|
-
if (!
|
|
24
|
+
if (!apiConfig) {
|
|
25
25
|
return Promise.reject(new errors.IncorrectUsageError());
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
if (!
|
|
28
|
+
if (!apiSerializers) {
|
|
29
29
|
return Promise.reject(new errors.IncorrectUsageError());
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
-
const mapNQLKeyValues = require('@
|
|
2
|
+
const mapNQLKeyValues = require('@tryghost/nql').utils.mapKeyValues;
|
|
3
3
|
const debug = require('@tryghost/debug')('api:v2:utils:serializers:input:pages');
|
|
4
4
|
const mobiledoc = require('../../../../../lib/mobiledoc');
|
|
5
5
|
const url = require('./utils/url');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
-
const mapNQLKeyValues = require('@
|
|
2
|
+
const mapNQLKeyValues = require('@tryghost/nql').utils.mapKeyValues;
|
|
3
3
|
const debug = require('@tryghost/debug')('api:v2:utils:serializers:input:posts');
|
|
4
4
|
const url = require('./utils/url');
|
|
5
5
|
const localUtils = require('../../index');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const debug = require('@tryghost/debug')('api:v3:utils:serializers:input:pages');
|
|
3
|
-
const mapNQLKeyValues = require('@
|
|
3
|
+
const mapNQLKeyValues = require('@tryghost/nql').utils.mapKeyValues;
|
|
4
4
|
const mobiledoc = require('../../../../../lib/mobiledoc');
|
|
5
5
|
const url = require('./utils/url');
|
|
6
6
|
const slugFilterOrder = require('./utils/slug-filter-order');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const debug = require('@tryghost/debug')('api:v3:utils:serializers:input:posts');
|
|
3
|
-
const mapNQLKeyValues = require('@
|
|
3
|
+
const mapNQLKeyValues = require('@tryghost/nql').utils.mapKeyValues;
|
|
4
4
|
const url = require('./utils/url');
|
|
5
5
|
const slugFilterOrder = require('./utils/slug-filter-order');
|
|
6
6
|
const localUtils = require('../../index');
|
|
@@ -32,9 +32,10 @@ function configure(dbConfig) {
|
|
|
32
32
|
process.env.BTHREADS_BACKEND = 'child_process';
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
if (client === '
|
|
36
|
-
dbConfig.connection.timezone = '
|
|
35
|
+
if (client === 'mysql2') {
|
|
36
|
+
dbConfig.connection.timezone = 'Z';
|
|
37
37
|
dbConfig.connection.charset = 'utf8mb4';
|
|
38
|
+
dbConfig.connection.decimalNumbers = true;
|
|
38
39
|
|
|
39
40
|
// NOTE: disabled so that worker processes can use the db without
|
|
40
41
|
// requiring logging and causing file desriptor leaks.
|