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.
- package/components/{tryghost-adapter-cache-memory-ttl-5.51.2.tgz → tryghost-adapter-cache-memory-ttl-5.52.1.tgz} +0 -0
- package/components/{tryghost-adapter-cache-redis-5.51.2.tgz → tryghost-adapter-cache-redis-5.52.1.tgz} +0 -0
- package/components/tryghost-adapter-manager-5.52.1.tgz +0 -0
- package/components/{tryghost-announcement-bar-settings-5.51.2.tgz → tryghost-announcement-bar-settings-5.52.1.tgz} +0 -0
- package/components/{tryghost-api-framework-5.51.2.tgz → tryghost-api-framework-5.52.1.tgz} +0 -0
- package/components/{tryghost-api-version-compatibility-service-5.51.2.tgz → tryghost-api-version-compatibility-service-5.52.1.tgz} +0 -0
- package/components/{tryghost-audience-feedback-5.51.2.tgz → tryghost-audience-feedback-5.52.1.tgz} +0 -0
- package/components/tryghost-bootstrap-socket-5.52.1.tgz +0 -0
- package/components/tryghost-collections-5.52.1.tgz +0 -0
- package/components/tryghost-constants-5.52.1.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.52.1.tgz +0 -0
- package/components/{tryghost-data-generator-5.51.2.tgz → tryghost-data-generator-5.52.1.tgz} +0 -0
- package/components/tryghost-domain-events-5.52.1.tgz +0 -0
- package/components/{tryghost-dynamic-routing-events-5.51.2.tgz → tryghost-dynamic-routing-events-5.52.1.tgz} +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.52.1.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.52.1.tgz +0 -0
- package/components/tryghost-email-content-generator-5.52.1.tgz +0 -0
- package/components/{tryghost-email-events-5.51.2.tgz → tryghost-email-events-5.52.1.tgz} +0 -0
- package/components/{tryghost-email-service-5.51.2.tgz → tryghost-email-service-5.52.1.tgz} +0 -0
- package/components/{tryghost-email-suppression-list-5.51.2.tgz → tryghost-email-suppression-list-5.52.1.tgz} +0 -0
- package/components/{tryghost-event-aware-cache-wrapper-5.51.2.tgz → tryghost-event-aware-cache-wrapper-5.52.1.tgz} +0 -0
- package/components/tryghost-express-dynamic-redirects-5.52.1.tgz +0 -0
- package/components/{tryghost-external-media-inliner-5.51.2.tgz → tryghost-external-media-inliner-5.52.1.tgz} +0 -0
- package/components/{tryghost-extract-api-key-5.51.2.tgz → tryghost-extract-api-key-5.52.1.tgz} +0 -0
- package/components/tryghost-html-to-plaintext-5.52.1.tgz +0 -0
- package/components/tryghost-i18n-5.52.1.tgz +0 -0
- package/components/{tryghost-importer-handler-content-files-5.51.2.tgz → tryghost-importer-handler-content-files-5.52.1.tgz} +0 -0
- package/components/tryghost-importer-revue-5.52.1.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.52.1.tgz +0 -0
- package/components/tryghost-job-manager-5.52.1.tgz +0 -0
- package/components/{tryghost-link-redirects-5.51.2.tgz → tryghost-link-redirects-5.52.1.tgz} +0 -0
- package/components/{tryghost-link-replacer-5.51.2.tgz → tryghost-link-replacer-5.52.1.tgz} +0 -0
- package/components/{tryghost-link-tracking-5.51.2.tgz → tryghost-link-tracking-5.52.1.tgz} +0 -0
- package/components/tryghost-magic-link-5.52.1.tgz +0 -0
- package/components/{tryghost-mailgun-client-5.51.2.tgz → tryghost-mailgun-client-5.52.1.tgz} +0 -0
- package/components/{tryghost-member-attribution-5.51.2.tgz → tryghost-member-attribution-5.52.1.tgz} +0 -0
- package/components/tryghost-member-events-5.52.1.tgz +0 -0
- package/components/tryghost-members-api-5.52.1.tgz +0 -0
- package/components/tryghost-members-csv-5.52.1.tgz +0 -0
- package/components/tryghost-members-events-service-5.52.1.tgz +0 -0
- package/components/tryghost-members-importer-5.52.1.tgz +0 -0
- package/components/tryghost-members-offers-5.52.1.tgz +0 -0
- package/components/tryghost-members-payments-5.52.1.tgz +0 -0
- package/components/tryghost-members-ssr-5.52.1.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.52.1.tgz +0 -0
- package/components/{tryghost-mentions-email-report-5.51.2.tgz → tryghost-mentions-email-report-5.52.1.tgz} +0 -0
- package/components/{tryghost-milestones-5.51.2.tgz → tryghost-milestones-5.52.1.tgz} +0 -0
- package/components/tryghost-minifier-5.52.1.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.52.1.tgz +0 -0
- package/components/{tryghost-mw-cache-control-5.51.2.tgz → tryghost-mw-cache-control-5.52.1.tgz} +0 -0
- package/components/tryghost-mw-error-handler-5.52.1.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.52.1.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.52.1.tgz +0 -0
- package/components/{tryghost-mw-version-match-5.51.2.tgz → tryghost-mw-version-match-5.52.1.tgz} +0 -0
- package/components/tryghost-mw-vhost-5.52.1.tgz +0 -0
- package/components/{tryghost-oembed-service-5.51.2.tgz → tryghost-oembed-service-5.52.1.tgz} +0 -0
- package/components/tryghost-package-json-5.52.1.tgz +0 -0
- package/components/{tryghost-post-revisions-5.51.2.tgz → tryghost-post-revisions-5.52.1.tgz} +0 -0
- package/components/tryghost-posts-service-5.52.1.tgz +0 -0
- package/components/{tryghost-referrers-5.51.2.tgz → tryghost-referrers-5.52.1.tgz} +0 -0
- package/components/tryghost-security-5.52.1.tgz +0 -0
- package/components/tryghost-session-service-5.52.1.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.52.1.tgz +0 -0
- package/components/{tryghost-slack-notifications-5.51.2.tgz → tryghost-slack-notifications-5.52.1.tgz} +0 -0
- package/components/{tryghost-staff-service-5.51.2.tgz → tryghost-staff-service-5.52.1.tgz} +0 -0
- package/components/{tryghost-stats-service-5.51.2.tgz → tryghost-stats-service-5.52.1.tgz} +0 -0
- package/components/{tryghost-tiers-5.51.2.tgz → tryghost-tiers-5.52.1.tgz} +0 -0
- package/components/tryghost-update-check-service-5.52.1.tgz +0 -0
- package/components/tryghost-verification-trigger-5.52.1.tgz +0 -0
- package/components/{tryghost-version-notifications-data-service-5.51.2.tgz → tryghost-version-notifications-data-service-5.52.1.tgz} +0 -0
- package/components/{tryghost-webmentions-5.51.2.tgz → tryghost-webmentions-5.52.1.tgz} +0 -0
- package/core/built/admin/assets/chunk.143.1106d36e1343f88742c0.js +35 -0
- package/core/built/admin/assets/chunk.178.3ab27e3161da629a8235.js +11 -0
- package/core/built/admin/assets/{chunk.976.c3f393d6685c89bbaaca.js → chunk.658.4548e702d296a29726d6.js} +65 -58
- package/core/built/admin/assets/{ghost-e6482a0a19b228a9b9e71a9c5ea134ba.css → ghost-a99d4bf5c4a056624c1c41bbf7c06532.css} +1 -1
- package/core/built/admin/assets/{ghost-eea3f9036ed762e933a3a007d4d4909b.js → ghost-b73f018429dcbc18d6d47037187d6a81.js} +3671 -3670
- package/core/built/admin/assets/{ghost-dark-2ec2e32362fa867723379a286fd0f0bd.css → ghost-dark-179670faac46fe5fe58d9da626e1c0a8.css} +1 -1
- package/core/built/admin/assets/{vendor-bc3bd83c56b822ae56193b0d231189c0.js → vendor-2e4b0ccaa065f6709f955c29ec9e18a3.js} +1 -1
- package/core/built/admin/index.html +6 -6
- package/core/frontend/helpers/ghost_head.js +2 -1
- package/core/frontend/src/cards/css/signup.css +4 -0
- package/core/server/api/endpoints/collections.js +19 -76
- package/core/server/api/endpoints/pages.js +1 -1
- package/core/server/api/endpoints/posts.js +25 -4
- package/core/server/api/endpoints/utils/serializers/input/pages.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/collections.js +10 -0
- package/core/server/api/endpoints/utils/serializers/output/index.js +4 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/collection-posts.js +19 -0
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +10 -0
- package/core/server/api/endpoints/utils/serializers/output/utils/clean.js +1 -0
- package/core/server/data/migrations/versions/5.21/2022-10-26-04-50-member-subscription-created-batch-id.js +7 -2
- package/core/server/data/migrations/versions/5.3/2022-07-06-09-26-add-ghost-explore-integration-api-key.js +6 -2
- package/core/server/data/migrations/versions/5.40/2023-03-21-19-02-add-self-serve-integration-api-key.js +6 -2
- package/core/server/data/migrations/versions/5.41/2023-03-27-17-51-fix-self-serve-integration-api-key-type.js +3 -1
- package/core/server/models/post.js +3 -2
- package/core/server/services/collections/PostsRepository.js +39 -0
- package/core/server/services/collections/index.js +34 -23
- package/core/server/services/segment/DomainEventsAnalytics.js +26 -0
- package/core/server/services/settings/SettingsBREADService.js +7 -0
- package/core/server/services/slack.js +9 -4
- package/core/server/web/api/endpoints/admin/middleware.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +1 -2
- package/core/shared/config/helpers.js +1 -1
- package/core/shared/config/utils.js +2 -2
- package/core/shared/labs.js +5 -1
- package/package.json +147 -146
- package/yarn.lock +715 -650
- package/components/tryghost-adapter-manager-5.51.2.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.51.2.tgz +0 -0
- package/components/tryghost-collections-5.51.2.tgz +0 -0
- package/components/tryghost-constants-5.51.2.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.51.2.tgz +0 -0
- package/components/tryghost-domain-events-5.51.2.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.51.2.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.51.2.tgz +0 -0
- package/components/tryghost-email-content-generator-5.51.2.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.51.2.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.51.2.tgz +0 -0
- package/components/tryghost-i18n-5.51.2.tgz +0 -0
- package/components/tryghost-importer-revue-5.51.2.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.51.2.tgz +0 -0
- package/components/tryghost-job-manager-5.51.2.tgz +0 -0
- package/components/tryghost-magic-link-5.51.2.tgz +0 -0
- package/components/tryghost-member-events-5.51.2.tgz +0 -0
- package/components/tryghost-members-api-5.51.2.tgz +0 -0
- package/components/tryghost-members-csv-5.51.2.tgz +0 -0
- package/components/tryghost-members-events-service-5.51.2.tgz +0 -0
- package/components/tryghost-members-importer-5.51.2.tgz +0 -0
- package/components/tryghost-members-offers-5.51.2.tgz +0 -0
- package/components/tryghost-members-payments-5.51.2.tgz +0 -0
- package/components/tryghost-members-ssr-5.51.2.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.51.2.tgz +0 -0
- package/components/tryghost-minifier-5.51.2.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.51.2.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.51.2.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.51.2.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.51.2.tgz +0 -0
- package/components/tryghost-mw-vhost-5.51.2.tgz +0 -0
- package/components/tryghost-package-json-5.51.2.tgz +0 -0
- package/components/tryghost-posts-service-5.51.2.tgz +0 -0
- package/components/tryghost-security-5.51.2.tgz +0 -0
- package/components/tryghost-session-service-5.51.2.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.51.2.tgz +0 -0
- package/components/tryghost-update-check-service-5.51.2.tgz +0 -0
- package/components/tryghost-verification-trigger-5.51.2.tgz +0 -0
- package/core/built/admin/assets/chunk.143.a99b8ed3cb168269b0e3.js +0 -35
- package/core/built/admin/assets/chunk.178.707e9fd52fc7ea86b76b.js +0 -10
- package/core/server/services/collections/built-in-collections.js +0 -8
- /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-
|
|
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.
|
|
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-
|
|
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-
|
|
60
|
-
<script src="assets/chunk.
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
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 (!
|
|
94
|
+
if (!isFilled && !preview) {
|
|
94
95
|
return '';
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -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
|
|
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
|
-
|
|
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
|
+
};
|
|
@@ -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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
174
|
-
|
|
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
|