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.
Files changed (107) 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 +31 -8
  24. package/content/themes/casper/default.hbs +8 -5
  25. package/content/themes/casper/gulpfile.js +1 -1
  26. package/content/themes/casper/package.json +9 -9
  27. package/content/themes/casper/yarn.lock +1154 -1249
  28. package/core/boot.js +5 -0
  29. package/core/built/assets/{chunk.3.dc389a0f93cb5fabd695.js → chunk.3.550552fbc71864fb9738.js} +20 -20
  30. package/core/built/assets/fonts/Inter.ttf +0 -0
  31. package/core/built/assets/ghost-dark-5c2a961b35311d7298136e02289d98b2.css +1 -0
  32. package/core/built/assets/ghost.min-a89d10b3b58c1a5ebaca68cef93a404c.css +1 -0
  33. package/core/built/assets/{ghost.min-f4bba3a2a5ef256b82641345505d4f0f.js → ghost.min-c75f224decd20f9538179d7564cd2ab4.js} +3025 -2883
  34. package/core/built/assets/icons/event-comment.svg +3 -0
  35. package/core/built/assets/{vendor.min-4076498ccd6c8412365f43b156084ed8.js → vendor.min-cf3af99dca0c71937669305afb3686a1.js} +6122 -3197
  36. package/core/frontend/helpers/comments.js +22 -10
  37. package/core/frontend/helpers/ghost_head.js +22 -4
  38. package/core/frontend/helpers/total_members.js +17 -0
  39. package/core/frontend/helpers/total_paid_members.js +16 -0
  40. package/core/frontend/utils/frontend-apps.js +33 -0
  41. package/core/frontend/utils/member-count.js +50 -0
  42. package/core/frontend/web/middleware/cors.js +2 -1
  43. package/core/server/api/endpoints/comments-comments.js +50 -32
  44. package/core/server/api/endpoints/offers-public.js +2 -2
  45. package/core/server/api/endpoints/offers.js +8 -8
  46. package/core/server/api/endpoints/settings.js +62 -30
  47. package/core/server/api/endpoints/utils/serializers/input/settings.js +1 -0
  48. package/core/server/api/endpoints/utils/serializers/output/config.js +2 -1
  49. package/core/server/api/endpoints/utils/serializers/output/index.js +0 -4
  50. package/core/server/api/endpoints/utils/serializers/output/mappers/activity-feed-events.js +17 -0
  51. package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +18 -0
  52. package/core/server/api/endpoints/utils/serializers/output/mappers/index.js +2 -0
  53. package/core/server/api/endpoints/utils/serializers/output/mappers/offers.js +28 -0
  54. package/core/server/api/endpoints/utils/serializers/output/members.js +12 -1
  55. package/core/server/api/endpoints/utils/serializers/output/settings.js +2 -1
  56. package/core/server/api/endpoints/utils/validators/input/settings.js +22 -2
  57. package/core/server/data/exporter/table-lists.js +2 -1
  58. package/core/server/data/migrations/versions/5.5/2022-07-18-14-29-add-comment-reporting-permissions.js +10 -0
  59. package/core/server/data/migrations/versions/5.5/2022-07-18-14-31-drop-reports-reason.js +3 -0
  60. package/core/server/data/migrations/versions/5.5/2022-07-18-14-32-drop-nullable-member-id-from-likes.js +4 -0
  61. package/core/server/data/migrations/versions/5.5/2022-07-18-14-33-fix-comments-on-delete-foreign-keys.js +119 -0
  62. package/core/server/data/migrations/versions/5.5/2022-07-21-08-56-add-jobs-table.js +11 -0
  63. package/core/server/data/schema/commands.js +7 -2
  64. package/core/server/data/schema/fixtures/fixtures.json +5 -0
  65. package/core/server/data/schema/schema.js +12 -4
  66. package/core/server/ghost-server.js +0 -22
  67. package/core/server/models/comment-report.js +34 -0
  68. package/core/server/models/comment.js +8 -7
  69. package/core/server/models/job.js +9 -0
  70. package/core/server/models/tag.js +4 -0
  71. package/core/server/services/comments/email-templates/new-comment-reply.hbs +2 -2
  72. package/core/server/services/comments/email-templates/new-comment-reply.txt.js +7 -8
  73. package/core/server/services/comments/email-templates/new-comment.hbs +2 -2
  74. package/core/server/services/comments/email-templates/new-comment.txt.js +7 -6
  75. package/core/server/services/comments/email-templates/report.hbs +199 -0
  76. package/core/server/services/comments/email-templates/report.txt.js +16 -0
  77. package/core/server/services/comments/emails.js +57 -1
  78. package/core/server/services/comments/service.js +194 -2
  79. package/core/server/services/jobs/job-service.js +24 -1
  80. package/core/server/services/mail/GhostMailer.js +1 -0
  81. package/core/server/services/members/SingleUseTokenProvider.js +3 -3
  82. package/core/server/services/members/api.js +2 -1
  83. package/core/server/services/members/config.js +4 -1
  84. package/core/server/services/members/middleware.js +14 -2
  85. package/core/server/services/members/settings.js +4 -90
  86. package/core/server/services/public-config/config.js +2 -1
  87. package/core/server/services/settings/emails/verify-email.js +166 -0
  88. package/core/server/services/settings/settings-bread-service.js +170 -4
  89. package/core/server/services/settings/settings-service.js +9 -1
  90. package/core/server/services/stripe/service.js +9 -1
  91. package/core/server/services/webhooks/serialize.js +5 -0
  92. package/core/server/web/admin/views/default-prod.html +4 -4
  93. package/core/server/web/admin/views/default.html +4 -4
  94. package/core/server/web/api/endpoints/admin/routes.js +6 -0
  95. package/core/server/web/api/endpoints/content/routes.js +2 -1
  96. package/core/server/web/api/middleware/cors.js +2 -1
  97. package/core/server/web/api/testmode/jobs/graceful-job.js +2 -2
  98. package/core/server/web/api/testmode/routes.js +14 -0
  99. package/core/server/web/comments/routes.js +2 -0
  100. package/core/server/web/members/app.js +2 -4
  101. package/core/shared/config/defaults.json +15 -7
  102. package/core/shared/config/env/config.testing.json +3 -2
  103. package/package.json +75 -60
  104. package/yarn.lock +1812 -1832
  105. package/core/built/assets/ghost-dark-9e5d1f0dfae41232e5e34e4d0df53ae0.css +0 -1
  106. package/core/built/assets/ghost.min-e7cfbd1800f8e99b9158f74f1e39cd76.css +0 -1
  107. package/core/server/api/endpoints/utils/serializers/output/offers.js +0 -16
@@ -1,6 +1,7 @@
1
1
  //@ts-check
2
2
  const debug = require('@tryghost/debug')('api:endpoints:utils:serializers:output:members');
3
3
  const {unparse} = require('@tryghost/members-csv');
4
+ const mappers = require('./mappers');
4
5
 
5
6
  module.exports = {
6
7
  browse: createSerializer('browse', paginatedMembers),
@@ -18,7 +19,7 @@ module.exports = {
18
19
  importCSV: createSerializer('importCSV', passthrough),
19
20
  memberStats: createSerializer('memberStats', passthrough),
20
21
  mrrStats: createSerializer('mrrStats', passthrough),
21
- activityFeed: createSerializer('activityFeed', passthrough)
22
+ activityFeed: createSerializer('activityFeed', activityFeed)
22
23
  };
23
24
 
24
25
  /**
@@ -73,6 +74,16 @@ function bulkAction(bulkActionResult, _apiConfig, frame) {
73
74
  };
74
75
  }
75
76
 
77
+ /**
78
+ *
79
+ * @returns {{events: any[]}}
80
+ */
81
+ function activityFeed(data, _apiConfig, frame) {
82
+ return {
83
+ events: data.events.map(e => mappers.activityFeedEvents(e, frame))
84
+ };
85
+ }
86
+
76
87
  /**
77
88
  * @template PageMeta
78
89
  *
@@ -52,7 +52,7 @@ function serializeSettings(models, apiConfig, frame) {
52
52
 
53
53
  frame.response = {
54
54
  settings: mappers.settings(filteredSettings),
55
- meta: {}
55
+ meta: models.meta ?? {}
56
56
  };
57
57
 
58
58
  if (frame.options.group) {
@@ -89,6 +89,7 @@ module.exports = {
89
89
  browse: serializeSettings,
90
90
  read: serializeSettings,
91
91
  edit: serializeSettings,
92
+ verifyKeyUpdate: serializeSettings,
92
93
 
93
94
  download: serializeData,
94
95
  upload: serializeData,
@@ -2,11 +2,12 @@ const Promise = require('bluebird');
2
2
  const _ = require('lodash');
3
3
  const {ValidationError, BadRequestError} = require('@tryghost/errors');
4
4
  const validator = require('@tryghost/validator');
5
+ const tpl = require('@tryghost/tpl');
5
6
 
6
7
  const messages = {
7
8
  invalidEmailReceived: 'Please send a valid email',
8
- invalidEmailTypeReceived: 'Invalid email type received',
9
- problemFindingSetting: 'Problem finding setting: {key}'
9
+ invalidEmailValueReceived: 'Please enter a valid email address.',
10
+ invalidEmailTypeReceived: 'Invalid email type received'
10
11
  };
11
12
 
12
13
  module.exports = {
@@ -22,6 +23,10 @@ module.exports = {
22
23
  'secondary_navigation'
23
24
  ];
24
25
 
26
+ const emailTypeSettings = [
27
+ 'members_support_address'
28
+ ];
29
+
25
30
  if (arrayTypeSettings.includes(setting.key)) {
26
31
  const typeError = new ValidationError({
27
32
  message: `Value in ${setting.key} should be an array.`,
@@ -43,6 +48,18 @@ module.exports = {
43
48
  }
44
49
  }
45
50
  }
51
+
52
+ if (emailTypeSettings.includes(setting.key)) {
53
+ const email = setting.value;
54
+
55
+ if (typeof email !== 'string' || (!validator.isEmail(email) && email !== 'noreply')) {
56
+ const typeError = new ValidationError({
57
+ message: tpl(messages.invalidEmailValueReceived),
58
+ property: setting.key
59
+ });
60
+ errors.push(typeError);
61
+ }
62
+ }
46
63
  });
47
64
 
48
65
  // Prevent setting icon to the resized one when sending all settings received from browse again in the edit endpoint
@@ -56,6 +73,9 @@ module.exports = {
56
73
  }
57
74
  },
58
75
 
76
+ /**
77
+ * @deprecated
78
+ */
59
79
  updateMembersEmail(apiConfig, frame) {
60
80
  const {email, type} = frame.data;
61
81
 
@@ -34,7 +34,8 @@ const BACKUP_TABLES = [
34
34
  'members_newsletters',
35
35
  'comments',
36
36
  'comment_likes',
37
- 'comment_reports'
37
+ 'comment_reports',
38
+ 'jobs'
38
39
  ];
39
40
 
40
41
  // NOTE: exposing only tables which are going to be included in a "default" export file
@@ -0,0 +1,10 @@
1
+ const {addPermissionWithRoles} = require('../../utils');
2
+
3
+ module.exports = addPermissionWithRoles({
4
+ name: 'Report comments',
5
+ action: 'report',
6
+ object: 'comment'
7
+ }, [
8
+ 'Administrator',
9
+ 'Admin Integration'
10
+ ]);
@@ -0,0 +1,3 @@
1
+ const {createDropColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createDropColumnMigration('comment_reports', 'reason', {type: 'text', maxlength: 65535, nullable: false});
@@ -0,0 +1,4 @@
1
+ const {createDropNullableMigration} = require('../../utils');
2
+
3
+ // We need to disable foreign key checks because if MySQL is missing the STRICT_TRANS_TABLES mode, we cannot drop nullable from a foreign key
4
+ module.exports = createDropNullableMigration('comment_likes', 'member_id', {disableForeignKeyChecks: true});
@@ -0,0 +1,119 @@
1
+ const {addForeign, dropForeign} = require('../../../schema/commands');
2
+ const logging = require('@tryghost/logging');
3
+ const {createTransactionalMigration} = require('../../utils');
4
+
5
+ module.exports = createTransactionalMigration(
6
+ async function up(knex) {
7
+ logging.info('Adding on delete SET NULL for comments');
8
+
9
+ await dropForeign({
10
+ fromTable: 'comments',
11
+ fromColumn: 'member_id',
12
+ toTable: 'members',
13
+ toColumn: 'id',
14
+ transaction: knex
15
+ });
16
+
17
+ await addForeign({
18
+ fromTable: 'comments',
19
+ fromColumn: 'member_id',
20
+ toTable: 'members',
21
+ toColumn: 'id',
22
+ setNullDelete: true,
23
+ transaction: knex
24
+ });
25
+
26
+ logging.info('Adding on delete CASCADE for comment_likes');
27
+
28
+ await dropForeign({
29
+ fromTable: 'comment_likes',
30
+ fromColumn: 'member_id',
31
+ toTable: 'members',
32
+ toColumn: 'id',
33
+ transaction: knex
34
+ });
35
+
36
+ await addForeign({
37
+ fromTable: 'comment_likes',
38
+ fromColumn: 'member_id',
39
+ toTable: 'members',
40
+ toColumn: 'id',
41
+ cascadeDelete: true,
42
+ transaction: knex
43
+ });
44
+
45
+ logging.info('Adding on delete SET NULL for comment_reports');
46
+
47
+ await dropForeign({
48
+ fromTable: 'comment_reports',
49
+ fromColumn: 'member_id',
50
+ toTable: 'members',
51
+ toColumn: 'id',
52
+ transaction: knex
53
+ });
54
+
55
+ await addForeign({
56
+ fromTable: 'comment_reports',
57
+ fromColumn: 'member_id',
58
+ toTable: 'members',
59
+ toColumn: 'id',
60
+ setNullDelete: true,
61
+ transaction: knex
62
+ });
63
+ },
64
+ async function down(knex) {
65
+ logging.info('Restoring foreign key for comments');
66
+
67
+ await dropForeign({
68
+ fromTable: 'comments',
69
+ fromColumn: 'member_id',
70
+ toTable: 'members',
71
+ toColumn: 'id',
72
+ transaction: knex
73
+ });
74
+
75
+ await addForeign({
76
+ fromTable: 'comments',
77
+ fromColumn: 'member_id',
78
+ toTable: 'members',
79
+ toColumn: 'id',
80
+ transaction: knex
81
+ });
82
+
83
+ logging.info('Restoring foreign key for comment_likes');
84
+
85
+ await dropForeign({
86
+ fromTable: 'comment_likes',
87
+ fromColumn: 'member_id',
88
+ toTable: 'members',
89
+ toColumn: 'id',
90
+ transaction: knex
91
+ });
92
+
93
+ await addForeign({
94
+ fromTable: 'comment_likes',
95
+ fromColumn: 'member_id',
96
+ toTable: 'members',
97
+ toColumn: 'id',
98
+ transaction: knex
99
+ });
100
+
101
+ logging.info('Restoring foreign key for comment_reports');
102
+
103
+ await dropForeign({
104
+ fromTable: 'comment_reports',
105
+ fromColumn: 'member_id',
106
+ toTable: 'members',
107
+ toColumn: 'id',
108
+ transaction: knex
109
+ });
110
+
111
+ await addForeign({
112
+ fromTable: 'comment_reports',
113
+ fromColumn: 'member_id',
114
+ toTable: 'members',
115
+ toColumn: 'id',
116
+ transaction: knex
117
+ });
118
+ }
119
+ );
@@ -0,0 +1,11 @@
1
+ const {addTable} = require('../../utils');
2
+
3
+ module.exports = addTable('jobs', {
4
+ id: {type: 'string', maxlength: 24, nullable: false, primary: true},
5
+ name: {type: 'string', maxlength: 191, nullable: false, unique: true},
6
+ status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'queued', validations: {isIn: [['started', 'finished', 'failed', 'queued']]}},
7
+ started_at: {type: 'dateTime', nullable: true},
8
+ finished_at: {type: 'dateTime', nullable: true},
9
+ created_at: {type: 'dateTime', nullable: false},
10
+ updated_at: {type: 'dateTime', nullable: true}
11
+ });
@@ -50,6 +50,8 @@ function addTableColumn(tableName, table, columnName, columnSpec = schema[tableN
50
50
  }
51
51
  if (Object.prototype.hasOwnProperty.call(columnSpec, 'cascadeDelete') && columnSpec.cascadeDelete === true) {
52
52
  column.onDelete('CASCADE');
53
+ } else if (Object.prototype.hasOwnProperty.call(columnSpec, 'setNullDelete') && columnSpec.setNullDelete === true) {
54
+ column.onDelete('SET NULL');
53
55
  }
54
56
  if (Object.prototype.hasOwnProperty.call(columnSpec, 'defaultTo')) {
55
57
  column.defaultTo(columnSpec.defaultTo);
@@ -206,10 +208,11 @@ async function hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, trans
206
208
  * @param {string} configuration.fromColumn - column of the table to add the foreign key to
207
209
  * @param {string} configuration.toTable - name of the table to point the foreign key to
208
210
  * @param {string} configuration.toColumn - column of the table to point the foreign key to
209
- * @param {Boolean} configuration.cascadeDelete - adds the "on delete cascade" option if true
211
+ * @param {Boolean} [configuration.cascadeDelete] - adds the "on delete cascade" option if true
212
+ * @param {Boolean} [configuration.setNullDelete] - adds the "on delete SET NULL" option if true
210
213
  * @param {import('knex')} configuration.transaction - connection object containing knex reference
211
214
  */
212
- async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDelete = false, transaction = db.knex}) {
215
+ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDelete = false, setNullDelete = false, transaction = db.knex}) {
213
216
  if (DatabaseInfo.isSQLite(transaction)) {
214
217
  const foreignKeyExists = await hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction});
215
218
  if (foreignKeyExists) {
@@ -232,6 +235,8 @@ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDele
232
235
  await transaction.schema.table(fromTable, function (table) {
233
236
  if (cascadeDelete) {
234
237
  table.foreign(fromColumn).references(`${toTable}.${toColumn}`).onDelete('CASCADE');
238
+ } else if (setNullDelete) {
239
+ table.foreign(fromColumn).references(`${toTable}.${toColumn}`).onDelete('SET NULL');
235
240
  } else {
236
241
  table.foreign(fromColumn).references(`${toTable}.${toColumn}`);
237
242
  }
@@ -610,6 +610,11 @@
610
610
  "name": "Unlike comments",
611
611
  "action_type": "unlike",
612
612
  "object_type": "comment"
613
+ },
614
+ {
615
+ "name": "Report comments",
616
+ "action_type": "report",
617
+ "object_type": "comment"
613
618
  }
614
619
  ]
615
620
  },
@@ -750,7 +750,7 @@ module.exports = {
750
750
  comments: {
751
751
  id: {type: 'string', maxlength: 24, nullable: false, primary: true},
752
752
  post_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'posts.id', cascadeDelete: true},
753
- member_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'members.id'},
753
+ member_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'members.id', setNullDelete: true},
754
754
  parent_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'comments.id'},
755
755
  status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'published', validations: {isIn: [['published', 'hidden', 'deleted']]}},
756
756
  html: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
@@ -761,16 +761,24 @@ module.exports = {
761
761
  comment_likes: {
762
762
  id: {type: 'string', maxlength: 24, nullable: false, primary: true},
763
763
  comment_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'comments.id', cascadeDelete: true},
764
- member_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'members.id'},
764
+ member_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'members.id', cascadeDelete: true},
765
765
  created_at: {type: 'dateTime', nullable: false},
766
766
  updated_at: {type: 'dateTime', nullable: false}
767
767
  },
768
768
  comment_reports: {
769
769
  id: {type: 'string', maxlength: 24, nullable: false, primary: true},
770
770
  comment_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'comments.id', cascadeDelete: true},
771
- member_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'members.id'},
772
- reason: {type: 'text', maxlength: 65535, nullable: false},
771
+ member_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'members.id', setNullDelete: true},
773
772
  created_at: {type: 'dateTime', nullable: false},
774
773
  updated_at: {type: 'dateTime', nullable: false}
774
+ },
775
+ jobs: {
776
+ id: {type: 'string', maxlength: 24, nullable: false, primary: true},
777
+ name: {type: 'string', maxlength: 191, nullable: false, unique: true},
778
+ status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'queued', validations: {isIn: [['started', 'finished', 'failed', 'queued']]}},
779
+ started_at: {type: 'dateTime', nullable: true},
780
+ finished_at: {type: 'dateTime', nullable: true},
781
+ created_at: {type: 'dateTime', nullable: false},
782
+ updated_at: {type: 'dateTime', nullable: true}
775
783
  }
776
784
  };
@@ -213,9 +213,6 @@ class GhostServer {
213
213
  * Internal Method for TestMode.
214
214
  */
215
215
  _startTestMode() {
216
- // This is horrible and very temporary
217
- const jobService = require('./services/jobs');
218
-
219
216
  // Output how many connections are open every 5 seconds
220
217
  const connectionInterval = setInterval(() => this.httpServer.getConnections(
221
218
  (err, connections) => logging.warn(`${connections} connections currently open`)
@@ -226,25 +223,6 @@ class GhostServer {
226
223
  clearInterval(connectionInterval);
227
224
  logging.warn('Server has fully closed');
228
225
  });
229
-
230
- // Output job queue length every 5 seconds
231
- setInterval(() => {
232
- logging.warn(`${jobService.queue.length()} jobs in the queue. Idle: ${jobService.queue.idle()}`);
233
-
234
- const runningScheduledjobs = Object.keys(jobService.bree.workers);
235
- if (Object.keys(jobService.bree.workers).length) {
236
- logging.warn(`${Object.keys(jobService.bree.workers).length} jobs running: ${runningScheduledjobs}`);
237
- }
238
-
239
- const scheduledJobs = Object.keys(jobService.bree.intervals);
240
- if (Object.keys(jobService.bree.intervals).length) {
241
- logging.warn(`${Object.keys(jobService.bree.intervals).length} scheduled jobs: ${scheduledJobs}`);
242
- }
243
-
244
- if (runningScheduledjobs.length === 0 && scheduledJobs.length === 0) {
245
- logging.warn('No scheduled or running jobs');
246
- }
247
- }, 5000);
248
226
  }
249
227
 
250
228
  /**
@@ -0,0 +1,34 @@
1
+ const ghostBookshelf = require('./base');
2
+
3
+ const CommentReport = ghostBookshelf.Model.extend({
4
+ tableName: 'comment_reports',
5
+
6
+ defaults: function defaults() {
7
+ return {};
8
+ },
9
+
10
+ comment() {
11
+ return this.belongsTo('Comment', 'comment_id');
12
+ },
13
+
14
+ member() {
15
+ return this.belongsTo('Member', 'member_id');
16
+ },
17
+
18
+ emitChange: function emitChange(event, options) {
19
+ const eventToTrigger = 'comment_report' + '.' + event;
20
+ ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
21
+ },
22
+
23
+ onCreated: function onCreated(model, options) {
24
+ ghostBookshelf.Model.prototype.onCreated.apply(this, arguments);
25
+
26
+ model.emitChange('added', options);
27
+ }
28
+ }, {
29
+
30
+ });
31
+
32
+ module.exports = {
33
+ CommentReport: ghostBookshelf.model('CommentReport', CommentReport)
34
+ };
@@ -2,7 +2,6 @@ const ghostBookshelf = require('./base');
2
2
  const _ = require('lodash');
3
3
  const errors = require('@tryghost/errors');
4
4
  const tpl = require('@tryghost/tpl');
5
- const commentsService = require('../services/comments');
6
5
 
7
6
  const messages = {
8
7
  commentNotFound: 'Comment could not be found',
@@ -70,14 +69,14 @@ const Comment = ghostBookshelf.Model.extend({
70
69
  onCreated: function onCreated(model, options) {
71
70
  ghostBookshelf.Model.prototype.onCreated.apply(this, arguments);
72
71
 
73
- if (!options.context.internal) {
74
- commentsService.api.sendNewCommentNotifications(model);
75
- }
76
-
77
72
  model.emitChange('added', options);
78
73
  },
79
74
 
80
- enforcedFilters: function enforcedFilters() {
75
+ enforcedFilters: function enforcedFilters(options) {
76
+ if (options.context && options.context.user) {
77
+ return null;
78
+ }
79
+
81
80
  return 'parent_id:null';
82
81
  }
83
82
 
@@ -151,7 +150,9 @@ const Comment = ghostBookshelf.Model.extend({
151
150
  defaultRelations: function defaultRelations(methodName, options) {
152
151
  // @todo: the default relations are not working for 'add' when we add it below
153
152
  if (['findAll', 'findPage', 'edit', 'findOne'].indexOf(methodName) !== -1) {
154
- options.withRelated = _.union(['member', 'likes', 'replies', 'replies.member', 'replies.likes'], options.withRelated || []);
153
+ if (!options.withRelated || options.withRelated.length === 0) {
154
+ options.withRelated = ['member', 'likes', 'replies', 'replies.member', 'replies.likes'];
155
+ }
155
156
  }
156
157
 
157
158
  return options;
@@ -0,0 +1,9 @@
1
+ const ghostBookshelf = require('./base');
2
+
3
+ const Job = ghostBookshelf.Model.extend({
4
+ tableName: 'jobs'
5
+ });
6
+
7
+ module.exports = {
8
+ Job: ghostBookshelf.model('Job', Job)
9
+ };
@@ -154,6 +154,10 @@ Tag = ghostBookshelf.Model.extend({
154
154
  actor_id: actor.id,
155
155
  actor_type: actor.type
156
156
  };
157
+ },
158
+
159
+ defaultColumnsToFetch() {
160
+ return ['id'];
157
161
  }
158
162
  }, {
159
163
  orderDefaultOptions: function orderDefaultOptions() {
@@ -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;">Someone just replied to your comment on <a href="https://ghost.org">{{postTitle}}</a>.</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;">Someone just replied to your comment on <a href="{{postUrl}}" target="_blank">{{postTitle}}</a>.</p>
120
120
 
121
121
  <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
122
122
  <tbody>
@@ -196,4 +196,4 @@
196
196
  </tr>
197
197
  </table>
198
198
  </body>
199
- </html>
199
+ </html>
@@ -1,14 +1,13 @@
1
1
  module.exports = function (data) {
2
- return `
3
- Hey there,
2
+ // Be careful when you indent the email, because whitespaces are visible in emails!
3
+ return `Hey there,
4
4
 
5
- Someone just replied to your comment on "${data.postTitle}"
5
+ Someone just replied to your comment on "${data.postTitle}"
6
6
 
7
- ${data.postUrl}#comments-area
7
+ ${data.postUrl}#ghost-comments-root
8
8
 
9
- ---
9
+ ---
10
10
 
11
- Sent to ${data.toEmail} from ${data.siteDomain}.
12
- You can manage your notification preferences at ${data.profileUrl}.
13
- `;
11
+ Sent to ${data.toEmail} from ${data.siteDomain}.
12
+ You can manage your notification preferences at ${data.profileUrl}.`;
14
13
  };
@@ -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;">Someone just left a comment on your post <a href="https://ghost.org">{{postTitle}}</a>.</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;">Someone just left a comment on your post <a href="{{postUrl}}" target="_blank">{{postTitle}}</a>.</p>
120
120
 
121
121
  <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
122
122
  <tbody>
@@ -196,4 +196,4 @@
196
196
  </tr>
197
197
  </table>
198
198
  </body>
199
- </html>
199
+ </html>
@@ -1,14 +1,15 @@
1
1
  module.exports = function (data) {
2
+ // Be careful when you indent the email, because whitespaces are visible in emails!
2
3
  return `
3
- Hey there,
4
+ Hey there,
4
5
 
5
- Someone just posted a comment on your post "${data.postTitle}"
6
+ Someone just posted a comment on your post "${data.postTitle}"
6
7
 
7
- ${data.postUrl}#comments-area
8
+ ${data.postUrl}#ghost-comments-root
8
9
 
9
- ---
10
+ ---
10
11
 
11
- Sent to ${data.toEmail} from ${data.siteDomain}.
12
- You can manage your notification preferences at ${data.staffUrl}.
12
+ Sent to ${data.toEmail} from ${data.siteDomain}.
13
+ You can manage your notification preferences at ${data.staffUrl}.
13
14
  `;
14
15
  };