ghost 5.13.0 → 5.14.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/README.md +1 -1
- package/components/tryghost-adapter-manager-5.14.0.tgz +0 -0
- package/components/{tryghost-api-framework-5.13.0.tgz → tryghost-api-framework-5.14.0.tgz} +0 -0
- package/components/{tryghost-api-version-compatibility-service-5.13.0.tgz → tryghost-api-version-compatibility-service-5.14.0.tgz} +0 -0
- package/components/{tryghost-bootstrap-socket-5.13.0.tgz → tryghost-bootstrap-socket-5.14.0.tgz} +0 -0
- package/components/tryghost-constants-5.14.0.tgz +0 -0
- package/components/{tryghost-custom-theme-settings-service-5.13.0.tgz → tryghost-custom-theme-settings-service-5.14.0.tgz} +0 -0
- package/components/tryghost-domain-events-5.14.0.tgz +0 -0
- package/components/{tryghost-email-analytics-provider-mailgun-5.13.0.tgz → tryghost-email-analytics-provider-mailgun-5.14.0.tgz} +0 -0
- package/components/tryghost-email-analytics-service-5.14.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.14.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.14.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.14.0.tgz +0 -0
- package/components/{tryghost-html-to-plaintext-5.13.0.tgz → tryghost-html-to-plaintext-5.14.0.tgz} +0 -0
- package/components/{tryghost-job-manager-5.13.0.tgz → tryghost-job-manager-5.14.0.tgz} +0 -0
- package/components/{tryghost-magic-link-5.13.0.tgz → tryghost-magic-link-5.14.0.tgz} +0 -0
- package/components/{tryghost-mailgun-client-5.13.0.tgz → tryghost-mailgun-client-5.14.0.tgz} +0 -0
- package/components/{tryghost-member-analytics-service-5.13.0.tgz → tryghost-member-analytics-service-5.14.0.tgz} +0 -0
- package/components/tryghost-member-attribution-5.14.0.tgz +0 -0
- package/components/tryghost-member-events-5.14.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.14.0.tgz +0 -0
- package/components/tryghost-members-api-5.14.0.tgz +0 -0
- package/components/{tryghost-members-csv-5.13.0.tgz → tryghost-members-csv-5.14.0.tgz} +0 -0
- package/components/tryghost-members-events-service-5.14.0.tgz +0 -0
- package/components/tryghost-members-importer-5.14.0.tgz +0 -0
- package/components/{tryghost-members-offers-5.13.0.tgz → tryghost-members-offers-5.14.0.tgz} +0 -0
- package/components/tryghost-members-payments-5.14.0.tgz +0 -0
- package/components/{tryghost-members-ssr-5.13.0.tgz → tryghost-members-ssr-5.14.0.tgz} +0 -0
- package/components/{tryghost-members-stripe-service-5.13.0.tgz → tryghost-members-stripe-service-5.14.0.tgz} +0 -0
- package/components/{tryghost-minifier-5.13.0.tgz → tryghost-minifier-5.14.0.tgz} +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.14.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.14.0.tgz +0 -0
- package/components/{tryghost-mw-error-handler-5.13.0.tgz → tryghost-mw-error-handler-5.14.0.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.14.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.14.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.14.0.tgz +0 -0
- package/components/{tryghost-oembed-service-5.13.0.tgz → tryghost-oembed-service-5.14.0.tgz} +0 -0
- package/components/{tryghost-package-json-5.13.0.tgz → tryghost-package-json-5.14.0.tgz} +0 -0
- package/components/tryghost-security-5.14.0.tgz +0 -0
- package/components/{tryghost-session-service-5.13.0.tgz → tryghost-session-service-5.14.0.tgz} +0 -0
- package/components/tryghost-settings-path-manager-5.14.0.tgz +0 -0
- package/components/tryghost-staff-service-5.14.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.14.0.tgz +0 -0
- package/components/{tryghost-verification-trigger-5.13.0.tgz → tryghost-verification-trigger-5.14.0.tgz} +0 -0
- package/components/{tryghost-version-notifications-data-service-5.13.0.tgz → tryghost-version-notifications-data-service-5.14.0.tgz} +0 -0
- package/core/boot.js +2 -0
- package/core/built/admin/assets/{chunk.143.e96aad00fdf7196692c7.js → chunk.143.53c5e3490ffdae025d84.js} +6 -6
- package/core/built/admin/assets/{chunk.174.8b8a64726921ecfda41b.js → chunk.174.2edaa0869bfc2d88cf90.js} +173 -172
- package/core/built/admin/assets/{chunk.178.1188f8d61af173f8c246.js → chunk.178.a31590ec7388630cd0d0.js} +4 -4
- package/core/built/admin/assets/{chunk.763.9a285d7351e1f4415f8d.js → chunk.579.2de3f4300baf25f9a0db.js} +22 -28
- package/core/built/admin/assets/{chunk.763.9a285d7351e1f4415f8d.js.LICENSE.txt → chunk.579.2de3f4300baf25f9a0db.js.LICENSE.txt} +0 -0
- package/core/built/admin/assets/ghost-40adc8310dcdd0be163cbf7b9d89c59a.css +1 -0
- package/core/built/admin/assets/{ghost-3203510c519d3195f1e71a34e9eecc59.js → ghost-84a4336c7d5c1f3fba00868b0a5237cd.js} +817 -915
- package/core/built/admin/assets/ghost-dark-13b669d50f494edf24d832b32ece2177.css +1 -0
- package/core/built/admin/assets/img/logos/orb-pink-3-a66abc6df2b6ab64d1459a6535b725cd.png +0 -0
- package/core/built/admin/assets/{vendor-ab4b5dfdf8b86f24d726115ac7de0980.js → vendor-22a37451d7619a2b641310ecbcca4c05.js} +84 -81
- package/core/built/admin/index.html +6 -6
- package/core/frontend/helpers/ghost_head.js +3 -2
- package/core/frontend/helpers/url.js +1 -1
- package/core/frontend/services/data/fetch-data.js +0 -1
- package/core/frontend/src/admin-auth/message-handler.js +4 -4
- package/core/server/adapters/cache/Memory.js +1 -1
- package/core/server/api/endpoints/comments-members.js +4 -64
- package/core/server/api/endpoints/utils/serializers/input/pages.js +3 -0
- package/core/server/api/endpoints/utils/serializers/input/posts.js +3 -0
- package/core/server/api/endpoints/utils/serializers/input/utils/clean.js +12 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +1 -1
- package/core/server/data/migrations/versions/5.14/2022-09-02-12-55-rename-members-bio-to-expertise.js +34 -0
- package/core/server/data/schema/schema.js +1 -1
- package/core/server/models/post.js +6 -0
- package/core/server/services/adapter-manager/index.js +3 -3
- package/core/server/services/adapter-manager/options-resolver.js +30 -9
- package/core/server/services/comments/controller.js +52 -1
- package/core/server/services/comments/email-templates/new-comment-reply.hbs +1 -1
- package/core/server/services/comments/email-templates/new-comment.hbs +1 -1
- package/core/server/services/comments/email-templates/report.hbs +1 -1
- package/core/server/services/comments/emails.js +3 -3
- package/core/server/services/comments/service.js +52 -0
- package/core/server/services/mega/post-email-serializer.js +371 -368
- package/core/server/services/mega/template.js +4 -8
- package/core/server/services/member-attribution/index.js +4 -18
- package/core/server/services/members/middleware.js +1 -1
- package/core/server/services/members/service.js +0 -12
- package/core/server/services/members/utils.js +1 -1
- package/core/server/services/members-events/index.js +40 -0
- package/core/server/services/nft-oembed.js +5 -3
- package/core/shared/config/defaults.json +4 -6
- package/core/shared/labs.js +1 -1
- package/core/shared/settings-cache/cache.js +1 -1
- package/package.json +96 -94
- package/yarn.lock +275 -207
- package/components/tryghost-adapter-manager-5.13.0.tgz +0 -0
- package/components/tryghost-constants-5.13.0.tgz +0 -0
- package/components/tryghost-domain-events-5.13.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.13.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.13.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.13.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.13.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.13.0.tgz +0 -0
- package/components/tryghost-member-events-5.13.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.13.0.tgz +0 -0
- package/components/tryghost-members-api-5.13.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.13.0.tgz +0 -0
- package/components/tryghost-members-importer-5.13.0.tgz +0 -0
- package/components/tryghost-members-payments-5.13.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.13.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.13.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.13.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.13.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.13.0.tgz +0 -0
- package/components/tryghost-security-5.13.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.13.0.tgz +0 -0
- package/components/tryghost-staff-service-5.13.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.13.0.tgz +0 -0
- package/content/themes/casper/assets/built/portal.min.js +0 -3
- package/core/built/admin/assets/ghost-647c9a79282265a4d29bf273c44f72c0.css +0 -1
- package/core/built/admin/assets/ghost-dark-d84a2701166840b73bbbbe657879b65e.css +0 -1
- package/core/built/admin/assets/img/logos/orb-pink-3-a2c52eb9fda9f2401ea706c3f24976ff.png +0 -0
- package/core/server/adapters/cache/Base.js +0 -12
- package/core/server/adapters/cache/ImageSizesCacheSyncInMemory.js +0 -7
- package/core/server/adapters/cache/SettingsCacheSyncInMemory.js +0 -7
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Ghost Admin</title>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.14%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
<link integrity="" rel="stylesheet" href="assets/vendor-733135cd6cbca8126c6fa223d63a5bf3.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-40adc8310dcdd0be163cbf7b9d89c59a.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
|
|
54
54
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
55
55
|
|
|
56
|
-
<script src="assets/vendor-
|
|
57
|
-
<script src="assets/chunk.
|
|
58
|
-
<script src="assets/chunk.143.
|
|
59
|
-
<script src="assets/ghost-
|
|
56
|
+
<script src="assets/vendor-22a37451d7619a2b641310ecbcca4c05.js"></script>
|
|
57
|
+
<script src="assets/chunk.579.2de3f4300baf25f9a0db.js"></script>
|
|
58
|
+
<script src="assets/chunk.143.53c5e3490ffdae025d84.js"></script>
|
|
59
|
+
<script src="assets/ghost-84a4336c7d5c1f3fba00868b0a5237cd.js"></script>
|
|
60
60
|
</body>
|
|
61
61
|
</html>
|
|
@@ -167,11 +167,12 @@ module.exports = async function ghost_head(options) { // eslint-disable-line cam
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
head.push('<link rel="canonical" href="' + escapeExpression(meta.canonicalUrl) + '" />');
|
|
170
|
-
head.push('<meta name="referrer" content="' + referrerPolicy + '" />');
|
|
171
170
|
|
|
172
|
-
// don't allow indexing of preview URLs!
|
|
173
171
|
if (_.includes(context, 'preview')) {
|
|
174
172
|
head.push(writeMetaTag('robots', 'noindex,nofollow', 'name'));
|
|
173
|
+
head.push(writeMetaTag('referrer', 'same-origin', 'name'));
|
|
174
|
+
} else {
|
|
175
|
+
head.push(writeMetaTag('referrer', referrerPolicy, 'name'));
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
// show amp link in post when 1. we are not on the amp page and 2. amp is enabled
|
|
@@ -17,7 +17,7 @@ module.exports = function url(options) {
|
|
|
17
17
|
let outputUrl = getMetaDataUrl(this, absolute);
|
|
18
18
|
|
|
19
19
|
try {
|
|
20
|
-
outputUrl = encodeURI(decodeURI(outputUrl));
|
|
20
|
+
outputUrl = encodeURI(decodeURI(outputUrl)).replace(/%5B/g, '[').replace(/%5D/g, ']');
|
|
21
21
|
} catch (err) {
|
|
22
22
|
// Happens when the outputURL contains an invalid URI character like "%%" or "%80"
|
|
23
23
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const adminUrl = window.location.href.replace('auth-frame/', '');
|
|
1
|
+
const adminUrl = window.location.href.replace('auth-frame/', '') + 'api/admin';
|
|
2
2
|
|
|
3
3
|
// At compile time, we'll replace the value with the actual origin.
|
|
4
4
|
const siteOrigin = '{{SITE_ORIGIN}}';
|
|
@@ -26,7 +26,7 @@ window.addEventListener('message', async function (event) {
|
|
|
26
26
|
if (data.action === 'getUser') {
|
|
27
27
|
try {
|
|
28
28
|
const res = await fetch(
|
|
29
|
-
adminUrl + '
|
|
29
|
+
adminUrl + '/users/me/'
|
|
30
30
|
);
|
|
31
31
|
const json = await res.json();
|
|
32
32
|
respond(null, json);
|
|
@@ -37,7 +37,7 @@ window.addEventListener('message', async function (event) {
|
|
|
37
37
|
|
|
38
38
|
if (data.action === 'hideComment') {
|
|
39
39
|
try {
|
|
40
|
-
const res = await fetch(adminUrl + '
|
|
40
|
+
const res = await fetch(adminUrl + '/comments/' + data.id + '/', {
|
|
41
41
|
method: 'PUT',
|
|
42
42
|
body: JSON.stringify({
|
|
43
43
|
comments: [{
|
|
@@ -58,7 +58,7 @@ window.addEventListener('message', async function (event) {
|
|
|
58
58
|
|
|
59
59
|
if (data.action === 'showComment') {
|
|
60
60
|
try {
|
|
61
|
-
const res = await fetch(adminUrl + '
|
|
61
|
+
const res = await fetch(adminUrl + '/comments/' + data.id + '/', {
|
|
62
62
|
method: 'PUT',
|
|
63
63
|
body: JSON.stringify({
|
|
64
64
|
comments: [{
|
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
const Promise = require('bluebird');
|
|
2
|
-
const tpl = require('@tryghost/tpl');
|
|
3
|
-
const errors = require('@tryghost/errors');
|
|
4
|
-
const models = require('../../models');
|
|
5
1
|
const commentsService = require('../../services/comments');
|
|
6
2
|
const ALLOWED_INCLUDES = ['member', 'replies', 'replies.member', 'replies.count.likes', 'replies.liked', 'count.replies', 'count.likes', 'liked', 'post', 'parent'];
|
|
7
3
|
const UNSAFE_ATTRS = ['status'];
|
|
8
4
|
|
|
9
|
-
const messages = {
|
|
10
|
-
commentNotFound: 'Comment could not be found',
|
|
11
|
-
memberNotFound: 'Unable to find member',
|
|
12
|
-
likeNotFound: 'Unable to find like',
|
|
13
|
-
alreadyLiked: 'This comment was liked already'
|
|
14
|
-
};
|
|
15
|
-
|
|
16
5
|
module.exports = {
|
|
17
6
|
docName: 'comments',
|
|
18
7
|
|
|
@@ -157,27 +146,7 @@ module.exports = {
|
|
|
157
146
|
},
|
|
158
147
|
permissions: true,
|
|
159
148
|
async query(frame) {
|
|
160
|
-
|
|
161
|
-
if (frame.options?.context?.member?.id) {
|
|
162
|
-
const data = {
|
|
163
|
-
member_id: frame.options.context.member.id,
|
|
164
|
-
comment_id: frame.options.id
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const existing = await models.CommentLike.findOne(data, frame.options);
|
|
168
|
-
|
|
169
|
-
if (existing) {
|
|
170
|
-
throw new errors.BadRequestError({
|
|
171
|
-
message: tpl(messages.alreadyLiked)
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return await models.CommentLike.add(data, frame.options);
|
|
176
|
-
} else {
|
|
177
|
-
throw new errors.NotFoundError({
|
|
178
|
-
message: tpl(messages.memberNotFound)
|
|
179
|
-
});
|
|
180
|
-
}
|
|
149
|
+
return await commentsService.controller.like(frame);
|
|
181
150
|
}
|
|
182
151
|
},
|
|
183
152
|
|
|
@@ -188,31 +157,8 @@ module.exports = {
|
|
|
188
157
|
],
|
|
189
158
|
validation: {},
|
|
190
159
|
permissions: true,
|
|
191
|
-
query(frame) {
|
|
192
|
-
|
|
193
|
-
if (frame.options?.context?.member?.id) {
|
|
194
|
-
return models.CommentLike.destroy({
|
|
195
|
-
...frame.options,
|
|
196
|
-
destroyBy: {
|
|
197
|
-
member_id: frame.options.context.member.id,
|
|
198
|
-
comment_id: frame.options.id
|
|
199
|
-
},
|
|
200
|
-
require: true
|
|
201
|
-
}).then(() => null)
|
|
202
|
-
.catch((err) => {
|
|
203
|
-
if (err instanceof models.CommentLike.NotFoundError) {
|
|
204
|
-
return Promise.reject(new errors.NotFoundError({
|
|
205
|
-
message: tpl(messages.likeNotFound)
|
|
206
|
-
}));
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
throw err;
|
|
210
|
-
});
|
|
211
|
-
} else {
|
|
212
|
-
return Promise.reject(new errors.NotFoundError({
|
|
213
|
-
message: tpl(messages.memberNotFound)
|
|
214
|
-
}));
|
|
215
|
-
}
|
|
160
|
+
async query(frame) {
|
|
161
|
+
return await commentsService.controller.unlike(frame);
|
|
216
162
|
}
|
|
217
163
|
},
|
|
218
164
|
|
|
@@ -224,13 +170,7 @@ module.exports = {
|
|
|
224
170
|
validation: {},
|
|
225
171
|
permissions: true,
|
|
226
172
|
async query(frame) {
|
|
227
|
-
|
|
228
|
-
return Promise.reject(new errors.UnauthorizedError({
|
|
229
|
-
message: tpl(messages.memberNotFound)
|
|
230
|
-
}));
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
await commentsService.api.reportComment(frame.options.id, frame.options?.context?.member);
|
|
173
|
+
await commentsService.controller.report(frame);
|
|
234
174
|
}
|
|
235
175
|
}
|
|
236
176
|
};
|
|
@@ -5,6 +5,7 @@ const url = require('./utils/url');
|
|
|
5
5
|
const slugFilterOrder = require('./utils/slug-filter-order');
|
|
6
6
|
const localUtils = require('../../index');
|
|
7
7
|
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
|
|
8
|
+
const clean = require('./utils/clean');
|
|
8
9
|
|
|
9
10
|
function removeMobiledocFormat(frame) {
|
|
10
11
|
if (frame.options.formats && frame.options.formats.includes('mobiledoc')) {
|
|
@@ -159,6 +160,8 @@ module.exports = {
|
|
|
159
160
|
frame.data.pages[0].tags[index] = {
|
|
160
161
|
name: tag
|
|
161
162
|
};
|
|
163
|
+
} else {
|
|
164
|
+
frame.data.pages[0].tags[index] = clean.pagesTag(tag);
|
|
162
165
|
}
|
|
163
166
|
});
|
|
164
167
|
}
|
|
@@ -5,6 +5,7 @@ const slugFilterOrder = require('./utils/slug-filter-order');
|
|
|
5
5
|
const localUtils = require('../../index');
|
|
6
6
|
const mobiledoc = require('../../../../../lib/mobiledoc');
|
|
7
7
|
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
|
|
8
|
+
const clean = require('./utils/clean');
|
|
8
9
|
|
|
9
10
|
function removeMobiledocFormat(frame) {
|
|
10
11
|
if (frame.options.formats && frame.options.formats.includes('mobiledoc')) {
|
|
@@ -175,6 +176,8 @@ module.exports = {
|
|
|
175
176
|
frame.data.posts[0].tags[index] = {
|
|
176
177
|
name: tag
|
|
177
178
|
};
|
|
179
|
+
} else {
|
|
180
|
+
frame.data.posts[0].tags[index] = clean.postsTag(tag);
|
|
178
181
|
}
|
|
179
182
|
});
|
|
180
183
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
const {createNonTransactionalMigration} = require('../../utils');
|
|
3
|
+
|
|
4
|
+
module.exports = createNonTransactionalMigration (
|
|
5
|
+
async function up(knex) {
|
|
6
|
+
// check if the column exists before trying to rename it
|
|
7
|
+
const hasBio = await knex.schema.hasColumn('members', 'bio');
|
|
8
|
+
const hasExpertise = await knex.schema.hasColumn('members', 'expertise');
|
|
9
|
+
|
|
10
|
+
if (hasBio && !hasExpertise) {
|
|
11
|
+
logging.info('Renaming members.bio to members.expertise');
|
|
12
|
+
await knex.schema.table('members', (table) => {
|
|
13
|
+
table.renameColumn('bio', 'expertise');
|
|
14
|
+
}
|
|
15
|
+
);
|
|
16
|
+
} else {
|
|
17
|
+
logging.info('members.bio does not exist, skipping rename');
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
async function down(knex) {
|
|
21
|
+
const hasBio = await knex.schema.hasColumn('members', 'bio');
|
|
22
|
+
const hasExpertise = await knex.schema.hasColumn('members', 'expertise');
|
|
23
|
+
|
|
24
|
+
if (hasExpertise && !hasBio) {
|
|
25
|
+
logging.info(`Renaming members.expertise to members.bio`);
|
|
26
|
+
await knex.schema.table('members', (table) => {
|
|
27
|
+
table.renameColumn('expertise', 'bio');
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
} else {
|
|
31
|
+
logging.warn('members.expertise does not exist, skipping');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
);
|
|
@@ -396,7 +396,7 @@ module.exports = {
|
|
|
396
396
|
}
|
|
397
397
|
},
|
|
398
398
|
name: {type: 'string', maxlength: 191, nullable: true},
|
|
399
|
-
|
|
399
|
+
expertise: {type: 'string', maxlength: 191, nullable: true, validations: {isLength: {max: 50}}},
|
|
400
400
|
note: {type: 'string', maxlength: 2000, nullable: true},
|
|
401
401
|
geolocation: {type: 'string', maxlength: 2000, nullable: true},
|
|
402
402
|
enable_comment_notifications: {type: 'boolean', nullable: false, defaultTo: true},
|
|
@@ -15,7 +15,7 @@ const adapterManager = new AdapterManager({
|
|
|
15
15
|
adapterManager.registerAdapter('storage', require('ghost-storage-base'));
|
|
16
16
|
adapterManager.registerAdapter('scheduling', require('../../adapters/scheduling/SchedulingBase'));
|
|
17
17
|
adapterManager.registerAdapter('sso', require('../../adapters/sso/Base'));
|
|
18
|
-
adapterManager.registerAdapter('cache', require('
|
|
18
|
+
adapterManager.registerAdapter('cache', require('@tryghost/adapter-base-cache'));
|
|
19
19
|
|
|
20
20
|
module.exports = {
|
|
21
21
|
/**
|
|
@@ -26,8 +26,8 @@ module.exports = {
|
|
|
26
26
|
getAdapter(name) {
|
|
27
27
|
const adapterServiceConfig = getAdapterServiceConfig(config);
|
|
28
28
|
|
|
29
|
-
const {
|
|
29
|
+
const {adapterClassName, adapterConfig} = resolveAdapterOptions(name, adapterServiceConfig);
|
|
30
30
|
|
|
31
|
-
return adapterManager.getAdapter(
|
|
31
|
+
return adapterManager.getAdapter(name, adapterClassName, adapterConfig);
|
|
32
32
|
}
|
|
33
33
|
};
|
|
@@ -1,18 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Resolves full configuration for an adapter. Combining base class configurations
|
|
3
|
+
* along with feature-specific ones
|
|
4
|
+
*
|
|
5
|
+
* @param {String} name
|
|
6
|
+
* @param {Object} adapterServiceConfig
|
|
7
|
+
*
|
|
8
|
+
* @returns {{adapterClassName: String, adapterConfig: Object}}
|
|
9
|
+
*/
|
|
10
|
+
const resolveAdapterOptions = (name, adapterServiceConfig) => {
|
|
2
11
|
const [adapterType, feature] = name.split(':');
|
|
3
12
|
const adapterSettings = adapterServiceConfig[adapterType];
|
|
4
13
|
|
|
5
|
-
let
|
|
14
|
+
let adapterClassName;
|
|
6
15
|
let adapterConfig;
|
|
7
16
|
|
|
8
|
-
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
const hasFeatureConfig = feature && adapterSettings[feature];
|
|
18
|
+
if (hasFeatureConfig && adapterSettings[adapterSettings[feature]]) {
|
|
19
|
+
// CASE: load resource-specific adapter when there is an adapter feature
|
|
20
|
+
// name (String) specified as well as custom feature config
|
|
21
|
+
adapterClassName = adapterSettings[feature];
|
|
22
|
+
adapterConfig = adapterSettings[adapterClassName];
|
|
23
|
+
} else if (hasFeatureConfig && adapterSettings[feature].adapter) {
|
|
24
|
+
// CASE: load resource-specific adapter when there is an adapter feature
|
|
25
|
+
// name (Object) specified as well as custom feature config
|
|
26
|
+
adapterClassName = adapterSettings[feature].adapter;
|
|
27
|
+
const commonAdapterConfig = {...adapterSettings[adapterClassName]};
|
|
28
|
+
const featureAdapterConfig = {...adapterSettings[feature]};
|
|
29
|
+
delete featureAdapterConfig.adapter;
|
|
30
|
+
adapterConfig = {...commonAdapterConfig, ...featureAdapterConfig};
|
|
12
31
|
} else {
|
|
13
|
-
|
|
14
|
-
adapterConfig = adapterSettings[
|
|
32
|
+
adapterClassName = adapterSettings.active;
|
|
33
|
+
adapterConfig = adapterSettings[adapterClassName];
|
|
15
34
|
}
|
|
16
35
|
|
|
17
|
-
return {
|
|
36
|
+
return {adapterClassName, adapterConfig};
|
|
18
37
|
};
|
|
38
|
+
|
|
39
|
+
module.exports = resolveAdapterOptions;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
+
const errors = require('@tryghost/errors');
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* @typedef {import('../../api/shared/frame')} Frame
|
|
@@ -8,7 +9,8 @@ const {MethodNotAllowedError} = require('@tryghost/errors');
|
|
|
8
9
|
const tpl = require('@tryghost/tpl');
|
|
9
10
|
|
|
10
11
|
const messages = {
|
|
11
|
-
cannotDestroyComments: 'You cannot destroy comments.'
|
|
12
|
+
cannotDestroyComments: 'You cannot destroy comments.',
|
|
13
|
+
memberNotFound: 'Unable to find member'
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
module.exports = class CommentsController {
|
|
@@ -21,6 +23,14 @@ module.exports = class CommentsController {
|
|
|
21
23
|
this.stats = stats;
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
#checkMember(frame) {
|
|
27
|
+
if (!frame.options?.context?.member?.id) {
|
|
28
|
+
throw new errors.UnauthorizedError({
|
|
29
|
+
message: tpl(messages.memberNotFound)
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
/**
|
|
25
35
|
* @param {Frame} frame
|
|
26
36
|
*/
|
|
@@ -46,6 +56,8 @@ module.exports = class CommentsController {
|
|
|
46
56
|
* @param {Frame} frame
|
|
47
57
|
*/
|
|
48
58
|
async edit(frame) {
|
|
59
|
+
this.#checkMember(frame);
|
|
60
|
+
|
|
49
61
|
if (frame.data.comments[0].status === 'deleted') {
|
|
50
62
|
return await this.service.deleteComment(
|
|
51
63
|
frame.options.id,
|
|
@@ -66,6 +78,7 @@ module.exports = class CommentsController {
|
|
|
66
78
|
* @param {Frame} frame
|
|
67
79
|
*/
|
|
68
80
|
async add(frame) {
|
|
81
|
+
this.#checkMember(frame);
|
|
69
82
|
const data = frame.data.comments[0];
|
|
70
83
|
|
|
71
84
|
if (data.parent_id) {
|
|
@@ -98,4 +111,42 @@ module.exports = class CommentsController {
|
|
|
98
111
|
|
|
99
112
|
return await this.stats.getCountsByPost(frame.data.ids);
|
|
100
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {Frame} frame
|
|
117
|
+
*/
|
|
118
|
+
async like(frame) {
|
|
119
|
+
this.#checkMember(frame);
|
|
120
|
+
|
|
121
|
+
return await this.service.likeComment(
|
|
122
|
+
frame.options.id,
|
|
123
|
+
frame.options?.context?.member,
|
|
124
|
+
frame.options
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @param {Frame} frame
|
|
130
|
+
*/
|
|
131
|
+
async unlike(frame) {
|
|
132
|
+
this.#checkMember(frame);
|
|
133
|
+
|
|
134
|
+
return await this.service.unlikeComment(
|
|
135
|
+
frame.options.id,
|
|
136
|
+
frame.options?.context?.member,
|
|
137
|
+
frame.options
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {Frame} frame
|
|
143
|
+
*/
|
|
144
|
+
async report(frame) {
|
|
145
|
+
this.#checkMember(frame);
|
|
146
|
+
|
|
147
|
+
return await this.service.reportComment(
|
|
148
|
+
frame.options.id,
|
|
149
|
+
frame.options?.context?.member
|
|
150
|
+
);
|
|
151
|
+
}
|
|
101
152
|
};
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
</td>
|
|
137
137
|
<td style="padding-right: 8px;">
|
|
138
138
|
<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; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">{{memberName}}</p>
|
|
139
|
-
<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: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">{{#if
|
|
139
|
+
<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: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">{{#if memberExpertise}}{{memberExpertise}} • {{/if}}{{replyDate}}</p>
|
|
140
140
|
</td>
|
|
141
141
|
</tr>
|
|
142
142
|
</table>
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
</td>
|
|
137
137
|
<td style="padding-right: 8px;">
|
|
138
138
|
<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; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">{{memberName}}</p>
|
|
139
|
-
<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: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">{{#if
|
|
139
|
+
<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: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">{{#if memberExpertise}}{{memberExpertise}} • {{/if}}{{commentDate}}</p>
|
|
140
140
|
</td>
|
|
141
141
|
</tr>
|
|
142
142
|
</table>
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
</td>
|
|
132
132
|
<td style="padding-right: 8px;">
|
|
133
133
|
<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; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">{{memberName}}</p>
|
|
134
|
-
<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: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">{{#if
|
|
134
|
+
<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: 13px; padding-right: 8px; padding: 0; margin: 0; color: #95A1AD;">{{#if memberExpertise}}{{memberExpertise}} • {{/if}}{{commentDate}}</p>
|
|
135
135
|
</td>
|
|
136
136
|
</tr>
|
|
137
137
|
</table>
|
|
@@ -41,7 +41,7 @@ class CommentsServiceEmails {
|
|
|
41
41
|
commentHtml: comment.get('html'),
|
|
42
42
|
commentDate: moment(comment.get('created_at')).tz(this.settingsCache.get('timezone')).format('D MMM YYYY'),
|
|
43
43
|
memberName: memberName,
|
|
44
|
-
|
|
44
|
+
memberExpertise: member.get('expertise'),
|
|
45
45
|
memberInitials: this.extractInitials(memberName),
|
|
46
46
|
accentColor: this.settingsCache.get('accent_color'),
|
|
47
47
|
fromEmail: this.notificationFromAddress,
|
|
@@ -90,7 +90,7 @@ class CommentsServiceEmails {
|
|
|
90
90
|
replyHtml: reply.get('html'),
|
|
91
91
|
replyDate: moment(reply.get('created_at')).tz(this.settingsCache.get('timezone')).format('D MMM YYYY'),
|
|
92
92
|
memberName: memberName,
|
|
93
|
-
|
|
93
|
+
memberExpertise: member.get('expertise'),
|
|
94
94
|
memberInitials: this.extractInitials(memberName),
|
|
95
95
|
accentColor: this.settingsCache.get('accent_color'),
|
|
96
96
|
fromEmail: this.notificationFromAddress,
|
|
@@ -140,7 +140,7 @@ class CommentsServiceEmails {
|
|
|
140
140
|
|
|
141
141
|
memberName: memberName,
|
|
142
142
|
memberEmail: member.get('email'),
|
|
143
|
-
|
|
143
|
+
memberExpertise: member.get('expertise'),
|
|
144
144
|
memberInitials: this.extractInitials(memberName),
|
|
145
145
|
accentColor: this.settingsCache.get('accent_color'),
|
|
146
146
|
fromEmail: this.notificationFromAddress,
|
|
@@ -87,6 +87,58 @@ class CommentsService {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
async likeComment(commentId, member, options = {}) {
|
|
91
|
+
this.checkEnabled();
|
|
92
|
+
|
|
93
|
+
const memberModel = await this.models.Member.findOne({
|
|
94
|
+
id: member.id
|
|
95
|
+
}, {
|
|
96
|
+
require: true,
|
|
97
|
+
...options,
|
|
98
|
+
withRelated: ['products']
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.checkCommentAccess(memberModel);
|
|
102
|
+
|
|
103
|
+
const data = {
|
|
104
|
+
member_id: memberModel.id,
|
|
105
|
+
comment_id: commentId
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const existing = await this.models.CommentLike.findOne(data, options);
|
|
109
|
+
|
|
110
|
+
if (existing) {
|
|
111
|
+
throw new errors.BadRequestError({
|
|
112
|
+
message: tpl(messages.alreadyLiked)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return await this.models.CommentLike.add(data, options);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async unlikeComment(commentId, member, options = {}) {
|
|
120
|
+
this.checkEnabled();
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await this.models.CommentLike.destroy({
|
|
124
|
+
...options,
|
|
125
|
+
destroyBy: {
|
|
126
|
+
member_id: member.id,
|
|
127
|
+
comment_id: commentId
|
|
128
|
+
},
|
|
129
|
+
require: true
|
|
130
|
+
});
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (err instanceof this.models.CommentLike.NotFoundError) {
|
|
133
|
+
return Promise.reject(new errors.NotFoundError({
|
|
134
|
+
message: tpl(messages.likeNotFound)
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
90
142
|
async reportComment(commentId, reporter) {
|
|
91
143
|
this.checkEnabled();
|
|
92
144
|
const comment = await this.models.Comment.findOne({id: commentId}, {require: true});
|