ghost 5.4.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.
Files changed (80) hide show
  1. package/components/tryghost-custom-theme-settings-service-0.0.0.tgz +0 -0
  2. package/components/tryghost-domain-events-0.0.0.tgz +0 -0
  3. package/components/tryghost-email-analytics-provider-mailgun-0.0.0.tgz +0 -0
  4. package/components/tryghost-email-analytics-service-0.0.0.tgz +0 -0
  5. package/components/tryghost-express-dynamic-redirects-0.0.0.tgz +0 -0
  6. package/components/tryghost-magic-link-0.0.0.tgz +0 -0
  7. package/components/tryghost-member-analytics-service-0.0.0.tgz +0 -0
  8. package/components/tryghost-member-events-0.0.0.tgz +0 -0
  9. package/components/tryghost-members-analytics-ingress-0.0.0.tgz +0 -0
  10. package/components/tryghost-members-api-0.0.0.tgz +0 -0
  11. package/components/tryghost-members-csv-0.0.0.tgz +0 -0
  12. package/components/tryghost-members-events-service-0.0.0.tgz +0 -0
  13. package/components/tryghost-members-importer-0.0.0.tgz +0 -0
  14. package/components/tryghost-members-offers-0.0.0.tgz +0 -0
  15. package/components/tryghost-members-payments-0.0.0.tgz +0 -0
  16. package/components/tryghost-members-ssr-0.0.0.tgz +0 -0
  17. package/components/tryghost-members-stripe-service-0.0.0.tgz +0 -0
  18. package/components/tryghost-verification-trigger-0.0.0.tgz +0 -0
  19. package/content/themes/casper/assets/built/global.css +1 -1
  20. package/content/themes/casper/assets/built/global.css.map +1 -1
  21. package/content/themes/casper/assets/built/screen.css +1 -1
  22. package/content/themes/casper/assets/built/screen.css.map +1 -1
  23. package/content/themes/casper/assets/css/screen.css +9 -1
  24. package/content/themes/casper/gulpfile.js +1 -1
  25. package/content/themes/casper/package.json +9 -9
  26. package/content/themes/casper/yarn.lock +1154 -1249
  27. package/core/boot.js +5 -0
  28. package/core/built/assets/{chunk.3.dc389a0f93cb5fabd695.js → chunk.3.550552fbc71864fb9738.js} +20 -20
  29. package/core/built/assets/fonts/Inter.ttf +0 -0
  30. package/core/built/assets/ghost-dark-5c2a961b35311d7298136e02289d98b2.css +1 -0
  31. package/core/built/assets/ghost.min-a89d10b3b58c1a5ebaca68cef93a404c.css +1 -0
  32. package/core/built/assets/{ghost.min-36b64813b14c45075770658269d4b478.js → ghost.min-c75f224decd20f9538179d7564cd2ab4.js} +3005 -2874
  33. package/core/built/assets/icons/event-comment.svg +3 -0
  34. package/core/built/assets/{vendor.min-be0129c9c6897c9f10425e2402881d77.js → vendor.min-cf3af99dca0c71937669305afb3686a1.js} +6110 -3186
  35. package/core/frontend/helpers/comments.js +22 -10
  36. package/core/frontend/helpers/ghost_head.js +22 -4
  37. package/core/frontend/utils/frontend-apps.js +33 -0
  38. package/core/server/api/endpoints/comments-comments.js +50 -32
  39. package/core/server/api/endpoints/utils/serializers/output/config.js +2 -1
  40. package/core/server/api/endpoints/utils/serializers/output/mappers/activity-feed-events.js +17 -0
  41. package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +18 -0
  42. package/core/server/api/endpoints/utils/serializers/output/mappers/index.js +1 -0
  43. package/core/server/api/endpoints/utils/serializers/output/members.js +12 -1
  44. package/core/server/data/exporter/table-lists.js +2 -1
  45. package/core/server/data/migrations/versions/5.5/2022-07-18-14-29-add-comment-reporting-permissions.js +10 -0
  46. package/core/server/data/migrations/versions/5.5/2022-07-18-14-31-drop-reports-reason.js +3 -0
  47. package/core/server/data/migrations/versions/5.5/2022-07-18-14-32-drop-nullable-member-id-from-likes.js +4 -0
  48. package/core/server/data/migrations/versions/5.5/2022-07-18-14-33-fix-comments-on-delete-foreign-keys.js +119 -0
  49. package/core/server/data/migrations/versions/5.5/2022-07-21-08-56-add-jobs-table.js +11 -0
  50. package/core/server/data/schema/commands.js +7 -2
  51. package/core/server/data/schema/fixtures/fixtures.json +5 -0
  52. package/core/server/data/schema/schema.js +12 -4
  53. package/core/server/ghost-server.js +0 -22
  54. package/core/server/models/comment-report.js +34 -0
  55. package/core/server/models/comment.js +8 -7
  56. package/core/server/models/job.js +9 -0
  57. package/core/server/services/comments/email-templates/new-comment-reply.hbs +2 -2
  58. package/core/server/services/comments/email-templates/new-comment-reply.txt.js +7 -8
  59. package/core/server/services/comments/email-templates/new-comment.hbs +2 -2
  60. package/core/server/services/comments/email-templates/new-comment.txt.js +7 -6
  61. package/core/server/services/comments/email-templates/report.hbs +199 -0
  62. package/core/server/services/comments/email-templates/report.txt.js +16 -0
  63. package/core/server/services/comments/emails.js +57 -1
  64. package/core/server/services/comments/service.js +194 -2
  65. package/core/server/services/jobs/job-service.js +24 -1
  66. package/core/server/services/mail/GhostMailer.js +1 -0
  67. package/core/server/services/members/api.js +2 -1
  68. package/core/server/services/public-config/config.js +2 -1
  69. package/core/server/services/stripe/service.js +9 -1
  70. package/core/server/web/admin/views/default-prod.html +4 -4
  71. package/core/server/web/admin/views/default.html +4 -4
  72. package/core/server/web/api/testmode/jobs/graceful-job.js +2 -2
  73. package/core/server/web/api/testmode/routes.js +14 -0
  74. package/core/server/web/comments/routes.js +2 -0
  75. package/core/shared/config/defaults.json +12 -7
  76. package/core/shared/config/env/config.testing.json +3 -2
  77. package/package.json +70 -56
  78. package/yarn.lock +1763 -1796
  79. package/core/built/assets/ghost-dark-739c1f5546bd048eeeb253965ef36712.css +0 -1
  80. package/core/built/assets/ghost.min-5211776b9497f36fac8c9e5f2584cbcc.css +0 -1
@@ -1,3 +1,16 @@
1
+ const tpl = require('@tryghost/tpl');
2
+ const errors = require('@tryghost/errors');
3
+ const {MemberCommentEvent} = require('@tryghost/member-events');
4
+ const DomainEvents = require('@tryghost/domain-events');
5
+
6
+ const messages = {
7
+ commentNotFound: 'Comment could not be found',
8
+ memberNotFound: 'Unable to find member',
9
+ likeNotFound: 'Unable to find like',
10
+ alreadyLiked: 'This comment was liked already',
11
+ replyToReply: 'Can not reply to a reply'
12
+ };
13
+
1
14
  class CommentsService {
2
15
  constructor({config, logging, models, mailer, settingsCache, urlService, urlUtils}) {
3
16
  this.config = config;
@@ -13,11 +26,190 @@ class CommentsService {
13
26
  }
14
27
 
15
28
  async sendNewCommentNotifications(comment) {
16
- this.emails.notifyPostAuthors(comment);
29
+ await this.emails.notifyPostAuthors(comment);
17
30
 
18
31
  if (comment.get('parent_id')) {
19
- this.emails.notifyParentCommentAuthor(comment);
32
+ await this.emails.notifyParentCommentAuthor(comment);
33
+ }
34
+ }
35
+
36
+ async reportComment(commentId, reporter) {
37
+ const comment = await this.models.Comment.findOne({id: commentId}, {require: true});
38
+
39
+ // Check if this reporter already reported this comment (then don't send an email)?
40
+ const existing = await this.models.CommentReport.findOne({
41
+ comment_id: comment.id,
42
+ member_id: reporter.id
43
+ });
44
+
45
+ if (existing) {
46
+ // Ignore silently for now
47
+ return;
48
+ }
49
+
50
+ // Save report model
51
+ await this.models.CommentReport.add({
52
+ comment_id: comment.id,
53
+ member_id: reporter.id
54
+ });
55
+
56
+ await this.emails.notifiyReport(comment, reporter);
57
+ }
58
+
59
+ /**
60
+ * @param {string} id - The ID of the Comment to get
61
+ * @param {any} options
62
+ */
63
+ async getCommentByID(id, options) {
64
+ const model = await this.models.Comment.findOne({id}, options);
65
+
66
+ if (!model) {
67
+ throw new errors.NotFoundError({
68
+ messages: tpl(messages.commentNotFound)
69
+ });
70
+ }
71
+
72
+ return model;
73
+ }
74
+
75
+ /**
76
+ * @param {string} post - The ID of the Post to comment on
77
+ * @param {string} member - The ID of the Member to comment as
78
+ * @param {string} comment - The HTML content of the Comment
79
+ * @param {any} options
80
+ */
81
+ async commentOnPost(post, member, comment, options) {
82
+ await this.models.Member.findOne({
83
+ id: member
84
+ }, {
85
+ require: true,
86
+ ...options
87
+ });
88
+
89
+ const model = await this.models.Comment.add({
90
+ post_id: post,
91
+ member_id: member,
92
+ parent_id: null,
93
+ html: comment,
94
+ status: 'published'
95
+ }, options);
96
+
97
+ if (!options.context.internal) {
98
+ await this.sendNewCommentNotifications(model);
99
+ }
100
+
101
+ DomainEvents.dispatch(MemberCommentEvent.create({
102
+ memberId: member,
103
+ postId: post,
104
+ commentId: model.id
105
+ }));
106
+
107
+ return model;
108
+ }
109
+
110
+ /**
111
+ * @param {string} parent - The ID of the Comment to reply to
112
+ * @param {string} member - The ID of the Member to comment as
113
+ * @param {string} comment - The HTML content of the Comment
114
+ * @param {any} options
115
+ */
116
+ async replyToComment(parent, member, comment, options) {
117
+ await this.models.Member.findOne({
118
+ id: member
119
+ }, {
120
+ require: true,
121
+ ...options
122
+ });
123
+
124
+ const parentComment = await this.getCommentByID(parent, options);
125
+ if (!parentComment) {
126
+ throw new errors.BadRequestError({
127
+ message: tpl(messages.commentNotFound)
128
+ });
129
+ }
130
+
131
+ if (parentComment.get('parent_id') !== null) {
132
+ throw new errors.BadRequestError({
133
+ message: tpl(messages.replyToReply)
134
+ });
135
+ }
136
+
137
+ const model = await this.models.Comment.add({
138
+ post_id: parentComment.get('post_id'),
139
+ member_id: member,
140
+ parent_id: parentComment.id,
141
+ html: comment,
142
+ status: 'published'
143
+ }, options);
144
+
145
+ if (!options.context.internal) {
146
+ await this.sendNewCommentNotifications(model);
20
147
  }
148
+
149
+ DomainEvents.dispatch(MemberCommentEvent.create({
150
+ memberId: member,
151
+ postId: parentComment.get('post_id'),
152
+ commentId: model.id
153
+ }));
154
+
155
+ return model;
156
+ }
157
+
158
+ /**
159
+ * @param {string} id - The ID of the Comment to delete
160
+ * @param {string} member - The ID of the Member to delete as
161
+ * @param {any} options
162
+ */
163
+ async deleteComment(id, member, options) {
164
+ const existingComment = await this.getCommentByID(id, options);
165
+
166
+ if (existingComment.get('member_id') !== member) {
167
+ throw new errors.NoPermissionError({
168
+ // todo fix message
169
+ message: tpl(messages.memberNotFound)
170
+ });
171
+ }
172
+
173
+ const model = await this.models.Comment.edit({
174
+ status: 'deleted'
175
+ }, {
176
+ id,
177
+ require: true,
178
+ ...options
179
+ });
180
+
181
+ return model;
182
+ }
183
+
184
+ /**
185
+ * @param {string} id - The ID of the Comment to edit
186
+ * @param {string} member - The ID of the Member to edit as
187
+ * @param {string} comment - The new HTML content of the Comment
188
+ * @param {any} options
189
+ */
190
+ async editCommentContent(id, member, comment, options) {
191
+ const existingComment = await this.getCommentByID(id, options);
192
+
193
+ if (!comment) {
194
+ return existingComment;
195
+ }
196
+
197
+ if (existingComment.get('member_id') !== member) {
198
+ throw new errors.NoPermissionError({
199
+ // todo fix message
200
+ message: tpl(messages.memberNotFound)
201
+ });
202
+ }
203
+
204
+ const model = await this.models.Comment.edit({
205
+ html: comment
206
+ }, {
207
+ id,
208
+ require: true,
209
+ ...options
210
+ });
211
+
212
+ return model;
21
213
  }
22
214
  }
23
215
 
@@ -5,6 +5,7 @@
5
5
 
6
6
  const JobManager = require('@tryghost/job-manager');
7
7
  const logging = require('@tryghost/logging');
8
+ const models = require('../../models');
8
9
  const sentry = require('../../../shared/sentry');
9
10
 
10
11
  const errorHandler = (error, workerMeta) => {
@@ -17,6 +18,28 @@ const workerMessageHandler = ({name, message}) => {
17
18
  logging.info(`Worker for job ${name} sent a message: ${message}`);
18
19
  };
19
20
 
20
- const jobManager = new JobManager({errorHandler, workerMessageHandler});
21
+ const initTestMode = () => {
22
+ // Output job queue length every 5 seconds
23
+ setInterval(() => {
24
+ logging.warn(`${jobManager.queue.length()} jobs in the queue. Idle: ${jobManager.queue.idle()}`);
25
+
26
+ const runningScheduledjobs = Object.keys(jobManager.bree.workers);
27
+ if (Object.keys(jobManager.bree.workers).length) {
28
+ logging.warn(`${Object.keys(jobManager.bree.workers).length} jobs running: ${runningScheduledjobs}`);
29
+ }
30
+
31
+ const scheduledJobs = Object.keys(jobManager.bree.intervals);
32
+ if (Object.keys(jobManager.bree.intervals).length) {
33
+ logging.warn(`${Object.keys(jobManager.bree.intervals).length} scheduled jobs: ${scheduledJobs}`);
34
+ }
35
+
36
+ if (runningScheduledjobs.length === 0 && scheduledJobs.length === 0) {
37
+ logging.warn('No scheduled or running jobs');
38
+ }
39
+ }, 5000);
40
+ };
41
+
42
+ const jobManager = new JobManager({errorHandler, workerMessageHandler, JobModel: models.Job});
21
43
 
22
44
  module.exports = jobManager;
45
+ module.exports.initTestMode = initTestMode;
@@ -95,6 +95,7 @@ module.exports = class GhostMailer {
95
95
  * @param {string} message.html - email content
96
96
  * @param {string} message.to - email recipient address
97
97
  * @param {string} [message.from] - sender email address
98
+ * @param {string} [message.text] - text version of this message
98
99
  * @param {boolean} [message.forceTextContent] - maps to generateTextFromHTML nodemailer option
99
100
  * which is: "if set to true uses HTML to generate plain text body part from the HTML if the text is not defined"
100
101
  * (ref: https://github.com/nodemailer/nodemailer/tree/da2f1d278f91b4262e940c0b37638e7027184b1d#e-mail-message-fields)
@@ -189,7 +189,8 @@ function createApiInstance(config) {
189
189
  StripeProduct: models.StripeProduct,
190
190
  StripePrice: models.StripePrice,
191
191
  Product: models.Product,
192
- Settings: models.Settings
192
+ Settings: models.Settings,
193
+ Comment: models.Comment
193
194
  },
194
195
  stripeAPIService: stripeService.api,
195
196
  offersAPI: offersService.api,
@@ -18,7 +18,8 @@ module.exports = function getConfigProperties() {
18
18
  mailgunIsConfigured: !!(config.get('bulkEmail') && config.get('bulkEmail').mailgun),
19
19
  emailAnalytics: config.get('emailAnalytics'),
20
20
  hostSettings: config.get('hostSettings'),
21
- tenor: config.get('tenor')
21
+ tenor: config.get('tenor'),
22
+ editor: config.get('editor')
22
23
  };
23
24
 
24
25
  const billingUrl = config.get('hostSettings:billing:enabled') ? config.get('hostSettings:billing:url') : '';
@@ -25,7 +25,15 @@ const debouncedConfigureApi = _.debounce(() => {
25
25
 
26
26
  module.exports = new StripeService({
27
27
  membersService,
28
- models: _.pick(models, ['Product', 'StripePrice', 'StripeCustomerSubscription', 'StripeProduct', 'MemberStripeCustomer', 'Offer', 'Settings']),
28
+ models: _.pick(models, [
29
+ 'Product',
30
+ 'StripePrice',
31
+ 'StripeCustomerSubscription',
32
+ 'StripeProduct',
33
+ 'MemberStripeCustomer',
34
+ 'Offer',
35
+ 'Settings'
36
+ ]),
29
37
  StripeWebhook: {
30
38
  async get() {
31
39
  return {
@@ -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%2F%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.4%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" />
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%2F%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.5%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" />
@@ -38,7 +38,7 @@
38
38
 
39
39
 
40
40
  <link rel="stylesheet" href="assets/vendor.min-4a6661c574707ceca220aa2e76558995.css">
41
- <link rel="stylesheet" href="assets/ghost.min-5211776b9497f36fac8c9e5f2584cbcc.css" title="light">
41
+ <link rel="stylesheet" href="assets/ghost.min-a89d10b3b58c1a5ebaca68cef93a404c.css" title="light">
42
42
 
43
43
 
44
44
 
@@ -56,8 +56,8 @@
56
56
  <div id="ember-basic-dropdown-wormhole"></div>
57
57
 
58
58
 
59
- <script src="assets/vendor.min-be0129c9c6897c9f10425e2402881d77.js"></script>
60
- <script src="assets/ghost.min-36b64813b14c45075770658269d4b478.js"></script>
59
+ <script src="assets/vendor.min-cf3af99dca0c71937669305afb3686a1.js"></script>
60
+ <script src="assets/ghost.min-c75f224decd20f9538179d7564cd2ab4.js"></script>
61
61
 
62
62
  </body>
63
63
  </html>
@@ -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%2F%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.4%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" />
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%2F%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.5%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" />
@@ -38,7 +38,7 @@
38
38
 
39
39
 
40
40
  <link rel="stylesheet" href="assets/vendor.min-4a6661c574707ceca220aa2e76558995.css">
41
- <link rel="stylesheet" href="assets/ghost.min-5211776b9497f36fac8c9e5f2584cbcc.css" title="light">
41
+ <link rel="stylesheet" href="assets/ghost.min-a89d10b3b58c1a5ebaca68cef93a404c.css" title="light">
42
42
 
43
43
 
44
44
 
@@ -56,8 +56,8 @@
56
56
  <div id="ember-basic-dropdown-wormhole"></div>
57
57
 
58
58
 
59
- <script src="assets/vendor.min-be0129c9c6897c9f10425e2402881d77.js"></script>
60
- <script src="assets/ghost.min-36b64813b14c45075770658269d4b478.js"></script>
59
+ <script src="assets/vendor.min-cf3af99dca0c71937669305afb3686a1.js"></script>
60
+ <script src="assets/ghost.min-c75f224decd20f9538179d7564cd2ab4.js"></script>
61
61
 
62
62
  </body>
63
63
  </html>
@@ -37,7 +37,7 @@ const internalContext = {context: {internal: true}};
37
37
 
38
38
  if (shutdown) {
39
39
  postParentPortMessage(`Job shutting down gracefully`);
40
- process.exit(0);
40
+ parentPort.postMessage('done');
41
41
  }
42
42
 
43
43
  postParentPortMessage(`Fetching posts`);
@@ -46,5 +46,5 @@ const internalContext = {context: {internal: true}};
46
46
  postParentPortMessage(`Found ${posts.data.length} posts. First one: ${posts.data[0].toJSON().slug}`);
47
47
  postParentPortMessage('Graceful job has completed!');
48
48
 
49
- process.exit(0);
49
+ parentPort.postMessage('done');
50
50
  })();
@@ -44,6 +44,20 @@ module.exports = function testRoutes() {
44
44
  res.sendStatus(202);
45
45
  });
46
46
 
47
+ router.get('/oneoff/:name', (req, res) => {
48
+ logging.info('Create Slow Job with timeout of', req.params.name);
49
+
50
+ const options = {};
51
+
52
+ options.solo = true;
53
+ options.name = req.params.name;
54
+ options.job = path.resolve(__dirname, 'jobs', `${options.name}.js`);
55
+
56
+ jobsService.addOneOffJob(options);
57
+
58
+ res.sendStatus(202);
59
+ });
60
+
47
61
  router.get('/schedule/:schedule/:name*?', (req, res) => {
48
62
  if (!req.params.schedule) {
49
63
  return res.sendStatus(400, 'schedule parameter cannot be mepty');
@@ -24,5 +24,7 @@ module.exports = function apiRoutes() {
24
24
  router.post('/:id/like', http(api.commentsComments.like));
25
25
  router.delete('/:id/like', http(api.commentsComments.unlike));
26
26
 
27
+ router.post('/:id/report', http(api.commentsComments.report));
28
+
27
29
  return router;
28
30
  };
@@ -133,12 +133,21 @@
133
133
  "emailAnalytics": true
134
134
  },
135
135
  "portal": {
136
- "url": "https://unpkg.com/@tryghost/portal@~2.3.0/umd/portal.min.js",
136
+ "url": "https://cdn.jsdelivr.net/npm/@tryghost/portal@~{version}/umd/portal.min.js",
137
137
  "version": "2.3"
138
138
  },
139
139
  "sodoSearch": {
140
- "url": "https://unpkg.com/@tryghost/sodo-search@~1.0.0/umd/sodo-search.min.js",
141
- "version": "1.0.0"
140
+ "url": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/sodo-search.min.js",
141
+ "styles": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/main.css",
142
+ "version": "1.1"
143
+ },
144
+ "comments": {
145
+ "url": "https://cdn.jsdelivr.net/npm/@tryghost/comments-ui@~{version}/umd/comments-ui.min.js",
146
+ "styles": "https://cdn.jsdelivr.net/npm/@tryghost/comments-ui@~{version}/umd/main.css",
147
+ "version": "0.2"
148
+ },
149
+ "editor": {
150
+ "url": ""
142
151
  },
143
152
  "tenor": {
144
153
  "publicReadOnlyApiKey": null,
@@ -152,9 +161,5 @@
152
161
  },
153
162
  "gravatar": {
154
163
  "url": "https://www.gravatar.com/avatar/{hash}?s={size}&r={rating}&d={_default}"
155
- },
156
- "comments": {
157
- "url": "https://unpkg.com/@tryghost/comments-ui@~0.1.0/umd/comments-ui.min.js",
158
- "version": "0.1.0"
159
164
  }
160
165
  }
@@ -56,7 +56,8 @@
56
56
  "urlCache": "test/utils/fixtures/urls"
57
57
  },
58
58
  "sodoSearch": {
59
- "url": "https://unpkg.com/@tryghost/sodo-search@~0.1.0/umd/sodo-search.min.js",
60
- "version": "0.1.0"
59
+ "url": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/sodo-search.min.js",
60
+ "styles": "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/main.css",
61
+ "version": "1.0"
61
62
  }
62
63
  }