ghost 4.38.1 → 4.40.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/Gruntfile.js +1 -1
- package/README.md +26 -18
- 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-6386b02480494a69c3bfe66206754836.js → ghost.min-88c665c3ba304b4f220d08b8bcf9d246.js} +525 -538
- package/core/built/assets/icons/{event-changed-subscription.svg → event-subscriptions.svg} +0 -1
- package/core/built/assets/icons/member.svg +3 -0
- package/core/built/assets/{vendor.min-c814d3c4b3f543c4cd5ef3aacd0fc645.js → vendor.min-ed945ad80ea22f1d3ffeec6d5ae63aee.js} +2355 -1419
- 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/public/ghost.css +205 -143
- package/core/frontend/public/ghost.min.css +1 -1
- package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +3 -1
- package/core/frontend/views/unsubscribe.hbs +28 -33
- package/core/frontend/web/middleware/error-handler.js +2 -2
- package/core/server/api/canary/authentication.js +7 -0
- package/core/server/api/canary/identities.js +0 -1
- package/core/server/api/canary/members.js +7 -1
- package/core/server/api/canary/tiers.js +3 -1
- 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/input/tiers.js +17 -0
- package/core/server/api/canary/utils/serializers/output/actions.js +2 -2
- package/core/server/api/canary/utils/serializers/output/authentication.js +3 -3
- package/core/server/api/canary/utils/serializers/output/authors.js +3 -3
- package/core/server/api/canary/utils/serializers/output/email-posts.js +2 -2
- package/core/server/api/canary/utils/serializers/output/emails.js +3 -3
- package/core/server/api/canary/utils/serializers/output/images.js +2 -2
- package/core/server/api/canary/utils/serializers/output/integrations.js +5 -6
- package/core/server/api/canary/utils/serializers/output/labels.js +3 -3
- package/core/server/api/canary/utils/serializers/output/mappers/actions.js +7 -0
- package/core/server/api/canary/utils/serializers/output/mappers/emails.js +17 -0
- package/core/server/api/canary/utils/serializers/output/mappers/images.js +5 -0
- package/core/server/api/canary/utils/serializers/output/mappers/index.js +12 -0
- package/core/server/api/canary/utils/serializers/output/mappers/integrations.js +13 -0
- package/core/server/api/canary/utils/serializers/output/mappers/labels.js +4 -0
- package/core/server/api/canary/utils/serializers/output/mappers/pages.js +11 -0
- package/core/server/api/canary/utils/serializers/output/mappers/posts.js +101 -0
- package/core/server/api/canary/utils/serializers/output/mappers/settings.js +37 -0
- package/core/server/api/canary/utils/serializers/output/mappers/tags.js +11 -0
- package/core/server/api/canary/utils/serializers/output/mappers/users.js +12 -0
- package/core/server/api/canary/utils/serializers/output/members.js +2 -7
- package/core/server/api/canary/utils/serializers/output/pages.js +3 -3
- package/core/server/api/canary/utils/serializers/output/posts.js +3 -3
- package/core/server/api/canary/utils/serializers/output/preview.js +2 -2
- package/core/server/api/canary/utils/serializers/output/settings.js +2 -2
- package/core/server/api/canary/utils/serializers/output/tags.js +3 -3
- package/core/server/api/canary/utils/serializers/output/users.js +3 -3
- 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/exporter/table-lists.js +1 -0
- package/core/server/data/importer/import-manager.js +152 -113
- 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.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/migrations/versions/4.40/2022-03-07-14-37-add-members-cancel-events-table.js +8 -0
- package/core/server/data/migrations/versions/4.40/2022-03-15-06-40-add-offers-admin-integration-permission-roles.js +23 -0
- package/core/server/data/migrations/versions/4.40/2022-03-15-06-40-add-tiers-admin-integration-permission-roles.js +20 -0
- package/core/server/data/schema/default-settings/default-settings.json +2 -2
- package/core/server/data/schema/fixtures/fixtures.json +17 -160
- package/core/server/data/schema/schema.js +6 -0
- 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/member-cancel-event.js +28 -0
- package/core/server/models/post.js +16 -6
- package/core/server/models/user.js +1 -1
- package/core/server/services/auth/setup.js +29 -13
- package/core/server/services/mega/mega.js +4 -4
- package/core/server/services/mega/template.js +2 -1
- package/core/server/services/members/api.js +1 -0
- package/core/server/services/members/content-gating.js +1 -1
- package/core/server/services/members/middleware.js +1 -0
- package/core/server/services/posts/posts-service.js +1 -1
- package/core/server/services/themes/validate.js +3 -3
- 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 +0 -3
- package/core/server/web/api/canary/admin/middleware.js +2 -0
- package/core/server/web/parent/backend.js +2 -1
- package/core/server/web/shared/middleware/uncapitalise.js +2 -1
- package/core/shared/config/defaults.json +2 -2
- package/core/shared/config/overrides.json +7 -3
- package/core/shared/labs.js +8 -10
- package/core/shared/url-utils.js +4 -1
- package/package.json +37 -36
- package/yarn.lock +513 -329
- package/core/built/assets/ghost-dark-9f760f16230b8bc52e188d6ce28516b0.css +0 -1
- package/core/built/assets/ghost.min-f4c59dd57a2136df8b0a34f87c099034.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
- package/core/server/api/canary/utils/serializers/output/utils/mapper.js +0 -213
- package/core/server/frontend/ghost.min.css +0 -1
|
@@ -58,7 +58,13 @@ module.exports = {
|
|
|
58
58
|
'include'
|
|
59
59
|
],
|
|
60
60
|
permissions: true,
|
|
61
|
-
validation: {
|
|
61
|
+
validation: {
|
|
62
|
+
options: {
|
|
63
|
+
include: {
|
|
64
|
+
values: allowedIncludes
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
62
68
|
async query(frame) {
|
|
63
69
|
const page = await membersService.api.memberBREADService.browse(frame.options);
|
|
64
70
|
|
|
@@ -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,3 +1,13 @@
|
|
|
1
|
+
const localUtils = require('../../index');
|
|
2
|
+
|
|
3
|
+
const forceActiveFilter = (frame) => {
|
|
4
|
+
if (frame.options.filter) {
|
|
5
|
+
frame.options.filter = `(${frame.options.filter})+active:true`;
|
|
6
|
+
} else {
|
|
7
|
+
frame.options.filter = 'active:true';
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
1
11
|
module.exports = {
|
|
2
12
|
all(_apiConfig, frame) {
|
|
3
13
|
if (!frame.options.withRelated) {
|
|
@@ -18,6 +28,13 @@ module.exports = {
|
|
|
18
28
|
});
|
|
19
29
|
},
|
|
20
30
|
|
|
31
|
+
browse(_apiConfig, frame) {
|
|
32
|
+
if (localUtils.isContentAPI(frame)) {
|
|
33
|
+
// CASE: content api can only has active tiers
|
|
34
|
+
forceActiveFilter(frame);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
|
|
21
38
|
add(_apiConfig, frame) {
|
|
22
39
|
if (frame.data.products) {
|
|
23
40
|
frame.data = frame.data.products[0];
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:actions');
|
|
2
|
-
const
|
|
2
|
+
const mappers = require('./mappers');
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
browse(models, apiConfig, frame) {
|
|
6
6
|
debug('browse');
|
|
7
7
|
|
|
8
8
|
frame.response = {
|
|
9
|
-
actions: models.data.map(model =>
|
|
9
|
+
actions: models.data.map(model => mappers.actions(model, frame)),
|
|
10
10
|
meta: models.meta
|
|
11
11
|
};
|
|
12
12
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const tpl = require('@tryghost/tpl');
|
|
2
|
-
const
|
|
2
|
+
const mappers = require('./mappers');
|
|
3
3
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:authentication');
|
|
4
4
|
|
|
5
5
|
const messages = {
|
|
@@ -12,7 +12,7 @@ module.exports = {
|
|
|
12
12
|
setup(user, apiConfig, frame) {
|
|
13
13
|
frame.response = {
|
|
14
14
|
users: [
|
|
15
|
-
|
|
15
|
+
mappers.users(user, {options: {context: {internal: true}}})
|
|
16
16
|
]
|
|
17
17
|
};
|
|
18
18
|
},
|
|
@@ -20,7 +20,7 @@ module.exports = {
|
|
|
20
20
|
updateSetup(user, apiConfig, frame) {
|
|
21
21
|
frame.response = {
|
|
22
22
|
users: [
|
|
23
|
-
|
|
23
|
+
mappers.users(user, {options: {context: {internal: true}}})
|
|
24
24
|
]
|
|
25
25
|
};
|
|
26
26
|
},
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:authors');
|
|
2
|
-
const
|
|
2
|
+
const mappers = require('./mappers');
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
browse(models, apiConfig, frame) {
|
|
6
6
|
debug('browse');
|
|
7
7
|
|
|
8
8
|
frame.response = {
|
|
9
|
-
authors: models.data.map(model =>
|
|
9
|
+
authors: models.data.map(model => mappers.users(model, frame)),
|
|
10
10
|
meta: models.meta
|
|
11
11
|
};
|
|
12
12
|
},
|
|
@@ -15,7 +15,7 @@ module.exports = {
|
|
|
15
15
|
debug('read');
|
|
16
16
|
|
|
17
17
|
frame.response = {
|
|
18
|
-
authors: [
|
|
18
|
+
authors: [mappers.users(model, frame)]
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const mappers = require('./mappers');
|
|
2
2
|
const gating = require('./utils/post-gating');
|
|
3
3
|
const membersService = require('../../../../../services/members');
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ module.exports = {
|
|
|
9
9
|
});
|
|
10
10
|
const tiers = tiersModels.data && tiersModels.data.map(tierModel => tierModel.toJSON());
|
|
11
11
|
|
|
12
|
-
const emailPost = await
|
|
12
|
+
const emailPost = await mappers.posts(model, frame, {tiers});
|
|
13
13
|
gating.forPost(emailPost, frame);
|
|
14
14
|
|
|
15
15
|
frame.response = {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
const
|
|
1
|
+
const mappers = require('./mappers');
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
read(email, apiConfig, frame) {
|
|
5
5
|
frame.response = {
|
|
6
|
-
emails: [
|
|
6
|
+
emails: [mappers.emails(email, frame)]
|
|
7
7
|
};
|
|
8
8
|
},
|
|
9
9
|
|
|
10
10
|
browse(page, apiConfig, frame) {
|
|
11
11
|
const data = {
|
|
12
|
-
emails: page.data.map(model =>
|
|
12
|
+
emails: page.data.map(model => mappers.emails(model, frame)),
|
|
13
13
|
meta: page.meta
|
|
14
14
|
};
|
|
15
15
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:images');
|
|
2
|
-
const
|
|
2
|
+
const mappers = require('./mappers');
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
upload(path, apiConfig, frame) {
|
|
@@ -7,7 +7,7 @@ module.exports = {
|
|
|
7
7
|
|
|
8
8
|
return frame.response = {
|
|
9
9
|
images: [{
|
|
10
|
-
url:
|
|
10
|
+
url: mappers.images(path),
|
|
11
11
|
// NOTE: ref field is here to have reference point on the client
|
|
12
12
|
// for example when substituting existing images in the mobiledoc
|
|
13
13
|
// this field would serve as an identifier to find images to replace
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:integrations');
|
|
2
|
-
const
|
|
2
|
+
const mappers = require('./mappers');
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
browse({data, meta}, apiConfig, frame) {
|
|
6
6
|
debug('browse');
|
|
7
7
|
|
|
8
8
|
frame.response = {
|
|
9
|
-
integrations: data.map(model =>
|
|
9
|
+
integrations: data.map(model => mappers.integrations(model, frame)),
|
|
10
10
|
meta
|
|
11
11
|
};
|
|
12
12
|
},
|
|
@@ -14,22 +14,21 @@ module.exports = {
|
|
|
14
14
|
debug('read');
|
|
15
15
|
|
|
16
16
|
frame.response = {
|
|
17
|
-
integrations: [
|
|
17
|
+
integrations: [mappers.integrations(model, frame)]
|
|
18
18
|
};
|
|
19
19
|
},
|
|
20
20
|
add(model, apiConfig, frame) {
|
|
21
21
|
debug('add');
|
|
22
22
|
|
|
23
23
|
frame.response = {
|
|
24
|
-
integrations: [
|
|
24
|
+
integrations: [mappers.integrations(model, frame)]
|
|
25
25
|
};
|
|
26
26
|
},
|
|
27
27
|
edit(model, apiConfig, frame) {
|
|
28
28
|
debug('edit');
|
|
29
29
|
|
|
30
30
|
frame.response = {
|
|
31
|
-
integrations: [
|
|
31
|
+
integrations: [mappers.integrations(model, frame)]
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
|
-
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:labels');
|
|
2
|
-
const
|
|
2
|
+
const mappers = require('./mappers');
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
all(models, apiConfig, frame) {
|
|
@@ -11,7 +11,7 @@ module.exports = {
|
|
|
11
11
|
|
|
12
12
|
if (models.meta) {
|
|
13
13
|
frame.response = {
|
|
14
|
-
labels: models.data.map(model =>
|
|
14
|
+
labels: models.data.map(model => mappers.labels(model, frame)),
|
|
15
15
|
meta: models.meta
|
|
16
16
|
};
|
|
17
17
|
|
|
@@ -19,7 +19,7 @@ module.exports = {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
frame.response = {
|
|
22
|
-
labels: [
|
|
22
|
+
labels: [mappers.labels(models, frame)]
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const mega = require('../../../../../../services/mega');
|
|
2
|
+
|
|
3
|
+
module.exports = (model, frame) => {
|
|
4
|
+
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
|
|
5
|
+
|
|
6
|
+
// Ensure we're not outputting unwanted replacement strings when viewing email contents
|
|
7
|
+
// TODO: extract this to a utility, it's duplicated in the email-preview API controller
|
|
8
|
+
const replacements = mega.postEmailSerializer.parseReplacements(jsonModel);
|
|
9
|
+
replacements.forEach((replacement) => {
|
|
10
|
+
jsonModel[replacement.format] = jsonModel[replacement.format].replace(
|
|
11
|
+
replacement.match,
|
|
12
|
+
replacement.fallback || ''
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return jsonModel;
|
|
17
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
actions: require('./actions'),
|
|
3
|
+
emails: require('./emails'),
|
|
4
|
+
images: require('./images'),
|
|
5
|
+
integrations: require('./integrations'),
|
|
6
|
+
labels: require('./labels'),
|
|
7
|
+
pages: require('./pages'),
|
|
8
|
+
posts: require('./posts'),
|
|
9
|
+
settings: require('./settings'),
|
|
10
|
+
tags: require('./tags'),
|
|
11
|
+
users: require('./users')
|
|
12
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module.exports = (model, frame) => {
|
|
2
|
+
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
|
|
3
|
+
|
|
4
|
+
if (jsonModel.api_keys) {
|
|
5
|
+
jsonModel.api_keys.forEach((key) => {
|
|
6
|
+
if (key.type === 'admin') {
|
|
7
|
+
key.secret = `${key.id}:${key.secret}`;
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return jsonModel;
|
|
13
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const mapPost = require('./posts');
|
|
2
|
+
|
|
3
|
+
module.exports = async (model, frame, options) => {
|
|
4
|
+
const jsonModel = await mapPost(model, frame, options);
|
|
5
|
+
|
|
6
|
+
delete jsonModel.email_subject;
|
|
7
|
+
delete jsonModel.email_recipient_filter;
|
|
8
|
+
delete jsonModel.email_only;
|
|
9
|
+
|
|
10
|
+
return jsonModel;
|
|
11
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
|
|
3
|
+
const mapTag = require('./tags');
|
|
4
|
+
const mapUser = require('./users');
|
|
5
|
+
const mapEmail = require('./emails');
|
|
6
|
+
|
|
7
|
+
const clean = require('../utils/clean');
|
|
8
|
+
const date = require('../utils/date');
|
|
9
|
+
const extraAttrs = require('../utils/extra-attrs');
|
|
10
|
+
const gating = require('../utils/post-gating');
|
|
11
|
+
const url = require('../utils/url');
|
|
12
|
+
|
|
13
|
+
const utils = require('../../../index');
|
|
14
|
+
|
|
15
|
+
const postsMetaSchema = require('../../../../../../data/schema').tables.posts_meta;
|
|
16
|
+
const labsService = require('../../../../../../../shared/labs');
|
|
17
|
+
|
|
18
|
+
const getPostServiceInstance = require('../../../../../../services/posts/posts-service');
|
|
19
|
+
const postsService = getPostServiceInstance('canary');
|
|
20
|
+
|
|
21
|
+
module.exports = async (model, frame, options = {}) => {
|
|
22
|
+
const {tiers: tiersData} = options || {};
|
|
23
|
+
const extendedOptions = Object.assign(_.cloneDeep(frame.options), {
|
|
24
|
+
extraProperties: ['canonical_url']
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const jsonModel = model.toJSON(extendedOptions);
|
|
28
|
+
|
|
29
|
+
url.forPost(model.id, jsonModel, frame);
|
|
30
|
+
|
|
31
|
+
extraAttrs.forPost(frame, model, jsonModel);
|
|
32
|
+
|
|
33
|
+
// Attach tiers to custom nql visibility filter
|
|
34
|
+
if (labsService.isSet('multipleProducts')
|
|
35
|
+
&& jsonModel.visibility
|
|
36
|
+
) {
|
|
37
|
+
if (['members', 'public'].includes(jsonModel.visibility) && jsonModel.tiers) {
|
|
38
|
+
jsonModel.tiers = tiersData || [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (jsonModel.visibility === 'paid' && jsonModel.tiers) {
|
|
42
|
+
jsonModel.tiers = tiersData ? tiersData.filter(t => t.type === 'paid') : [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!['members', 'public', 'paid', 'tiers'].includes(jsonModel.visibility)) {
|
|
46
|
+
const tiers = await postsService.getProductsFromVisibilityFilter(jsonModel.visibility);
|
|
47
|
+
|
|
48
|
+
jsonModel.visibility = 'tiers';
|
|
49
|
+
jsonModel.tiers = tiers;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (utils.isContentAPI(frame)) {
|
|
54
|
+
// Content api v2 still expects page prop
|
|
55
|
+
if (jsonModel.type === 'page') {
|
|
56
|
+
jsonModel.page = true;
|
|
57
|
+
}
|
|
58
|
+
date.forPost(jsonModel);
|
|
59
|
+
gating.forPost(jsonModel, frame);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Transforms post/page metadata to flat structure
|
|
63
|
+
let metaAttrs = _.keys(_.omit(postsMetaSchema, ['id', 'post_id']));
|
|
64
|
+
_(metaAttrs).filter((k) => {
|
|
65
|
+
return (!frame.options.columns || (frame.options.columns && frame.options.columns.includes(k)));
|
|
66
|
+
}).each((attr) => {
|
|
67
|
+
// NOTE: the default of `email_only` is `false` which is why we default to `false` instead of `null`
|
|
68
|
+
// The undefined value is possible because `posts_meta` table is lazily created only one of the
|
|
69
|
+
// values is assigned.
|
|
70
|
+
const defaultValue = (attr === 'email_only') ? false : null;
|
|
71
|
+
jsonModel[attr] = _.get(jsonModel.posts_meta, attr) || defaultValue;
|
|
72
|
+
});
|
|
73
|
+
delete jsonModel.posts_meta;
|
|
74
|
+
|
|
75
|
+
clean.post(jsonModel, frame);
|
|
76
|
+
|
|
77
|
+
if (frame.options && frame.options.withRelated) {
|
|
78
|
+
frame.options.withRelated.forEach((relation) => {
|
|
79
|
+
// @NOTE: this block also decorates primary_tag/primary_author objects as they
|
|
80
|
+
// are being passed by reference in tags/authors. Might be refactored into more explicit call
|
|
81
|
+
// in the future, but is good enough for current use-case
|
|
82
|
+
if (relation === 'tags' && jsonModel.tags) {
|
|
83
|
+
jsonModel.tags = jsonModel.tags.map(tag => mapTag(tag, frame));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (relation === 'authors' && jsonModel.authors) {
|
|
87
|
+
jsonModel.authors = jsonModel.authors.map(author => mapUser(author, frame));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (relation === 'email' && jsonModel.email) {
|
|
91
|
+
jsonModel.email = mapEmail(jsonModel.email, frame);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (relation === 'email' && _.isEmpty(jsonModel.email)) {
|
|
95
|
+
jsonModel.email = null;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return jsonModel;
|
|
101
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
|
|
3
|
+
const extraAttrs = require('../utils/extra-attrs');
|
|
4
|
+
const url = require('../utils/url');
|
|
5
|
+
|
|
6
|
+
module.exports = (attrs, frame) => {
|
|
7
|
+
url.forSettings(attrs);
|
|
8
|
+
extraAttrs.forSettings(attrs, frame);
|
|
9
|
+
|
|
10
|
+
// NOTE: The cleanup of deprecated ghost_head/ghost_foot has to happen here
|
|
11
|
+
// because codeinjection_head/codeinjection_foot are assigned on a previous
|
|
12
|
+
// `forSettings` step. This logic can be rewritten once we get rid of deprecated
|
|
13
|
+
// fields completely.
|
|
14
|
+
if (_.isArray(attrs)) {
|
|
15
|
+
const keysToFilter = ['ghost_head', 'ghost_foot'];
|
|
16
|
+
|
|
17
|
+
// NOTE: to support edits of deprecated 'slack' setting artificial 'slack_url' and 'slack_username'
|
|
18
|
+
// were added to the request body in the input serializer. These should not be returned in response
|
|
19
|
+
// body unless directly requested
|
|
20
|
+
if (frame.original.body && frame.original.body.settings) {
|
|
21
|
+
const requestedEditSlackUrl = frame.original.body.settings.find(s => s.key === 'slack_url');
|
|
22
|
+
const requestedEditSlackUsername = frame.original.body.settings.find(s => s.key === 'slack_username');
|
|
23
|
+
|
|
24
|
+
if (!requestedEditSlackUrl) {
|
|
25
|
+
keysToFilter.push('slack_url');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!requestedEditSlackUsername) {
|
|
29
|
+
keysToFilter.push('slack_username');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
attrs = _.filter(attrs, attr => !(keysToFilter.includes(attr.key)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return attrs;
|
|
37
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const clean = require('../utils/clean');
|
|
2
|
+
const url = require('../utils/url');
|
|
3
|
+
|
|
4
|
+
module.exports = (model, frame) => {
|
|
5
|
+
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
|
|
6
|
+
|
|
7
|
+
url.forTag(model.id, jsonModel, frame.options);
|
|
8
|
+
clean.tag(jsonModel, frame);
|
|
9
|
+
|
|
10
|
+
return jsonModel;
|
|
11
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const clean = require('../utils/clean');
|
|
2
|
+
const url = require('../utils/url');
|
|
3
|
+
|
|
4
|
+
module.exports = (model, frame) => {
|
|
5
|
+
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
|
|
6
|
+
|
|
7
|
+
url.forUser(model.id, jsonModel, frame.options);
|
|
8
|
+
|
|
9
|
+
clean.author(jsonModel, frame);
|
|
10
|
+
|
|
11
|
+
return jsonModel;
|
|
12
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
//@ts-check
|
|
2
2
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:members');
|
|
3
3
|
const {unparse} = require('@tryghost/members-csv');
|
|
4
|
-
const labs = require('../../../../../../shared/labs');
|
|
5
4
|
|
|
6
5
|
module.exports = {
|
|
7
6
|
hasActiveStripeSubscriptions: createSerializer('hasActiveStripeSubscriptions', passthrough),
|
|
@@ -17,7 +16,6 @@ module.exports = {
|
|
|
17
16
|
exportCSV: createSerializer('exportCSV', exportCSV),
|
|
18
17
|
|
|
19
18
|
importCSV: createSerializer('importCSV', passthrough),
|
|
20
|
-
stats: createSerializer('stats', passthrough),
|
|
21
19
|
memberStats: createSerializer('memberStats', passthrough),
|
|
22
20
|
mrrStats: createSerializer('mrrStats', passthrough),
|
|
23
21
|
subscriberStats: createSerializer('subscriberStats', passthrough),
|
|
@@ -125,13 +123,10 @@ function serializeMember(member, options) {
|
|
|
125
123
|
email_opened_count: json.email_opened_count,
|
|
126
124
|
email_open_rate: json.email_open_rate,
|
|
127
125
|
email_recipients: json.email_recipients,
|
|
128
|
-
status: json.status
|
|
126
|
+
status: json.status,
|
|
127
|
+
last_seen_at: json.last_seen_at
|
|
129
128
|
};
|
|
130
129
|
|
|
131
|
-
if (labs.isSet('membersLastSeenFilter')) {
|
|
132
|
-
serialized.last_seen_at = json.last_seen_at;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
130
|
if (json.products) {
|
|
136
131
|
serialized.products = json.products;
|
|
137
132
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:pages');
|
|
2
|
-
const
|
|
2
|
+
const mappers = require('./mappers');
|
|
3
3
|
const membersService = require('../../../../../services/members');
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
@@ -19,7 +19,7 @@ module.exports = {
|
|
|
19
19
|
|
|
20
20
|
if (models.meta) {
|
|
21
21
|
for (let model of models.data) {
|
|
22
|
-
let page = await
|
|
22
|
+
let page = await mappers.pages(model, frame, {tiers});
|
|
23
23
|
pages.push(page);
|
|
24
24
|
}
|
|
25
25
|
frame.response = {
|
|
@@ -29,7 +29,7 @@ module.exports = {
|
|
|
29
29
|
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
|
-
let page = await
|
|
32
|
+
let page = await mappers.pages(models, frame, {tiers});
|
|
33
33
|
frame.response = {
|
|
34
34
|
pages: [page]
|
|
35
35
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:posts');
|
|
2
|
-
const
|
|
2
|
+
const mappers = require('./mappers');
|
|
3
3
|
const membersService = require('../../../../../services/members');
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
@@ -18,7 +18,7 @@ module.exports = {
|
|
|
18
18
|
const tiers = tiersModels.data ? tiersModels.data.map(tierModel => tierModel.toJSON()) : [];
|
|
19
19
|
if (models.meta) {
|
|
20
20
|
for (let model of models.data) {
|
|
21
|
-
let post = await
|
|
21
|
+
let post = await mappers.posts(model, frame, {tiers});
|
|
22
22
|
posts.push(post);
|
|
23
23
|
}
|
|
24
24
|
frame.response = {
|
|
@@ -28,7 +28,7 @@ module.exports = {
|
|
|
28
28
|
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
-
let post = await
|
|
31
|
+
let post = await mappers.posts(models, frame, {tiers});
|
|
32
32
|
frame.response = {
|
|
33
33
|
posts: [post]
|
|
34
34
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const mappers = require('./mappers');
|
|
2
2
|
const membersService = require('../../../../../services/members');
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
@@ -8,7 +8,7 @@ module.exports = {
|
|
|
8
8
|
});
|
|
9
9
|
const tiers = tiersModels.data ? tiersModels.data.map(tierModel => tierModel.toJSON()) : [];
|
|
10
10
|
|
|
11
|
-
const data = await
|
|
11
|
+
const data = await mappers.posts(model, frame, {tiers});
|
|
12
12
|
frame.response = {
|
|
13
13
|
preview: [data]
|
|
14
14
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const utils = require('../../index');
|
|
3
|
-
const
|
|
3
|
+
const mappers = require('./mappers');
|
|
4
4
|
const _private = {};
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -34,7 +34,7 @@ module.exports = {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
frame.response = {
|
|
37
|
-
settings:
|
|
37
|
+
settings: mappers.settings(filteredSettings, frame),
|
|
38
38
|
meta: {}
|
|
39
39
|
};
|
|
40
40
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:tags');
|
|
2
|
-
const
|
|
2
|
+
const mappers = require('./mappers');
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
all(models, apiConfig, frame) {
|
|
@@ -11,7 +11,7 @@ module.exports = {
|
|
|
11
11
|
|
|
12
12
|
if (models.meta) {
|
|
13
13
|
frame.response = {
|
|
14
|
-
tags: models.data.map(model =>
|
|
14
|
+
tags: models.data.map(model => mappers.tags(model, frame)),
|
|
15
15
|
meta: models.meta
|
|
16
16
|
};
|
|
17
17
|
|
|
@@ -19,7 +19,7 @@ module.exports = {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
frame.response = {
|
|
22
|
-
tags: [
|
|
22
|
+
tags: [mappers.tags(models, frame)]
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
};
|