ghost 5.87.1 → 5.87.3

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 (169) hide show
  1. package/components/tryghost-adapter-cache-memory-ttl-5.87.3.tgz +0 -0
  2. package/components/tryghost-adapter-cache-redis-5.87.3.tgz +0 -0
  3. package/components/{tryghost-adapter-manager-5.87.1.tgz → tryghost-adapter-manager-5.87.3.tgz} +0 -0
  4. package/components/{tryghost-announcement-bar-settings-5.87.1.tgz → tryghost-announcement-bar-settings-5.87.3.tgz} +0 -0
  5. package/components/{tryghost-api-framework-5.87.1.tgz → tryghost-api-framework-5.87.3.tgz} +0 -0
  6. package/components/{tryghost-api-version-compatibility-service-5.87.1.tgz → tryghost-api-version-compatibility-service-5.87.3.tgz} +0 -0
  7. package/components/tryghost-audience-feedback-5.87.3.tgz +0 -0
  8. package/components/tryghost-bookshelf-repository-5.87.3.tgz +0 -0
  9. package/components/{tryghost-bootstrap-socket-5.87.1.tgz → tryghost-bootstrap-socket-5.87.3.tgz} +0 -0
  10. package/components/tryghost-collections-5.87.3.tgz +0 -0
  11. package/components/{tryghost-constants-5.87.1.tgz → tryghost-constants-5.87.3.tgz} +0 -0
  12. package/components/{tryghost-custom-theme-settings-service-5.87.1.tgz → tryghost-custom-theme-settings-service-5.87.3.tgz} +0 -0
  13. package/components/{tryghost-data-generator-5.87.1.tgz → tryghost-data-generator-5.87.3.tgz} +0 -0
  14. package/components/tryghost-domain-events-5.87.3.tgz +0 -0
  15. package/components/tryghost-donations-5.87.3.tgz +0 -0
  16. package/components/tryghost-dynamic-routing-events-5.87.3.tgz +0 -0
  17. package/components/tryghost-email-addresses-5.87.3.tgz +0 -0
  18. package/components/tryghost-email-analytics-provider-mailgun-5.87.3.tgz +0 -0
  19. package/components/{tryghost-email-analytics-service-5.87.1.tgz → tryghost-email-analytics-service-5.87.3.tgz} +0 -0
  20. package/components/tryghost-email-content-generator-5.87.3.tgz +0 -0
  21. package/components/{tryghost-email-events-5.87.1.tgz → tryghost-email-events-5.87.3.tgz} +0 -0
  22. package/components/tryghost-email-service-5.87.3.tgz +0 -0
  23. package/components/tryghost-email-suppression-list-5.87.3.tgz +0 -0
  24. package/components/tryghost-express-dynamic-redirects-5.87.3.tgz +0 -0
  25. package/components/tryghost-external-media-inliner-5.87.3.tgz +0 -0
  26. package/components/tryghost-extract-api-key-5.87.3.tgz +0 -0
  27. package/components/tryghost-ghost-5.87.3.tgz +0 -0
  28. package/components/tryghost-html-to-plaintext-5.87.3.tgz +0 -0
  29. package/components/tryghost-i18n-5.87.3.tgz +0 -0
  30. package/components/tryghost-importer-handler-content-files-5.87.3.tgz +0 -0
  31. package/components/{tryghost-importer-revue-5.87.1.tgz → tryghost-importer-revue-5.87.3.tgz} +0 -0
  32. package/components/tryghost-in-memory-repository-5.87.3.tgz +0 -0
  33. package/components/{tryghost-job-manager-5.87.1.tgz → tryghost-job-manager-5.87.3.tgz} +0 -0
  34. package/components/tryghost-link-redirects-5.87.3.tgz +0 -0
  35. package/components/tryghost-link-replacer-5.87.3.tgz +0 -0
  36. package/components/{tryghost-link-tracking-5.87.1.tgz → tryghost-link-tracking-5.87.3.tgz} +0 -0
  37. package/components/{tryghost-magic-link-5.87.1.tgz → tryghost-magic-link-5.87.3.tgz} +0 -0
  38. package/components/tryghost-mail-events-5.87.3.tgz +0 -0
  39. package/components/tryghost-mailgun-client-5.87.3.tgz +0 -0
  40. package/components/{tryghost-member-attribution-5.87.1.tgz → tryghost-member-attribution-5.87.3.tgz} +0 -0
  41. package/components/tryghost-member-events-5.87.3.tgz +0 -0
  42. package/components/tryghost-members-api-5.87.3.tgz +0 -0
  43. package/components/tryghost-members-csv-5.87.3.tgz +0 -0
  44. package/components/tryghost-members-events-service-5.87.3.tgz +0 -0
  45. package/components/{tryghost-members-importer-5.87.1.tgz → tryghost-members-importer-5.87.3.tgz} +0 -0
  46. package/components/{tryghost-members-offers-5.87.1.tgz → tryghost-members-offers-5.87.3.tgz} +0 -0
  47. package/components/{tryghost-members-payments-5.87.1.tgz → tryghost-members-payments-5.87.3.tgz} +0 -0
  48. package/components/{tryghost-members-ssr-5.87.1.tgz → tryghost-members-ssr-5.87.3.tgz} +0 -0
  49. package/components/tryghost-members-stripe-service-5.87.3.tgz +0 -0
  50. package/components/{tryghost-mentions-email-report-5.87.1.tgz → tryghost-mentions-email-report-5.87.3.tgz} +0 -0
  51. package/components/tryghost-milestones-5.87.3.tgz +0 -0
  52. package/components/{tryghost-minifier-5.87.1.tgz → tryghost-minifier-5.87.3.tgz} +0 -0
  53. package/components/tryghost-model-to-domain-event-interceptor-5.87.3.tgz +0 -0
  54. package/components/tryghost-mw-api-version-mismatch-5.87.3.tgz +0 -0
  55. package/components/tryghost-mw-cache-control-5.87.3.tgz +0 -0
  56. package/components/{tryghost-mw-error-handler-5.87.1.tgz → tryghost-mw-error-handler-5.87.3.tgz} +0 -0
  57. package/components/tryghost-mw-session-from-token-5.87.3.tgz +0 -0
  58. package/components/tryghost-mw-update-user-last-seen-5.87.3.tgz +0 -0
  59. package/components/tryghost-mw-version-match-5.87.3.tgz +0 -0
  60. package/components/tryghost-mw-vhost-5.87.3.tgz +0 -0
  61. package/components/tryghost-nql-filter-expansions-5.87.3.tgz +0 -0
  62. package/components/tryghost-oembed-service-5.87.3.tgz +0 -0
  63. package/components/{tryghost-package-json-5.87.1.tgz → tryghost-package-json-5.87.3.tgz} +0 -0
  64. package/components/tryghost-post-events-5.87.3.tgz +0 -0
  65. package/components/tryghost-post-revisions-5.87.3.tgz +0 -0
  66. package/components/{tryghost-posts-service-5.87.1.tgz → tryghost-posts-service-5.87.3.tgz} +0 -0
  67. package/components/tryghost-recommendations-5.87.3.tgz +0 -0
  68. package/components/tryghost-referrers-5.87.3.tgz +0 -0
  69. package/components/{tryghost-security-5.87.1.tgz → tryghost-security-5.87.3.tgz} +0 -0
  70. package/components/{tryghost-session-service-5.87.1.tgz → tryghost-session-service-5.87.3.tgz} +0 -0
  71. package/components/tryghost-settings-path-manager-5.87.3.tgz +0 -0
  72. package/components/{tryghost-slack-notifications-5.87.1.tgz → tryghost-slack-notifications-5.87.3.tgz} +0 -0
  73. package/components/{tryghost-staff-service-5.87.1.tgz → tryghost-staff-service-5.87.3.tgz} +0 -0
  74. package/components/{tryghost-stats-service-5.87.1.tgz → tryghost-stats-service-5.87.3.tgz} +0 -0
  75. package/components/{tryghost-tiers-5.87.1.tgz → tryghost-tiers-5.87.3.tgz} +0 -0
  76. package/components/{tryghost-update-check-service-5.87.1.tgz → tryghost-update-check-service-5.87.3.tgz} +0 -0
  77. package/components/tryghost-verification-trigger-5.87.3.tgz +0 -0
  78. package/components/tryghost-version-notifications-data-service-5.87.3.tgz +0 -0
  79. package/components/tryghost-webmentions-5.87.3.tgz +0 -0
  80. package/content/themes/casper/assets/built/screen.css +1 -1
  81. package/content/themes/casper/assets/built/screen.css.map +1 -1
  82. package/content/themes/casper/assets/css/screen.css +2 -0
  83. package/content/themes/casper/package.json +1 -1
  84. package/content/themes/source/assets/built/screen.css +1 -1
  85. package/content/themes/source/assets/built/screen.css.map +1 -1
  86. package/content/themes/source/assets/css/screen.css +1 -0
  87. package/content/themes/source/package.json +1 -1
  88. package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +2 -2
  89. package/core/built/admin/assets/admin-x-activitypub/{index-9a6a8e4b.mjs → index-b4a353e7.mjs} +4481 -4431
  90. package/core/built/admin/assets/admin-x-activitypub/{modals-5aad7e41.mjs → modals-f64a65f9.mjs} +213 -215
  91. package/core/built/admin/assets/admin-x-demo/admin-x-demo.js +2 -2
  92. package/core/built/admin/assets/admin-x-demo/{index-f1fae1e6.mjs → index-5e68f89b.mjs} +4 -4
  93. package/core/built/admin/assets/admin-x-demo/{modals-344029a3.mjs → modals-f789a6c4.mjs} +2 -2
  94. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-1a93227f.mjs → CodeEditorView-e762149f.mjs} +2 -2
  95. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
  96. package/core/built/admin/assets/admin-x-settings/{index-1caa03f1.mjs → index-b5d002c9.mjs} +2 -2
  97. package/core/built/admin/assets/admin-x-settings/{index-bb355172.mjs → index-e85d0dcf.mjs} +2441 -2441
  98. package/core/built/admin/assets/admin-x-settings/{modals-f496bf28.mjs → modals-ccd29d36.mjs} +278 -282
  99. package/core/built/admin/assets/{chunk.42.167fc5e3f3a1c1088f2e.js → chunk.42.0879e40a4de3db25b177.js} +6292 -6282
  100. package/core/built/admin/assets/{chunk.42.167fc5e3f3a1c1088f2e.js.LICENSE.txt → chunk.42.0879e40a4de3db25b177.js.LICENSE.txt} +1 -1
  101. package/core/built/admin/assets/{chunk.524.a032fbf6d6bbe20b0eb3.js → chunk.524.036bdde7e8805a7ab0a7.js} +5 -5
  102. package/core/built/admin/assets/{chunk.582.bc65953e4022c385c144.js → chunk.582.e06ceab285e98b1aa982.js} +6 -6
  103. package/core/built/admin/assets/{ghost-b645099f33022aa37d96a2be8d8ddad1.js → ghost-07422c98448d7259d957d65c6737a274.js} +371 -372
  104. package/core/built/admin/assets/{ghost-cc31d867156dbb76ec96cdd93bd39d36.css → ghost-cb98187b163a0d8b42d752ab890e039a.css} +1 -1
  105. package/core/built/admin/assets/{ghost-dark-69968977cce01f9101da418724176290.css → ghost-dark-0d78b0a11f5c2604992de20c1bfe396f.css} +1 -1
  106. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +28961 -30084
  107. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +176 -162
  108. package/core/built/admin/assets/{vendor-4e0f92ca2b1c0850606b912c70852e32.js → vendor-051d43ec47258b77ffd2fcc21a6d8c50.js} +64 -63
  109. package/core/built/admin/index.html +6 -6
  110. package/core/frontend/services/routing/controllers/unsubscribe.js +1 -2
  111. package/core/frontend/src/cards/css/bookmark.css +1 -1
  112. package/core/frontend/src/member-attribution/member-attribution.js +15 -1
  113. package/core/server/data/importer/importers/data/ProductsImporter.js +18 -0
  114. package/core/server/lib/image/ImageSize.js +15 -3
  115. package/core/server/lib/image/ImageUtils.js +2 -1
  116. package/core/server/services/custom-redirects/CustomRedirectsAPI.js +3 -4
  117. package/core/server/services/custom-redirects/validation.js +3 -21
  118. package/core/server/services/recommendations/RecommendationServiceWrapper.js +6 -11
  119. package/core/shared/labs.js +0 -4
  120. package/package.json +152 -153
  121. package/yarn.lock +955 -434
  122. package/components/tryghost-adapter-cache-memory-ttl-5.87.1.tgz +0 -0
  123. package/components/tryghost-adapter-cache-redis-5.87.1.tgz +0 -0
  124. package/components/tryghost-audience-feedback-5.87.1.tgz +0 -0
  125. package/components/tryghost-bookshelf-repository-5.87.1.tgz +0 -0
  126. package/components/tryghost-collections-5.87.1.tgz +0 -0
  127. package/components/tryghost-domain-events-5.87.1.tgz +0 -0
  128. package/components/tryghost-donations-5.87.1.tgz +0 -0
  129. package/components/tryghost-dynamic-routing-events-5.87.1.tgz +0 -0
  130. package/components/tryghost-email-addresses-5.87.1.tgz +0 -0
  131. package/components/tryghost-email-analytics-provider-mailgun-5.87.1.tgz +0 -0
  132. package/components/tryghost-email-content-generator-5.87.1.tgz +0 -0
  133. package/components/tryghost-email-service-5.87.1.tgz +0 -0
  134. package/components/tryghost-email-suppression-list-5.87.1.tgz +0 -0
  135. package/components/tryghost-express-dynamic-redirects-5.87.1.tgz +0 -0
  136. package/components/tryghost-external-media-inliner-5.87.1.tgz +0 -0
  137. package/components/tryghost-extract-api-key-5.87.1.tgz +0 -0
  138. package/components/tryghost-ghost-5.87.1.tgz +0 -0
  139. package/components/tryghost-html-to-plaintext-5.87.1.tgz +0 -0
  140. package/components/tryghost-i18n-5.87.1.tgz +0 -0
  141. package/components/tryghost-importer-handler-content-files-5.87.1.tgz +0 -0
  142. package/components/tryghost-in-memory-repository-5.87.1.tgz +0 -0
  143. package/components/tryghost-link-redirects-5.87.1.tgz +0 -0
  144. package/components/tryghost-link-replacer-5.87.1.tgz +0 -0
  145. package/components/tryghost-mail-events-5.87.1.tgz +0 -0
  146. package/components/tryghost-mailgun-client-5.87.1.tgz +0 -0
  147. package/components/tryghost-member-events-5.87.1.tgz +0 -0
  148. package/components/tryghost-members-api-5.87.1.tgz +0 -0
  149. package/components/tryghost-members-csv-5.87.1.tgz +0 -0
  150. package/components/tryghost-members-events-service-5.87.1.tgz +0 -0
  151. package/components/tryghost-members-stripe-service-5.87.1.tgz +0 -0
  152. package/components/tryghost-milestones-5.87.1.tgz +0 -0
  153. package/components/tryghost-model-to-domain-event-interceptor-5.87.1.tgz +0 -0
  154. package/components/tryghost-mw-api-version-mismatch-5.87.1.tgz +0 -0
  155. package/components/tryghost-mw-cache-control-5.87.1.tgz +0 -0
  156. package/components/tryghost-mw-session-from-token-5.87.1.tgz +0 -0
  157. package/components/tryghost-mw-update-user-last-seen-5.87.1.tgz +0 -0
  158. package/components/tryghost-mw-version-match-5.87.1.tgz +0 -0
  159. package/components/tryghost-mw-vhost-5.87.1.tgz +0 -0
  160. package/components/tryghost-nql-filter-expansions-5.87.1.tgz +0 -0
  161. package/components/tryghost-oembed-service-5.87.1.tgz +0 -0
  162. package/components/tryghost-post-events-5.87.1.tgz +0 -0
  163. package/components/tryghost-post-revisions-5.87.1.tgz +0 -0
  164. package/components/tryghost-recommendations-5.87.1.tgz +0 -0
  165. package/components/tryghost-referrers-5.87.1.tgz +0 -0
  166. package/components/tryghost-settings-path-manager-5.87.1.tgz +0 -0
  167. package/components/tryghost-verification-trigger-5.87.1.tgz +0 -0
  168. package/components/tryghost-version-notifications-data-service-5.87.1.tgz +0 -0
  169. package/components/tryghost-webmentions-5.87.1.tgz +0 -0
@@ -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%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%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.87%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%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%2247bfbf88f0%22%2C%22adminXDemoFilename%22%3A%22admin-x-demo.js%22%2C%22adminXDemoHash%22%3A%229c49d06c68%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%2236ec618662%22%2C%22adminXActivitypubFilename%22%3A%22admin-x-activitypub.js%22%2C%22adminXActivitypubHash%22%3A%22b147b1abae%22%7D" />
11
+ <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%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.87%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%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%2208854be47b%22%2C%22adminXDemoFilename%22%3A%22admin-x-demo.js%22%2C%22adminXDemoHash%22%3A%220539de3930%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%22e8dff6adeb%22%2C%22adminXActivitypubFilename%22%3A%22admin-x-activitypub.js%22%2C%22adminXActivitypubHash%22%3A%22a94b140a1a%22%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-0ede59da8efb5e28fa929557f7ff7154.css">
40
- <link integrity="" rel="stylesheet" href="assets/ghost-cc31d867156dbb76ec96cdd93bd39d36.css" title="light">
40
+ <link integrity="" rel="stylesheet" href="assets/ghost-cb98187b163a0d8b42d752ab890e039a.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-4e0f92ca2b1c0850606b912c70852e32.js"></script>
60
- <script src="assets/chunk.42.167fc5e3f3a1c1088f2e.js"></script>
61
- <script src="assets/chunk.524.a032fbf6d6bbe20b0eb3.js"></script>
62
- <script src="assets/ghost-b645099f33022aa37d96a2be8d8ddad1.js"></script>
59
+ <script src="assets/vendor-051d43ec47258b77ffd2fcc21a6d8c50.js"></script>
60
+ <script src="assets/chunk.42.0879e40a4de3db25b177.js"></script>
61
+ <script src="assets/chunk.524.036bdde7e8805a7ab0a7.js"></script>
62
+ <script src="assets/ghost-07422c98448d7259d957d65c6737a274.js"></script>
63
63
  </body>
64
64
  </html>
@@ -2,7 +2,6 @@ const debug = require('@tryghost/debug')('services:routing:controllers:unsubscri
2
2
  const url = require('url');
3
3
  const members = require('../../../../server/services/members');
4
4
  const urlUtils = require('../../../../shared/url-utils');
5
- const labs = require('../../../../shared/labs');
6
5
  const logging = require('@tryghost/logging');
7
6
 
8
7
  module.exports = async function unsubscribeController(req, res) {
@@ -15,7 +14,7 @@ module.exports = async function unsubscribeController(req, res) {
15
14
  return res.end('Email address not found.');
16
15
  }
17
16
 
18
- if (req.method === 'POST' && labs.isSet('listUnsubscribeHeader')) {
17
+ if (req.method === 'POST') {
19
18
  logging.info('[List-Unsubscribe] Received POST unsubscribe for ' + query.uuid + ', newsletter: ' + (query.newsletter ?? 'null') + ', comments: ' + (query.comments ?? 'false'));
20
19
 
21
20
  // Do an actual unsubscribe
@@ -17,7 +17,7 @@
17
17
  border-radius: 6px;
18
18
  border: 1px solid rgb(124 139 154 / 25%);
19
19
  overflow: hidden;
20
- color: inherit;
20
+ color: #222;
21
21
  }
22
22
 
23
23
  .kg-bookmark-content {
@@ -76,6 +76,8 @@ const LIMIT = 15;
76
76
  let sourceParam;
77
77
  let utmSourceParam;
78
78
  let utmMediumParam;
79
+ let referrerSource;
80
+
79
81
  try {
80
82
  // Fetch source/medium from query param
81
83
  const url = new URL(window.location.href);
@@ -83,11 +85,23 @@ const LIMIT = 15;
83
85
  sourceParam = url.searchParams.get('source');
84
86
  utmSourceParam = url.searchParams.get('utm_source');
85
87
  utmMediumParam = url.searchParams.get('utm_medium');
88
+
89
+ referrerSource = refParam || sourceParam || utmSourceParam || null;
90
+
91
+ // if referrerSource is not set, check to see if the url contains a hash like ghost.org/#/portal/signup?ref=ghost and pull the ref from the hash
92
+ if (!referrerSource && url.hash && url.hash.includes('#/portal')) {
93
+ const hashUrl = new URL(window.location.href.replace('/#/portal', ''));
94
+ refParam = hashUrl.searchParams.get('ref');
95
+ sourceParam = hashUrl.searchParams.get('source');
96
+ utmSourceParam = hashUrl.searchParams.get('utm_source');
97
+ utmMediumParam = hashUrl.searchParams.get('utm_medium');
98
+
99
+ referrerSource = refParam || sourceParam || utmSourceParam || null;
100
+ }
86
101
  } catch (e) {
87
102
  console.error('[Member Attribution] Parsing referrer from querystring failed', e);
88
103
  }
89
104
 
90
- const referrerSource = refParam || sourceParam || utmSourceParam || null;
91
105
  const referrerMedium = utmMediumParam || null;
92
106
  const referrerUrl = window.document.referrer || null;
93
107
 
@@ -122,8 +122,26 @@ class ProductsImporter extends BaseImporter {
122
122
  this.dataToImport = this.dataToImport.filter(item => !duplicateProducts.includes(item.id));
123
123
  }
124
124
 
125
+ preventInvalidFree() {
126
+ let invalidFreeProducts = [];
127
+ _.each(this.dataToImport, (product) => {
128
+ // A free product must not have any pricing data (otherwise it wouldn't be free, duh!)
129
+ if (product.type === 'free' && product.currency && product.monthly_price && product.yearly_price) {
130
+ this.problems.push({
131
+ message: 'Entry was not imported and ignored. Detected invalid entry.',
132
+ help: this.modelName,
133
+ context: JSON.stringify({product})
134
+ });
135
+ invalidFreeProducts.push(product.id);
136
+ }
137
+ });
138
+ // ignore invalid free products
139
+ this.dataToImport = this.dataToImport.filter(item => !invalidFreeProducts.includes(item.id));
140
+ }
141
+
125
142
  beforeImport() {
126
143
  this.populatePriceData();
144
+ this.preventInvalidFree();
127
145
  return super.beforeImport();
128
146
  }
129
147
 
@@ -1,6 +1,6 @@
1
1
  const debug = require('@tryghost/debug')('utils:image-size');
2
2
  const sizeOf = require('image-size');
3
- const probeSizeOf = require('probe-image-size');
3
+
4
4
  const url = require('url');
5
5
  const path = require('path');
6
6
  const _ = require('lodash');
@@ -17,13 +17,14 @@ const FETCH_ONLY_FORMATS = [
17
17
  ];
18
18
 
19
19
  class ImageSize {
20
- constructor({config, storage, storageUtils, validator, urlUtils, request}) {
20
+ constructor({config, storage, storageUtils, validator, urlUtils, request, probe}) {
21
21
  this.config = config;
22
22
  this.storage = storage;
23
23
  this.storageUtils = storageUtils;
24
24
  this.validator = validator;
25
25
  this.urlUtils = urlUtils;
26
26
  this.request = request;
27
+ this.probe = probe;
27
28
 
28
29
  this.REQUEST_OPTIONS = {
29
30
  // we need the user-agent, otherwise some https request may fail (e.g. cloudfare)
@@ -82,7 +83,18 @@ class ImageSize {
82
83
  }));
83
84
  }
84
85
 
85
- return probeSizeOf(imageUrl, this.NEEDLE_OPTIONS);
86
+ // wrap probe-image-size in a promise in case it is unresponsive/the timeout itself doesn't work
87
+ return (Promise.race([
88
+ this.probe(imageUrl, this.NEEDLE_OPTIONS),
89
+ new Promise((res, rej) => {
90
+ setTimeout(() => {
91
+ rej(new errors.InternalServerError({
92
+ message: 'Probe unresponsive.',
93
+ code: 'IMAGE_SIZE_URL'
94
+ }));
95
+ }, this.NEEDLE_OPTIONS.response_timeout);
96
+ })
97
+ ]));
86
98
  }
87
99
 
88
100
  // download full image then use image-size to get it's dimensions
@@ -2,11 +2,12 @@ const BlogIcon = require('./BlogIcon');
2
2
  const CachedImageSizeFromUrl = require('./CachedImageSizeFromUrl');
3
3
  const Gravatar = require('./Gravatar');
4
4
  const ImageSize = require('./ImageSize');
5
+ const probe = require('probe-image-size');
5
6
 
6
7
  class ImageUtils {
7
8
  constructor({config, urlUtils, settingsCache, storageUtils, storage, validator, request, cacheStore}) {
8
9
  this.blogIcon = new BlogIcon({config, urlUtils, settingsCache, storageUtils});
9
- this.imageSize = new ImageSize({config, storage, storageUtils, validator, urlUtils, request});
10
+ this.imageSize = new ImageSize({config, storage, storageUtils, validator, urlUtils, request, probe});
10
11
  this.cachedImageSizeFromUrl = new CachedImageSizeFromUrl({
11
12
  getImageSizeFromUrl: this.imageSize.getImageSizeFromUrl.bind(this.imageSize),
12
13
  cache: cacheStore
@@ -211,6 +211,9 @@ class CustomRedirectsAPI {
211
211
  */
212
212
  async setFromFilePath(filePath, ext = '.json') {
213
213
  const redirectsFilePath = await this.getRedirectsFilePath();
214
+ const content = await readRedirectsFile(filePath);
215
+ const parsed = parseRedirectsFile(content, ext);
216
+ this.validate(parsed);
214
217
 
215
218
  if (redirectsFilePath) {
216
219
  const backupRedirectsPath = this.getBackupFilePath(redirectsFilePath);
@@ -223,10 +226,6 @@ class CustomRedirectsAPI {
223
226
  await fs.move(redirectsFilePath, backupRedirectsPath);
224
227
  }
225
228
 
226
- const content = await readRedirectsFile(filePath);
227
- const parsed = parseRedirectsFile(content, ext);
228
- this.validate(parsed);
229
-
230
229
  if (ext === '.json') {
231
230
  await fs.writeFile(this.createRedirectsFilePath('.json'), JSON.stringify(parsed), 'utf-8');
232
231
  } else if (ext === '.yaml') {
@@ -1,7 +1,6 @@
1
1
  const _ = require('lodash');
2
2
  const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
- const {isSafePattern} = require('redos-detector');
5
4
 
6
5
  const messages = {
7
6
  redirectsWrongFormat: 'Incorrect redirects file format.',
@@ -34,35 +33,18 @@ const validate = (redirects) => {
34
33
  if (!redirect.from || !redirect.to) {
35
34
  throw new errors.ValidationError({
36
35
  message: tpl(messages.redirectsWrongFormat),
36
+ context: redirect,
37
37
  help: tpl(messages.redirectsHelp)
38
38
  });
39
39
  }
40
40
 
41
- // Ensure valid regex
42
41
  try {
42
+ // each 'from' property should be a valid RegExp string
43
43
  new RegExp(redirect.from);
44
44
  } catch (error) {
45
45
  throw new errors.ValidationError({
46
46
  message: tpl(messages.invalidRedirectsFromRegex),
47
- errorDetails: {
48
- redirect,
49
- invalid: true
50
- },
51
- help: tpl(messages.redirectsHelp)
52
- });
53
- }
54
-
55
- // Ensure safe regex
56
- const analysis = isSafePattern(redirect.from);
57
-
58
- if (analysis.safe === false) {
59
- throw new errors.ValidationError({
60
- message: tpl(messages.invalidRedirectsFromRegex),
61
- errorDetails: {
62
- redirect,
63
- unsafe: true,
64
- reason: analysis.error
65
- },
47
+ context: redirect,
66
48
  help: tpl(messages.redirectsHelp)
67
49
  });
68
50
  }
@@ -43,7 +43,6 @@ class RecommendationServiceWrapper {
43
43
  return;
44
44
  }
45
45
 
46
- const labs = require('../../../shared/labs');
47
46
  const config = require('../../../shared/config');
48
47
  const urlUtils = require('../../../shared/url-utils');
49
48
  const models = require('../../models');
@@ -146,10 +145,8 @@ class RecommendationServiceWrapper {
146
145
  service: this.incomingRecommendationService
147
146
  });
148
147
 
149
- if (labs.isSet('recommendations')) {
150
- this.service.init().catch(logging.error);
151
- this.incomingRecommendationService.init().catch(logging.error);
152
- }
148
+ this.service.init().catch(logging.error);
149
+ this.incomingRecommendationService.init().catch(logging.error);
153
150
 
154
151
  const PATH_SUFFIX = '/.well-known/recommendations.json';
155
152
 
@@ -169,12 +166,10 @@ class RecommendationServiceWrapper {
169
166
 
170
167
  // Listen for incoming webmentions
171
168
  DomainEvents.subscribe(MentionCreatedEvent, async (event) => {
172
- if (labs.isSet('recommendations')) {
173
- // Check if this is a recommendation
174
- if (event.data.mention.verified && isRecommendationUrl(event.data.mention.source)) {
175
- logging.info('[INCOMING RECOMMENDATION] Received recommendation from ' + event.data.mention.source);
176
- await this.incomingRecommendationService.sendRecommendationEmail(event.data.mention);
177
- }
169
+ // Check if this is a recommendation
170
+ if (event.data.mention.verified && isRecommendationUrl(event.data.mention.source)) {
171
+ logging.info('[INCOMING RECOMMENDATION] Received recommendation from ' + event.data.mention.source);
172
+ await this.incomingRecommendationService.sendRecommendationEmail(event.data.mention);
178
173
  }
179
174
  });
180
175
  }
@@ -19,10 +19,6 @@ const GA_FEATURES = [
19
19
  'themeErrorsNotification',
20
20
  'outboundLinkTagging',
21
21
  'announcementBar',
22
- 'signupForm',
23
- 'recommendations',
24
- 'listUnsubscribeHeader',
25
- 'filterEmailDisabled',
26
22
  'newEmailAddresses',
27
23
  'internalLinking'
28
24
  ];