ghost 5.8.2 → 5.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/components/tryghost-adapter-manager-0.0.0.tgz +0 -0
  2. package/components/tryghost-api-framework-0.0.0.tgz +0 -0
  3. package/components/tryghost-bootstrap-socket-0.0.0.tgz +0 -0
  4. package/components/tryghost-custom-theme-settings-service-0.0.0.tgz +0 -0
  5. package/components/tryghost-email-analytics-provider-mailgun-0.0.0.tgz +0 -0
  6. package/components/tryghost-email-analytics-service-0.0.0.tgz +0 -0
  7. package/components/tryghost-job-manager-0.0.0.tgz +0 -0
  8. package/components/tryghost-mailgun-client-0.0.0.tgz +0 -0
  9. package/components/tryghost-member-analytics-service-0.0.0.tgz +0 -0
  10. package/components/tryghost-members-api-0.0.0.tgz +0 -0
  11. package/components/tryghost-members-importer-0.0.0.tgz +0 -0
  12. package/components/tryghost-members-offers-0.0.0.tgz +0 -0
  13. package/components/tryghost-members-payments-0.0.0.tgz +0 -0
  14. package/components/tryghost-members-ssr-0.0.0.tgz +0 -0
  15. package/components/tryghost-members-stripe-service-0.0.0.tgz +0 -0
  16. package/components/tryghost-minifier-0.0.0.tgz +0 -0
  17. package/components/tryghost-mw-cache-control-0.0.0.tgz +0 -0
  18. package/components/tryghost-mw-error-handler-0.0.0.tgz +0 -0
  19. package/components/tryghost-package-json-0.0.0.tgz +0 -0
  20. package/components/tryghost-session-service-0.0.0.tgz +0 -0
  21. package/components/tryghost-settings-path-manager-0.0.0.tgz +0 -0
  22. package/components/tryghost-update-check-service-0.0.0.tgz +0 -0
  23. package/content/themes/casper/README.md +1 -1
  24. package/content/themes/casper/assets/built/portal.min.js +3 -0
  25. package/content/themes/casper/assets/built/screen.css +1 -1
  26. package/content/themes/casper/assets/built/screen.css.map +1 -1
  27. package/content/themes/casper/assets/css/screen.css +119 -37
  28. package/content/themes/casper/package.json +3 -3
  29. package/content/themes/casper/partials/post-card.hbs +3 -1
  30. package/content/themes/casper/post.hbs +7 -7
  31. package/content/themes/casper/yarn.lock +69 -68
  32. package/core/boot.js +2 -0
  33. package/core/built/admin/assets/{chunk.143.52be0261357eddd34833.js → chunk.143.9b9ae7a21f4620dc58b9.js} +5 -5
  34. package/core/built/admin/assets/{chunk.178.5e51541237cbe44b2a57.js → chunk.178.711d94cc8a84182a6c38.js} +4 -4
  35. package/core/built/admin/assets/{chunk.351.cbc224ca65c14ef5322d.js → chunk.351.73f27952f867334a8228.js} +3 -3
  36. package/core/built/admin/assets/{chunk.351.cbc224ca65c14ef5322d.js.LICENSE.txt → chunk.351.73f27952f867334a8228.js.LICENSE.txt} +0 -0
  37. package/core/built/admin/assets/{ghost-b469423d0fbe5e40af17b560f7e3cead.css → ghost-686c383caa6a3469cefb939ab10e21b6.css} +1 -1
  38. package/core/built/admin/assets/{ghost-dark-bcb6f4517a2dfe23a0a280632bfca00c.css → ghost-dark-6814c399ff5b3d9c8efe2d92bc7ec779.css} +1 -1
  39. package/core/built/admin/assets/{ghost-a66a04418efe85083a3adca0fb16bb52.js → ghost-eca1a709a74b1af277e48aad4e16c9db.js} +94 -92
  40. package/core/built/admin/assets/{vendor-46baf13852f545c6c89756c8e0ccbff2.js → vendor-516c9e43b4aeb92079dc1ab92c9ce492.js} +3 -3
  41. package/core/built/admin/index.html +6 -6
  42. package/core/frontend/helpers/comment_count.js +7 -15
  43. package/core/frontend/helpers/comments.js +4 -16
  44. package/core/frontend/helpers/ghost_head.js +1 -1
  45. package/core/frontend/public/ghost.min.css +1 -1
  46. package/core/frontend/src/comment-counts/js/comment-counts.js +12 -1
  47. package/core/server/api/endpoints/comments-members.js +23 -1
  48. package/core/server/api/endpoints/index.js +52 -52
  49. package/core/server/api/endpoints/utils/serializers/input/comments.js +18 -0
  50. package/core/server/api/endpoints/utils/serializers/input/db.js +1 -1
  51. package/core/server/api/endpoints/utils/serializers/input/index.js +4 -0
  52. package/core/server/api/endpoints/utils/serializers/input/integrations.js +2 -2
  53. package/core/server/api/endpoints/utils/serializers/input/tiers.js +1 -1
  54. package/core/server/api/{shared → endpoints/utils}/serializers/input/utils/settings-filter-type-group-mapper.js +0 -0
  55. package/core/server/api/{shared → endpoints/utils}/serializers/input/utils/settings-key-group-mapper.js +0 -0
  56. package/core/server/api/{shared → endpoints/utils}/serializers/input/utils/settings-key-type-mapper.js +0 -0
  57. package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +12 -12
  58. package/core/server/api/endpoints/utils/serializers/output/tiers.js +1 -1
  59. package/core/server/api/index.js +0 -2
  60. package/core/server/data/db/connection.js +0 -4
  61. package/core/server/data/importer/importers/data/settings.js +2 -2
  62. package/core/server/data/migrations/versions/5.9/2022-08-09-08-32-added-new-integration-type.js +24 -0
  63. package/core/server/data/schema/clients/mysql.js +0 -15
  64. package/core/server/data/schema/commands.js +0 -9
  65. package/core/server/data/schema/fixtures/fixtures.json +1 -1
  66. package/core/server/data/schema/schema.js +3 -3
  67. package/core/server/models/base/plugins/user-type.js +1 -9
  68. package/core/server/models/comment.js +96 -15
  69. package/core/server/models/label.js +14 -0
  70. package/core/server/models/newsletter.js +21 -0
  71. package/core/server/models/stripe-customer-subscription.js +6 -0
  72. package/core/server/models/tag.js +20 -0
  73. package/core/server/models/user.js +20 -0
  74. package/core/server/run-update-check.js +1 -1
  75. package/core/server/services/auth/api-key/admin.js +1 -1
  76. package/core/server/services/auth/api-key/content.js +1 -1
  77. package/core/server/services/bulk-email/bulk-email-processor.js +18 -11
  78. package/core/server/services/bulk-email/index.js +1 -17
  79. package/core/server/services/comments/controller.js +9 -0
  80. package/core/server/services/comments/email-templates/new-comment-reply.hbs +2 -2
  81. package/core/server/services/comments/email-templates/new-comment.hbs +2 -2
  82. package/core/server/services/comments/email-templates/report.hbs +2 -2
  83. package/core/server/services/comments/service.js +16 -3
  84. package/core/server/services/comments/stats.js +2 -0
  85. package/core/server/services/email-analytics/jobs/fetch-latest.js +1 -1
  86. package/core/server/services/mega/post-email-serializer.js +2 -2
  87. package/core/server/services/permissions/can-this.js +154 -161
  88. package/core/server/services/permissions/parse-context.js +1 -8
  89. package/core/server/services/webhooks/serialize.js +3 -3
  90. package/core/server/web/api/endpoints/admin/routes.js +1 -1
  91. package/core/server/web/api/endpoints/content/routes.js +1 -1
  92. package/core/server/web/api/testmode/jobs/graceful-job.js +1 -1
  93. package/core/server/web/comments/routes.js +2 -1
  94. package/core/server/web/shared/middleware/index.js +1 -1
  95. package/core/shared/config/defaults.json +2 -2
  96. package/core/shared/labs.js +0 -1
  97. package/package.json +32 -29
  98. package/yarn.lock +330 -276
  99. package/core/server/api/README.md +0 -130
  100. package/core/server/api/shared/frame.js +0 -95
  101. package/core/server/api/shared/headers.js +0 -152
  102. package/core/server/api/shared/http.js +0 -127
  103. package/core/server/api/shared/index.js +0 -25
  104. package/core/server/api/shared/pipeline.js +0 -259
  105. package/core/server/api/shared/serializers/handle.js +0 -140
  106. package/core/server/api/shared/serializers/index.js +0 -13
  107. package/core/server/api/shared/serializers/input/all.js +0 -41
  108. package/core/server/api/shared/serializers/input/index.js +0 -5
  109. package/core/server/api/shared/serializers/output/index.js +0 -1
  110. package/core/server/api/shared/utils/index.js +0 -5
  111. package/core/server/api/shared/utils/options.js +0 -23
  112. package/core/server/api/shared/validators/handle.js +0 -68
  113. package/core/server/api/shared/validators/index.js +0 -9
  114. package/core/server/api/shared/validators/input/all.js +0 -213
  115. package/core/server/api/shared/validators/input/index.js +0 -5
  116. package/core/server/services/bulk-email/mailgun.js +0 -122
  117. package/core/server/web/shared/middleware/cache-control.js +0 -43
@@ -94,7 +94,7 @@
94
94
  border-bottom-color: #EEF5F8;
95
95
  }
96
96
  a {
97
- color: #3A464C;
97
+ color: #15212A;
98
98
  }
99
99
  </style>
100
100
  </head>
@@ -116,7 +116,7 @@
116
116
  <tr>
117
117
  <td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
118
118
  <p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 25px; margin: 0; margin-bottom: 15px;">Hey there,</p>
119
- <p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 32px;">{{reporter}} has reported the comment below on <a href="{{postUrl}}" target="_blank">{{postTitle}}</a>. This comment will remain visible until you choose to remove it, which can be done directly on the post.</p>
119
+ <p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 32px;">{{reporter}} has reported the comment below on <a href="{{postUrl}}" target="_blank" style="font-weight: bold; text-decoration: underline; color: #15212A;">{{postTitle}}</a>. This comment will remain visible until you choose to remove it, which can be done directly on the post.</p>
120
120
 
121
121
  <table width="100" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
122
122
  <tbody>
@@ -115,7 +115,18 @@ class CommentsService {
115
115
  */
116
116
  async getComments(options) {
117
117
  this.checkEnabled();
118
- const page = await this.models.Comment.findPage(options);
118
+ const page = await this.models.Comment.findPage({...options, parentId: null});
119
+
120
+ return page;
121
+ }
122
+
123
+ /**
124
+ * @param {string} id - The ID of the Comment to get replies from
125
+ * @param {any} options
126
+ */
127
+ async getReplies(id, options) {
128
+ this.checkEnabled();
129
+ const page = await this.models.Comment.findPage({...options, parentId: id});
119
130
 
120
131
  return page;
121
132
  }
@@ -181,7 +192,8 @@ class CommentsService {
181
192
  commentId: model.id
182
193
  }));
183
194
 
184
- return model;
195
+ // Instead of returning the model, fetch it again, so we have all the relations properly fetched
196
+ return await this.models.Comment.findOne({id: model.id}, {...options, require: true});
185
197
  }
186
198
 
187
199
  /**
@@ -240,7 +252,8 @@ class CommentsService {
240
252
  commentId: model.id
241
253
  }));
242
254
 
243
- return model;
255
+ // Instead of returning the model, fetch it again, so we have all the relations properly fetched
256
+ return await this.models.Comment.findOne({id: model.id}, {...options, require: true});
244
257
  }
245
258
 
246
259
  /**
@@ -11,6 +11,7 @@ module.exports = class CommentsStatsService {
11
11
  const results = await this.db.knex('comments')
12
12
  .select(this.db.knex.raw(`COUNT(*) AS count, post_id`))
13
13
  .groupBy('post_id')
14
+ .where('status', 'published')
14
15
  .whereIn('post_id', ids);
15
16
 
16
17
  const counts = ids.reduce((memo, id) => {
@@ -30,6 +31,7 @@ module.exports = class CommentsStatsService {
30
31
  async getAllCounts() {
31
32
  const results = await this.db.knex('comments')
32
33
  .select(this.db.knex.raw(`COUNT(*) AS count, post_id`))
34
+ .where('status', 'published')
33
35
  .groupBy('post_id');
34
36
 
35
37
  /** @type Object<string, number> */
@@ -1,4 +1,4 @@
1
- const {parentPort} = require('bthreads');
1
+ const {parentPort} = require('worker_threads');
2
2
  const debug = require('@tryghost/debug')('jobs:email-analytics:fetch-latest');
3
3
 
4
4
  // recurring job to fetch analytics since the most recently seen event timestamp
@@ -5,7 +5,7 @@ const urlUtils = require('../../../shared/url-utils');
5
5
  const labs = require('../../../shared/labs');
6
6
  const moment = require('moment-timezone');
7
7
  const api = require('../../api').endpoints;
8
- const apiShared = require('../../api').shared;
8
+ const apiFramework = require('@tryghost/api-framework');
9
9
  const {URL} = require('url');
10
10
  const mobiledocLib = require('../../lib/mobiledoc');
11
11
  const htmlToPlaintext = require('@tryghost/html-to-plaintext');
@@ -104,7 +104,7 @@ const serializePostModel = async (model) => {
104
104
  const frame = {options: {context: {user: true}, formats: 'mobiledoc'}};
105
105
  const docName = 'posts';
106
106
 
107
- await apiShared
107
+ await apiFramework
108
108
  .serializers
109
109
  .handle
110
110
  .output(model, {docName: docName, method: 'read'}, api.serializers.output, frame);
@@ -12,186 +12,179 @@ const messages = {
12
12
  noActionsMapFoundError: 'No actions map found, ensure you have loaded permissions into database and then call permissions.init() before use.'
13
13
  };
14
14
 
15
- let canThis;
16
- let CanThisResult;
17
-
18
- // Base class for canThis call results
19
- CanThisResult = function () {
20
- };
21
-
22
- CanThisResult.prototype.buildObjectTypeHandlers = function (objTypes, actType, context, permissionLoad) {
23
- const objectTypeModelMap = {
24
- post: models.Post,
25
- role: models.Role,
26
- user: models.User,
27
- permission: models.Permission,
28
- setting: models.Settings,
29
- invite: models.Invite,
30
- integration: models.Integration,
31
- comment: models.Comment
32
- };
33
-
34
- // Iterate through the object types, i.e. ['post', 'tag', 'user']
35
- return _.reduce(objTypes, function (objTypeHandlers, objType) {
36
- // Grab the TargetModel through the objectTypeModelMap
37
- const TargetModel = objectTypeModelMap[objType];
38
-
39
- // Create the 'handler' for the object type;
40
- // the '.post()' in canThis(user).edit.post()
41
- objTypeHandlers[objType] = function (modelOrId, unsafeAttrs) {
42
- let modelId;
43
- unsafeAttrs = unsafeAttrs || {};
44
-
45
- // If it's an internal request, resolve immediately
46
- if (context.internal) {
47
- return Promise.resolve();
48
- }
49
-
50
- if (_.isNumber(modelOrId) || _.isString(modelOrId)) {
51
- // It's an id already, do nothing
52
- modelId = modelOrId;
53
- } else if (modelOrId) {
54
- // It's a model, get the id
55
- modelId = modelOrId.id;
56
- }
57
- // Wait for the user loading to finish
58
- return permissionLoad.then(function (loadedPermissions) {
59
- // Iterate through the user permissions looking for an affirmation
60
- const userPermissions = loadedPermissions.user ? loadedPermissions.user.permissions : null;
61
- const apiKeyPermissions = loadedPermissions.apiKey ? loadedPermissions.apiKey.permissions : null;
62
- const memberPermissions = loadedPermissions.member ? loadedPermissions.member.permissions : null;
63
-
64
- let hasUserPermission;
65
- let hasApiKeyPermission;
66
- let hasMemberPermission = false;
67
-
68
- const checkPermission = function (perm) {
69
- let permObjId;
70
-
71
- // Look for a matching action type and object type first
72
- if (perm.get('action_type') !== actType || perm.get('object_type') !== objType) {
73
- return false;
74
- }
75
-
76
- // Grab the object id (if specified, could be null)
77
- permObjId = perm.get('object_id');
78
-
79
- // If we didn't specify a model (any thing)
80
- // or the permission didn't have an id scope set
81
- // then the "thing" has permission
82
- if (!modelId || !permObjId) {
83
- return true;
84
- }
85
-
86
- // Otherwise, check if the id's match
87
- // TODO: String vs Int comparison possibility here?
88
- return modelId === permObjId;
89
- };
90
-
91
- if (loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Owner'})) {
92
- hasUserPermission = true;
93
- } else if (!_.isEmpty(userPermissions)) {
94
- hasUserPermission = _.some(userPermissions, checkPermission);
95
- }
15
+ class CanThisResult {
16
+ buildObjectTypeHandlers(objTypes, actType, context, permissionLoad) {
17
+ const objectTypeModelMap = {
18
+ post: models.Post,
19
+ role: models.Role,
20
+ user: models.User,
21
+ permission: models.Permission,
22
+ setting: models.Settings,
23
+ invite: models.Invite,
24
+ integration: models.Integration,
25
+ comment: models.Comment
26
+ };
96
27
 
97
- if (loadedPermissions.member) {
98
- hasMemberPermission = _.some(memberPermissions, checkPermission);
99
- }
28
+ // Iterate through the object types, i.e. ['post', 'tag', 'user']
29
+ return _.reduce(objTypes, function (objTypeHandlers, objType) {
30
+ // Grab the TargetModel through the objectTypeModelMap
31
+ const TargetModel = objectTypeModelMap[objType];
100
32
 
101
- // Check api key permissions if they were passed
102
- hasApiKeyPermission = true;
103
- if (!_.isNull(apiKeyPermissions)) {
104
- // api key request have no user, but we want the user permissions checks to pass
105
- hasUserPermission = true;
106
- hasApiKeyPermission = _.some(apiKeyPermissions, checkPermission);
107
- }
33
+ // Create the 'handler' for the object type;
34
+ // the '.post()' in canThis(user).edit.post()
35
+ objTypeHandlers[objType] = function (modelOrId, unsafeAttrs) {
36
+ let modelId;
37
+ unsafeAttrs = unsafeAttrs || {};
108
38
 
109
- // Offer a chance for the TargetModel to override the results
110
- if (TargetModel && _.isFunction(TargetModel.permissible)) {
111
- return TargetModel.permissible(
112
- modelId, actType, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission, hasMemberPermission
113
- );
39
+ // If it's an internal request, resolve immediately
40
+ if (context.internal) {
41
+ return Promise.resolve();
114
42
  }
115
43
 
116
- if (hasUserPermission && hasApiKeyPermission) {
117
- return;
44
+ if (_.isNumber(modelOrId) || _.isString(modelOrId)) {
45
+ // It's an id already, do nothing
46
+ modelId = modelOrId;
47
+ } else if (modelOrId) {
48
+ // It's a model, get the id
49
+ modelId = modelOrId.id;
118
50
  }
51
+ // Wait for the user loading to finish
52
+ return permissionLoad.then(function (loadedPermissions) {
53
+ // Iterate through the user permissions looking for an affirmation
54
+ const userPermissions = loadedPermissions.user ? loadedPermissions.user.permissions : null;
55
+ const apiKeyPermissions = loadedPermissions.apiKey ? loadedPermissions.apiKey.permissions : null;
56
+ const memberPermissions = loadedPermissions.member ? loadedPermissions.member.permissions : null;
57
+
58
+ let hasUserPermission;
59
+ let hasApiKeyPermission;
60
+ let hasMemberPermission = false;
61
+
62
+ const checkPermission = function (perm) {
63
+ let permObjId;
64
+
65
+ // Look for a matching action type and object type first
66
+ if (perm.get('action_type') !== actType || perm.get('object_type') !== objType) {
67
+ return false;
68
+ }
69
+
70
+ // Grab the object id (if specified, could be null)
71
+ permObjId = perm.get('object_id');
72
+
73
+ // If we didn't specify a model (any thing)
74
+ // or the permission didn't have an id scope set
75
+ // then the "thing" has permission
76
+ if (!modelId || !permObjId) {
77
+ return true;
78
+ }
79
+
80
+ // Otherwise, check if the id's match
81
+ // TODO: String vs Int comparison possibility here?
82
+ return modelId === permObjId;
83
+ };
84
+
85
+ if (loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Owner'})) {
86
+ hasUserPermission = true;
87
+ } else if (!_.isEmpty(userPermissions)) {
88
+ hasUserPermission = _.some(userPermissions, checkPermission);
89
+ }
119
90
 
120
- return Promise.reject(new errors.NoPermissionError({message: tpl(messages.noPermissionToAction)}));
121
- });
122
- };
123
-
124
- return objTypeHandlers;
125
- }, {});
126
- };
91
+ if (loadedPermissions.member) {
92
+ hasMemberPermission = _.some(memberPermissions, checkPermission);
93
+ }
127
94
 
128
- CanThisResult.prototype.beginCheck = function (context) {
129
- const self = this;
130
- let userPermissionLoad;
131
- let apiKeyPermissionLoad;
132
- let memberPermissionLoad;
133
- let permissionsLoad;
95
+ // Check api key permissions if they were passed
96
+ hasApiKeyPermission = true;
97
+ if (!_.isNull(apiKeyPermissions)) {
98
+ // api key request have no user, but we want the user permissions checks to pass
99
+ hasUserPermission = true;
100
+ hasApiKeyPermission = _.some(apiKeyPermissions, checkPermission);
101
+ }
134
102
 
135
- // Get context.user, context.api_key and context.app
136
- context = parseContext(context);
103
+ // Offer a chance for the TargetModel to override the results
104
+ if (TargetModel && _.isFunction(TargetModel.permissible)) {
105
+ return TargetModel.permissible(
106
+ modelId, actType, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission, hasMemberPermission
107
+ );
108
+ }
137
109
 
138
- if (actionsMap.empty()) {
139
- throw new errors.InternalServerError({message: tpl(messages.noActionsMapFoundError)});
140
- }
110
+ if (hasUserPermission && hasApiKeyPermission) {
111
+ return;
112
+ }
141
113
 
142
- // Kick off loading of user permissions if necessary
143
- if (context.user) {
144
- userPermissionLoad = providers.user(context.user);
145
- } else {
146
- // Resolve null if no context.user to prevent db call
147
- userPermissionLoad = Promise.resolve(null);
148
- }
114
+ return Promise.reject(new errors.NoPermissionError({message: tpl(messages.noPermissionToAction)}));
115
+ });
116
+ };
149
117
 
150
- // Kick off loading of api key permissions if necessary
151
- if (context.api_key) {
152
- apiKeyPermissionLoad = providers.apiKey(context.api_key.id);
153
- } else {
154
- // Resolve null if no context.api_key
155
- apiKeyPermissionLoad = Promise.resolve(null);
118
+ return objTypeHandlers;
119
+ }, {});
156
120
  }
157
121
 
158
- if (context.member) {
159
- memberPermissionLoad = providers.member(context.member.id);
160
- }
122
+ beginCheck(context) {
123
+ const self = this;
124
+ let userPermissionLoad;
125
+ let apiKeyPermissionLoad;
126
+ let memberPermissionLoad;
127
+ let permissionsLoad;
128
+
129
+ // Get context.user, context.api_key and context.app
130
+ context = parseContext(context);
131
+
132
+ if (actionsMap.empty()) {
133
+ throw new errors.InternalServerError({message: tpl(messages.noActionsMapFoundError)});
134
+ }
135
+
136
+ // Kick off loading of user permissions if necessary
137
+ if (context.user) {
138
+ userPermissionLoad = providers.user(context.user);
139
+ } else {
140
+ // Resolve null if no context.user to prevent db call
141
+ userPermissionLoad = Promise.resolve(null);
142
+ }
143
+
144
+ // Kick off loading of api key permissions if necessary
145
+ if (context.api_key) {
146
+ apiKeyPermissionLoad = providers.apiKey(context.api_key.id);
147
+ } else {
148
+ // Resolve null if no context.api_key
149
+ apiKeyPermissionLoad = Promise.resolve(null);
150
+ }
151
+
152
+ if (context.member) {
153
+ memberPermissionLoad = providers.member(context.member.id);
154
+ }
155
+
156
+ // Wait for both user and app permissions to load
157
+ permissionsLoad = Promise.all([userPermissionLoad, apiKeyPermissionLoad, memberPermissionLoad]).then(function (result) {
158
+ return {
159
+ user: result[0],
160
+ apiKey: result[1],
161
+ member: result[2]
162
+ };
163
+ });
161
164
 
162
- // Wait for both user and app permissions to load
163
- permissionsLoad = Promise.all([userPermissionLoad, apiKeyPermissionLoad, memberPermissionLoad]).then(function (result) {
164
- return {
165
- user: result[0],
166
- apiKey: result[1],
167
- member: result[2]
168
- };
169
- });
170
-
171
- // Iterate through the actions and their related object types
172
- _.each(actionsMap.getAll(), function (objTypes, actType) {
173
- // Build up the object type handlers;
174
- // the '.post()' parts in canThis(user).edit.post()
175
- const objTypeHandlers = self.buildObjectTypeHandlers(objTypes, actType, context, permissionsLoad);
176
-
177
- // Define a property for the action on the result;
178
- // the '.edit' in canThis(user).edit.post()
179
- Object.defineProperty(self, actType, {
180
- writable: false,
181
- enumerable: false,
182
- configurable: false,
183
- value: objTypeHandlers
165
+ // Iterate through the actions and their related object types
166
+ _.each(actionsMap.getAll(), function (objTypes, actType) {
167
+ // Build up the object type handlers;
168
+ // the '.post()' parts in canThis(user).edit.post()
169
+ const objTypeHandlers = self.buildObjectTypeHandlers(objTypes, actType, context, permissionsLoad);
170
+
171
+ // Define a property for the action on the result;
172
+ // the '.edit' in canThis(user).edit.post()
173
+ Object.defineProperty(self, actType, {
174
+ writable: false,
175
+ enumerable: false,
176
+ configurable: false,
177
+ value: objTypeHandlers
178
+ });
184
179
  });
185
- });
186
180
 
187
- // Return this for chaining
188
- return this;
189
- };
181
+ // Return this for chaining
182
+ return this;
183
+ }
184
+ }
190
185
 
191
- canThis = function (context) {
186
+ module.exports = function canThis(context) {
192
187
  const result = new CanThisResult();
193
188
 
194
189
  return result.beginCheck(context);
195
190
  };
196
-
197
- module.exports = canThis;
@@ -3,12 +3,11 @@
3
3
  *
4
4
  * Utility function, to expand strings out into objects.
5
5
  * @param {Object|String} context
6
- * @return {{internal: boolean, external: boolean, user: integer|null, public: boolean, api_key: Object|null}}
6
+ * @return {{internal: boolean, user: integer|null, public: boolean, api_key: Object|null}}
7
7
  */
8
8
  module.exports = function parseContext(context) {
9
9
  const parsed = {
10
10
  internal: false,
11
- external: false,
12
11
  user: null,
13
12
  api_key: null,
14
13
  integration: null,
@@ -16,12 +15,6 @@ module.exports = function parseContext(context) {
16
15
  public: true
17
16
  };
18
17
 
19
- // NOTE: We use the `external` context for subscribers only at the moment.
20
- if (context && (context === 'external' || context.external)) {
21
- parsed.external = true;
22
- parsed.public = false;
23
- }
24
-
25
18
  if (context && (context === 'internal' || context.internal)) {
26
19
  parsed.internal = true;
27
20
  parsed.public = false;
@@ -2,7 +2,7 @@ module.exports = (event, model) => {
2
2
  const _ = require('lodash');
3
3
  const {sequence} = require('@tryghost/promise');
4
4
  const api = require('../../api').endpoints;
5
- const apiShared = require('../../api').shared;
5
+ const apiFramework = require('@tryghost/api-framework');
6
6
 
7
7
  const resourceName = event.match(/(\w+)\./)[1];
8
8
  const docName = `${resourceName}s`;
@@ -23,7 +23,7 @@ module.exports = (event, model) => {
23
23
  };
24
24
  }
25
25
 
26
- return apiShared
26
+ return apiFramework
27
27
  .serializers
28
28
  .handle
29
29
  .output(model, {docName: docName, method: 'read'}, api.serializers.output, frame)
@@ -46,7 +46,7 @@ module.exports = (event, model) => {
46
46
  frame.options.withRelated = ['tags', 'authors'];
47
47
  }
48
48
 
49
- return apiShared
49
+ return apiFramework
50
50
  .serializers
51
51
  .handle
52
52
  .output(model, {docName: docName, method: 'read'}, api.serializers.output, frame)
@@ -1,6 +1,6 @@
1
1
  const express = require('../../../../../shared/express');
2
2
  const api = require('../../../../api').endpoints;
3
- const http = require('../../../../api').shared.http;
3
+ const {http} = require('@tryghost/api-framework');
4
4
  const apiMw = require('../../middleware');
5
5
  const mw = require('./middleware');
6
6
 
@@ -1,7 +1,7 @@
1
1
  const express = require('../../../../../shared/express');
2
2
  const cors = require('cors');
3
3
  const api = require('../../../../api').endpoints;
4
- const http = require('../../../../api').shared.http;
4
+ const {http} = require('@tryghost/api-framework');
5
5
  const mw = require('./middleware');
6
6
  const config = require('../../../../../shared/config');
7
7
 
@@ -1,4 +1,4 @@
1
- const {parentPort} = require('bthreads');
1
+ const {parentPort} = require('worker_threads');
2
2
  const util = require('util');
3
3
 
4
4
  let shutdown = false;
@@ -1,6 +1,6 @@
1
1
  const express = require('../../../shared/express');
2
2
  const api = require('../../api').endpoints;
3
- const http = require('../../api').shared.http;
3
+ const {http} = require('@tryghost/api-framework');
4
4
 
5
5
  const bodyParser = require('body-parser');
6
6
  const membersService = require('../../../server/services/members');
@@ -23,6 +23,7 @@ module.exports = function apiRoutes() {
23
23
 
24
24
  router.post('/:id/like', http(api.commentsMembers.like));
25
25
  router.delete('/:id/like', http(api.commentsMembers.unlike));
26
+ router.get('/:id/replies', http(api.commentsMembers.replies));
26
27
 
27
28
  router.post('/:id/report', http(api.commentsMembers.report));
28
29
 
@@ -8,7 +8,7 @@ module.exports = {
8
8
  },
9
9
 
10
10
  get cacheControl() {
11
- return require('./cache-control');
11
+ return require('@tryghost/mw-cache-control');
12
12
  },
13
13
 
14
14
  get prettyUrls() {
@@ -137,7 +137,7 @@
137
137
  },
138
138
  "portal": {
139
139
  "url": "https://cdn.jsdelivr.net/npm/@tryghost/portal@~{version}/umd/portal.min.js",
140
- "version": "2.5"
140
+ "version": "2.7"
141
141
  },
142
142
  "sodoSearch": {
143
143
  "url": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/sodo-search.min.js",
@@ -147,7 +147,7 @@
147
147
  "comments": {
148
148
  "url": "https://cdn.jsdelivr.net/npm/@tryghost/comments-ui@~{version}/umd/comments-ui.min.js",
149
149
  "styles": "https://cdn.jsdelivr.net/npm/@tryghost/comments-ui@~{version}/umd/main.css",
150
- "version": "0.4"
150
+ "version": "0.8"
151
151
  },
152
152
  "editor": {
153
153
  "url": "https://unpkg.com/@tryghost/koenig-react/dist/umd/koenig-react.min.js"
@@ -27,7 +27,6 @@ const BETA_FEATURES = [
27
27
  const ALPHA_FEATURES = [
28
28
  'urlCache',
29
29
  'beforeAfterCard',
30
- 'comments',
31
30
  'freeTrial'
32
31
  ];
33
32