ghost 5.22.11 → 5.24.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 (165) hide show
  1. package/.c8rc.e2e.json +21 -0
  2. package/README.md +0 -2
  3. package/components/tryghost-adapter-manager-5.24.0.tgz +0 -0
  4. package/components/tryghost-api-framework-5.24.0.tgz +0 -0
  5. package/components/tryghost-api-version-compatibility-service-5.24.0.tgz +0 -0
  6. package/components/tryghost-audience-feedback-5.24.0.tgz +0 -0
  7. package/components/tryghost-bootstrap-socket-5.24.0.tgz +0 -0
  8. package/components/tryghost-constants-5.24.0.tgz +0 -0
  9. package/components/tryghost-custom-theme-settings-service-5.24.0.tgz +0 -0
  10. package/components/tryghost-data-generator-5.24.0.tgz +0 -0
  11. package/components/tryghost-domain-events-5.24.0.tgz +0 -0
  12. package/components/tryghost-email-analytics-provider-mailgun-5.24.0.tgz +0 -0
  13. package/components/tryghost-email-analytics-service-5.24.0.tgz +0 -0
  14. package/components/tryghost-email-content-generator-5.24.0.tgz +0 -0
  15. package/components/tryghost-email-events-5.24.0.tgz +0 -0
  16. package/components/tryghost-email-service-5.24.0.tgz +0 -0
  17. package/components/tryghost-email-suppression-list-5.24.0.tgz +0 -0
  18. package/components/tryghost-express-dynamic-redirects-5.24.0.tgz +0 -0
  19. package/components/tryghost-extract-api-key-5.24.0.tgz +0 -0
  20. package/components/tryghost-html-to-plaintext-5.24.0.tgz +0 -0
  21. package/components/{tryghost-job-manager-5.22.11.tgz → tryghost-job-manager-5.24.0.tgz} +0 -0
  22. package/components/tryghost-link-redirects-5.24.0.tgz +0 -0
  23. package/components/tryghost-link-replacer-5.24.0.tgz +0 -0
  24. package/components/tryghost-link-tracking-5.24.0.tgz +0 -0
  25. package/components/{tryghost-magic-link-5.22.11.tgz → tryghost-magic-link-5.24.0.tgz} +0 -0
  26. package/components/tryghost-mailgun-client-5.24.0.tgz +0 -0
  27. package/components/{tryghost-member-attribution-5.22.11.tgz → tryghost-member-attribution-5.24.0.tgz} +0 -0
  28. package/components/{tryghost-member-events-5.22.11.tgz → tryghost-member-events-5.24.0.tgz} +0 -0
  29. package/components/tryghost-members-api-5.24.0.tgz +0 -0
  30. package/components/tryghost-members-csv-5.24.0.tgz +0 -0
  31. package/components/tryghost-members-events-service-5.24.0.tgz +0 -0
  32. package/components/tryghost-members-importer-5.24.0.tgz +0 -0
  33. package/components/{tryghost-members-offers-5.22.11.tgz → tryghost-members-offers-5.24.0.tgz} +0 -0
  34. package/components/tryghost-members-payments-5.24.0.tgz +0 -0
  35. package/components/tryghost-members-ssr-5.24.0.tgz +0 -0
  36. package/components/tryghost-members-stripe-service-5.24.0.tgz +0 -0
  37. package/components/tryghost-minifier-5.24.0.tgz +0 -0
  38. package/components/tryghost-mw-api-version-mismatch-5.24.0.tgz +0 -0
  39. package/components/tryghost-mw-cache-control-5.24.0.tgz +0 -0
  40. package/components/tryghost-mw-error-handler-5.24.0.tgz +0 -0
  41. package/components/tryghost-mw-session-from-token-5.24.0.tgz +0 -0
  42. package/components/tryghost-mw-update-user-last-seen-5.24.0.tgz +0 -0
  43. package/components/tryghost-mw-vhost-5.24.0.tgz +0 -0
  44. package/components/tryghost-oembed-service-5.24.0.tgz +0 -0
  45. package/components/tryghost-package-json-5.24.0.tgz +0 -0
  46. package/components/tryghost-referrers-5.24.0.tgz +0 -0
  47. package/components/tryghost-security-5.24.0.tgz +0 -0
  48. package/components/tryghost-session-service-5.24.0.tgz +0 -0
  49. package/components/{tryghost-settings-path-manager-5.22.11.tgz → tryghost-settings-path-manager-5.24.0.tgz} +0 -0
  50. package/components/tryghost-staff-service-5.24.0.tgz +0 -0
  51. package/components/{tryghost-stats-service-5.22.11.tgz → tryghost-stats-service-5.24.0.tgz} +0 -0
  52. package/components/{tryghost-tiers-5.22.11.tgz → tryghost-tiers-5.24.0.tgz} +0 -0
  53. package/components/tryghost-update-check-service-5.24.0.tgz +0 -0
  54. package/components/tryghost-verification-trigger-5.24.0.tgz +0 -0
  55. package/components/tryghost-version-notifications-data-service-5.24.0.tgz +0 -0
  56. package/content/themes/casper/assets/built/screen.css +1 -1
  57. package/content/themes/casper/assets/built/screen.css.map +1 -1
  58. package/content/themes/casper/assets/css/screen.css +3 -5
  59. package/content/themes/casper/default.hbs +2 -2
  60. package/content/themes/casper/package.json +1 -1
  61. package/core/boot.js +5 -1
  62. package/core/built/admin/assets/{chunk.143.8f4f86908026af3b9484.js → chunk.143.dd395a3e804fef2c3b21.js} +14 -14
  63. package/core/built/admin/assets/{chunk.178.0aff330fc5d8e74617b5.js → chunk.178.ec67ba4dc75bcec75c6f.js} +4 -4
  64. package/core/built/admin/assets/chunk.507.71dd4bfc4ccb354cc629.js +267 -0
  65. package/core/built/admin/assets/{chunk.613.695f31829550fb00d43c.js → chunk.613.6bbcc18224567657fc2e.js} +3089 -3074
  66. package/core/built/admin/assets/{chunk.613.695f31829550fb00d43c.js.LICENSE.txt → chunk.613.6bbcc18224567657fc2e.js.LICENSE.txt} +0 -0
  67. package/core/built/admin/assets/{ghost-b204dcc6ad523053868da9b2d8d65f80.js → ghost-34bc21923675def87aa2516f72ca15d7.js} +1287 -1248
  68. package/core/built/admin/assets/ghost-dark-a2076b08f23a9e6340072bc7b06ec9e7.css +1 -0
  69. package/core/built/admin/assets/ghost-f428683b68c0eea9042acc7c021641e0.css +1 -0
  70. package/core/built/admin/assets/{vendor-dc9f883b3468ff84794cf13741e6c4b4.js → vendor-04415b2b8a59aa9567dfa5d819ada71c.js} +315 -303
  71. package/core/built/admin/index.html +6 -6
  72. package/core/cli/record-test.js +47 -0
  73. package/core/frontend/apps/amp/lib/helpers/amp_content.js +5 -1
  74. package/core/frontend/apps/amp/lib/views/amp.hbs +10 -0
  75. package/core/frontend/helpers/ghost_head.js +1 -7
  76. package/core/server/api/endpoints/db.js +17 -11
  77. package/core/server/api/endpoints/email-previews.js +10 -2
  78. package/core/server/api/endpoints/emails.js +20 -14
  79. package/core/server/api/endpoints/posts.js +4 -1
  80. package/core/server/api/endpoints/utils/serializers/input/posts.js +4 -11
  81. package/core/server/api/endpoints/utils/serializers/output/db.js +3 -7
  82. package/core/server/api/endpoints/utils/serializers/output/members.js +5 -0
  83. package/core/server/data/importer/email-template.js +163 -0
  84. package/core/server/data/importer/import-manager.js +116 -35
  85. package/core/server/data/importer/importers/data/base.js +1 -0
  86. package/core/server/data/importer/importers/data/data-importer.js +27 -1
  87. package/core/server/data/migrations/versions/5.24/2022-11-21-09-32-add-source-columns-to-emails-table.js +17 -0
  88. package/core/server/data/migrations/versions/5.24/2022-11-21-15-03-populate-source-column-with-html-for-emails.js +19 -0
  89. package/core/server/data/migrations/versions/5.24/2022-11-21-15-57-add-error-columns-for-email-batches.js +22 -0
  90. package/core/server/data/schema/default-settings/default-settings.json +1 -1
  91. package/core/server/data/schema/schema.js +11 -0
  92. package/core/server/models/base/plugins/bulk-operations.js +0 -1
  93. package/core/server/models/comment.js +1 -1
  94. package/core/server/models/email.js +2 -1
  95. package/core/server/models/member-newsletter.js +9 -0
  96. package/core/server/models/member.js +1 -9
  97. package/core/server/models/stripe-customer-subscription.js +3 -7
  98. package/core/server/services/bulk-email/bulk-email-processor.js +14 -23
  99. package/core/server/services/email-service/index.js +3 -0
  100. package/core/server/services/email-service/wrapper.js +64 -0
  101. package/core/server/services/email-suppression-list/index.js +1 -0
  102. package/core/server/services/email-suppression-list/service.js +38 -0
  103. package/core/server/services/mega/mega.js +65 -6
  104. package/core/server/services/mega/segment-parser.js +1 -3
  105. package/core/server/services/members/api.js +4 -2
  106. package/core/server/services/members/middleware.js +19 -3
  107. package/core/server/services/members/service.js +36 -29
  108. package/core/server/services/members/utils.js +7 -0
  109. package/core/server/services/posts/posts-service.js +19 -6
  110. package/core/server/web/members/app.js +12 -10
  111. package/core/shared/config/defaults.json +1 -1
  112. package/core/shared/labs.js +3 -7
  113. package/core/shared/sentry.js +25 -3
  114. package/ghost.js +1 -0
  115. package/package.json +111 -106
  116. package/playwright.config.js +26 -0
  117. package/yarn.lock +263 -358
  118. package/components/tryghost-adapter-manager-5.22.11.tgz +0 -0
  119. package/components/tryghost-api-framework-5.22.11.tgz +0 -0
  120. package/components/tryghost-api-version-compatibility-service-5.22.11.tgz +0 -0
  121. package/components/tryghost-audience-feedback-5.22.11.tgz +0 -0
  122. package/components/tryghost-bootstrap-socket-5.22.11.tgz +0 -0
  123. package/components/tryghost-constants-5.22.11.tgz +0 -0
  124. package/components/tryghost-custom-theme-settings-service-5.22.11.tgz +0 -0
  125. package/components/tryghost-data-generator-5.22.11.tgz +0 -0
  126. package/components/tryghost-domain-events-5.22.11.tgz +0 -0
  127. package/components/tryghost-email-analytics-provider-mailgun-5.22.11.tgz +0 -0
  128. package/components/tryghost-email-analytics-service-5.22.11.tgz +0 -0
  129. package/components/tryghost-email-content-generator-5.22.11.tgz +0 -0
  130. package/components/tryghost-express-dynamic-redirects-5.22.11.tgz +0 -0
  131. package/components/tryghost-extract-api-key-5.22.11.tgz +0 -0
  132. package/components/tryghost-html-to-plaintext-5.22.11.tgz +0 -0
  133. package/components/tryghost-link-redirects-5.22.11.tgz +0 -0
  134. package/components/tryghost-link-replacer-5.22.11.tgz +0 -0
  135. package/components/tryghost-link-tracking-5.22.11.tgz +0 -0
  136. package/components/tryghost-mailgun-client-5.22.11.tgz +0 -0
  137. package/components/tryghost-member-analytics-service-5.22.11.tgz +0 -0
  138. package/components/tryghost-members-analytics-ingress-5.22.11.tgz +0 -0
  139. package/components/tryghost-members-api-5.22.11.tgz +0 -0
  140. package/components/tryghost-members-csv-5.22.11.tgz +0 -0
  141. package/components/tryghost-members-events-service-5.22.11.tgz +0 -0
  142. package/components/tryghost-members-importer-5.22.11.tgz +0 -0
  143. package/components/tryghost-members-payments-5.22.11.tgz +0 -0
  144. package/components/tryghost-members-ssr-5.22.11.tgz +0 -0
  145. package/components/tryghost-members-stripe-service-5.22.11.tgz +0 -0
  146. package/components/tryghost-minifier-5.22.11.tgz +0 -0
  147. package/components/tryghost-mw-api-version-mismatch-5.22.11.tgz +0 -0
  148. package/components/tryghost-mw-cache-control-5.22.11.tgz +0 -0
  149. package/components/tryghost-mw-error-handler-5.22.11.tgz +0 -0
  150. package/components/tryghost-mw-session-from-token-5.22.11.tgz +0 -0
  151. package/components/tryghost-mw-update-user-last-seen-5.22.11.tgz +0 -0
  152. package/components/tryghost-mw-vhost-5.22.11.tgz +0 -0
  153. package/components/tryghost-oembed-service-5.22.11.tgz +0 -0
  154. package/components/tryghost-package-json-5.22.11.tgz +0 -0
  155. package/components/tryghost-referrers-5.22.11.tgz +0 -0
  156. package/components/tryghost-security-5.22.11.tgz +0 -0
  157. package/components/tryghost-session-service-5.22.11.tgz +0 -0
  158. package/components/tryghost-staff-service-5.22.11.tgz +0 -0
  159. package/components/tryghost-update-check-service-5.22.11.tgz +0 -0
  160. package/components/tryghost-verification-trigger-5.22.11.tgz +0 -0
  161. package/components/tryghost-version-notifications-data-service-5.22.11.tgz +0 -0
  162. package/core/built/admin/assets/chunk.174.3a133d51d9b45097c101.js +0 -245
  163. package/core/built/admin/assets/ghost-03c7a25d23ad4d0725da171f8d7c7b2a.css +0 -1
  164. package/core/built/admin/assets/ghost-dark-8896a076fc06ec2b09343b1c9df7feca.css +0 -1
  165. package/core/server/models/member-analytic-event.js +0 -9
@@ -8,7 +8,7 @@
8
8
  <title>Ghost Admin</title>
9
9
 
10
10
 
11
- <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.22%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%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%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.24%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
12
12
 
13
13
  <meta name="HandheldFriendly" content="True" />
14
14
  <meta name="MobileOptimized" content="320" />
@@ -37,7 +37,7 @@
37
37
  </style>
38
38
 
39
39
  <link integrity="" rel="stylesheet" href="assets/vendor-3e6947aa681f0fb82b193090e520dc73.css">
40
- <link integrity="" rel="stylesheet" href="assets/ghost-03c7a25d23ad4d0725da171f8d7c7b2a.css" title="light">
40
+ <link integrity="" rel="stylesheet" href="assets/ghost-f428683b68c0eea9042acc7c021641e0.css" title="light">
41
41
 
42
42
 
43
43
  </head>
@@ -56,9 +56,9 @@
56
56
 
57
57
  <div id="ember-basic-dropdown-wormhole"></div>
58
58
 
59
- <script src="assets/vendor-dc9f883b3468ff84794cf13741e6c4b4.js"></script>
60
- <script src="assets/chunk.613.695f31829550fb00d43c.js"></script>
61
- <script src="assets/chunk.143.8f4f86908026af3b9484.js"></script>
62
- <script src="assets/ghost-b204dcc6ad523053868da9b2d8d65f80.js"></script>
59
+ <script src="assets/vendor-04415b2b8a59aa9567dfa5d819ada71c.js"></script>
60
+ <script src="assets/chunk.613.6bbcc18224567657fc2e.js"></script>
61
+ <script src="assets/chunk.143.dd395a3e804fef2c3b21.js"></script>
62
+ <script src="assets/ghost-34bc21923675def87aa2516f72ca15d7.js"></script>
63
63
  </body>
64
64
  </html>
@@ -0,0 +1,47 @@
1
+ const {chromium} = require('@playwright/test');
2
+ const Command = require('./command');
3
+ const testUtils = require('../../test/utils');
4
+
5
+ module.exports = class RecordTest extends Command {
6
+ setup() {
7
+ this.help('Use PlayWright to record a browser-based test');
8
+ this.argument('--admin', {type: 'boolean', defaultValue: false, desc: 'Start browser-based test in Ghost admin'});
9
+ this.argument('--no-setup', {type: 'boolean', defaultValue: false, desc: 'Disable the default setup, for testing Ghost admin setup'});
10
+ this.argument('--fixtures', {type: 'array', defaultValue: [], delimiter: ',', desc: 'A list of comma-separated fixtures to include'});
11
+ }
12
+
13
+ permittedEnvironments() {
14
+ return ['development', 'testing'];
15
+ }
16
+
17
+ async handle(argv) {
18
+ const app = await testUtils.startGhost();
19
+
20
+ if (argv.fixtures.length > 0) {
21
+ await testUtils.initFixtures(...argv.fixtures);
22
+ }
23
+
24
+ const browser = await chromium.launch({headless: false});
25
+
26
+ const baseURL = argv.admin ? `${app.url}ghost/` : app.url;
27
+ const context = await browser.newContext({
28
+ baseURL
29
+ });
30
+
31
+ // Pause the page, and start recording manually.
32
+ const page = await context.newPage();
33
+ await page.goto('');
34
+
35
+ if (argv.admin && !argv['no-setup']) {
36
+ await page.getByPlaceholder('The Daily Awesome').click();
37
+ await page.getByPlaceholder('The Daily Awesome').fill('The Local Test');
38
+ await page.getByPlaceholder('Jamie Larson').fill('Testy McTesterson');
39
+ await page.getByPlaceholder('jamie@example.com').fill('testy@example.com');
40
+ await page.getByPlaceholder('At least 10 characters').fill('Mc.T3ster$0n');
41
+ await page.getByPlaceholder('At least 10 characters').press('Enter');
42
+ await page.locator('.gh-done-pink').click();
43
+ }
44
+
45
+ await page.pause();
46
+ }
47
+ };
@@ -108,7 +108,7 @@ allowedAMPAttributes = {
108
108
  'amp-audio': ['src', 'width', 'height', 'autoplay', 'loop', 'muted', 'controls'],
109
109
  'amp-iframe': ['src', 'srcdoc', 'width', 'height', 'layout', 'frameborder', 'allowfullscreen', 'allowtransparency',
110
110
  'sandbox', 'referrerpolicy'],
111
- 'amp-youtube': ['src', 'width', 'height', 'layout', 'frameborder', 'autoplay', 'loop', 'data-videoid', 'data-live-channelid']
111
+ 'amp-youtube': ['src', 'layout', 'frameborder', 'autoplay', 'loop', 'data-videoid', 'data-live-channelid', 'width', 'height']
112
112
  };
113
113
 
114
114
  function getAmperizeHTML(html, post) {
@@ -193,6 +193,10 @@ module.exports = async function amp_content() { // eslint-disable-line camelcase
193
193
  $('audio').children('source').remove();
194
194
  $('audio').children('track').remove();
195
195
 
196
+ $('amp-youtube').attr('layout', 'responsive');
197
+ $('amp-youtube').attr('height', '350');
198
+ $('amp-youtube').attr('width', '600');
199
+
196
200
  ampHTML = $.html();
197
201
 
198
202
  // @TODO: remove this, when Amperize supports HTML sanitizing
@@ -182,6 +182,16 @@
182
182
  amp-img img {
183
183
  object-fit: cover;
184
184
  }
185
+
186
+ amp-youtube {
187
+ height: calc(100vw / 1.78);
188
+ width: 100vw;
189
+ position: relative;
190
+ }
191
+
192
+ amp-youtube img {
193
+ position: absolute;
194
+ }
185
195
 
186
196
  .page-header {
187
197
  padding: 50px 5vmin 30px;
@@ -2,7 +2,7 @@
2
2
  // Usage: `{{ghost_head}}`
3
3
  //
4
4
  // Outputs scripts and other assets at the top of a Ghost theme
5
- const {metaData, settingsCache, config, blogIcon, urlUtils, labs, getFrontendKey} = require('../services/proxy');
5
+ const {metaData, settingsCache, config, blogIcon, urlUtils, getFrontendKey} = require('../services/proxy');
6
6
  const {escapeExpression, SafeString} = require('../services/handlebars');
7
7
 
8
8
  // BAD REQUIRE
@@ -207,12 +207,6 @@ module.exports = async function ghost_head(options) { // eslint-disable-line cam
207
207
  head.push('<meta name="generator" content="Ghost ' +
208
208
  escapeExpression(safeVersion) + '" />');
209
209
 
210
- // Ghost analytics tag
211
- if (labs.isSet('membersActivity')) {
212
- const postId = (dataRoot && dataRoot.post) ? dataRoot.post.id : '';
213
- head.push(writeMetaTag('ghost-analytics-id', postId, 'name'));
214
- }
215
-
216
210
  head.push('<link rel="alternate" type="application/rss+xml" title="' +
217
211
  escapeExpression(meta.site.title) + '" href="' +
218
212
  escapeExpression(meta.rssUrl) + '" />');
@@ -1,9 +1,11 @@
1
1
  const Promise = require('bluebird');
2
+ const moment = require('moment-timezone');
2
3
  const dbBackup = require('../../data/db/backup');
3
4
  const exporter = require('../../data/exporter');
4
5
  const importer = require('../../data/importer');
5
6
  const errors = require('@tryghost/errors');
6
7
  const models = require('../../models');
8
+ const settingsCache = require('../../../shared/settings-cache');
7
9
 
8
10
  module.exports = {
9
11
  docName: 'db',
@@ -68,22 +70,26 @@ module.exports = {
68
70
  },
69
71
 
70
72
  importContent: {
73
+ statusCode(result) {
74
+ if (result && (result.db || result.problems)) {
75
+ return 200;
76
+ } else {
77
+ return 202;
78
+ }
79
+ },
71
80
  headers: {
72
81
  cacheInvalidate: true
73
82
  },
74
- options: [
75
- 'include'
76
- ],
77
- validation: {
78
- options: {
79
- include: {
80
- values: exporter.BACKUP_TABLES
81
- }
82
- }
83
- },
84
83
  permissions: true,
85
84
  query(frame) {
86
- return importer.importFromFile(frame.file, {include: frame.options.withRelated});
85
+ const siteTimezone = settingsCache.get('timezone');
86
+ const importTag = `Import ${moment().tz(siteTimezone).format('YYYY-MM-DD HH:mm')}`;
87
+ return importer.importFromFile(frame.file, {
88
+ user: {
89
+ email: frame.user.get('email')
90
+ },
91
+ importTag
92
+ });
87
93
  }
88
94
  },
89
95
 
@@ -2,7 +2,8 @@ const models = require('../../models');
2
2
  const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
4
  const mega = require('../../services/mega');
5
-
5
+ const emailService = require('../../services/email-service');
6
+ const labs = require('../../../shared/labs');
6
7
  const messages = {
7
8
  postNotFound: 'Post not found.'
8
9
  };
@@ -29,6 +30,10 @@ module.exports = {
29
30
  ],
30
31
  permissions: true,
31
32
  async query(frame) {
33
+ if (labs.isSet('emailStability')) {
34
+ return await emailService.controller.previewEmail(frame);
35
+ }
36
+
32
37
  const options = Object.assign(frame.options, {formats: 'html,plaintext', withRelated: ['authors', 'posts_meta']});
33
38
  const data = Object.assign(frame.data, {status: 'all'});
34
39
 
@@ -61,6 +66,10 @@ module.exports = {
61
66
  },
62
67
  permissions: true,
63
68
  async query(frame) {
69
+ if (labs.isSet('emailStability')) {
70
+ return await emailService.controller.sendTestEmail(frame);
71
+ }
72
+
64
73
  const options = Object.assign(frame.options, {status: 'all'});
65
74
  let model = await models.Post.findOne(options, {withRelated: ['authors']});
66
75
 
@@ -69,7 +78,6 @@ module.exports = {
69
78
  message: tpl(messages.postNotFound)
70
79
  });
71
80
  }
72
-
73
81
  const {emails = [], memberSegment} = frame.data;
74
82
  return await mega.mega.sendTestEmail(model, emails, memberSegment);
75
83
  }
@@ -2,6 +2,8 @@ const models = require('../../models');
2
2
  const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
4
  const megaService = require('../../services/mega');
5
+ const emailService = require('../../services/email-service');
6
+ const labs = require('../../../shared/labs');
5
7
 
6
8
  const messages = {
7
9
  emailNotFound: 'Email not found.',
@@ -57,23 +59,27 @@ module.exports = {
57
59
  'id'
58
60
  ],
59
61
  permissions: true,
60
- query(frame) {
61
- return models.Email.findOne(frame.data, frame.options)
62
- .then(async (model) => {
63
- if (!model) {
64
- throw new errors.NotFoundError({
65
- message: tpl(messages.emailNotFound)
66
- });
67
- }
62
+ // (complexity removed with new labs flag)
63
+ // eslint-disable-next-line ghost/ghost-custom/max-api-complexity
64
+ async query(frame) {
65
+ if (labs.isSet('emailStability')) {
66
+ return await emailService.controller.retryFailedEmail(frame);
67
+ }
68
68
 
69
- if (model.get('status') !== 'failed') {
70
- throw new errors.IncorrectUsageError({
71
- message: tpl(messages.retryNotAllowed)
72
- });
73
- }
69
+ const model = await models.Email.findOne(frame.data, frame.options);
70
+ if (!model) {
71
+ throw new errors.NotFoundError({
72
+ message: tpl(messages.emailNotFound)
73
+ });
74
+ }
74
75
 
75
- return await megaService.mega.retryFailedEmail(model);
76
+ if (model.get('status') !== 'failed') {
77
+ throw new errors.IncorrectUsageError({
78
+ message: tpl(messages.retryNotAllowed)
76
79
  });
80
+ }
81
+
82
+ return await megaService.mega.retryFailedEmail(model);
77
83
  }
78
84
  }
79
85
  };
@@ -9,10 +9,13 @@ const allowedIncludes = [
9
9
  'email',
10
10
  'tiers',
11
11
  'newsletter',
12
+ 'count.conversions',
12
13
  'count.signups',
13
14
  'count.paid_conversions',
14
15
  'count.clicks',
15
- 'sentiment'
16
+ 'sentiment',
17
+ 'count.positive_feedback',
18
+ 'count.negative_feedback'
16
19
  ];
17
20
  const unsafeAttrs = ['status', 'authors', 'visibility'];
18
21
 
@@ -6,7 +6,6 @@ const localUtils = require('../../index');
6
6
  const mobiledoc = require('../../../../../lib/mobiledoc');
7
7
  const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
8
8
  const clean = require('./utils/clean');
9
- const labs = require('../../../../../../shared/labs');
10
9
 
11
10
  function removeSourceFormats(frame) {
12
11
  if (frame.options.formats?.includes('mobiledoc') || frame.options.formats?.includes('lexical')) {
@@ -22,11 +21,9 @@ function removeSourceFormats(frame) {
22
21
  function mapWithRelated(frame) {
23
22
  if (frame.options.withRelated) {
24
23
  // Map sentiment to count.sentiment
25
- if (labs.isSet('audienceFeedback')) {
26
- frame.options.withRelated = frame.options.withRelated.map((relation) => {
27
- return relation === 'sentiment' ? 'count.sentiment' : relation;
28
- });
29
- }
24
+ frame.options.withRelated = frame.options.withRelated.map((relation) => {
25
+ return relation === 'sentiment' ? 'count.sentiment' : relation;
26
+ });
30
27
  return;
31
28
  }
32
29
  }
@@ -44,11 +41,7 @@ function defaultRelations(frame) {
44
41
  return false;
45
42
  }
46
43
 
47
- if (labs.isSet('audienceFeedback')) {
48
- frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.conversions', 'count.clicks', 'count.sentiment', 'count.positive_feedback', 'count.negative_feedback'];
49
- } else {
50
- frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.paid_conversions', 'count.clicks'];
51
- }
44
+ frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.clicks'];
52
45
  }
53
46
 
54
47
  function setDefaultOrder(frame) {
@@ -20,15 +20,11 @@ module.exports = {
20
20
  importContent(response, apiConfig, frame) {
21
21
  debug('importContent');
22
22
 
23
- // NOTE: response can contain 2 objects if images are imported
24
- const problems = (response.length === 2)
25
- ? response[1].problems
26
- : response[0].problems;
27
-
28
23
  frame.response = {
29
- db: [],
30
- problems: problems
24
+ db: []
31
25
  };
26
+
27
+ frame.response.problems = response?.data?.problems ?? [];
32
28
  },
33
29
 
34
30
  deleteAllContent(response, apiConfig, frame) {
@@ -2,6 +2,7 @@
2
2
  const debug = require('@tryghost/debug')('api:endpoints:utils:serializers:output:members');
3
3
  const {unparse} = require('@tryghost/members-csv');
4
4
  const mappers = require('./mappers');
5
+ const labs = require('../../../../../../shared/labs');
5
6
 
6
7
  module.exports = {
7
8
  browse: createSerializer('browse', paginatedMembers),
@@ -171,6 +172,10 @@ function serializeMember(member, options) {
171
172
  delete subscription.price.product;
172
173
  }
173
174
 
175
+ if (labs.isSet('suppressionList')) {
176
+ serialized.email_suppression = json.email_suppression;
177
+ }
178
+
174
179
  if (json.newsletters) {
175
180
  serialized.newsletters = json.newsletters
176
181
  .filter(newsletter => newsletter.status === 'active')
@@ -0,0 +1,163 @@
1
+ module.exports = ({result, siteUrl, postsUrl, emailRecipient}) => `
2
+ <!doctype html>
3
+ <html>
4
+ <head>
5
+ <meta name="viewport" content="width=device-width">
6
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
7
+ <title>Your content import is complete</title>
8
+ <style>
9
+ /* -------------------------------------
10
+ RESPONSIVE AND MOBILE FRIENDLY STYLES
11
+ ------------------------------------- */
12
+ @media only screen and (max-width: 620px) {
13
+ table[class=body] h1 {
14
+ font-size: 28px !important;
15
+ margin-bottom: 10px !important;
16
+ }
17
+ table[class=body] p,
18
+ table[class=body] ul,
19
+ table[class=body] ol,
20
+ table[class=body] td,
21
+ table[class=body] span,
22
+ table[class=body] a {
23
+ font-size: 16px !important;
24
+ }
25
+ table[class=body] .title {
26
+ font-size: 22px !important;
27
+ }
28
+ table[class=body] .wrapper,
29
+ table[class=body] .article {
30
+ padding: 10px !important;
31
+ }
32
+ table[class=body] .content {
33
+ padding: 0 !important;
34
+ }
35
+ table[class=body] .container {
36
+ padding: 0 !important;
37
+ width: 100% !important;
38
+ }
39
+ table[class=body] .main {
40
+ border-left-width: 0 !important;
41
+ border-radius: 0 !important;
42
+ border-right-width: 0 !important;
43
+ }
44
+ table[class=body] .btn table {
45
+ width: 100% !important;
46
+ }
47
+ table[class=body] .btn a {
48
+ width: 100% !important;
49
+ }
50
+ table[class=body] .img-responsive {
51
+ height: auto !important;
52
+ max-width: 100% !important;
53
+ width: auto !important;
54
+ }
55
+ table[class=body] p[class=small],
56
+ table[class=body] a[class=small] {
57
+ font-size: 12x !important;
58
+ }
59
+ }
60
+ /* -------------------------------------
61
+ PRESERVE THESE STYLES IN THE HEAD
62
+ ------------------------------------- */
63
+ @media all {
64
+ .ExternalClass {
65
+ width: 100%;
66
+ }
67
+ .ExternalClass,
68
+ .ExternalClass p,
69
+ .ExternalClass span,
70
+ .ExternalClass font,
71
+ .ExternalClass td,
72
+ .ExternalClass div {
73
+ line-height: 100%;
74
+ }
75
+ .recipient-link a {
76
+ color: inherit !important;
77
+ font-family: inherit !important;
78
+ font-size: inherit !important;
79
+ font-weight: inherit !important;
80
+ line-height: inherit !important;
81
+ text-decoration: none !important;
82
+ }
83
+ #MessageViewBody a {
84
+ color: inherit;
85
+ text-decoration: none;
86
+ font-size: inherit;
87
+ font-family: inherit;
88
+ font-weight: inherit;
89
+ line-height: inherit;
90
+ }
91
+ }
92
+ hr {
93
+ border-width: 0;
94
+ height: 0;
95
+ margin-top: 34px;
96
+ margin-bottom: 34px;
97
+ border-bottom-width: 1px;
98
+ border-bottom-color: #EEF5F8;
99
+ }
100
+ a {
101
+ color: #3A464C;
102
+ }
103
+ </style>
104
+ </head>
105
+ <body class="" style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
106
+ <table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
107
+ <tr>
108
+ <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;">&nbsp;</td>
109
+ <td class="container" 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; display: block; Margin: 0 auto; max-width: 540px; padding: 10px; width: 540px;">
110
+ <div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 600px; padding: 30px 20px;">
111
+
112
+ <!-- START CENTERED CONTAINER -->
113
+ <span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Your content import is complete</span>
114
+ <table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
115
+
116
+ <!-- START MAIN CONTENT AREA -->
117
+ <tr>
118
+ <td class="wrapper" 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; box-sizing: border-box;">
119
+ <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
120
+ <tr>
121
+ <td align="center" style="padding-top: 20px; padding-bottom: 12px;"><img src="https://static.ghost.org/v4.0.0/images/ghost-orb-4.png" width="60" height="60" style="width: 60px; height: 60px;" /></td>
122
+ </tr>
123
+ <tr>
124
+ <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: 16px; vertical-align: top;">
125
+ <p class="title" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 21px; color: #3A464C; font-weight: normal; line-height: 25px; margin-bottom: 30px; margin-top: 50px; font-weight: 600; color: #15212A;">${result?.data?.errors ? 'Import unsuccessful' : 'Your content import has finished successfully'}</p>
126
+ </td>
127
+ </tr>
128
+ ${result?.data?.errors ? `
129
+ <tr>
130
+ <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; padding-bottom: 16px;">One or more error occured while importing your content. Please contact support or report on the <a href="https://forum.ghost.org/">Ghost Community Forum</a>.</td>
131
+ </tr>
132
+ ` : `
133
+ <tr>
134
+ <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; padding-bottom: 12px; padding-top: 16px;">
135
+ <a href="${postsUrl.href}" target="_blank" style="display: inline-block; color: #ffffff; background-color: #15212A; border: solid 1px #15212A; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: #15212A;">View posts</a>
136
+ </td>
137
+ </tr>
138
+ `}
139
+ </table>
140
+ </td>
141
+ </tr>
142
+ <tr>
143
+ <td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; vertical-align: top; padding-top: 80px; padding-bottom: 10px;">
144
+ <div class="footer">
145
+ <p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; color: #738A94; font-weight: normal; margin: 0; line-height: 18px; margin-bottom: 0px; font-size: 11px;">This email was sent from <a href="${siteUrl.href}" style="color: #738A94;">${siteUrl.host}</a> to <a href="mailto:${emailRecipient}" style="color: #738A94;">${emailRecipient}</a></p>
146
+ </div>
147
+ </td>
148
+ </tr>
149
+
150
+ <!-- END MAIN CONTENT AREA -->
151
+ </table>
152
+
153
+
154
+ <!-- END CENTERED CONTAINER -->
155
+ </div>
156
+ </td>
157
+ <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;">&nbsp;</td>
158
+ </tr>
159
+ </table>
160
+ </body>
161
+ </html>
162
+ `;
163
+