ghost 5.51.2 → 5.52.1

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 (149) hide show
  1. package/components/{tryghost-adapter-cache-memory-ttl-5.51.2.tgz → tryghost-adapter-cache-memory-ttl-5.52.1.tgz} +0 -0
  2. package/components/{tryghost-adapter-cache-redis-5.51.2.tgz → tryghost-adapter-cache-redis-5.52.1.tgz} +0 -0
  3. package/components/tryghost-adapter-manager-5.52.1.tgz +0 -0
  4. package/components/{tryghost-announcement-bar-settings-5.51.2.tgz → tryghost-announcement-bar-settings-5.52.1.tgz} +0 -0
  5. package/components/{tryghost-api-framework-5.51.2.tgz → tryghost-api-framework-5.52.1.tgz} +0 -0
  6. package/components/{tryghost-api-version-compatibility-service-5.51.2.tgz → tryghost-api-version-compatibility-service-5.52.1.tgz} +0 -0
  7. package/components/{tryghost-audience-feedback-5.51.2.tgz → tryghost-audience-feedback-5.52.1.tgz} +0 -0
  8. package/components/tryghost-bootstrap-socket-5.52.1.tgz +0 -0
  9. package/components/tryghost-collections-5.52.1.tgz +0 -0
  10. package/components/tryghost-constants-5.52.1.tgz +0 -0
  11. package/components/tryghost-custom-theme-settings-service-5.52.1.tgz +0 -0
  12. package/components/{tryghost-data-generator-5.51.2.tgz → tryghost-data-generator-5.52.1.tgz} +0 -0
  13. package/components/tryghost-domain-events-5.52.1.tgz +0 -0
  14. package/components/{tryghost-dynamic-routing-events-5.51.2.tgz → tryghost-dynamic-routing-events-5.52.1.tgz} +0 -0
  15. package/components/tryghost-email-analytics-provider-mailgun-5.52.1.tgz +0 -0
  16. package/components/tryghost-email-analytics-service-5.52.1.tgz +0 -0
  17. package/components/tryghost-email-content-generator-5.52.1.tgz +0 -0
  18. package/components/{tryghost-email-events-5.51.2.tgz → tryghost-email-events-5.52.1.tgz} +0 -0
  19. package/components/{tryghost-email-service-5.51.2.tgz → tryghost-email-service-5.52.1.tgz} +0 -0
  20. package/components/{tryghost-email-suppression-list-5.51.2.tgz → tryghost-email-suppression-list-5.52.1.tgz} +0 -0
  21. package/components/{tryghost-event-aware-cache-wrapper-5.51.2.tgz → tryghost-event-aware-cache-wrapper-5.52.1.tgz} +0 -0
  22. package/components/tryghost-express-dynamic-redirects-5.52.1.tgz +0 -0
  23. package/components/{tryghost-external-media-inliner-5.51.2.tgz → tryghost-external-media-inliner-5.52.1.tgz} +0 -0
  24. package/components/{tryghost-extract-api-key-5.51.2.tgz → tryghost-extract-api-key-5.52.1.tgz} +0 -0
  25. package/components/tryghost-html-to-plaintext-5.52.1.tgz +0 -0
  26. package/components/tryghost-i18n-5.52.1.tgz +0 -0
  27. package/components/{tryghost-importer-handler-content-files-5.51.2.tgz → tryghost-importer-handler-content-files-5.52.1.tgz} +0 -0
  28. package/components/tryghost-importer-revue-5.52.1.tgz +0 -0
  29. package/components/tryghost-in-memory-repository-5.52.1.tgz +0 -0
  30. package/components/tryghost-job-manager-5.52.1.tgz +0 -0
  31. package/components/{tryghost-link-redirects-5.51.2.tgz → tryghost-link-redirects-5.52.1.tgz} +0 -0
  32. package/components/{tryghost-link-replacer-5.51.2.tgz → tryghost-link-replacer-5.52.1.tgz} +0 -0
  33. package/components/{tryghost-link-tracking-5.51.2.tgz → tryghost-link-tracking-5.52.1.tgz} +0 -0
  34. package/components/tryghost-magic-link-5.52.1.tgz +0 -0
  35. package/components/{tryghost-mailgun-client-5.51.2.tgz → tryghost-mailgun-client-5.52.1.tgz} +0 -0
  36. package/components/{tryghost-member-attribution-5.51.2.tgz → tryghost-member-attribution-5.52.1.tgz} +0 -0
  37. package/components/tryghost-member-events-5.52.1.tgz +0 -0
  38. package/components/tryghost-members-api-5.52.1.tgz +0 -0
  39. package/components/tryghost-members-csv-5.52.1.tgz +0 -0
  40. package/components/tryghost-members-events-service-5.52.1.tgz +0 -0
  41. package/components/tryghost-members-importer-5.52.1.tgz +0 -0
  42. package/components/tryghost-members-offers-5.52.1.tgz +0 -0
  43. package/components/tryghost-members-payments-5.52.1.tgz +0 -0
  44. package/components/tryghost-members-ssr-5.52.1.tgz +0 -0
  45. package/components/tryghost-members-stripe-service-5.52.1.tgz +0 -0
  46. package/components/{tryghost-mentions-email-report-5.51.2.tgz → tryghost-mentions-email-report-5.52.1.tgz} +0 -0
  47. package/components/{tryghost-milestones-5.51.2.tgz → tryghost-milestones-5.52.1.tgz} +0 -0
  48. package/components/tryghost-minifier-5.52.1.tgz +0 -0
  49. package/components/tryghost-mw-api-version-mismatch-5.52.1.tgz +0 -0
  50. package/components/{tryghost-mw-cache-control-5.51.2.tgz → tryghost-mw-cache-control-5.52.1.tgz} +0 -0
  51. package/components/tryghost-mw-error-handler-5.52.1.tgz +0 -0
  52. package/components/tryghost-mw-session-from-token-5.52.1.tgz +0 -0
  53. package/components/tryghost-mw-update-user-last-seen-5.52.1.tgz +0 -0
  54. package/components/{tryghost-mw-version-match-5.51.2.tgz → tryghost-mw-version-match-5.52.1.tgz} +0 -0
  55. package/components/tryghost-mw-vhost-5.52.1.tgz +0 -0
  56. package/components/{tryghost-oembed-service-5.51.2.tgz → tryghost-oembed-service-5.52.1.tgz} +0 -0
  57. package/components/tryghost-package-json-5.52.1.tgz +0 -0
  58. package/components/{tryghost-post-revisions-5.51.2.tgz → tryghost-post-revisions-5.52.1.tgz} +0 -0
  59. package/components/tryghost-posts-service-5.52.1.tgz +0 -0
  60. package/components/{tryghost-referrers-5.51.2.tgz → tryghost-referrers-5.52.1.tgz} +0 -0
  61. package/components/tryghost-security-5.52.1.tgz +0 -0
  62. package/components/tryghost-session-service-5.52.1.tgz +0 -0
  63. package/components/tryghost-settings-path-manager-5.52.1.tgz +0 -0
  64. package/components/{tryghost-slack-notifications-5.51.2.tgz → tryghost-slack-notifications-5.52.1.tgz} +0 -0
  65. package/components/{tryghost-staff-service-5.51.2.tgz → tryghost-staff-service-5.52.1.tgz} +0 -0
  66. package/components/{tryghost-stats-service-5.51.2.tgz → tryghost-stats-service-5.52.1.tgz} +0 -0
  67. package/components/{tryghost-tiers-5.51.2.tgz → tryghost-tiers-5.52.1.tgz} +0 -0
  68. package/components/tryghost-update-check-service-5.52.1.tgz +0 -0
  69. package/components/tryghost-verification-trigger-5.52.1.tgz +0 -0
  70. package/components/{tryghost-version-notifications-data-service-5.51.2.tgz → tryghost-version-notifications-data-service-5.52.1.tgz} +0 -0
  71. package/components/{tryghost-webmentions-5.51.2.tgz → tryghost-webmentions-5.52.1.tgz} +0 -0
  72. package/core/built/admin/assets/chunk.143.1106d36e1343f88742c0.js +35 -0
  73. package/core/built/admin/assets/chunk.178.3ab27e3161da629a8235.js +11 -0
  74. package/core/built/admin/assets/{chunk.976.c3f393d6685c89bbaaca.js → chunk.658.4548e702d296a29726d6.js} +65 -58
  75. package/core/built/admin/assets/{ghost-e6482a0a19b228a9b9e71a9c5ea134ba.css → ghost-a99d4bf5c4a056624c1c41bbf7c06532.css} +1 -1
  76. package/core/built/admin/assets/{ghost-eea3f9036ed762e933a3a007d4d4909b.js → ghost-b73f018429dcbc18d6d47037187d6a81.js} +3671 -3670
  77. package/core/built/admin/assets/{ghost-dark-2ec2e32362fa867723379a286fd0f0bd.css → ghost-dark-179670faac46fe5fe58d9da626e1c0a8.css} +1 -1
  78. package/core/built/admin/assets/{vendor-bc3bd83c56b822ae56193b0d231189c0.js → vendor-2e4b0ccaa065f6709f955c29ec9e18a3.js} +1 -1
  79. package/core/built/admin/index.html +6 -6
  80. package/core/frontend/helpers/ghost_head.js +2 -1
  81. package/core/frontend/src/cards/css/signup.css +4 -0
  82. package/core/server/api/endpoints/collections.js +19 -76
  83. package/core/server/api/endpoints/pages.js +1 -1
  84. package/core/server/api/endpoints/posts.js +25 -4
  85. package/core/server/api/endpoints/utils/serializers/input/pages.js +1 -1
  86. package/core/server/api/endpoints/utils/serializers/output/collections.js +10 -0
  87. package/core/server/api/endpoints/utils/serializers/output/index.js +4 -0
  88. package/core/server/api/endpoints/utils/serializers/output/mappers/collection-posts.js +19 -0
  89. package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +10 -0
  90. package/core/server/api/endpoints/utils/serializers/output/utils/clean.js +1 -0
  91. package/core/server/data/migrations/versions/5.21/2022-10-26-04-50-member-subscription-created-batch-id.js +7 -2
  92. package/core/server/data/migrations/versions/5.3/2022-07-06-09-26-add-ghost-explore-integration-api-key.js +6 -2
  93. package/core/server/data/migrations/versions/5.40/2023-03-21-19-02-add-self-serve-integration-api-key.js +6 -2
  94. package/core/server/data/migrations/versions/5.41/2023-03-27-17-51-fix-self-serve-integration-api-key-type.js +3 -1
  95. package/core/server/models/post.js +3 -2
  96. package/core/server/services/collections/PostsRepository.js +39 -0
  97. package/core/server/services/collections/index.js +34 -23
  98. package/core/server/services/segment/DomainEventsAnalytics.js +26 -0
  99. package/core/server/services/settings/SettingsBREADService.js +7 -0
  100. package/core/server/services/slack.js +9 -4
  101. package/core/server/web/api/endpoints/admin/middleware.js +1 -1
  102. package/core/server/web/api/endpoints/admin/routes.js +1 -2
  103. package/core/shared/config/helpers.js +1 -1
  104. package/core/shared/config/utils.js +2 -2
  105. package/core/shared/labs.js +5 -1
  106. package/package.json +147 -146
  107. package/yarn.lock +715 -650
  108. package/components/tryghost-adapter-manager-5.51.2.tgz +0 -0
  109. package/components/tryghost-bootstrap-socket-5.51.2.tgz +0 -0
  110. package/components/tryghost-collections-5.51.2.tgz +0 -0
  111. package/components/tryghost-constants-5.51.2.tgz +0 -0
  112. package/components/tryghost-custom-theme-settings-service-5.51.2.tgz +0 -0
  113. package/components/tryghost-domain-events-5.51.2.tgz +0 -0
  114. package/components/tryghost-email-analytics-provider-mailgun-5.51.2.tgz +0 -0
  115. package/components/tryghost-email-analytics-service-5.51.2.tgz +0 -0
  116. package/components/tryghost-email-content-generator-5.51.2.tgz +0 -0
  117. package/components/tryghost-express-dynamic-redirects-5.51.2.tgz +0 -0
  118. package/components/tryghost-html-to-plaintext-5.51.2.tgz +0 -0
  119. package/components/tryghost-i18n-5.51.2.tgz +0 -0
  120. package/components/tryghost-importer-revue-5.51.2.tgz +0 -0
  121. package/components/tryghost-in-memory-repository-5.51.2.tgz +0 -0
  122. package/components/tryghost-job-manager-5.51.2.tgz +0 -0
  123. package/components/tryghost-magic-link-5.51.2.tgz +0 -0
  124. package/components/tryghost-member-events-5.51.2.tgz +0 -0
  125. package/components/tryghost-members-api-5.51.2.tgz +0 -0
  126. package/components/tryghost-members-csv-5.51.2.tgz +0 -0
  127. package/components/tryghost-members-events-service-5.51.2.tgz +0 -0
  128. package/components/tryghost-members-importer-5.51.2.tgz +0 -0
  129. package/components/tryghost-members-offers-5.51.2.tgz +0 -0
  130. package/components/tryghost-members-payments-5.51.2.tgz +0 -0
  131. package/components/tryghost-members-ssr-5.51.2.tgz +0 -0
  132. package/components/tryghost-members-stripe-service-5.51.2.tgz +0 -0
  133. package/components/tryghost-minifier-5.51.2.tgz +0 -0
  134. package/components/tryghost-mw-api-version-mismatch-5.51.2.tgz +0 -0
  135. package/components/tryghost-mw-error-handler-5.51.2.tgz +0 -0
  136. package/components/tryghost-mw-session-from-token-5.51.2.tgz +0 -0
  137. package/components/tryghost-mw-update-user-last-seen-5.51.2.tgz +0 -0
  138. package/components/tryghost-mw-vhost-5.51.2.tgz +0 -0
  139. package/components/tryghost-package-json-5.51.2.tgz +0 -0
  140. package/components/tryghost-posts-service-5.51.2.tgz +0 -0
  141. package/components/tryghost-security-5.51.2.tgz +0 -0
  142. package/components/tryghost-session-service-5.51.2.tgz +0 -0
  143. package/components/tryghost-settings-path-manager-5.51.2.tgz +0 -0
  144. package/components/tryghost-update-check-service-5.51.2.tgz +0 -0
  145. package/components/tryghost-verification-trigger-5.51.2.tgz +0 -0
  146. package/core/built/admin/assets/chunk.143.a99b8ed3cb168269b0e3.js +0 -35
  147. package/core/built/admin/assets/chunk.178.707e9fd52fc7ea86b76b.js +0 -10
  148. package/core/server/services/collections/built-in-collections.js +0 -8
  149. /package/core/built/admin/assets/{chunk.976.c3f393d6685c89bbaaca.js.LICENSE.txt → chunk.658.4548e702d296a29726d6.js.LICENSE.txt} +0 -0
@@ -14093,4 +14093,4 @@ return void 0===n&&(n=(0,t.createStorage)(null,(()=>!1)),r.set(e,n)),n}dirtyStor
14093
14093
  r&&(0,t.setValue)(r,null)}constructor(e){o(this,"storages",new WeakMap),o(this,"vals",void 0),this.vals=new WeakSet(e)}has(e){return(0,t.getValue)(this.storageFor(e)),this.vals.has(e)}add(e){return this.vals.add(e),this.dirtyStorageFor(e),this}delete(e){return this.dirtyStorageFor(e),this.vals.delete(e)}get[i](){return this.vals[Symbol.toStringTag]}}e.TrackedWeakSet=s,Object.setPrototypeOf(s.prototype,WeakSet.prototype)})),define("tracked-built-ins/index",["exports","tracked-built-ins/-private/decorator","tracked-built-ins/-private/array","tracked-built-ins/-private/object","tracked-built-ins/-private/map","tracked-built-ins/-private/set"],(function(e,t,r,n,i,o){"use strict"
14094
14094
  Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"TrackedArray",{enumerable:!0,get:function(){return r.default}}),Object.defineProperty(e,"TrackedMap",{enumerable:!0,get:function(){return i.TrackedMap}}),Object.defineProperty(e,"TrackedObject",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(e,"TrackedSet",{enumerable:!0,get:function(){return o.TrackedSet}}),Object.defineProperty(e,"TrackedWeakMap",{enumerable:!0,get:function(){return i.TrackedWeakMap}}),Object.defineProperty(e,"TrackedWeakSet",{enumerable:!0,get:function(){return o.TrackedWeakSet}}),Object.defineProperty(e,"tracked",{enumerable:!0,get:function(){return t.default}})}))
14095
14095
 
14096
- //# sourceMappingURL=vendor-bc3bd83c56b822ae56193b0d231189c0.map
14096
+ //# sourceMappingURL=vendor-2e4b0ccaa065f6709f955c29ec9e18a3.map
@@ -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.51%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22ember-websockets%22%3A%7B%22socketIO%22%3Atrue%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.52%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22ember-websockets%22%3A%7B%22socketIO%22%3Atrue%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-e6482a0a19b228a9b9e71a9c5ea134ba.css" title="light">
40
+ <link integrity="" rel="stylesheet" href="assets/ghost-a99d4bf5c4a056624c1c41bbf7c06532.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-bc3bd83c56b822ae56193b0d231189c0.js"></script>
60
- <script src="assets/chunk.976.c3f393d6685c89bbaaca.js"></script>
61
- <script src="assets/chunk.143.a99b8ed3cb168269b0e3.js"></script>
62
- <script src="assets/ghost-eea3f9036ed762e933a3a007d4d4909b.js"></script>
59
+ <script src="assets/vendor-2e4b0ccaa065f6709f955c29ec9e18a3.js"></script>
60
+ <script src="assets/chunk.658.4548e702d296a29726d6.js"></script>
61
+ <script src="assets/chunk.143.1106d36e1343f88742c0.js"></script>
62
+ <script src="assets/ghost-b73f018429dcbc18d6d47037187d6a81.js"></script>
63
63
  </body>
64
64
  </html>
@@ -89,8 +89,9 @@ function getSearchHelper(frontendKey) {
89
89
 
90
90
  function getAnnouncementBarHelper(data) {
91
91
  const preview = data?.site?._preview;
92
+ const isFilled = settingsCache.get('announcement_content') && settingsCache.get('announcement_visibility').length;
92
93
 
93
- if (!settingsCache.get('announcement_content') && !preview) {
94
+ if (!isFilled && !preview) {
94
95
  return '';
95
96
  }
96
97
 
@@ -69,6 +69,10 @@
69
69
  object-position: center;
70
70
  }
71
71
 
72
+ .kg-background-size-contain .kg-signup-card-image {
73
+ object-fit: contain;
74
+ }
75
+
72
76
  .kg-signup-card-image.kg-style-accent {
73
77
  background-color: var(--ghost-accent-color);
74
78
  }
@@ -25,6 +25,25 @@ module.exports = {
25
25
  }
26
26
  },
27
27
 
28
+ browsePosts: {
29
+ headers: {
30
+ cacheInvalidate: false
31
+ },
32
+ data: [
33
+ 'id'
34
+ ],
35
+ options: [
36
+ 'limit',
37
+ 'page'
38
+ ],
39
+ permissions: {
40
+ method: 'browse'
41
+ },
42
+ query(frame) {
43
+ return collectionsService.api.getAllPosts(frame.data.id, frame.options);
44
+ }
45
+ },
46
+
28
47
  read: {
29
48
  headers: {
30
49
  cacheInvalidate: false
@@ -96,47 +115,6 @@ module.exports = {
96
115
  }
97
116
  },
98
117
 
99
- addPost: {
100
- statusCode: 200,
101
- headers: {
102
- cacheInvalidate: false
103
- },
104
- options: [
105
- 'id'
106
- ],
107
- data: [
108
- 'post_id'
109
- ],
110
- validation: {
111
- options: {
112
- id: {
113
- required: true
114
- }
115
- },
116
- data: {
117
- post_id: {
118
- required: true
119
- }
120
- }
121
- },
122
- permissions: {
123
- method: 'edit'
124
- },
125
- async query(frame) {
126
- const collectionPost = await collectionsService.api.addPostToCollection(frame.options.id, {
127
- id: frame.data.posts[0].id
128
- });
129
-
130
- if (!collectionPost) {
131
- throw new errors.NotFoundError({
132
- message: tpl(messages.collectionNotFound)
133
- });
134
- }
135
-
136
- return collectionPost;
137
- }
138
- },
139
-
140
118
  destroy: {
141
119
  statusCode: 204,
142
120
  headers: {
@@ -156,40 +134,5 @@ module.exports = {
156
134
  async query(frame) {
157
135
  return await collectionsService.api.destroy(frame.options.id);
158
136
  }
159
- },
160
-
161
- destroyPost: {
162
- statusCode: 200,
163
- headers: {
164
- cacheInvalidate: true
165
- },
166
- options: [
167
- 'id',
168
- 'post_id'
169
- ],
170
- validation: {
171
- options: {
172
- id: {
173
- required: true
174
- },
175
- post_id: {
176
- required: true
177
- }
178
- }
179
- },
180
- permissions: {
181
- method: 'edit'
182
- },
183
- async query(frame) {
184
- const collection = await collectionsService.api.removePostFromCollection(frame.options.id, frame.options.post_id);
185
-
186
- if (!collection) {
187
- throw new errors.NotFoundError({
188
- message: tpl(messages.collectionNotFound)
189
- });
190
- }
191
-
192
- return collection;
193
- }
194
137
  }
195
138
  };
@@ -2,7 +2,7 @@ const models = require('../../models');
2
2
  const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
4
  const getPostServiceInstance = require('../../services/posts/posts-service');
5
- const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.paid_conversions'];
5
+ const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.paid_conversions', 'post_revisions', 'post_revisions.author'];
6
6
  const UNSAFE_ATTRS = ['status', 'authors', 'visibility'];
7
7
 
8
8
  const messages = {
@@ -1,3 +1,4 @@
1
+ const urlUtils = require('../../../shared/url-utils');
1
2
  const models = require('../../models');
2
3
  const getPostServiceInstance = require('../../services/posts/posts-service');
3
4
  const allowedIncludes = [
@@ -21,6 +22,22 @@ const unsafeAttrs = ['status', 'authors', 'visibility'];
21
22
 
22
23
  const postsService = getPostServiceInstance();
23
24
 
25
+ /**
26
+ * @param {string} event
27
+ */
28
+ function getCacheHeaderFromEventString(event, dto) {
29
+ if (event === 'published_updated' || event === 'unpublished') {
30
+ return true;
31
+ }
32
+ if (event === 'scheduled_updated' || event === 'draft_updated') {
33
+ return {
34
+ value: urlUtils.urlFor({
35
+ relativeUrl: urlUtils.urlJoin('/p', dto.uuid, '/')
36
+ })
37
+ };
38
+ }
39
+ }
40
+
24
41
  module.exports = {
25
42
  docName: 'posts',
26
43
  browse: {
@@ -31,6 +48,7 @@ module.exports = {
31
48
  'include',
32
49
  'filter',
33
50
  'fields',
51
+ 'collection',
34
52
  'formats',
35
53
  'limit',
36
54
  'order',
@@ -52,7 +70,7 @@ module.exports = {
52
70
  unsafeAttrs: unsafeAttrs
53
71
  },
54
72
  query(frame) {
55
- return models.Post.findPage(frame.options);
73
+ return postsService.browsePosts(frame.options);
56
74
  }
57
75
  },
58
76
 
@@ -162,6 +180,7 @@ module.exports = {
162
180
 
163
181
  edit: {
164
182
  headers: {
183
+ /** @type {boolean | {value: string}} */
165
184
  cacheInvalidate: false
166
185
  },
167
186
  options: [
@@ -194,9 +213,11 @@ module.exports = {
194
213
  unsafeAttrs: unsafeAttrs
195
214
  },
196
215
  async query(frame) {
197
- let model = await postsService.editPost(frame);
198
-
199
- this.headers.cacheInvalidate = postsService.handleCacheInvalidation(model);
216
+ let model = await postsService.editPost(frame, {
217
+ eventHandler: (event, dto) => {
218
+ this.headers.cacheInvalidate = getCacheHeaderFromEventString(event, dto);
219
+ }
220
+ });
200
221
 
201
222
  return model;
202
223
  }
@@ -24,7 +24,7 @@ function defaultRelations(frame) {
24
24
  return false;
25
25
  }
26
26
 
27
- frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.paid_conversions'];
27
+ frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'tiers', 'count.signups', 'count.paid_conversions', 'post_revisions', 'post_revisions.author'];
28
28
  }
29
29
 
30
30
  function setDefaultOrder(frame) {
@@ -0,0 +1,10 @@
1
+ const collectionPostsMapper = require('./mappers/collection-posts');
2
+
3
+ module.exports = {
4
+ browsePosts(response, apiConfig, frame) {
5
+ frame.response = {
6
+ collection_posts: response.data.map(model => collectionPostsMapper(model)),
7
+ meta: response.meta
8
+ };
9
+ }
10
+ };
@@ -21,6 +21,10 @@ module.exports = {
21
21
  return require('./authentication');
22
22
  },
23
23
 
24
+ get collections() {
25
+ return require('./collections');
26
+ },
27
+
24
28
  get db() {
25
29
  return require('./db');
26
30
  },
@@ -0,0 +1,19 @@
1
+ /**
2
+ *
3
+ * @param {import('@tryghost/collections').CollectionPostListItemDTO[]} collectionPosts[]
4
+ *
5
+ * @returns {SerializedCollectionPost}
6
+ */
7
+ const mapper = (collectionPost) => {
8
+ return collectionPost;
9
+ };
10
+
11
+ /**
12
+ * @typedef {Object} SerializedCollectionPost
13
+ * @prop {string} id
14
+ * @prop {string} title
15
+ * @prop {string} slug
16
+ * @prop {string} feature_image
17
+ */
18
+
19
+ module.exports = mapper;
@@ -46,6 +46,16 @@ module.exports = async (model, frame, options = {}) => {
46
46
 
47
47
  extraAttrs.forPost(frame.options, model, jsonModel);
48
48
 
49
+ const defaultFormats = ['html'];
50
+ const formatsToKeep = frame.options.formats || frame.options.columns || defaultFormats;
51
+
52
+ // Iterate over all known formats, and if they are not in the keep list, remove them
53
+ _.each(['mobiledoc', 'lexical', 'html', 'plaintext'], function (format) {
54
+ if (formatsToKeep.indexOf(format) === -1) {
55
+ delete jsonModel[format];
56
+ }
57
+ });
58
+
49
59
  // Attach tiers to custom nql visibility filter
50
60
  if (jsonModel.visibility) {
51
61
  if (['members', 'public'].includes(jsonModel.visibility) && jsonModel.tiers) {
@@ -78,6 +78,7 @@ const author = (attrs, frame) => {
78
78
  const post = (attrs, frame) => {
79
79
  const columns = frame && frame.options && frame.options.columns || null;
80
80
  const fields = frame && frame.original && frame.original.query && frame.original.query.fields || null;
81
+
81
82
  if (localUtils.isContentAPI(frame)) {
82
83
  delete attrs.status;
83
84
  delete attrs.email_only;
@@ -3,6 +3,7 @@ const logging = require('@tryghost/logging');
3
3
  const ObjectId = require('bson-objectid').default;
4
4
  const {createTransactionalMigration} = require('../../utils');
5
5
  const DatabaseInfo = require('@tryghost/database-info');
6
+ const GhostError = require('@tryghost/errors').GhostError;
6
7
 
7
8
  // This migration links together members_created_events and members_subscription_created_events
8
9
 
@@ -46,7 +47,9 @@ module.exports = createTransactionalMigration(
46
47
 
47
48
  if (response1[0] !== 0) {
48
49
  logging.error(`Inserted ${response1[0]} members_created_events, expected 0`);
49
- throw new Error('Rolling back');
50
+ throw new GhostError({
51
+ message: 'Rolling back'
52
+ });
50
53
  }
51
54
 
52
55
  const response2 = await knex('members_subscription_created_events').insert(batch.map((r) => {
@@ -61,7 +64,9 @@ module.exports = createTransactionalMigration(
61
64
 
62
65
  if (response2[0] !== 0) {
63
66
  logging.error(`Inserted ${response1[0]} members_subscription_created_events, expected 0`);
64
- throw new Error('Rolling back');
67
+ throw new GhostError({
68
+ message: 'Rolling back'
69
+ });
65
70
  }
66
71
  }
67
72
  logging.info(`Linked ${rows.length} members_created_events and members_subscription_created_events`);
@@ -14,7 +14,9 @@ module.exports = createTransactionalMigration(
14
14
  }).first();
15
15
 
16
16
  if (!integration) {
17
- throw new InternalServerError('Could not find Ghost Explore Integration');
17
+ throw new InternalServerError({
18
+ message: 'Could not find Ghost Explore Integration'
19
+ });
18
20
  }
19
21
 
20
22
  const role = await knex('roles').where({
@@ -22,7 +24,9 @@ module.exports = createTransactionalMigration(
22
24
  }).first();
23
25
 
24
26
  if (!role) {
25
- throw new InternalServerError('Could not find Ghost Explore Integration Role');
27
+ throw new InternalServerError({
28
+ message: 'Could not find Ghost Explore Integration Role'
29
+ });
26
30
  }
27
31
 
28
32
  const existingKey = await knex('api_keys').where({
@@ -14,7 +14,9 @@ module.exports = createTransactionalMigration(
14
14
  }).first();
15
15
 
16
16
  if (!integration) {
17
- throw new InternalServerError('Could not find "Self-Serve Migration Integration"');
17
+ throw new InternalServerError({
18
+ message: 'Could not find "Self-Serve Migration Integration"'
19
+ });
18
20
  }
19
21
 
20
22
  const role = await knex('roles').where({
@@ -22,7 +24,9 @@ module.exports = createTransactionalMigration(
22
24
  }).first();
23
25
 
24
26
  if (!role) {
25
- throw new InternalServerError('Could not find "Self-Serve Migration Integration" Role');
27
+ throw new InternalServerError({
28
+ message: 'Could not find "Self-Serve Migration Integration" Role'
29
+ });
26
30
  }
27
31
 
28
32
  const existingKey = await knex('api_keys').where({
@@ -12,7 +12,9 @@ module.exports = module.exports = createTransactionalMigration(
12
12
  }).first();
13
13
 
14
14
  if (!integration) {
15
- throw new InternalServerError('Could not find "Self-Serve Migration Integration"');
15
+ throw new InternalServerError({
16
+ message: 'Could not find "Self-Serve Migration Integration"'
17
+ });
16
18
  }
17
19
 
18
20
  const existingKey = await knex('api_keys').where({
@@ -997,6 +997,9 @@ Post = ghostBookshelf.Model.extend({
997
997
  /**
998
998
  * If the `formats` option is not used, we return `html` be default.
999
999
  * Otherwise we return what is requested e.g. `?formats=mobiledoc,plaintext`
1000
+ *
1001
+ * This method is only used by the raw-knex plugin.
1002
+ * We have moved the logic into the serializers for the API.
1000
1003
  */
1001
1004
  formatsToJSON: function formatsToJSON(attrs, options) {
1002
1005
  const defaultFormats = ['html'];
@@ -1016,8 +1019,6 @@ Post = ghostBookshelf.Model.extend({
1016
1019
  const options = Post.filterOptions(unfilteredOptions, 'toJSON');
1017
1020
  let attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);
1018
1021
 
1019
- attrs = this.formatsToJSON(attrs, options);
1020
-
1021
1022
  // CASE: never expose the mobiledoc revisions
1022
1023
  delete attrs.mobiledoc_revisions;
1023
1024
 
@@ -0,0 +1,39 @@
1
+ class PostsRepository {
2
+ constructor({models, browsePostsAPI}) {
3
+ this.models = models;
4
+ this.browsePostsAPI = browsePostsAPI;
5
+ }
6
+
7
+ async getAll({filter}) {
8
+ const posts = await this.models.Post.findAll({
9
+ // @NOTE: enforce "post" type to avoid ever fetching pages
10
+ filter: `(${filter})+type:post`
11
+ });
12
+
13
+ return posts.toJSON();
14
+ }
15
+
16
+ async getBulk(ids) {
17
+ const response = await this.browsePostsAPI({
18
+ options: {
19
+ filter: `id:[${ids.join(',')}]+type:post`
20
+ }
21
+ });
22
+
23
+ return response.posts;
24
+ }
25
+ }
26
+
27
+ module.exports = PostsRepository;
28
+
29
+ module.exports.getInstance = () => {
30
+ const models = require('../../models');
31
+ const browsePostsAPI = async (options) => {
32
+ const rawPosts = await require('../../api/').endpoints.posts.browse.query(options);
33
+ await require('../../api/').endpoints.serializers.output.posts.all(rawPosts, {}, options);
34
+
35
+ return options.response;
36
+ };
37
+
38
+ return new PostsRepository({models, browsePostsAPI});
39
+ };
@@ -2,28 +2,51 @@ const {
2
2
  CollectionsService,
3
3
  CollectionsRepositoryInMemory
4
4
  } = require('@tryghost/collections');
5
+ const labs = require('../../../shared/labs');
5
6
 
6
7
  class CollectionsServiceWrapper {
7
8
  /** @type {CollectionsService} */
8
9
  api;
9
10
 
10
11
  constructor() {
11
- const models = require('../../models');
12
- const events = require('../../lib/common/events');
12
+ const postsRepository = require('./PostsRepository').getInstance();
13
13
  const collectionsRepositoryInMemory = new CollectionsRepositoryInMemory();
14
14
 
15
15
  const collectionsService = new CollectionsService({
16
16
  collectionsRepository: collectionsRepositoryInMemory,
17
- postsRepository: {
18
- getAll: async ({filter}) => {
19
- return models.Post.findAll({
20
- // @NOTE: enforce "post" type to avoid ever fetching pages
21
- filter: `(${filter})+type:post`
22
- });
23
- }
24
- }
17
+ postsRepository: postsRepository
25
18
  });
26
19
 
20
+ this.api = collectionsService;
21
+ }
22
+
23
+ async init() {
24
+ if (!labs.isSet('collections')) {
25
+ return;
26
+ }
27
+ const existingBuiltins = await this.api.getAll({filter: 'slug:featured'});
28
+
29
+ if (!existingBuiltins.data.length) {
30
+ await this.api.createCollection({
31
+ title: 'Index',
32
+ slug: 'index',
33
+ description: 'Collection with all posts',
34
+ type: 'automatic',
35
+ deletable: false,
36
+ filter: 'status:published'
37
+ });
38
+
39
+ await this.api.createCollection({
40
+ title: 'Featured Posts',
41
+ slug: 'featured',
42
+ description: 'Collection of featured posts',
43
+ type: 'automatic',
44
+ deletable: false,
45
+ filter: 'featured:true'
46
+ });
47
+ }
48
+
49
+ const events = require('../../lib/common/events');
27
50
  // @NOTE: these should be reworked to use the "Event" classes
28
51
  // instead of Bookshelf model events
29
52
  const updateEvents = require('./update-events');
@@ -31,19 +54,7 @@ class CollectionsServiceWrapper {
31
54
  // @NOTE: naive update implementation to keep things simple for the first version
32
55
  for (const event of updateEvents) {
33
56
  events.on(event, () => {
34
- collectionsService.updateAutomaticCollections();
35
- });
36
- }
37
-
38
- this.api = collectionsService;
39
- }
40
-
41
- async init() {
42
- const featuredCollections = await this.api.getAll({filter: 'slug:featured'});
43
-
44
- if (!featuredCollections.data.length) {
45
- require('./built-in-collections').forEach((collection) => {
46
- this.api.createCollection(collection);
57
+ this.api.updateAutomaticCollections();
47
58
  });
48
59
  }
49
60
  }
@@ -1,4 +1,5 @@
1
1
  const {MilestoneCreatedEvent} = require('@tryghost/milestones');
2
+ const {StripeLiveEnabledEvent, StripeLiveDisabledEvent} = require('@tryghost/members-stripe-service').events;
2
3
 
3
4
  /**
4
5
  * @typedef {import('@tryghost/logging')} logging
@@ -75,9 +76,34 @@ module.exports = class DomainEventsAnalytics {
75
76
  }
76
77
  }
77
78
 
79
+ /**
80
+ *
81
+ * @param {StripeLiveEnabledEvent|StripeLiveDisabledEvent} type
82
+ *
83
+ * @returns {Promise<void>}
84
+ */
85
+ async #handleStripeEvent(type) {
86
+ const eventName = type === StripeLiveDisabledEvent ? 'Stripe Live Disabled' : 'Stripe Live Enabled';
87
+
88
+ try {
89
+ this.#analytics.track(Object.assign(this.#trackDefaults, {}, {event: this.#prefix + eventName}));
90
+ } catch (err) {
91
+ this.#logging.error(err);
92
+ this.#exceptionHandler.captureException(err);
93
+ }
94
+ }
95
+
78
96
  subscribeToEvents() {
79
97
  this.#DomainEvents.subscribe(MilestoneCreatedEvent, async (event) => {
80
98
  await this.#handleMilestoneCreatedEvent(event);
81
99
  });
100
+
101
+ this.#DomainEvents.subscribe(StripeLiveEnabledEvent, async () => {
102
+ await this.#handleStripeEvent(StripeLiveEnabledEvent);
103
+ });
104
+
105
+ this.#DomainEvents.subscribe(StripeLiveDisabledEvent, async () => {
106
+ await this.#handleStripeEvent(StripeLiveDisabledEvent);
107
+ });
82
108
  }
83
109
  };
@@ -208,6 +208,13 @@ class SettingsBREADService {
208
208
  key: 'stripe_connect_account_id',
209
209
  value: stripeConnectData.account_id
210
210
  });
211
+
212
+ if (stripeConnectData.public_key.match(/pk_live/)) {
213
+ // Require the Stripe service here as it breaks existing tests otherwise
214
+ const stripeService = require('../stripe');
215
+ // This method currently only triggers a DomainEvent
216
+ await stripeService.connect();
217
+ }
211
218
  }
212
219
 
213
220
  // remove any email properties that are not allowed to be set without verification
@@ -153,7 +153,7 @@ function ping(post) {
153
153
  }
154
154
  }
155
155
 
156
- function listener(model, options) {
156
+ function slackListener(model, options) {
157
157
  // CASE: do not ping slack if we import a database
158
158
  // TODO: refactor post.published events to never fire on importing
159
159
  if (options && options.importing) {
@@ -163,15 +163,20 @@ function listener(model, options) {
163
163
  ping(model.toJSON());
164
164
  }
165
165
 
166
- function testPing() {
166
+ function slackTestPing() {
167
167
  ping({
168
168
  message: 'Heya! This is a test notification from your Ghost blog :smile:. Seems to work fine!'
169
169
  });
170
170
  }
171
171
 
172
172
  function listen() {
173
- events.on('post.published', listener);
174
- events.on('slack.test', testPing);
173
+ if (!events.hasRegisteredListener('post.published', 'slackListener')) {
174
+ events.on('post.published', slackListener);
175
+ }
176
+
177
+ if (!events.hasRegisteredListener('slack.test', 'slackTestPing')) {
178
+ events.on('slack.test', slackTestPing);
179
+ }
175
180
  }
176
181
 
177
182
  // Public API