ghost 5.19.3 → 5.21.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 (180) hide show
  1. package/components/tryghost-adapter-manager-5.21.0.tgz +0 -0
  2. package/components/{tryghost-api-framework-5.19.3.tgz → tryghost-api-framework-5.21.0.tgz} +0 -0
  3. package/components/tryghost-api-version-compatibility-service-5.21.0.tgz +0 -0
  4. package/components/tryghost-audience-feedback-5.21.0.tgz +0 -0
  5. package/components/tryghost-bootstrap-socket-5.21.0.tgz +0 -0
  6. package/components/tryghost-constants-5.21.0.tgz +0 -0
  7. package/components/tryghost-custom-theme-settings-service-5.21.0.tgz +0 -0
  8. package/components/tryghost-data-generator-5.21.0.tgz +0 -0
  9. package/components/tryghost-domain-events-5.21.0.tgz +0 -0
  10. package/components/tryghost-email-analytics-provider-mailgun-5.21.0.tgz +0 -0
  11. package/components/tryghost-email-analytics-service-5.21.0.tgz +0 -0
  12. package/components/tryghost-email-content-generator-5.21.0.tgz +0 -0
  13. package/components/tryghost-express-dynamic-redirects-5.21.0.tgz +0 -0
  14. package/components/tryghost-extract-api-key-5.21.0.tgz +0 -0
  15. package/components/tryghost-html-to-plaintext-5.21.0.tgz +0 -0
  16. package/components/{tryghost-job-manager-5.19.3.tgz → tryghost-job-manager-5.21.0.tgz} +0 -0
  17. package/components/tryghost-link-redirects-5.21.0.tgz +0 -0
  18. package/components/tryghost-link-replacer-5.21.0.tgz +0 -0
  19. package/components/tryghost-link-tracking-5.21.0.tgz +0 -0
  20. package/components/tryghost-magic-link-5.21.0.tgz +0 -0
  21. package/components/tryghost-mailgun-client-5.21.0.tgz +0 -0
  22. package/components/tryghost-member-analytics-service-5.21.0.tgz +0 -0
  23. package/components/tryghost-member-attribution-5.21.0.tgz +0 -0
  24. package/components/tryghost-member-events-5.21.0.tgz +0 -0
  25. package/components/tryghost-members-analytics-ingress-5.21.0.tgz +0 -0
  26. package/components/tryghost-members-api-5.21.0.tgz +0 -0
  27. package/components/tryghost-members-csv-5.21.0.tgz +0 -0
  28. package/components/tryghost-members-events-service-5.21.0.tgz +0 -0
  29. package/components/tryghost-members-importer-5.21.0.tgz +0 -0
  30. package/components/tryghost-members-offers-5.21.0.tgz +0 -0
  31. package/components/tryghost-members-payments-5.21.0.tgz +0 -0
  32. package/components/{tryghost-members-ssr-5.19.3.tgz → tryghost-members-ssr-5.21.0.tgz} +0 -0
  33. package/components/tryghost-members-stripe-service-5.21.0.tgz +0 -0
  34. package/components/tryghost-minifier-5.21.0.tgz +0 -0
  35. package/components/tryghost-mw-api-version-mismatch-5.21.0.tgz +0 -0
  36. package/components/tryghost-mw-cache-control-5.21.0.tgz +0 -0
  37. package/components/tryghost-mw-error-handler-5.21.0.tgz +0 -0
  38. package/components/tryghost-mw-session-from-token-5.21.0.tgz +0 -0
  39. package/components/tryghost-mw-update-user-last-seen-5.21.0.tgz +0 -0
  40. package/components/tryghost-mw-vhost-5.21.0.tgz +0 -0
  41. package/components/tryghost-oembed-service-5.21.0.tgz +0 -0
  42. package/components/{tryghost-package-json-5.19.3.tgz → tryghost-package-json-5.21.0.tgz} +0 -0
  43. package/components/tryghost-referrers-5.21.0.tgz +0 -0
  44. package/components/tryghost-security-5.21.0.tgz +0 -0
  45. package/components/tryghost-session-service-5.21.0.tgz +0 -0
  46. package/components/tryghost-settings-path-manager-5.21.0.tgz +0 -0
  47. package/components/tryghost-staff-service-5.21.0.tgz +0 -0
  48. package/components/tryghost-stats-service-5.21.0.tgz +0 -0
  49. package/components/tryghost-tiers-5.21.0.tgz +0 -0
  50. package/components/{tryghost-update-check-service-5.19.3.tgz → tryghost-update-check-service-5.21.0.tgz} +0 -0
  51. package/components/tryghost-verification-trigger-5.21.0.tgz +0 -0
  52. package/components/tryghost-version-notifications-data-service-5.21.0.tgz +0 -0
  53. package/core/boot.js +2 -0
  54. package/core/built/admin/assets/{chunk.143.c035c61595ed02eee886.js → chunk.143.9cddfa7bd1a8b9cf3d4b.js} +7 -7
  55. package/core/built/admin/assets/{chunk.178.998dfbcebcec635146b1.js → chunk.178.6de14cfdb28df721b66e.js} +4 -4
  56. package/core/built/admin/assets/{chunk.613.f1d519ad47e7f9024263.js → chunk.613.695f31829550fb00d43c.js} +352 -421
  57. package/core/built/admin/assets/{chunk.613.f1d519ad47e7f9024263.js.LICENSE.txt → chunk.613.695f31829550fb00d43c.js.LICENSE.txt} +0 -0
  58. package/core/built/admin/assets/{ghost-5ce6f5a730c83c91fc258b12c537ea35.js → ghost-192fee3b46a193df1e65c49a67a7d694.js} +2866 -2707
  59. package/core/built/admin/assets/ghost-9873519a8ad69b5b23284f0a9e050bc6.css +1 -0
  60. package/core/built/admin/assets/ghost-dark-190bdce42b125c3d4be930bd7599b442.css +1 -0
  61. package/core/built/admin/assets/{vendor-5c7d7063620bec13668c4370145cd4b4.js → vendor-26cca1d4d56660dc6e915a12ccc3b330.js} +1079 -1032
  62. package/core/built/admin/index.html +7 -7
  63. package/core/cli/generate-data.js +51 -0
  64. package/core/frontend/helpers/ghost_head.js +1 -1
  65. package/core/server/api/endpoints/links.js +34 -1
  66. package/core/server/api/endpoints/members.js +1 -4
  67. package/core/server/api/endpoints/posts-public.js +1 -1
  68. package/core/server/api/endpoints/posts.js +2 -1
  69. package/core/server/api/endpoints/tiers-public.js +2 -14
  70. package/core/server/api/endpoints/tiers.js +5 -51
  71. package/core/server/api/endpoints/utils/serializers/input/posts.js +21 -1
  72. package/core/server/api/endpoints/utils/serializers/input/settings.js +1 -0
  73. package/core/server/api/endpoints/utils/serializers/input/tiers.js +18 -27
  74. package/core/server/api/endpoints/utils/serializers/output/index.js +4 -0
  75. package/core/server/api/endpoints/utils/serializers/output/links.js +5 -0
  76. package/core/server/api/endpoints/utils/serializers/output/mappers/activity-feed-events.js +89 -15
  77. package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +20 -6
  78. package/core/server/api/endpoints/utils/serializers/output/mappers/snippets.js +2 -2
  79. package/core/server/api/endpoints/utils/serializers/output/members.js +6 -5
  80. package/core/server/api/endpoints/utils/serializers/output/tiers.js +15 -55
  81. package/core/server/data/db/backup.js +17 -10
  82. package/core/server/data/importer/importers/data/custom-theme-settings.js +81 -0
  83. package/core/server/data/importer/importers/data/data-importer.js +2 -0
  84. package/core/server/data/migrations/utils/permissions.js +35 -24
  85. package/core/server/data/migrations/versions/5.20/2022-10-18-05-39-drop-nullable-tier-id.js +3 -0
  86. package/core/server/data/migrations/versions/5.20/2022-10-18-10-13-add-ghost-subscription-id-column-to-mscs.js +10 -0
  87. package/core/server/data/migrations/versions/5.20/2022-10-19-11-17-add-link-browse-permissions.js +10 -0
  88. package/core/server/data/migrations/versions/5.20/2022-10-20-02-52-add-link-edit-permissions.js +10 -0
  89. package/core/server/data/migrations/versions/5.21/2022-10-24-07-23-disable-feedback-enabled.js +20 -0
  90. package/core/server/data/migrations/versions/5.21/2022-10-25-12-05-backfill-missed-products-columns.js +35 -0
  91. package/core/server/data/migrations/versions/5.21/2022-10-26-04-49-add-batch-id-members-created-events.js +7 -0
  92. package/core/server/data/migrations/versions/5.21/2022-10-26-04-49-add-batch-id-subscription-created-events.js +7 -0
  93. package/core/server/data/migrations/versions/5.21/2022-10-26-04-50-member-subscription-created-batch-id.js +72 -0
  94. package/core/server/data/migrations/versions/5.21/2022-10-26-09-32-add-feedback-enabled-column-to-emails.js +7 -0
  95. package/core/server/data/migrations/versions/5.21/2022-10-27-09-50-add-member-track-source-setting.js +8 -0
  96. package/core/server/data/schema/commands.js +107 -48
  97. package/core/server/data/schema/default-settings/default-settings.json +8 -0
  98. package/core/server/data/schema/fixtures/fixture-manager.js +16 -14
  99. package/core/server/data/schema/fixtures/fixtures.json +14 -2
  100. package/core/server/data/schema/schema.js +7 -3
  101. package/core/server/models/base/plugins/actions.js +1 -1
  102. package/core/server/models/base/plugins/crud.js +12 -0
  103. package/core/server/models/email-recipient.js +14 -0
  104. package/core/server/models/email.js +2 -5
  105. package/core/server/models/member-click-event.js +37 -0
  106. package/core/server/models/member-created-event.js +23 -0
  107. package/core/server/models/member-paid-subscription-event.js +19 -0
  108. package/core/server/models/member.js +6 -0
  109. package/core/server/models/post.js +33 -2
  110. package/core/server/models/redirect.js +1 -0
  111. package/core/server/models/subscription-created-event.js +7 -0
  112. package/core/server/services/audience-feedback/index.js +2 -0
  113. package/core/server/services/link-redirection/LinkRedirectRepository.js +16 -5
  114. package/core/server/services/link-tracking/PostLinkRepository.js +26 -2
  115. package/core/server/services/link-tracking/index.js +3 -1
  116. package/core/server/services/mega/feedback-buttons.js +87 -16
  117. package/core/server/services/mega/mega.js +1 -0
  118. package/core/server/services/mega/template.js +3 -0
  119. package/core/server/services/member-attribution/index.js +3 -1
  120. package/core/server/services/members/api.js +4 -1
  121. package/core/server/services/members/service.js +8 -1
  122. package/core/server/services/newsletters/index.js +3 -1
  123. package/core/server/services/newsletters/service.js +11 -1
  124. package/core/server/services/tiers/TierRepository.js +116 -0
  125. package/core/server/services/tiers/index.js +1 -0
  126. package/core/server/services/tiers/service.js +32 -0
  127. package/core/server/services/url/UrlGenerator.js +4 -2
  128. package/core/server/web/api/endpoints/admin/routes.js +1 -0
  129. package/core/shared/config/defaults.json +1 -1
  130. package/core/shared/labs.js +7 -8
  131. package/ghost.js +1 -0
  132. package/package.json +117 -113
  133. package/yarn.lock +1187 -1129
  134. package/components/tryghost-adapter-manager-5.19.3.tgz +0 -0
  135. package/components/tryghost-api-version-compatibility-service-5.19.3.tgz +0 -0
  136. package/components/tryghost-audience-feedback-5.19.3.tgz +0 -0
  137. package/components/tryghost-bootstrap-socket-5.19.3.tgz +0 -0
  138. package/components/tryghost-constants-5.19.3.tgz +0 -0
  139. package/components/tryghost-custom-theme-settings-service-5.19.3.tgz +0 -0
  140. package/components/tryghost-domain-events-5.19.3.tgz +0 -0
  141. package/components/tryghost-email-analytics-provider-mailgun-5.19.3.tgz +0 -0
  142. package/components/tryghost-email-analytics-service-5.19.3.tgz +0 -0
  143. package/components/tryghost-email-content-generator-5.19.3.tgz +0 -0
  144. package/components/tryghost-express-dynamic-redirects-5.19.3.tgz +0 -0
  145. package/components/tryghost-extract-api-key-5.19.3.tgz +0 -0
  146. package/components/tryghost-html-to-plaintext-5.19.3.tgz +0 -0
  147. package/components/tryghost-link-redirects-5.19.3.tgz +0 -0
  148. package/components/tryghost-link-replacer-5.19.3.tgz +0 -0
  149. package/components/tryghost-link-tracking-5.19.3.tgz +0 -0
  150. package/components/tryghost-magic-link-5.19.3.tgz +0 -0
  151. package/components/tryghost-mailgun-client-5.19.3.tgz +0 -0
  152. package/components/tryghost-member-analytics-service-5.19.3.tgz +0 -0
  153. package/components/tryghost-member-attribution-5.19.3.tgz +0 -0
  154. package/components/tryghost-member-events-5.19.3.tgz +0 -0
  155. package/components/tryghost-members-analytics-ingress-5.19.3.tgz +0 -0
  156. package/components/tryghost-members-api-5.19.3.tgz +0 -0
  157. package/components/tryghost-members-csv-5.19.3.tgz +0 -0
  158. package/components/tryghost-members-events-service-5.19.3.tgz +0 -0
  159. package/components/tryghost-members-importer-5.19.3.tgz +0 -0
  160. package/components/tryghost-members-offers-5.19.3.tgz +0 -0
  161. package/components/tryghost-members-payments-5.19.3.tgz +0 -0
  162. package/components/tryghost-members-stripe-service-5.19.3.tgz +0 -0
  163. package/components/tryghost-minifier-5.19.3.tgz +0 -0
  164. package/components/tryghost-mw-api-version-mismatch-5.19.3.tgz +0 -0
  165. package/components/tryghost-mw-cache-control-5.19.3.tgz +0 -0
  166. package/components/tryghost-mw-error-handler-5.19.3.tgz +0 -0
  167. package/components/tryghost-mw-session-from-token-5.19.3.tgz +0 -0
  168. package/components/tryghost-mw-update-user-last-seen-5.19.3.tgz +0 -0
  169. package/components/tryghost-mw-vhost-5.19.3.tgz +0 -0
  170. package/components/tryghost-oembed-service-5.19.3.tgz +0 -0
  171. package/components/tryghost-referrers-5.19.3.tgz +0 -0
  172. package/components/tryghost-security-5.19.3.tgz +0 -0
  173. package/components/tryghost-session-service-5.19.3.tgz +0 -0
  174. package/components/tryghost-settings-path-manager-5.19.3.tgz +0 -0
  175. package/components/tryghost-staff-service-5.19.3.tgz +0 -0
  176. package/components/tryghost-stats-service-5.19.3.tgz +0 -0
  177. package/components/tryghost-verification-trigger-5.19.3.tgz +0 -0
  178. package/components/tryghost-version-notifications-data-service-5.19.3.tgz +0 -0
  179. package/core/built/admin/assets/ghost-982146a4ada3a5af1981d1919ae01d08.css +0 -1
  180. package/core/built/admin/assets/ghost-dark-41929e4857de411a23597a9de49a4e4f.css +0 -1
@@ -76,11 +76,12 @@ function bulkAction(bulkActionResult, _apiConfig, frame) {
76
76
 
77
77
  /**
78
78
  *
79
- * @returns {{events: any[]}}
79
+ * @returns {{events: any[], meta: any}}
80
80
  */
81
81
  function activityFeed(data, _apiConfig, frame) {
82
82
  return {
83
- events: data.events.map(e => mappers.activityFeedEvents(e, frame))
83
+ events: data.events.map(e => mappers.activityFeedEvents(e, frame)),
84
+ meta: data.meta
84
85
  };
85
86
  }
86
87
 
@@ -216,15 +217,15 @@ function createSerializer(debugString, serialize) {
216
217
  * @prop {string} id
217
218
  * @prop {string} uuid
218
219
  * @prop {string} email
219
- * @prop {string=} name
220
- * @prop {string=} note
220
+ * @prop {string} [name]
221
+ * @prop {string} [note]
221
222
  * @prop {null|string} geolocation
222
223
  * @prop {boolean} subscribed
223
224
  * @prop {string} created_at
224
225
  * @prop {string} updated_at
225
226
  * @prop {string[]} labels
226
227
  * @prop {SerializedMemberStripeSubscription[]} subscriptions
227
- * @prop {SerializedMemberProduct[]=} products
228
+ * @prop {SerializedMemberProduct[]} [products]
228
229
  * @prop {string} avatar_image
229
230
  * @prop {boolean} comped
230
231
  * @prop {number} email_count
@@ -1,11 +1,6 @@
1
1
  //@ts-check
2
2
  const debug = require('@tryghost/debug')('api:endpoints:utils:serializers:output:tiers');
3
3
 
4
- const allowedIncludes = ['monthly_price', 'yearly_price'];
5
- const localUtils = require('../../index');
6
- const {utils} = require('@tryghost/api-framework');
7
- const labs = require('../../../../../../shared/labs');
8
-
9
4
  module.exports = {
10
5
  browse: createSerializer('browse', paginatedTiers),
11
6
  read: createSerializer('read', singleTier),
@@ -49,11 +44,10 @@ function singleTier(model, _apiConfig, frame) {
49
44
  /**
50
45
  * @param {import('bookshelf').Model} tier
51
46
  * @param {object} options
52
- * @param {object} frame
53
47
  *
54
48
  * @returns {SerializedTier}
55
49
  */
56
- function serializeTier(tier, options, frame) {
50
+ function serializeTier(tier, options) {
57
51
  const json = tier.toJSON(options);
58
52
 
59
53
  const serialized = {
@@ -61,66 +55,32 @@ function serializeTier(tier, options, frame) {
61
55
  name: json.name,
62
56
  description: json.description,
63
57
  slug: json.slug,
64
- active: json.active,
58
+ active: json.status === 'active',
65
59
  type: json.type,
66
- welcome_page_url: json.welcome_page_url,
67
- created_at: json.created_at,
68
- updated_at: json.updated_at,
60
+ welcome_page_url: json.welcomePageURL,
61
+ created_at: json.createdAt,
62
+ updated_at: json.updatedAt,
69
63
  visibility: json.visibility,
70
- benefits: null
64
+ benefits: json.benefits,
65
+ currency: json.currency,
66
+ monthly_price: json.monthlyPrice,
67
+ yearly_price: json.yearlyPrice,
68
+ trial_days: json.trialDays
71
69
  };
72
70
 
73
- if (labs.isSet('freeTrial')) {
74
- serialized.trial_days = json.trial_days;
75
- }
76
-
77
- if (Array.isArray(json.benefits)) {
78
- serialized.benefits = json.benefits.map(benefit => benefit.name);
79
- } else {
71
+ if (!Array.isArray(serialized.benefits)) {
80
72
  serialized.benefits = null;
81
73
  }
82
74
 
83
- if (serialized.type === 'paid') {
84
- serialized.currency = json.monthlyPrice?.currency;
85
- serialized.monthly_price = json.monthlyPrice?.amount;
86
- serialized.yearly_price = json.yearlyPrice?.amount;
87
- }
88
-
89
- if (!localUtils.isContentAPI(frame)) {
90
- const requestedQueryIncludes = frame.original && frame.original.query && frame.original.query.include && frame.original.query.include.split(',') || [];
91
- const requestedOptionsIncludes = utils.options.trimAndLowerCase(frame.original && frame.original.options && frame.original.options.include || []);
92
-
93
- return cleanIncludes(
94
- allowedIncludes,
95
- requestedQueryIncludes.concat(requestedOptionsIncludes),
96
- serialized
97
- );
75
+ if (serialized.type === 'free') {
76
+ delete serialized.currency;
77
+ delete serialized.monthly_price;
78
+ delete serialized.yearly_price;
98
79
  }
99
80
 
100
81
  return serialized;
101
82
  }
102
83
 
103
- /**
104
- * @template Data
105
- *
106
- * @param {string[]} allowed
107
- * @param {string[]} requested
108
- * @param {Data & Object<string, any>} data
109
- *
110
- * @returns {Data}
111
- */
112
- function cleanIncludes(allowed, requested, data) {
113
- const cleaned = {
114
- ...data
115
- };
116
- for (const include of allowed) {
117
- if (!requested.includes(include)) {
118
- delete cleaned[include];
119
- }
120
- }
121
- return cleaned;
122
- }
123
-
124
84
  /**
125
85
  * @template Data
126
86
  * @template Response
@@ -9,12 +9,21 @@ const logging = require('@tryghost/logging');
9
9
  const urlUtils = require('../../../shared/url-utils');
10
10
  const exporter = require('../exporter');
11
11
 
12
- const writeExportFile = function writeExportFile(exportResult) {
12
+ /**
13
+ * @param {object} exportResult
14
+ * @param {string} exportResult.filename
15
+ * @param {object} exportResult.data
16
+ */
17
+ const writeExportFile = async (exportResult) => {
13
18
  const filename = path.resolve(urlUtils.urlJoin(config.get('paths').contentPath, 'data', exportResult.filename));
14
19
 
15
- return Promise.resolve(fs.writeFile(filename, JSON.stringify(exportResult.data))).return(filename);
20
+ await fs.writeFile(filename, JSON.stringify(exportResult.data));
21
+ return filename;
16
22
  };
17
23
 
24
+ /**
25
+ * @param {string} filename
26
+ */
18
27
  const readBackup = async (filename) => {
19
28
  const parsedFileName = path.parse(filename);
20
29
  const sanitized = `${parsedFileName.name}${parsedFileName.ext}`;
@@ -35,21 +44,19 @@ const readBackup = async (filename) => {
35
44
  * does an export, and stores this in a local file
36
45
  * @returns {Promise<*>}
37
46
  */
38
- const backup = function backup(options) {
47
+ const backup = async function backup(options = {}) {
39
48
  logging.info('Creating database backup');
40
- options = options || {};
41
49
 
42
50
  const props = {
43
51
  data: exporter.doExport(options),
44
52
  filename: exporter.fileName(options)
45
53
  };
46
54
 
47
- return Promise.props(props)
48
- .then(writeExportFile)
49
- .then(function successMessage(filename) {
50
- logging.info('Database backup written to: ' + filename);
51
- return filename;
52
- });
55
+ const exportResult = await Promise.props(props);
56
+ const filename = await writeExportFile(exportResult);
57
+
58
+ logging.info('Database backup written to: ' + filename);
59
+ return filename;
53
60
  };
54
61
 
55
62
  module.exports = {
@@ -0,0 +1,81 @@
1
+ const _ = require('lodash');
2
+ const Promise = require('bluebird');
3
+ const debug = require('@tryghost/debug')('importer:roles');
4
+ const BaseImporter = require('./base');
5
+ const models = require('../../../../models');
6
+ const {activate} = require('../../../../services/themes/activate');
7
+
8
+ class CustomThemeSettingsImporter extends BaseImporter {
9
+ constructor(allDataFromFile) {
10
+ super(allDataFromFile, {
11
+ modelName: 'CustomThemeSetting',
12
+ dataKeyToImport: 'custom_theme_settings'
13
+ });
14
+ }
15
+
16
+ beforeImport() {
17
+ debug('beforeImport');
18
+ return super.beforeImport();
19
+ }
20
+
21
+ doImport(options, importOptions) {
22
+ debug('doImport', this.modelName, this.dataToImport.length);
23
+
24
+ let ops = [];
25
+
26
+ _.each(this.dataToImport, (item) => {
27
+ ops.push(models.CustomThemeSetting.findOne({theme: item.theme, key: item.key}, options)
28
+ .then((setting) => {
29
+ if (_.isObject(item.value)) {
30
+ item.value = JSON.stringify(item.value);
31
+ }
32
+
33
+ if (setting) {
34
+ setting.set('value', item.value);
35
+ if (setting.hasChanged()) {
36
+ return setting.save(null, options)
37
+ .then((importedModel) => {
38
+ if (importOptions.returnImportedData) {
39
+ this.importedDataToReturn.push(importedModel.toJSON());
40
+ }
41
+ return importedModel;
42
+ })
43
+ .catch((err) => {
44
+ return this.handleError(err, item);
45
+ });
46
+ }
47
+
48
+ return Promise.resolve();
49
+ }
50
+
51
+ return models.CustomThemeSetting.add(item, options)
52
+ .then((importedModel) => {
53
+ if (importOptions.returnImportedData) {
54
+ this.importedDataToReturn.push(importedModel.toJSON());
55
+ }
56
+ return importedModel;
57
+ })
58
+ .catch((err) => {
59
+ return this.handleError(err, item);
60
+ });
61
+ })
62
+ .reflect());
63
+ });
64
+
65
+ const opsPromise = Promise.all(ops);
66
+
67
+ // activate function is called to refresh cache when importing custom theme settings for active theme
68
+ opsPromise.then(() => {
69
+ models.Settings.findOne({key: 'active_theme'})
70
+ .then((theme) => {
71
+ const currentTheme = theme.get('value');
72
+ if (this.dataToImport.some(themeSetting => themeSetting.theme === currentTheme)) {
73
+ activate(currentTheme);
74
+ }
75
+ });
76
+ });
77
+
78
+ return opsPromise;
79
+ }
80
+ }
81
+ module.exports = CustomThemeSettingsImporter;
@@ -13,6 +13,7 @@ const NewslettersImporter = require('./newsletters');
13
13
  const ProductsImporter = require('./products');
14
14
  const StripeProductsImporter = require('./stripe-products');
15
15
  const StripePricesImporter = require('./stripe-prices');
16
+ const CustomThemeSettingsImporter = require('./custom-theme-settings');
16
17
  const RolesImporter = require('./roles');
17
18
  let importers = {};
18
19
  let DataImporter;
@@ -35,6 +36,7 @@ DataImporter = {
35
36
  importers.stripe_products = new StripeProductsImporter(importData.data);
36
37
  importers.stripe_prices = new StripePricesImporter(importData.data);
37
38
  importers.posts = new PostsImporter(importData.data);
39
+ importers.custom_theme_settings = new CustomThemeSettingsImporter(importData.data);
38
40
 
39
41
  return importData;
40
42
  },
@@ -10,6 +10,10 @@ const messages = {
10
10
  permissionRoleActionError: 'Cannot {action} permission({permission}) with role({role}) - {resource} does not exist'
11
11
  };
12
12
 
13
+ /**
14
+ * @param {import('knex').Knex} connection
15
+ * @param {PermissionConfig} config
16
+ */
13
17
  async function addPermissionHelper(connection, config) {
14
18
  const existingPermission = await connection('permissions').where({
15
19
  name: config.name,
@@ -38,6 +42,10 @@ async function addPermissionHelper(connection, config) {
38
42
  });
39
43
  }
40
44
 
45
+ /**
46
+ * @param {import('knex').Knex} connection
47
+ * @param {PermissionConfig} config
48
+ */
41
49
  async function removePermissionHelper(connection, config) {
42
50
  const existingPermission = await connection('permissions').where({
43
51
  name: config.name,
@@ -61,10 +69,7 @@ async function removePermissionHelper(connection, config) {
61
69
  /**
62
70
  * Creates a migration which will add a permission to the database
63
71
  *
64
- * @param {Object} config
65
- * @param {string} config.name - The name of the permission
66
- * @param {string} config.action - The action_type of the permission
67
- * @param {string} config.object - The object_type of the permission
72
+ * @param {PermissionConfig} config
68
73
  *
69
74
  * @returns {Migration}
70
75
  */
@@ -82,10 +87,7 @@ function addPermission(config) {
82
87
  /**
83
88
  * Creates a migration which will remove a permission from the database
84
89
  *
85
- * @param {Object} config
86
- * @param {string} config.name - The name of the permission
87
- * @param {string} config.action - The action_type of the permission
88
- * @param {string} config.object - The object_type of the permission
90
+ * @param {PermissionConfig} config
89
91
  *
90
92
  * @returns {Migration}
91
93
  */
@@ -100,6 +102,10 @@ function removePermission(config) {
100
102
  );
101
103
  }
102
104
 
105
+ /**
106
+ * @param {import('knex').Knex} connection
107
+ * @param {PermissionRoleConfig} config
108
+ */
103
109
  async function addPermissionToRoleHelper(connection, config) {
104
110
  const permission = await connection('permissions').where({
105
111
  name: config.permission
@@ -149,6 +155,10 @@ async function addPermissionToRoleHelper(connection, config) {
149
155
  });
150
156
  }
151
157
 
158
+ /**
159
+ * @param {import('knex').Knex} connection
160
+ * @param {PermissionRoleConfig} config
161
+ */
152
162
  async function removePermissionFromRoleHelper(connection, config) {
153
163
  const permission = await connection('permissions').where({
154
164
  name: config.permission
@@ -188,9 +198,7 @@ async function removePermissionFromRoleHelper(connection, config) {
188
198
  /**
189
199
  * Creates a migration which will link a permission to a role in the database
190
200
  *
191
- * @param {Object} config
192
- * @param {string} config.permission - The name of the permission
193
- * @param {string} config.role - The name of the role
201
+ * @param {PermissionRoleConfig} config
194
202
  *
195
203
  * @returns {Migration}
196
204
  */
@@ -208,9 +216,7 @@ function addPermissionToRole(config) {
208
216
  /**
209
217
  * Creates a migration which will remove the permission from roles
210
218
  *
211
- * @param {Object} config
212
- * @param {string} config.permission - The name of the permission
213
- * @param {string} config.role - The name of the role
219
+ * @param {PermissionRoleConfig} config
214
220
  *
215
221
  * @returns {Migration}
216
222
  */
@@ -228,11 +234,7 @@ function removePermissionFromRole(config) {
228
234
  /**
229
235
  * Creates a migration which will add a permission to the database, and then link it to roles
230
236
  *
231
- * @param {Object} config
232
- * @param {string} config.name - The name of the permission
233
- * @param {string} config.action - The action_type of the permission
234
- * @param {string} config.object - The object_type of the permission
235
- *
237
+ * @param {PermissionConfig} config
236
238
  * @param {string[]} roles - A list of role names
237
239
  *
238
240
  * @returns {Migration}
@@ -247,11 +249,7 @@ function addPermissionWithRoles(config, roles) {
247
249
  /**
248
250
  * Creates a migration which will remove permissions from roles, and then remove the permission
249
251
  *
250
- * @param {Object} config
251
- * @param {string} config.name - The name of the permission
252
- * @param {string} config.action - The action_type of the permission
253
- * @param {string} config.object - The object_type of the permission
254
- *
252
+ * @param {PermissionConfig} config
255
253
  * @param {string[]} roles - A list of role names
256
254
  *
257
255
  * @returns {Migration}
@@ -270,6 +268,19 @@ module.exports = {
270
268
  createRemovePermissionMigration
271
269
  };
272
270
 
271
+ /**
272
+ * @typedef {Object} PermissionConfig
273
+ * @prop {string} config.name - The name of the permission
274
+ * @prop {string} config.action - The action_type of the permission
275
+ * @prop {string} config.object - The object_type of the permission
276
+ */
277
+
278
+ /**
279
+ * @typedef {Object} PermissionRoleConfig
280
+ * @prop {string} config.permission - The name of the permission
281
+ * @prop {string} config.role - The role to assign the Permission to
282
+ */
283
+
273
284
  /**
274
285
  * @typedef {Object} TransactionalMigrationFunctionOptions
275
286
  *
@@ -0,0 +1,3 @@
1
+ const {createDropNullableMigration} = require('../../utils');
2
+
3
+ module.exports = createDropNullableMigration('subscriptions', 'tier_id');
@@ -0,0 +1,10 @@
1
+ const {createAddColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createAddColumnMigration('members_stripe_customers_subscriptions', 'ghost_subscription_id', {
4
+ type: 'string',
5
+ maxlength: 24,
6
+ nullable: true,
7
+ references: 'subscriptions.id',
8
+ constraintName: 'mscs_ghost_subscription_id_foreign',
9
+ cascadeDelete: true
10
+ });
@@ -0,0 +1,10 @@
1
+ const {addPermissionWithRoles} = require('../../utils');
2
+
3
+ module.exports = addPermissionWithRoles({
4
+ name: 'Browse links',
5
+ action: 'browse',
6
+ object: 'link'
7
+ }, [
8
+ 'Administrator',
9
+ 'Admin Integration'
10
+ ]);
@@ -0,0 +1,10 @@
1
+ const {addPermissionWithRoles} = require('../../utils');
2
+
3
+ module.exports = addPermissionWithRoles({
4
+ name: 'Edit links',
5
+ action: 'edit',
6
+ object: 'link'
7
+ }, [
8
+ 'Administrator',
9
+ 'Admin Integration'
10
+ ]);
@@ -0,0 +1,20 @@
1
+ const logging = require('@tryghost/logging');
2
+ const {createTransactionalMigration} = require('../../utils');
3
+
4
+ module.exports = createTransactionalMigration(
5
+ async function up(connection) {
6
+ const affectedRows = await connection('newsletters')
7
+ .update({
8
+ feedback_enabled: false
9
+ })
10
+ .where('feedback_enabled', true);
11
+
12
+ if (affectedRows > 0) {
13
+ // Only log if this site was affected by the issue.
14
+ logging.info(`Disabled feedback for ${affectedRows} newsletter(s)`);
15
+ }
16
+ },
17
+ async function down() {
18
+ // no-op: we don't need to change it back
19
+ }
20
+ );
@@ -0,0 +1,35 @@
1
+ const logging = require('@tryghost/logging');
2
+ const {createTransactionalMigration} = require('../../utils');
3
+
4
+ module.exports = createTransactionalMigration(
5
+ async function up(knex) {
6
+ logging.info(`Fixing currency/monthly_price/yearly_price values for default paid tiers`);
7
+
8
+ const currencyUpdated = await knex('products')
9
+ .update('currency', 'usd')
10
+ .where({
11
+ currency: null,
12
+ type: 'paid'
13
+ });
14
+ logging.info(`Updated ${currencyUpdated} tier(s) where currency=null, type=paid to currency=USD`);
15
+
16
+ const monthlyPriceUpdated = await knex('products')
17
+ .update('monthly_price', 500)
18
+ .where({
19
+ monthly_price: null,
20
+ type: 'paid'
21
+ });
22
+ logging.info(`Updated ${monthlyPriceUpdated} tier(s) where monthly_price=null, type=paid to monthly_price=500`);
23
+
24
+ const yearlyPriceUpdated = await knex('products')
25
+ .update('yearly_price', 5000)
26
+ .where({
27
+ yearly_price: null,
28
+ type: 'paid'
29
+ });
30
+ logging.info(`Updated ${yearlyPriceUpdated} tier(s) where yearly_price=null, type=paid to yearly_price=5000`);
31
+ },
32
+ async function down(/* knex */) {
33
+ // no-op: we don't want to revert to bad data
34
+ }
35
+ );
@@ -0,0 +1,7 @@
1
+ const {createAddColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createAddColumnMigration('members_created_events', 'batch_id', {
4
+ type: 'string',
5
+ maxlength: 24,
6
+ nullable: true
7
+ });
@@ -0,0 +1,7 @@
1
+ const {createAddColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createAddColumnMigration('members_subscription_created_events', 'batch_id', {
4
+ type: 'string',
5
+ maxlength: 24,
6
+ nullable: true
7
+ });
@@ -0,0 +1,72 @@
1
+ const _ = require('lodash');
2
+ const logging = require('@tryghost/logging');
3
+ const ObjectId = require('bson-objectid').default;
4
+ const {createTransactionalMigration} = require('../../utils');
5
+ const DatabaseInfo = require('@tryghost/database-info');
6
+
7
+ // This migration links together members_created_events and members_subscription_created_events
8
+
9
+ module.exports = createTransactionalMigration(
10
+ async function up(knex) {
11
+ if (DatabaseInfo.isSQLite(knex)) {
12
+ logging.info('Skipped linking members_created_events and members_subscription_created_events on SQLite');
13
+ return;
14
+ }
15
+
16
+ // All events that happened within 15 minutes of each other will be linked
17
+ const rows = await knex('members_created_events as m')
18
+ .select('m.id as m_id', 's.id as s_id', 'm.member_id as member_id', 's.subscription_id as subscription_id')
19
+ .join('members_subscription_created_events AS s', 's.member_id', 'm.member_id')
20
+ .whereRaw('TIMESTAMPDIFF(MINUTE, s.created_at, m.created_at) between -15 and 15');
21
+
22
+ if (!rows.length) {
23
+ logging.info('Did not find linkable members_created_events and members_subscription_created_events');
24
+ return;
25
+ }
26
+
27
+ // Attach a unique id to each row
28
+ for (const row of rows) { // eslint-disable-line no-restricted-syntax
29
+ row.batch_id = ObjectId().toHexString();
30
+ }
31
+
32
+ // Create batches (insertBatch doesn't support the onConflict option)
33
+ const batches = _.chunk(rows, 1000);
34
+
35
+ for (const batch of batches) { // eslint-disable-line no-restricted-syntax
36
+ // Update the members_created_events table using INSERT ON DUPLICATE KEY UPDATE trick
37
+ const response1 = await knex('members_created_events').insert(batch.map((r) => {
38
+ return {
39
+ id: r.m_id,
40
+ batch_id: r.batch_id,
41
+ member_id: r.member_id, // added to make the insert work
42
+ source: '', // added to make the insert work
43
+ created_at: knex.raw('NOW()') // added to make the insert work
44
+ };
45
+ })).onConflict('id').merge(['batch_id']);
46
+
47
+ if (response1[0] !== 0) {
48
+ logging.error(`Inserted ${response1[0]} members_created_events, expected 0`);
49
+ throw new Error('Rolling back');
50
+ }
51
+
52
+ const response2 = await knex('members_subscription_created_events').insert(batch.map((r) => {
53
+ return {
54
+ id: r.s_id,
55
+ batch_id: r.batch_id,
56
+ member_id: r.member_id, // added to make the insert work
57
+ subscription_id: r.subscription_id, // added to make the insert work
58
+ created_at: knex.raw('NOW()') // added to make the insert work
59
+ };
60
+ })).onConflict('id').merge(['batch_id']);
61
+
62
+ if (response2[0] !== 0) {
63
+ logging.error(`Inserted ${response1[0]} members_subscription_created_events, expected 0`);
64
+ throw new Error('Rolling back');
65
+ }
66
+ }
67
+ logging.info(`Linked ${rows.length} members_created_events and members_subscription_created_events`);
68
+ },
69
+ async function down() {
70
+ // noop
71
+ }
72
+ );
@@ -0,0 +1,7 @@
1
+ const {createAddColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createAddColumnMigration('emails', 'feedback_enabled', {
4
+ type: 'boolean',
5
+ nullable: false,
6
+ defaultTo: false
7
+ });
@@ -0,0 +1,8 @@
1
+ const {addSetting} = require('../../utils');
2
+
3
+ module.exports = addSetting({
4
+ key: 'members_track_sources',
5
+ value: 'true',
6
+ type: 'boolean',
7
+ group: 'members'
8
+ });