ghost 5.3.1 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-custom-theme-settings-service-0.0.0.tgz +0 -0
- package/components/tryghost-domain-events-0.0.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-0.0.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-0.0.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-0.0.0.tgz +0 -0
- package/components/tryghost-magic-link-0.0.0.tgz +0 -0
- package/components/tryghost-member-analytics-service-0.0.0.tgz +0 -0
- package/components/tryghost-member-events-0.0.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-0.0.0.tgz +0 -0
- package/components/tryghost-members-api-0.0.0.tgz +0 -0
- package/components/tryghost-members-csv-0.0.0.tgz +0 -0
- package/components/tryghost-members-events-service-0.0.0.tgz +0 -0
- package/components/tryghost-members-importer-0.0.0.tgz +0 -0
- package/components/tryghost-members-offers-0.0.0.tgz +0 -0
- package/components/tryghost-members-payments-0.0.0.tgz +0 -0
- package/components/tryghost-members-ssr-0.0.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-0.0.0.tgz +0 -0
- package/components/tryghost-verification-trigger-0.0.0.tgz +0 -0
- 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/screen.css +31 -8
- package/content/themes/casper/default.hbs +8 -5
- package/content/themes/casper/gulpfile.js +1 -1
- package/content/themes/casper/package.json +9 -9
- package/content/themes/casper/yarn.lock +1154 -1249
- package/core/boot.js +5 -0
- package/core/built/assets/{chunk.3.dc389a0f93cb5fabd695.js → chunk.3.550552fbc71864fb9738.js} +20 -20
- package/core/built/assets/fonts/Inter.ttf +0 -0
- package/core/built/assets/ghost-dark-5c2a961b35311d7298136e02289d98b2.css +1 -0
- package/core/built/assets/ghost.min-a89d10b3b58c1a5ebaca68cef93a404c.css +1 -0
- package/core/built/assets/{ghost.min-f4bba3a2a5ef256b82641345505d4f0f.js → ghost.min-c75f224decd20f9538179d7564cd2ab4.js} +3025 -2883
- package/core/built/assets/icons/event-comment.svg +3 -0
- package/core/built/assets/{vendor.min-4076498ccd6c8412365f43b156084ed8.js → vendor.min-cf3af99dca0c71937669305afb3686a1.js} +6122 -3197
- package/core/frontend/helpers/comments.js +22 -10
- package/core/frontend/helpers/ghost_head.js +22 -4
- package/core/frontend/helpers/total_members.js +17 -0
- package/core/frontend/helpers/total_paid_members.js +16 -0
- package/core/frontend/utils/frontend-apps.js +33 -0
- package/core/frontend/utils/member-count.js +50 -0
- package/core/frontend/web/middleware/cors.js +2 -1
- package/core/server/api/endpoints/comments-comments.js +50 -32
- package/core/server/api/endpoints/offers-public.js +2 -2
- package/core/server/api/endpoints/offers.js +8 -8
- package/core/server/api/endpoints/settings.js +62 -30
- package/core/server/api/endpoints/utils/serializers/input/settings.js +1 -0
- package/core/server/api/endpoints/utils/serializers/output/config.js +2 -1
- package/core/server/api/endpoints/utils/serializers/output/index.js +0 -4
- package/core/server/api/endpoints/utils/serializers/output/mappers/activity-feed-events.js +17 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +18 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/index.js +2 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/offers.js +28 -0
- package/core/server/api/endpoints/utils/serializers/output/members.js +12 -1
- package/core/server/api/endpoints/utils/serializers/output/settings.js +2 -1
- package/core/server/api/endpoints/utils/validators/input/settings.js +22 -2
- package/core/server/data/exporter/table-lists.js +2 -1
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-29-add-comment-reporting-permissions.js +10 -0
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-31-drop-reports-reason.js +3 -0
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-32-drop-nullable-member-id-from-likes.js +4 -0
- package/core/server/data/migrations/versions/5.5/2022-07-18-14-33-fix-comments-on-delete-foreign-keys.js +119 -0
- package/core/server/data/migrations/versions/5.5/2022-07-21-08-56-add-jobs-table.js +11 -0
- package/core/server/data/schema/commands.js +7 -2
- package/core/server/data/schema/fixtures/fixtures.json +5 -0
- package/core/server/data/schema/schema.js +12 -4
- package/core/server/ghost-server.js +0 -22
- package/core/server/models/comment-report.js +34 -0
- package/core/server/models/comment.js +8 -7
- package/core/server/models/job.js +9 -0
- package/core/server/models/tag.js +4 -0
- package/core/server/services/comments/email-templates/new-comment-reply.hbs +2 -2
- package/core/server/services/comments/email-templates/new-comment-reply.txt.js +7 -8
- package/core/server/services/comments/email-templates/new-comment.hbs +2 -2
- package/core/server/services/comments/email-templates/new-comment.txt.js +7 -6
- package/core/server/services/comments/email-templates/report.hbs +199 -0
- package/core/server/services/comments/email-templates/report.txt.js +16 -0
- package/core/server/services/comments/emails.js +57 -1
- package/core/server/services/comments/service.js +194 -2
- package/core/server/services/jobs/job-service.js +24 -1
- package/core/server/services/mail/GhostMailer.js +1 -0
- package/core/server/services/members/SingleUseTokenProvider.js +3 -3
- package/core/server/services/members/api.js +2 -1
- package/core/server/services/members/config.js +4 -1
- package/core/server/services/members/middleware.js +14 -2
- package/core/server/services/members/settings.js +4 -90
- package/core/server/services/public-config/config.js +2 -1
- package/core/server/services/settings/emails/verify-email.js +166 -0
- package/core/server/services/settings/settings-bread-service.js +170 -4
- package/core/server/services/settings/settings-service.js +9 -1
- package/core/server/services/stripe/service.js +9 -1
- package/core/server/services/webhooks/serialize.js +5 -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/endpoints/admin/routes.js +6 -0
- package/core/server/web/api/endpoints/content/routes.js +2 -1
- package/core/server/web/api/middleware/cors.js +2 -1
- package/core/server/web/api/testmode/jobs/graceful-job.js +2 -2
- package/core/server/web/api/testmode/routes.js +14 -0
- package/core/server/web/comments/routes.js +2 -0
- package/core/server/web/members/app.js +2 -4
- package/core/shared/config/defaults.json +15 -7
- package/core/shared/config/env/config.testing.json +3 -2
- package/package.json +75 -60
- package/yarn.lock +1812 -1832
- package/core/built/assets/ghost-dark-9e5d1f0dfae41232e5e34e4d0df53ae0.css +0 -1
- package/core/built/assets/ghost.min-e7cfbd1800f8e99b9158f74f1e39cd76.css +0 -1
- package/core/server/api/endpoints/utils/serializers/output/offers.js +0 -16
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
const {SafeString} = require('../services/handlebars');
|
|
2
|
-
const {
|
|
2
|
+
const {urlUtils, getFrontendKey, labs, settingsCache} = require('../services/proxy');
|
|
3
|
+
const {getFrontendAppConfig, getDataAttributes} = require('../utils/frontend-apps');
|
|
3
4
|
|
|
4
5
|
async function comments(options) {
|
|
5
6
|
// todo: For now check on the comment id to exclude normal pages (we probably have a better way to do this)
|
|
6
7
|
|
|
7
8
|
const commentId = this.comment_id;
|
|
8
|
-
|
|
9
|
+
|
|
9
10
|
if (!commentId) {
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* We need to check if comments enabled, because the theme might not be using the other available helpers to check
|
|
16
|
+
* if comments is enabled + the member has access
|
|
17
|
+
* @type {'all'|'paid'|'off'}
|
|
18
|
+
*/
|
|
19
|
+
const commentsEnabled = settingsCache.get('comments_enabled');
|
|
20
|
+
const hasAccess = !!this.access;
|
|
21
|
+
|
|
22
|
+
if (commentsEnabled === 'off' || !hasAccess) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
13
26
|
let colorScheme = 'auto';
|
|
14
27
|
if (options.hash.color_scheme === 'dark' || options.hash.color_scheme === 'light') {
|
|
15
28
|
colorScheme = options.hash.color_scheme;
|
|
@@ -26,28 +39,27 @@ async function comments(options) {
|
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
const frontendKey = await getFrontendKey();
|
|
42
|
+
const {scriptUrl, stylesUrl, appVersion} = getFrontendAppConfig('comments');
|
|
29
43
|
|
|
30
44
|
const data = {
|
|
31
45
|
'ghost-comments': urlUtils.getSiteUrl(),
|
|
32
46
|
api: urlUtils.urlFor('api', {type: 'content'}, true),
|
|
33
47
|
admin: urlUtils.urlFor('admin', true),
|
|
34
48
|
key: frontendKey,
|
|
49
|
+
styles: stylesUrl,
|
|
35
50
|
'post-id': this.id,
|
|
36
51
|
'sentry-dsn': '', /* todo: insert sentry dsn key here */
|
|
37
52
|
'color-scheme': colorScheme,
|
|
38
53
|
'avatar-saturation': avatarSaturation,
|
|
39
54
|
'accent-color': accentColor,
|
|
40
|
-
'app-version':
|
|
55
|
+
'app-version': appVersion,
|
|
56
|
+
'comments-enabled': commentsEnabled
|
|
41
57
|
};
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
46
|
-
dataAttributes += `data-${key}="${value}" `;
|
|
47
|
-
});
|
|
59
|
+
const dataAttributes = getDataAttributes(data);
|
|
48
60
|
|
|
49
61
|
return new SafeString(`
|
|
50
|
-
<script defer src="${
|
|
62
|
+
<script defer src="${scriptUrl}" ${dataAttributes} crossorigin="anonymous"></script>
|
|
51
63
|
`);
|
|
52
64
|
}
|
|
53
65
|
|
|
@@ -13,6 +13,7 @@ const logging = require('@tryghost/logging');
|
|
|
13
13
|
const _ = require('lodash');
|
|
14
14
|
const debug = require('@tryghost/debug')('ghost_head');
|
|
15
15
|
const templateStyles = require('./tpl/styles');
|
|
16
|
+
const {getFrontendAppConfig, getDataAttributes} = require('../utils/frontend-apps');
|
|
16
17
|
|
|
17
18
|
const {get: getMetaData, getAssetUrl} = metaData;
|
|
18
19
|
|
|
@@ -47,9 +48,20 @@ function getMembersHelper(data, frontendKey) {
|
|
|
47
48
|
if (!settingsCache.get('members_enabled')) {
|
|
48
49
|
return '';
|
|
49
50
|
}
|
|
51
|
+
const {scriptUrl} = getFrontendAppConfig('portal');
|
|
52
|
+
|
|
53
|
+
const colorString = (_.has(data, 'site._preview') && data.site.accent_color) ? data.site.accent_color : '';
|
|
54
|
+
const attributes = {
|
|
55
|
+
ghost: urlUtils.getSiteUrl(),
|
|
56
|
+
key: frontendKey,
|
|
57
|
+
api: urlUtils.urlFor('api', {type: 'content'}, true)
|
|
58
|
+
};
|
|
59
|
+
if (colorString) {
|
|
60
|
+
attributes['accent-color'] = colorString;
|
|
61
|
+
}
|
|
62
|
+
const dataAttributes = getDataAttributes(attributes);
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
let membersHelper = `<script defer src="${config.get('portal:url')}" data-ghost="${urlUtils.getSiteUrl()}"${colorString} data-key="${frontendKey}" data-api="${urlUtils.urlFor('api', {type: 'content'}, true)}" crossorigin="anonymous"></script>`;
|
|
64
|
+
let membersHelper = `<script defer src="${scriptUrl}" ${dataAttributes} crossorigin="anonymous"></script>`;
|
|
53
65
|
membersHelper += (`<style id="gh-members-styles">${templateStyles}</style>`);
|
|
54
66
|
if (settingsCache.get('paid_members_enabled')) {
|
|
55
67
|
membersHelper += '<script async src="https://js.stripe.com/v3/"></script>';
|
|
@@ -59,8 +71,14 @@ function getMembersHelper(data, frontendKey) {
|
|
|
59
71
|
|
|
60
72
|
function getSearchHelper(frontendKey) {
|
|
61
73
|
const adminUrl = urlUtils.getAdminUrl() || urlUtils.getSiteUrl();
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
const {scriptUrl, stylesUrl} = getFrontendAppConfig('sodoSearch');
|
|
75
|
+
const attrs = {
|
|
76
|
+
key: frontendKey,
|
|
77
|
+
styles: stylesUrl,
|
|
78
|
+
'sodo-search': adminUrl
|
|
79
|
+
};
|
|
80
|
+
const dataAttrs = getDataAttributes(attrs);
|
|
81
|
+
let helper = `<script defer src="${scriptUrl}" ${dataAttrs} crossorigin="anonymous"></script>`;
|
|
64
82
|
|
|
65
83
|
return helper;
|
|
66
84
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// # Total Members Helper
|
|
2
|
+
// Usage: `{{total_members}}`
|
|
3
|
+
|
|
4
|
+
const {SafeString} = require('../services/handlebars');
|
|
5
|
+
const {memberCountRounding, getMemberStats} = require('../utils/member-count');
|
|
6
|
+
|
|
7
|
+
module.exports = async function total_members () { //eslint-disable-line
|
|
8
|
+
if (this.total) {
|
|
9
|
+
return new SafeString(memberCountRounding(this.total));
|
|
10
|
+
} else {
|
|
11
|
+
let memberStats = await getMemberStats();
|
|
12
|
+
const {total} = memberStats;
|
|
13
|
+
return new SafeString(total > 0 ? memberCountRounding(total) : 0);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
module.exports.async = true;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// {{total_paid_members}} helper
|
|
2
|
+
|
|
3
|
+
const {SafeString} = require('../services/handlebars');
|
|
4
|
+
const {memberCountRounding, getMemberStats} = require('../utils/member-count');
|
|
5
|
+
|
|
6
|
+
module.exports = async function total_paid_members () { //eslint-disable-line
|
|
7
|
+
if (this.paid) {
|
|
8
|
+
return new SafeString(memberCountRounding(this.paid));
|
|
9
|
+
} else {
|
|
10
|
+
let memberStats = await getMemberStats();
|
|
11
|
+
const {paid} = memberStats;
|
|
12
|
+
return new SafeString(paid > 0 ? memberCountRounding(paid) : 0);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
module.exports.async = true;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const {config} = require('../services/proxy');
|
|
2
|
+
|
|
3
|
+
function getFrontendAppConfig(app) {
|
|
4
|
+
const appVersion = config.get(`${app}:version`);
|
|
5
|
+
let scriptUrl = config.get(`${app}:url`);
|
|
6
|
+
let stylesUrl = config.get(`${app}:styles`);
|
|
7
|
+
if (scriptUrl.includes('{version}')) {
|
|
8
|
+
scriptUrl = scriptUrl.replace('{version}', appVersion);
|
|
9
|
+
}
|
|
10
|
+
if (stylesUrl?.includes('{version}')) {
|
|
11
|
+
stylesUrl = stylesUrl.replace('{version}', appVersion);
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
scriptUrl,
|
|
15
|
+
stylesUrl,
|
|
16
|
+
appVersion
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getDataAttributes(data) {
|
|
21
|
+
let dataAttributes = '';
|
|
22
|
+
|
|
23
|
+
if (!data) {
|
|
24
|
+
return dataAttributes;
|
|
25
|
+
}
|
|
26
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
27
|
+
dataAttributes += `data-${key}="${value}" `;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return dataAttributes.trim();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {getFrontendAppConfig, getDataAttributes};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const humanNumber = require('human-number');
|
|
2
|
+
const {api} = require('../services/proxy');
|
|
3
|
+
|
|
4
|
+
async function getMemberStats() {
|
|
5
|
+
let memberStats = this.data || await api.stats.memberCountHistory.query();
|
|
6
|
+
const {free, paid, comped} = memberStats.meta.totals;
|
|
7
|
+
let total = free + paid + comped;
|
|
8
|
+
return {free, paid, comped, total};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const numberWithCommas = (n) => {
|
|
12
|
+
return n.toLocaleString();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const rounding = (n, roundTo) => {
|
|
16
|
+
return Math.floor(n / roundTo) * roundTo;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Rounding https://github.com/TryGhost/Team/issues/1667
|
|
20
|
+
const memberCountRounding = (memberCount) => {
|
|
21
|
+
if (memberCount <= 50) {
|
|
22
|
+
return memberCount;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (memberCount > 50 && memberCount <= 100) {
|
|
26
|
+
return `${numberWithCommas(rounding(memberCount, 10))}+`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (memberCount > 100 && memberCount <= 1000) {
|
|
30
|
+
return `${numberWithCommas(rounding(memberCount, 50))}+`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (memberCount > 1000 && memberCount <= 10000) {
|
|
34
|
+
return `${numberWithCommas(rounding(memberCount, 100))}+`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (memberCount > 10000 && memberCount <= 100000) {
|
|
38
|
+
return `${numberWithCommas(rounding(memberCount, 1000))}+`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (memberCount > 100000 && memberCount <= 1000000) {
|
|
42
|
+
return `${humanNumber(rounding(memberCount, 10000)).toLowerCase()}+`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (memberCount > 1000000) {
|
|
46
|
+
return `${humanNumber(rounding(memberCount, 100000)).toLowerCase()}+`;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
module.exports = {memberCountRounding, getMemberStats};
|
|
@@ -13,7 +13,8 @@ function corsOptionsDelegate(req, callback) {
|
|
|
13
13
|
const origin = req.header('Origin');
|
|
14
14
|
const corsOptions = {
|
|
15
15
|
origin: false, // disallow cross-origin requests by default
|
|
16
|
-
credentials: true // required to allow admin-client to login to private sites
|
|
16
|
+
credentials: true, // required to allow admin-client to login to private sites
|
|
17
|
+
maxAge: config.get('caching:cors:maxAge')
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
if (!origin || origin === 'null') {
|
|
@@ -3,6 +3,7 @@ const tpl = require('@tryghost/tpl');
|
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
4
|
const models = require('../../models');
|
|
5
5
|
const db = require('../../data/db');
|
|
6
|
+
const commentsService = require('../../services/comments');
|
|
6
7
|
const ALLOWED_INCLUDES = ['post', 'member', 'likes', 'replies'];
|
|
7
8
|
const UNSAFE_ATTRS = ['status'];
|
|
8
9
|
|
|
@@ -51,17 +52,8 @@ module.exports = {
|
|
|
51
52
|
}
|
|
52
53
|
},
|
|
53
54
|
permissions: true,
|
|
54
|
-
query(frame) {
|
|
55
|
-
return
|
|
56
|
-
.then((model) => {
|
|
57
|
-
if (!model) {
|
|
58
|
-
return Promise.reject(new errors.NotFoundError({
|
|
59
|
-
message: tpl(messages.commentNotFound)
|
|
60
|
-
}));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return model;
|
|
64
|
-
});
|
|
55
|
+
async query(frame) {
|
|
56
|
+
return await commentsService.api.getCommentByID(frame.data.id, frame.options);
|
|
65
57
|
}
|
|
66
58
|
},
|
|
67
59
|
|
|
@@ -82,17 +74,21 @@ module.exports = {
|
|
|
82
74
|
}
|
|
83
75
|
},
|
|
84
76
|
permissions: true,
|
|
85
|
-
query(frame) {
|
|
86
|
-
|
|
87
|
-
.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
77
|
+
async query(frame) {
|
|
78
|
+
if (frame.data.comments[0].status === 'deleted') {
|
|
79
|
+
return await commentsService.api.deleteComment(
|
|
80
|
+
frame.options.id,
|
|
81
|
+
frame?.options?.context?.member?.id,
|
|
82
|
+
frame.options
|
|
83
|
+
);
|
|
84
|
+
}
|
|
93
85
|
|
|
94
|
-
|
|
95
|
-
|
|
86
|
+
return await commentsService.api.editCommentContent(
|
|
87
|
+
frame.options.id,
|
|
88
|
+
frame?.options?.context?.member?.id,
|
|
89
|
+
frame.data.comments[0].html,
|
|
90
|
+
frame.options
|
|
91
|
+
);
|
|
96
92
|
}
|
|
97
93
|
},
|
|
98
94
|
|
|
@@ -115,20 +111,24 @@ module.exports = {
|
|
|
115
111
|
permissions: {
|
|
116
112
|
unsafeAttrs: UNSAFE_ATTRS
|
|
117
113
|
},
|
|
118
|
-
query(frame) {
|
|
119
|
-
// TODO: move to comment service
|
|
114
|
+
async query(frame) {
|
|
120
115
|
const data = frame.data.comments[0];
|
|
121
116
|
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
message: tpl(messages.memberNotFound)
|
|
130
|
-
}));
|
|
117
|
+
if (data.parent_id) {
|
|
118
|
+
return await commentsService.api.replyToComment(
|
|
119
|
+
data.parent_id,
|
|
120
|
+
frame.options.context.member.id,
|
|
121
|
+
data.html,
|
|
122
|
+
frame.options
|
|
123
|
+
);
|
|
131
124
|
}
|
|
125
|
+
|
|
126
|
+
return await commentsService.api.commentOnPost(
|
|
127
|
+
data.post_id,
|
|
128
|
+
frame.options.context.member.id,
|
|
129
|
+
data.html,
|
|
130
|
+
frame.options
|
|
131
|
+
);
|
|
132
132
|
}
|
|
133
133
|
},
|
|
134
134
|
|
|
@@ -243,5 +243,23 @@ module.exports = {
|
|
|
243
243
|
}));
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
report: {
|
|
249
|
+
statusCode: 204,
|
|
250
|
+
options: [
|
|
251
|
+
'id'
|
|
252
|
+
],
|
|
253
|
+
validation: {},
|
|
254
|
+
permissions: true,
|
|
255
|
+
async query(frame) {
|
|
256
|
+
if (!frame.options?.context?.member?.id) {
|
|
257
|
+
return Promise.reject(new errors.UnauthorizedError({
|
|
258
|
+
message: tpl(messages.memberNotFound)
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
await commentsService.api.reportComment(frame.options.id, frame.options?.context?.member);
|
|
263
|
+
}
|
|
246
264
|
}
|
|
247
265
|
};
|
|
@@ -16,8 +16,8 @@ module.exports = {
|
|
|
16
16
|
permissions: true,
|
|
17
17
|
async query(frame) {
|
|
18
18
|
const offers = await offersService.api.listOffers(frame.options);
|
|
19
|
-
|
|
20
|
-
offers
|
|
19
|
+
return {
|
|
20
|
+
data: offers
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
},
|
|
@@ -33,8 +33,8 @@ module.exports = {
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
return {
|
|
37
|
+
data: [offer]
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
},
|
|
@@ -57,8 +57,8 @@ module.exports = {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
return {
|
|
61
|
+
data: [offer]
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
},
|
|
@@ -70,8 +70,8 @@ module.exports = {
|
|
|
70
70
|
},
|
|
71
71
|
async query(frame) {
|
|
72
72
|
const offer = await offersService.api.createOffer(frame.data.offers[0]);
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
return {
|
|
74
|
+
data: [offer]
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -2,17 +2,15 @@ const Promise = require('bluebird');
|
|
|
2
2
|
const _ = require('lodash');
|
|
3
3
|
const models = require('../../models');
|
|
4
4
|
const routeSettings = require('../../services/route-settings');
|
|
5
|
-
const tpl = require('@tryghost/tpl');
|
|
6
5
|
const {BadRequestError} = require('@tryghost/errors');
|
|
7
6
|
const settingsService = require('../../services/settings/settings-service');
|
|
8
7
|
const membersService = require('../../services/members');
|
|
9
8
|
const stripeService = require('../../services/stripe');
|
|
10
|
-
|
|
9
|
+
const tpl = require('@tryghost/tpl');
|
|
11
10
|
const settingsBREADService = settingsService.getSettingsBREADServiceInstance();
|
|
12
11
|
|
|
13
12
|
const messages = {
|
|
14
13
|
failedSendingEmail: 'Failed Sending Email'
|
|
15
|
-
|
|
16
14
|
};
|
|
17
15
|
|
|
18
16
|
async function getStripeConnectData(frame) {
|
|
@@ -59,6 +57,65 @@ module.exports = {
|
|
|
59
57
|
}
|
|
60
58
|
},
|
|
61
59
|
|
|
60
|
+
verifyKeyUpdate: {
|
|
61
|
+
headers: {
|
|
62
|
+
cacheInvalidate: true
|
|
63
|
+
},
|
|
64
|
+
permissions: {
|
|
65
|
+
method: 'edit'
|
|
66
|
+
},
|
|
67
|
+
data: [
|
|
68
|
+
'token'
|
|
69
|
+
],
|
|
70
|
+
async query(frame) {
|
|
71
|
+
await settingsBREADService.verifyKeyUpdate(frame.data.token);
|
|
72
|
+
|
|
73
|
+
// We need to return all settings here, because we have calculated settings that might change
|
|
74
|
+
const browse = await settingsBREADService.browse(frame.options.context);
|
|
75
|
+
|
|
76
|
+
return browse;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @deprecated
|
|
82
|
+
*/
|
|
83
|
+
updateMembersEmail: {
|
|
84
|
+
statusCode: 204,
|
|
85
|
+
permissions: {
|
|
86
|
+
method: 'edit'
|
|
87
|
+
},
|
|
88
|
+
data: [
|
|
89
|
+
'email',
|
|
90
|
+
'type'
|
|
91
|
+
],
|
|
92
|
+
async query(frame) {
|
|
93
|
+
const {email, type} = frame.data;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// Mapped internally to the newer method of changing emails
|
|
97
|
+
const actionToKeyMapping = {
|
|
98
|
+
supportAddressUpdate: 'members_support_address'
|
|
99
|
+
};
|
|
100
|
+
const edit = {
|
|
101
|
+
key: actionToKeyMapping[type],
|
|
102
|
+
value: email
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
await settingsBREADService.edit([edit], frame.options, null);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
throw new BadRequestError({
|
|
108
|
+
err,
|
|
109
|
+
message: tpl(messages.failedSendingEmail)
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @todo can get removed, since this is moved to verifyKeyUpdate
|
|
117
|
+
* @deprecated: keep to not break existing email verification links, but remove after 1 - 2 releases
|
|
118
|
+
*/
|
|
62
119
|
validateMembersEmailUpdate: {
|
|
63
120
|
options: [
|
|
64
121
|
'token',
|
|
@@ -108,33 +165,6 @@ module.exports = {
|
|
|
108
165
|
}
|
|
109
166
|
},
|
|
110
167
|
|
|
111
|
-
updateMembersEmail: {
|
|
112
|
-
statusCode: 204,
|
|
113
|
-
permissions: {
|
|
114
|
-
method: 'edit'
|
|
115
|
-
},
|
|
116
|
-
data: [
|
|
117
|
-
'email',
|
|
118
|
-
'type'
|
|
119
|
-
],
|
|
120
|
-
async query(frame) {
|
|
121
|
-
const {email, type} = frame.data;
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
// Send magic link to update fromAddress
|
|
125
|
-
await membersService.settings.sendEmailAddressUpdateMagicLink({
|
|
126
|
-
email,
|
|
127
|
-
type
|
|
128
|
-
});
|
|
129
|
-
} catch (err) {
|
|
130
|
-
throw new BadRequestError({
|
|
131
|
-
err,
|
|
132
|
-
message: tpl(messages.failedSendingEmail)
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
|
|
138
168
|
disconnectStripeConnectIntegration: {
|
|
139
169
|
statusCode: 204,
|
|
140
170
|
permissions: {
|
|
@@ -197,6 +227,8 @@ module.exports = {
|
|
|
197
227
|
|
|
198
228
|
// We need to return all settings here, because we have calculated settings that might change
|
|
199
229
|
const browse = await settingsBREADService.browse(frame.options.context);
|
|
230
|
+
browse.meta = result.meta || {};
|
|
231
|
+
|
|
200
232
|
return browse;
|
|
201
233
|
}
|
|
202
234
|
},
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const mapComment = require('./comments');
|
|
2
|
+
|
|
3
|
+
const commentEventMapper = (json, frame) => {
|
|
4
|
+
return {
|
|
5
|
+
...json,
|
|
6
|
+
data: mapComment(json.data, frame)
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const activityFeedMapper = (event, frame) => {
|
|
11
|
+
if (event.type === 'comment_event') {
|
|
12
|
+
return commentEventMapper(event, frame);
|
|
13
|
+
}
|
|
14
|
+
return event;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
module.exports = activityFeedMapper;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
+
const url = require('../utils/url');
|
|
2
3
|
|
|
3
4
|
const commentFields = [
|
|
4
5
|
'id',
|
|
@@ -16,6 +17,13 @@ const memberFields = [
|
|
|
16
17
|
'avatar_image'
|
|
17
18
|
];
|
|
18
19
|
|
|
20
|
+
const postFields = [
|
|
21
|
+
'id',
|
|
22
|
+
'uuid',
|
|
23
|
+
'title',
|
|
24
|
+
'url'
|
|
25
|
+
];
|
|
26
|
+
|
|
19
27
|
const commentMapper = (model, frame) => {
|
|
20
28
|
const jsonModel = model.toJSON ? model.toJSON(frame.options) : model;
|
|
21
29
|
|
|
@@ -37,6 +45,16 @@ const commentMapper = (model, frame) => {
|
|
|
37
45
|
response.replies = jsonModel.replies.map(reply => commentMapper(reply, frame));
|
|
38
46
|
}
|
|
39
47
|
|
|
48
|
+
if (jsonModel.parent) {
|
|
49
|
+
response.parent = commentMapper(jsonModel.parent, frame);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (jsonModel.post) {
|
|
53
|
+
// We could use the post mapper here, but we need less field + don't need al the async behaviour support
|
|
54
|
+
url.forPost(jsonModel.post.id, jsonModel.post, frame);
|
|
55
|
+
response.post = _.pick(jsonModel.post, postFields);
|
|
56
|
+
}
|
|
57
|
+
|
|
40
58
|
// todo
|
|
41
59
|
response.liked = false;
|
|
42
60
|
if (jsonModel.likes && frame.original.context.member && frame.original.context.member.id) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
actions: require('./actions'),
|
|
3
|
+
activityFeedEvents: require('./activity-feed-events'),
|
|
3
4
|
authors: require('./authors'),
|
|
4
5
|
comments: require('./comments'),
|
|
5
6
|
emails: require('./emails'),
|
|
@@ -10,6 +11,7 @@ module.exports = {
|
|
|
10
11
|
settings: require('./settings'),
|
|
11
12
|
snippets: require('./snippets'),
|
|
12
13
|
tags: require('./tags'),
|
|
14
|
+
offers: require('./offers'),
|
|
13
15
|
newsletters: require('./newsletters'),
|
|
14
16
|
users: require('./users')
|
|
15
17
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const utils = require('../../../index');
|
|
2
|
+
|
|
3
|
+
module.exports = (model, frame) => {
|
|
4
|
+
// Offer data is already returned as json via members service
|
|
5
|
+
const jsonModel = model;
|
|
6
|
+
|
|
7
|
+
if (utils.isContentAPI(frame)) {
|
|
8
|
+
const serialized = {
|
|
9
|
+
id: jsonModel.id,
|
|
10
|
+
name: jsonModel.name,
|
|
11
|
+
display_title: jsonModel.display_title,
|
|
12
|
+
display_description: jsonModel.display_description,
|
|
13
|
+
type: jsonModel.type,
|
|
14
|
+
cadence: jsonModel.cadence,
|
|
15
|
+
amount: jsonModel.amount,
|
|
16
|
+
duration: jsonModel.duration,
|
|
17
|
+
duration_in_months: jsonModel.duration_in_months,
|
|
18
|
+
currency_restriction: jsonModel.currency_restriction,
|
|
19
|
+
currency: jsonModel.currency,
|
|
20
|
+
status: jsonModel.status,
|
|
21
|
+
tier: jsonModel.tier
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return serialized;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return jsonModel;
|
|
28
|
+
};
|