ghost 5.54.4 → 5.55.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 (154) hide show
  1. package/components/tryghost-adapter-cache-memory-ttl-5.55.0.tgz +0 -0
  2. package/components/tryghost-adapter-cache-redis-5.55.0.tgz +0 -0
  3. package/components/{tryghost-adapter-manager-5.54.4.tgz → tryghost-adapter-manager-5.55.0.tgz} +0 -0
  4. package/components/tryghost-announcement-bar-settings-5.55.0.tgz +0 -0
  5. package/components/{tryghost-api-framework-5.54.4.tgz → tryghost-api-framework-5.55.0.tgz} +0 -0
  6. package/components/tryghost-api-version-compatibility-service-5.55.0.tgz +0 -0
  7. package/components/tryghost-audience-feedback-5.55.0.tgz +0 -0
  8. package/components/{tryghost-bootstrap-socket-5.54.4.tgz → tryghost-bootstrap-socket-5.55.0.tgz} +0 -0
  9. package/components/tryghost-collections-5.55.0.tgz +0 -0
  10. package/components/tryghost-constants-5.55.0.tgz +0 -0
  11. package/components/{tryghost-custom-theme-settings-service-5.54.4.tgz → tryghost-custom-theme-settings-service-5.55.0.tgz} +0 -0
  12. package/components/{tryghost-data-generator-5.54.4.tgz → tryghost-data-generator-5.55.0.tgz} +0 -0
  13. package/components/tryghost-domain-events-5.55.0.tgz +0 -0
  14. package/components/tryghost-dynamic-routing-events-5.55.0.tgz +0 -0
  15. package/components/tryghost-email-analytics-provider-mailgun-5.55.0.tgz +0 -0
  16. package/components/tryghost-email-analytics-service-5.55.0.tgz +0 -0
  17. package/components/tryghost-email-content-generator-5.55.0.tgz +0 -0
  18. package/components/tryghost-email-events-5.55.0.tgz +0 -0
  19. package/components/tryghost-email-service-5.55.0.tgz +0 -0
  20. package/components/tryghost-email-suppression-list-5.55.0.tgz +0 -0
  21. package/components/tryghost-event-aware-cache-wrapper-5.55.0.tgz +0 -0
  22. package/components/tryghost-express-dynamic-redirects-5.55.0.tgz +0 -0
  23. package/components/{tryghost-external-media-inliner-5.54.4.tgz → tryghost-external-media-inliner-5.55.0.tgz} +0 -0
  24. package/components/tryghost-extract-api-key-5.55.0.tgz +0 -0
  25. package/components/tryghost-html-to-plaintext-5.55.0.tgz +0 -0
  26. package/components/tryghost-i18n-5.55.0.tgz +0 -0
  27. package/components/{tryghost-importer-handler-content-files-5.54.4.tgz → tryghost-importer-handler-content-files-5.55.0.tgz} +0 -0
  28. package/components/{tryghost-importer-revue-5.54.4.tgz → tryghost-importer-revue-5.55.0.tgz} +0 -0
  29. package/components/tryghost-in-memory-repository-5.55.0.tgz +0 -0
  30. package/components/{tryghost-job-manager-5.54.4.tgz → tryghost-job-manager-5.55.0.tgz} +0 -0
  31. package/components/tryghost-link-redirects-5.55.0.tgz +0 -0
  32. package/components/{tryghost-link-replacer-5.54.4.tgz → tryghost-link-replacer-5.55.0.tgz} +0 -0
  33. package/components/tryghost-link-tracking-5.55.0.tgz +0 -0
  34. package/components/{tryghost-magic-link-5.54.4.tgz → tryghost-magic-link-5.55.0.tgz} +0 -0
  35. package/components/tryghost-mail-events-5.55.0.tgz +0 -0
  36. package/components/tryghost-mailgun-client-5.55.0.tgz +0 -0
  37. package/components/{tryghost-member-attribution-5.54.4.tgz → tryghost-member-attribution-5.55.0.tgz} +0 -0
  38. package/components/tryghost-member-events-5.55.0.tgz +0 -0
  39. package/components/tryghost-members-api-5.55.0.tgz +0 -0
  40. package/components/tryghost-members-csv-5.55.0.tgz +0 -0
  41. package/components/{tryghost-members-events-service-5.54.4.tgz → tryghost-members-events-service-5.55.0.tgz} +0 -0
  42. package/components/tryghost-members-importer-5.55.0.tgz +0 -0
  43. package/components/tryghost-members-offers-5.55.0.tgz +0 -0
  44. package/components/tryghost-members-payments-5.55.0.tgz +0 -0
  45. package/components/{tryghost-members-ssr-5.54.4.tgz → tryghost-members-ssr-5.55.0.tgz} +0 -0
  46. package/components/tryghost-members-stripe-service-5.55.0.tgz +0 -0
  47. package/components/tryghost-mentions-email-report-5.55.0.tgz +0 -0
  48. package/components/{tryghost-milestones-5.54.4.tgz → tryghost-milestones-5.55.0.tgz} +0 -0
  49. package/components/tryghost-minifier-5.55.0.tgz +0 -0
  50. package/components/tryghost-model-to-domain-event-interceptor-5.55.0.tgz +0 -0
  51. package/components/tryghost-mw-api-version-mismatch-5.55.0.tgz +0 -0
  52. package/components/tryghost-mw-cache-control-5.55.0.tgz +0 -0
  53. package/components/{tryghost-mw-error-handler-5.54.4.tgz → tryghost-mw-error-handler-5.55.0.tgz} +0 -0
  54. package/components/tryghost-mw-session-from-token-5.55.0.tgz +0 -0
  55. package/components/tryghost-mw-update-user-last-seen-5.55.0.tgz +0 -0
  56. package/components/tryghost-mw-version-match-5.55.0.tgz +0 -0
  57. package/components/tryghost-mw-vhost-5.55.0.tgz +0 -0
  58. package/components/tryghost-oembed-service-5.55.0.tgz +0 -0
  59. package/components/{tryghost-package-json-5.54.4.tgz → tryghost-package-json-5.55.0.tgz} +0 -0
  60. package/components/tryghost-post-revisions-5.55.0.tgz +0 -0
  61. package/components/tryghost-posts-service-5.55.0.tgz +0 -0
  62. package/components/tryghost-referrers-5.55.0.tgz +0 -0
  63. package/components/{tryghost-security-5.54.4.tgz → tryghost-security-5.55.0.tgz} +0 -0
  64. package/components/tryghost-session-service-5.55.0.tgz +0 -0
  65. package/components/tryghost-settings-path-manager-5.55.0.tgz +0 -0
  66. package/components/tryghost-slack-notifications-5.55.0.tgz +0 -0
  67. package/components/{tryghost-staff-service-5.54.4.tgz → tryghost-staff-service-5.55.0.tgz} +0 -0
  68. package/components/tryghost-stats-service-5.55.0.tgz +0 -0
  69. package/components/{tryghost-tiers-5.54.4.tgz → tryghost-tiers-5.55.0.tgz} +0 -0
  70. package/components/tryghost-update-check-service-5.55.0.tgz +0 -0
  71. package/components/tryghost-verification-trigger-5.55.0.tgz +0 -0
  72. package/components/tryghost-version-notifications-data-service-5.55.0.tgz +0 -0
  73. package/components/{tryghost-webmentions-5.54.4.tgz → tryghost-webmentions-5.55.0.tgz} +0 -0
  74. package/content/themes/casper/assets/built/screen.css +1 -1
  75. package/content/themes/casper/assets/built/screen.css.map +1 -1
  76. package/content/themes/casper/assets/css/screen.css +22 -6
  77. package/content/themes/casper/page.hbs +26 -24
  78. package/core/built/admin/assets/{chunk.143.3416d003faf26a5d1543.js → chunk.143.60a057de7aaa4abc6a26.js} +6 -7
  79. package/core/built/admin/assets/{chunk.178.6e1499eeefacd04470fe.js → chunk.178.f18f14cc0e6c67a6fd36.js} +4 -4
  80. package/core/built/admin/assets/{chunk.486.3896d2a7816cf03a0794.js → chunk.757.c768dcbeaf1cee916395.js} +1146 -1360
  81. package/core/built/admin/assets/{ghost-14eecea754ec854244d71e1300ec75f2.js → ghost-135607a3c421c472feb9f59f14c625fb.js} +782 -901
  82. package/core/built/admin/assets/{vendor-0d244308df4eb04209cbd5fa6abe358b.js → vendor-0a8fc851393c473cd269f04fc4e97974.js} +2167 -2552
  83. package/core/built/admin/index.html +5 -5
  84. package/core/frontend/helpers/collection.js +146 -0
  85. package/core/frontend/src/cards/css/header_v2.css +283 -0
  86. package/core/frontend/src/cards/css/signup.css +1 -0
  87. package/core/server/api/endpoints/collections.js +0 -19
  88. package/core/server/api/endpoints/posts-public.js +5 -2
  89. package/core/server/api/endpoints/utils/serializers/output/index.js +0 -4
  90. package/core/server/data/migrations/versions/5.55/2023-07-10-05-15-55-add-built-in-collections.js +60 -0
  91. package/core/server/data/migrations/versions/5.55/2023-07-10-05-16-55-add-built-in-collection-posts.js +99 -0
  92. package/core/server/data/schema/fixtures/fixtures.json +33 -0
  93. package/core/server/lib/lexical.js +2 -2
  94. package/core/server/models/post.js +1 -1
  95. package/core/server/services/collections/BookshelfCollectionsRepository.js +39 -10
  96. package/core/server/services/collections/PostsRepository.js +17 -52
  97. package/core/server/services/collections/service.js +4 -24
  98. package/core/server/web/api/endpoints/admin/routes.js +0 -1
  99. package/package.json +144 -144
  100. package/yarn.lock +1820 -1678
  101. package/components/tryghost-adapter-cache-memory-ttl-5.54.4.tgz +0 -0
  102. package/components/tryghost-adapter-cache-redis-5.54.4.tgz +0 -0
  103. package/components/tryghost-announcement-bar-settings-5.54.4.tgz +0 -0
  104. package/components/tryghost-api-version-compatibility-service-5.54.4.tgz +0 -0
  105. package/components/tryghost-audience-feedback-5.54.4.tgz +0 -0
  106. package/components/tryghost-collections-5.54.4.tgz +0 -0
  107. package/components/tryghost-constants-5.54.4.tgz +0 -0
  108. package/components/tryghost-domain-events-5.54.4.tgz +0 -0
  109. package/components/tryghost-dynamic-routing-events-5.54.4.tgz +0 -0
  110. package/components/tryghost-email-analytics-provider-mailgun-5.54.4.tgz +0 -0
  111. package/components/tryghost-email-analytics-service-5.54.4.tgz +0 -0
  112. package/components/tryghost-email-content-generator-5.54.4.tgz +0 -0
  113. package/components/tryghost-email-events-5.54.4.tgz +0 -0
  114. package/components/tryghost-email-service-5.54.4.tgz +0 -0
  115. package/components/tryghost-email-suppression-list-5.54.4.tgz +0 -0
  116. package/components/tryghost-event-aware-cache-wrapper-5.54.4.tgz +0 -0
  117. package/components/tryghost-express-dynamic-redirects-5.54.4.tgz +0 -0
  118. package/components/tryghost-extract-api-key-5.54.4.tgz +0 -0
  119. package/components/tryghost-html-to-plaintext-5.54.4.tgz +0 -0
  120. package/components/tryghost-i18n-5.54.4.tgz +0 -0
  121. package/components/tryghost-in-memory-repository-5.54.4.tgz +0 -0
  122. package/components/tryghost-link-redirects-5.54.4.tgz +0 -0
  123. package/components/tryghost-link-tracking-5.54.4.tgz +0 -0
  124. package/components/tryghost-mail-events-5.54.4.tgz +0 -0
  125. package/components/tryghost-mailgun-client-5.54.4.tgz +0 -0
  126. package/components/tryghost-member-events-5.54.4.tgz +0 -0
  127. package/components/tryghost-members-api-5.54.4.tgz +0 -0
  128. package/components/tryghost-members-csv-5.54.4.tgz +0 -0
  129. package/components/tryghost-members-importer-5.54.4.tgz +0 -0
  130. package/components/tryghost-members-offers-5.54.4.tgz +0 -0
  131. package/components/tryghost-members-payments-5.54.4.tgz +0 -0
  132. package/components/tryghost-members-stripe-service-5.54.4.tgz +0 -0
  133. package/components/tryghost-mentions-email-report-5.54.4.tgz +0 -0
  134. package/components/tryghost-minifier-5.54.4.tgz +0 -0
  135. package/components/tryghost-model-to-domain-event-interceptor-5.54.4.tgz +0 -0
  136. package/components/tryghost-mw-api-version-mismatch-5.54.4.tgz +0 -0
  137. package/components/tryghost-mw-cache-control-5.54.4.tgz +0 -0
  138. package/components/tryghost-mw-session-from-token-5.54.4.tgz +0 -0
  139. package/components/tryghost-mw-update-user-last-seen-5.54.4.tgz +0 -0
  140. package/components/tryghost-mw-version-match-5.54.4.tgz +0 -0
  141. package/components/tryghost-mw-vhost-5.54.4.tgz +0 -0
  142. package/components/tryghost-oembed-service-5.54.4.tgz +0 -0
  143. package/components/tryghost-post-revisions-5.54.4.tgz +0 -0
  144. package/components/tryghost-posts-service-5.54.4.tgz +0 -0
  145. package/components/tryghost-referrers-5.54.4.tgz +0 -0
  146. package/components/tryghost-session-service-5.54.4.tgz +0 -0
  147. package/components/tryghost-settings-path-manager-5.54.4.tgz +0 -0
  148. package/components/tryghost-slack-notifications-5.54.4.tgz +0 -0
  149. package/components/tryghost-stats-service-5.54.4.tgz +0 -0
  150. package/components/tryghost-update-check-service-5.54.4.tgz +0 -0
  151. package/components/tryghost-verification-trigger-5.54.4.tgz +0 -0
  152. package/components/tryghost-version-notifications-data-service-5.54.4.tgz +0 -0
  153. package/core/server/api/endpoints/utils/serializers/output/collections.js +0 -10
  154. /package/core/built/admin/assets/{chunk.486.3896d2a7816cf03a0794.js.LICENSE.txt → chunk.757.c768dcbeaf1cee916395.js.LICENSE.txt} +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%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.54%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%22cdnUrl%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.55%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" />
@@ -56,9 +56,9 @@
56
56
 
57
57
  <div id="ember-basic-dropdown-wormhole"></div>
58
58
 
59
- <script src="assets/vendor-0d244308df4eb04209cbd5fa6abe358b.js"></script>
60
- <script src="assets/chunk.486.3896d2a7816cf03a0794.js"></script>
61
- <script src="assets/chunk.143.3416d003faf26a5d1543.js"></script>
62
- <script src="assets/ghost-14eecea754ec854244d71e1300ec75f2.js"></script>
59
+ <script src="assets/vendor-0a8fc851393c473cd269f04fc4e97974.js"></script>
60
+ <script src="assets/chunk.757.c768dcbeaf1cee916395.js"></script>
61
+ <script src="assets/chunk.143.60a057de7aaa4abc6a26.js"></script>
62
+ <script src="assets/ghost-135607a3c421c472feb9f59f14c625fb.js"></script>
63
63
  </body>
64
64
  </html>
@@ -0,0 +1,146 @@
1
+ // # Get Helper
2
+ // Usage: `{{#get "posts" limit="5"}}`, `{{#get "tags" limit="all"}}`
3
+ // Fetches data from the API
4
+ const {config, api, prepareContextResource} = require('../services/proxy');
5
+ const {hbs} = require('../services/handlebars');
6
+
7
+ const logging = require('@tryghost/logging');
8
+ const errors = require('@tryghost/errors');
9
+ const tpl = require('@tryghost/tpl');
10
+
11
+ const messages = {
12
+ mustBeCalledAsBlock: 'The {\\{{helperName}}} helper must be called as a block. E.g. {{#{helperName}}}...{{/{helperName}}}',
13
+ invalidResource: 'Invalid "{resource}" resource given to get helper'
14
+ };
15
+
16
+ const createFrame = hbs.handlebars.createFrame;
17
+
18
+ /**
19
+ * ## Parse Options
20
+ * Ensure options passed in make sense
21
+ *
22
+ * @param {Object} options
23
+ * @returns {*}
24
+ */
25
+ function parseOptions(options) {
26
+ if (options.limit === 'all' || !options.limit) {
27
+ return {
28
+ limit: 3
29
+ };
30
+ }
31
+
32
+ return {
33
+ limit: options.limit
34
+ };
35
+ }
36
+
37
+ /**
38
+ *
39
+ * @param {String} resource
40
+ * @param {String} controllerName
41
+ * @param {String} action
42
+ * @param {Object} apiOptions
43
+ * @returns {Promise<Object>}
44
+ */
45
+ async function makeAPICall(resource, controllerName, action, apiOptions) {
46
+ const controller = api[controllerName];
47
+
48
+ let timer;
49
+
50
+ try {
51
+ let response;
52
+
53
+ if (config.get('optimization:getHelper:timeout:threshold')) {
54
+ const logLevel = config.get('optimization:getHelper:timeout:level') || 'error';
55
+ const threshold = config.get('optimization:getHelper:timeout:threshold');
56
+
57
+ const apiResponse = controller[action](apiOptions);
58
+
59
+ const timeout = new Promise((resolve) => {
60
+ timer = setTimeout(() => {
61
+ logging[logLevel](new errors.HelperWarning({
62
+ message: `{{#get}} took longer than ${threshold}ms and was aborted`,
63
+ code: 'ABORTED_GET_HELPER',
64
+ errorDetails: {
65
+ api: `${controllerName}.${action}`,
66
+ apiOptions
67
+ }
68
+ }));
69
+
70
+ resolve({[resource]: []});
71
+ }, threshold);
72
+ });
73
+
74
+ response = await Promise.race([apiResponse, timeout]);
75
+ clearTimeout(timer);
76
+ } else {
77
+ response = await controller[action](apiOptions);
78
+ }
79
+
80
+ return response;
81
+ } catch (err) {
82
+ clearTimeout(timer);
83
+ throw err;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * ## Get
89
+ * @param {string} slug
90
+ * @param {object} options
91
+ * @returns {Promise<any>}
92
+ */
93
+ module.exports = async function collection(slug, options) {
94
+ options = options || {};
95
+ options.hash = options.hash || {};
96
+ options.data = options.data || {};
97
+
98
+ const self = this;
99
+ const data = createFrame(options.data);
100
+
101
+ let apiOptions = options.hash;
102
+
103
+ if (!options.fn) {
104
+ data.error = tpl(messages.mustBeCalledAsBlock, {helperName: 'collection'});
105
+ logging.warn(data.error);
106
+ return;
107
+ }
108
+
109
+ const resource = 'posts';
110
+ const controllerName = 'postsPublic';
111
+ const action = 'browse';
112
+
113
+ // Parse the options we're going to pass to the API
114
+ apiOptions = parseOptions(apiOptions);
115
+ apiOptions.context = {member: data.member};
116
+ apiOptions.collection = slug;
117
+
118
+ try {
119
+ const response = await makeAPICall(resource, controllerName, action, apiOptions);
120
+
121
+ // prepare data properties for use with handlebars
122
+ if (response[resource] && response[resource].length) {
123
+ response[resource].forEach(prepareContextResource);
124
+ }
125
+
126
+ // block params allows the theme developer to name the data using something like
127
+ // `{{#get "posts" as |result pageInfo|}}`
128
+ const blockParams = [response[resource]];
129
+ if (response.meta && response.meta.pagination) {
130
+ response.pagination = response.meta.pagination;
131
+ blockParams.push(response.meta.pagination);
132
+ }
133
+
134
+ // Call the main template function
135
+ return options.fn(response, {
136
+ data: data,
137
+ blockParams: blockParams
138
+ });
139
+ } catch (error) {
140
+ logging.error(error);
141
+ data.error = error.message;
142
+ return options.inverse(self, {data: data});
143
+ }
144
+ };
145
+
146
+ module.exports.async = true;
@@ -0,0 +1,283 @@
1
+ .kg-header-card-v2 {
2
+ position: relative;
3
+ }
4
+
5
+ .kg-header-card-v2,
6
+ .kg-header-card-v2 * {
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ .kg-header-card-v2 a,
11
+ .kg-header-card-v2 a span {
12
+ color: currentColor;
13
+ }
14
+
15
+ .kg-header-card-v2.kg-style-accent {
16
+ background-color: var(--ghost-accent-color);
17
+ }
18
+
19
+ .kg-layout-split .kg-header-card-v2-content {
20
+ display: grid;
21
+ grid-template-columns: 1fr 1fr;
22
+ }
23
+
24
+ .kg-header-card-v2-text {
25
+ position: relative;
26
+ display: flex;
27
+ flex-direction: column;
28
+ align-items: flex-start;
29
+ justify-content: center;
30
+ height: 100%;
31
+ padding: 4vmax;
32
+ background-size: cover;
33
+ background-position: center;
34
+ text-align: left;
35
+ }
36
+
37
+ .kg-width-wide .kg-header-card-v2-text {
38
+ padding: 6.4vmax;
39
+ }
40
+
41
+ .kg-width-full .kg-header-card-v2-text {
42
+ padding: 12vmax 0;
43
+ }
44
+
45
+ .kg-layout-split .kg-header-card-v2-text {
46
+ padding: 12vmax 4vmax;
47
+ }
48
+
49
+ .kg-layout-split.kg-content-wide .kg-header-card-v2-text {
50
+ padding: 10vmax 0 10vmax 4vmax;
51
+ }
52
+
53
+ .kg-layout-split.kg-content-wide.kg-swapped .kg-header-card-v2-text {
54
+ padding: 10vmax 4vmax 10vmax 0;
55
+ }
56
+
57
+ .kg-swapped .kg-header-card-v2-text {
58
+ grid-row: 1;
59
+ }
60
+
61
+ .kg-header-card-v2-text.kg-align-center {
62
+ align-items: center;
63
+ text-align: center;
64
+ }
65
+
66
+ .kg-header-card-v2.kg-style-image h2.kg-header-card-v2-heading,
67
+ .kg-header-card-v2.kg-style-image h3.kg-header-card-v2-subheading,
68
+ .kg-header-card-v2.kg-style-image .kg-header-card-v2-button {
69
+ z-index: 999;
70
+ }
71
+
72
+ /* Background image */
73
+
74
+ .kg-header-card-v2 > picture > .kg-header-card-v2-image {
75
+ position: absolute;
76
+ top: 0;
77
+ left: 0;
78
+ width: 100%;
79
+ height: 100%;
80
+ object-fit: cover;
81
+ object-position: center;
82
+ background-color: #FFFFFF;
83
+ pointer-events: none;
84
+ }
85
+
86
+ /* Split layout image */
87
+
88
+ .kg-header-card-v2-content .kg-header-card-v2-image {
89
+ /* this will force the image to follow the signup card height */
90
+ height: 0;
91
+ min-height: 100%;
92
+ /**/
93
+ object-fit: cover;
94
+ object-position: center;
95
+ }
96
+
97
+ .kg-content-wide .kg-header-card-v2-content .kg-header-card-v2-image {
98
+ height: 100%;
99
+ padding: 8rem 0;
100
+ object-fit: contain;
101
+ }
102
+
103
+ /* Heading */
104
+
105
+ .kg-header-card-v2 h2.kg-header-card-v2-heading {
106
+ margin: 0;
107
+ font-size: clamp(2.8rem, 4vw, 4rem);
108
+ font-weight: 700;
109
+ line-height: 1em;
110
+ letter-spacing: -0.01em;
111
+ }
112
+
113
+ .kg-header-card-v2.kg-width-wide h2.kg-header-card-v2-heading {
114
+ font-size: clamp(2.8rem, 5vw, 5.2rem);
115
+ }
116
+
117
+ .kg-header-card-v2.kg-width-full h2.kg-header-card-v2-heading {
118
+ font-size: clamp(3.6rem, 5.6vw, 6.4rem);
119
+ }
120
+
121
+ .kg-header-card-v2.kg-width-full.kg-layout-split h2.kg-header-card-v2-heading {
122
+ font-size: clamp(3.2rem, 4vw, 5.6rem);
123
+ }
124
+
125
+ .kg-header-card-v2.kg-width-full.kg-layout-split.kg-content-wide h2.kg-header-card-v2-heading {
126
+ font-size: clamp(2.8rem, 4vw, 5.2rem);
127
+ }
128
+
129
+ /* Subheading */
130
+
131
+ .kg-header-card-v2-subheading {
132
+ margin: 0 0 2em;
133
+ }
134
+
135
+ .kg-header-card-v2 h3.kg-header-card-v2-subheading {
136
+ max-width: 40em;
137
+ margin: 0;
138
+ font-size: clamp(1.05em, 2vw, 2rem);
139
+ font-weight: 500;
140
+ line-height: 1.4em;
141
+ }
142
+
143
+ .kg-header-card-v2 h2 + h3.kg-header-card-v2-subheading {
144
+ margin: 0.6em 0 0;
145
+ }
146
+
147
+ .kg-header-card-v2 h3.kg-header-card-v2-subheading strong {
148
+ font-weight: 600;
149
+ }
150
+
151
+ .kg-header-card-v2.kg-width-wide h3.kg-header-card-v2-subheading {
152
+ font-size: clamp(1.05em, 2vw, 2.4rem);
153
+ }
154
+
155
+ .kg-header-card-v2.kg-width-full h3.kg-header-card-v2-subheading:not(.kg-layout-split h3.kg-header-card-v2-subheading) {
156
+ max-width: 65vmax;
157
+ font-size: clamp(1.05em, 2vw, 2.6rem);
158
+ }
159
+
160
+ .kg-header-card-v2.kg-width-full.kg-layout-split h3.kg-header-card-v2-subheading {
161
+ font-size: clamp(1.05em, 2vw, 2.4rem);
162
+ }
163
+
164
+ .kg-width-wide .kg-header-card-v2-fields,
165
+ .kg-width-full .kg-header-card-v2-fields {
166
+ width: 100%;
167
+ max-width: 500px;
168
+ }
169
+
170
+ .kg-header-card-v2-input {
171
+ width: 100%;
172
+ height: 4.6rem;
173
+ margin-right: 3px;
174
+ padding: 12px 16px;
175
+ border: none;
176
+ font-size: 1.1em;
177
+ }
178
+
179
+ .kg-header-card-v2-input:focus,
180
+ .kg-header-card-v2-input:focus-visible {
181
+ outline: none;
182
+ }
183
+
184
+ .kg-header-card-v2-button {
185
+ display: flex;
186
+ position: relative;
187
+ align-items: center;
188
+ height: 4.6rem;
189
+ padding: 0 1.2em;
190
+ outline: none;
191
+ border: none;
192
+ font-size: 1em;
193
+ font-weight: 600;
194
+ line-height: 1em;
195
+ text-align: center;
196
+ text-decoration: none;
197
+ letter-spacing: .2px;
198
+ white-space: nowrap;
199
+ text-overflow: ellipsis;
200
+ border-radius: 3px;
201
+ transition: opacity .2s ease;
202
+ }
203
+
204
+ .kg-header-card-v2-button.kg-style-accent {
205
+ background-color: var(--ghost-accent-color);
206
+ }
207
+
208
+ .kg-header-card-v2 h2 + .kg-header-card-v2-button,
209
+ .kg-header-card-v2 h3 + .kg-header-card-v2-button {
210
+ margin: 1.5em 0 0;
211
+ }
212
+
213
+ .kg-header-card-v2 .kg-header-card-v2-button:hover {
214
+ opacity: 0.85;
215
+ }
216
+
217
+ .kg-header-card-v2.kg-width-wide .kg-header-card-v2-button {
218
+ font-size: 1.05em;
219
+ }
220
+
221
+ .kg-header-card-v2.kg-width-wide h2 + .kg-header-card-v2-button,
222
+ .kg-header-card-v2.kg-width-wide h3 + .kg-header-card-v2-button {
223
+ margin-top: 1.75em;
224
+ }
225
+
226
+ .kg-header-card-v2.kg-width-full .kg-header-card-v2-button {
227
+ font-size: 1.1em;
228
+ }
229
+
230
+ .kg-header-card-v2.kg-width-full h2 + .kg-header-card-v2-button,
231
+ .kg-header-card-v2.kg-width-full h3 + .kg-header-card-v2-button {
232
+ margin-top: 2em;
233
+ }
234
+
235
+ /* Responsive styles */
236
+
237
+ @media (max-width: 640px) {
238
+ .kg-layout-split .kg-header-card-v2-content {
239
+ grid-template-columns: 1fr;
240
+ }
241
+
242
+ .kg-width-wide .kg-header-card-v2-text {
243
+ padding: 6.4vmax 4vmax;
244
+ }
245
+
246
+ .kg-layout-split.kg-content-wide .kg-header-card-v2-text,
247
+ .kg-layout-split.kg-content-wide.kg-swapped .kg-header-card-v2-text {
248
+ padding: 9.6vmax 0;
249
+ }
250
+
251
+ .kg-header-card-v2.kg-width-full h3.kg-header-card-v2-subheading:not(.kg-layout-split h3.kg-header-card-v2-subheading) {
252
+ max-width: unset;
253
+ }
254
+
255
+ .kg-header-card-v2-content .kg-header-card-v2-image:not(.kg-content-wide .kg-header-card-v2-content .kg-header-card-v2-image) {
256
+ height: auto;
257
+ min-height: unset;
258
+ aspect-ratio: 1 / 1;
259
+ }
260
+
261
+ .kg-content-wide .kg-header-card-v2-content .kg-header-card-v2-image {
262
+ padding: 2.4rem 0 0;
263
+ }
264
+
265
+ .kg-content-wide.kg-swapped .kg-header-card-v2-content .kg-header-card-v2-image {
266
+ padding: 0 0 2.4rem;
267
+ }
268
+
269
+ .kg-header-card-v2-input {
270
+ height: 4.2rem;
271
+ padding: 6px 12px;
272
+ font-size: 1em;
273
+ }
274
+
275
+ .kg-header-card-v2-button {
276
+ height: 4.2rem;
277
+ }
278
+
279
+ .kg-header-card-v2.kg-width-wide .kg-header-card-v2-button,
280
+ .kg-header-card-v2.kg-width-full .kg-header-card-v2-button {
281
+ font-size: 1em;
282
+ }
283
+ }
@@ -210,6 +210,7 @@
210
210
  margin-right: 3px;
211
211
  padding: 12px 16px;
212
212
  border: none;
213
+ background: #FFFFFF;
213
214
  font-size: 1.1em;
214
215
  }
215
216
 
@@ -25,25 +25,6 @@ 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
-
47
28
  read: {
48
29
  headers: {
49
30
  cacheInvalidate: false
@@ -3,6 +3,8 @@ const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
4
  const {mapQuery} = require('@tryghost/mongo-utils');
5
5
  const postsPublicService = require('../../services/posts-public');
6
+ const getPostServiceInstance = require('../../services/posts/posts-service');
7
+ const postsService = getPostServiceInstance();
6
8
 
7
9
  const allowedIncludes = ['tags', 'authors', 'tiers', 'sentiment'];
8
10
 
@@ -38,7 +40,8 @@ module.exports = {
38
40
  'order',
39
41
  'page',
40
42
  'debug',
41
- 'absolute_urls'
43
+ 'absolute_urls',
44
+ 'collection'
42
45
  ],
43
46
  validation: {
44
47
  options: {
@@ -56,7 +59,7 @@ module.exports = {
56
59
  ...frame.options,
57
60
  mongoTransformer: rejectPrivateFieldsTransformer
58
61
  };
59
- return models.Post.findPage(options);
62
+ return postsService.browsePosts(options);
60
63
  }
61
64
  },
62
65
 
@@ -21,10 +21,6 @@ module.exports = {
21
21
  return require('./authentication');
22
22
  },
23
23
 
24
- get collections() {
25
- return require('./collections');
26
- },
27
-
28
24
  get db() {
29
25
  return require('./db');
30
26
  },
@@ -0,0 +1,60 @@
1
+ const logging = require('@tryghost/logging');
2
+ const {default: ObjectID} = require('bson-objectid');
3
+ const {createTransactionalMigration} = require('../../utils');
4
+
5
+ module.exports = createTransactionalMigration(
6
+ async function up(knex) {
7
+ logging.info('Creating built in collections');
8
+
9
+ const existingLatestCollection = await knex('collections')
10
+ .where({
11
+ slug: 'latest'
12
+ })
13
+ .first();
14
+
15
+ if (existingLatestCollection) {
16
+ logging.warn('Latest collection already exists, skipping');
17
+ } else {
18
+ await knex('collections').insert({
19
+ id: (new ObjectID()).toHexString(),
20
+ title: 'Latest',
21
+ slug: 'latest',
22
+ description: 'All posts',
23
+ type: 'automatic',
24
+ filter: '',
25
+ created_at: knex.raw('current_timestamp')
26
+ });
27
+ }
28
+
29
+ const existingFeaturedCollection = await knex('collections')
30
+ .where({
31
+ slug: 'featured'
32
+ })
33
+ .first();
34
+
35
+ if (existingFeaturedCollection) {
36
+ logging.warn('Featured collection already exists, skipping');
37
+ } else {
38
+ await knex('collections').insert({
39
+ id: (new ObjectID()).toHexString(),
40
+ title: 'Featured',
41
+ slug: 'featured',
42
+ description: 'Featured posts',
43
+ type: 'automatic',
44
+ filter: 'featured:true',
45
+ created_at: knex.raw('current_timestamp')
46
+ });
47
+ }
48
+ },
49
+ async function down(knex) {
50
+ logging.info('Deleting built in collections');
51
+
52
+ await knex('collections').where({
53
+ slug: 'latest'
54
+ }).del();
55
+
56
+ await knex('collections').where({
57
+ slug: 'featured'
58
+ }).del();
59
+ }
60
+ );
@@ -0,0 +1,99 @@
1
+ const logging = require('@tryghost/logging');
2
+ const {default: ObjectID} = require('bson-objectid');
3
+ const {createTransactionalMigration} = require('../../utils');
4
+
5
+ const insertPostCollections = async (knex, collectionId, postIds) => {
6
+ logging.warn(`Batch inserting ${postIds.length} collection posts for collection ${collectionId}`);
7
+
8
+ const collectionPosts = postIds.map((postId) => {
9
+ return {
10
+ id: (new ObjectID()).toHexString(),
11
+ collection_id: collectionId,
12
+ post_id: postId,
13
+ sort_order: 0
14
+ };
15
+ });
16
+
17
+ await knex.batchInsert('collections_posts', collectionPosts, 1000);
18
+ };
19
+
20
+ module.exports = createTransactionalMigration(
21
+ async function up(knex) {
22
+ logging.info('Populating built-in collections');
23
+
24
+ const existingLatestCollection = await knex('collections')
25
+ .where({
26
+ slug: 'latest'
27
+ })
28
+ .first();
29
+
30
+ if (!existingLatestCollection) {
31
+ logging.warn('Latest collection does not exists, skipping');
32
+ } else {
33
+ const latestPostsRows = await knex('posts')
34
+ .select('id')
35
+ .where({
36
+ type: 'post'
37
+ });
38
+
39
+ const latestPostsIds = latestPostsRows.map(row => row.id);
40
+
41
+ await insertPostCollections(knex, existingLatestCollection.id, latestPostsIds);
42
+ }
43
+
44
+ const existingFeaturedCollection = await knex('collections')
45
+ .where({
46
+ slug: 'featured'
47
+ })
48
+ .first();
49
+
50
+ if (!existingFeaturedCollection) {
51
+ logging.warn('Featured collection does not exist, skipping');
52
+ } else {
53
+ const featuredPostsRows = await knex('posts')
54
+ .select('id')
55
+ .where({
56
+ featured: true,
57
+ type: 'post'
58
+ });
59
+
60
+ const featuredPostsIds = featuredPostsRows.map(row => row.id);
61
+
62
+ await insertPostCollections(knex, existingFeaturedCollection.id, featuredPostsIds);
63
+ }
64
+ },
65
+ async function down(knex) {
66
+ logging.info('Deleting built in collection_posts');
67
+
68
+ const existingLatestCollection = await knex('collections')
69
+ .where({
70
+ slug: 'latest'
71
+ })
72
+ .first();
73
+
74
+ if (existingLatestCollection) {
75
+ logging.info(`Deleting collection_posts for latest collection: ${existingLatestCollection.id}`);
76
+ await knex('collections_posts')
77
+ .where({
78
+ collection_id: existingLatestCollection.id
79
+ })
80
+ .del();
81
+ }
82
+
83
+ const existingFeaturedCollection = await knex('collections')
84
+ .where({
85
+ slug: 'featured'
86
+ })
87
+ .first();
88
+
89
+ if (existingFeaturedCollection) {
90
+ logging.info(`Deleting collection_posts for featured collection: ${existingFeaturedCollection.id}`);
91
+
92
+ await knex('collections_posts')
93
+ .where({
94
+ collection_id: existingFeaturedCollection.id
95
+ })
96
+ .del();
97
+ }
98
+ }
99
+ );