ghost 5.67.0 → 5.69.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.
- package/components/tryghost-adapter-cache-memory-ttl-5.69.0.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.69.0.tgz +0 -0
- package/components/tryghost-adapter-manager-5.69.0.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.69.0.tgz +0 -0
- package/components/tryghost-api-framework-5.69.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.69.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.69.0.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.69.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.69.0.tgz +0 -0
- package/components/tryghost-collections-5.69.0.tgz +0 -0
- package/components/tryghost-constants-5.69.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.69.0.tgz +0 -0
- package/components/{tryghost-data-generator-5.67.0.tgz → tryghost-data-generator-5.69.0.tgz} +0 -0
- package/components/tryghost-domain-events-5.69.0.tgz +0 -0
- package/components/tryghost-donations-5.69.0.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.69.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.69.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.69.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.69.0.tgz +0 -0
- package/components/tryghost-email-events-5.69.0.tgz +0 -0
- package/components/{tryghost-email-service-5.67.0.tgz → tryghost-email-service-5.69.0.tgz} +0 -0
- package/components/tryghost-email-suppression-list-5.69.0.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.69.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.69.0.tgz +0 -0
- package/components/tryghost-external-media-inliner-5.69.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.69.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.69.0.tgz +0 -0
- package/components/tryghost-i18n-5.69.0.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.69.0.tgz +0 -0
- package/components/tryghost-importer-revue-5.69.0.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.69.0.tgz +0 -0
- package/components/tryghost-job-manager-5.69.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.69.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.69.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.69.0.tgz +0 -0
- package/components/tryghost-magic-link-5.69.0.tgz +0 -0
- package/components/tryghost-mail-events-5.69.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.69.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.69.0.tgz +0 -0
- package/components/tryghost-member-events-5.69.0.tgz +0 -0
- package/components/{tryghost-members-api-5.67.0.tgz → tryghost-members-api-5.69.0.tgz} +0 -0
- package/components/tryghost-members-csv-5.69.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.69.0.tgz +0 -0
- package/components/tryghost-members-importer-5.69.0.tgz +0 -0
- package/components/tryghost-members-offers-5.69.0.tgz +0 -0
- package/components/tryghost-members-payments-5.69.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.69.0.tgz +0 -0
- package/components/{tryghost-members-stripe-service-5.67.0.tgz → tryghost-members-stripe-service-5.69.0.tgz} +0 -0
- package/components/tryghost-mentions-email-report-5.69.0.tgz +0 -0
- package/components/tryghost-milestones-5.69.0.tgz +0 -0
- package/components/tryghost-minifier-5.69.0.tgz +0 -0
- package/components/tryghost-model-to-domain-event-interceptor-5.69.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.69.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.69.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.69.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.69.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.69.0.tgz +0 -0
- package/components/tryghost-mw-version-match-5.69.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.69.0.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.69.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.69.0.tgz +0 -0
- package/components/tryghost-package-json-5.69.0.tgz +0 -0
- package/components/tryghost-post-events-5.69.0.tgz +0 -0
- package/components/tryghost-post-revisions-5.69.0.tgz +0 -0
- package/components/tryghost-posts-service-5.69.0.tgz +0 -0
- package/components/tryghost-recommendations-5.69.0.tgz +0 -0
- package/components/tryghost-referrers-5.69.0.tgz +0 -0
- package/components/tryghost-security-5.69.0.tgz +0 -0
- package/components/tryghost-session-service-5.69.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.69.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.69.0.tgz +0 -0
- package/components/tryghost-staff-service-5.69.0.tgz +0 -0
- package/components/tryghost-stats-service-5.69.0.tgz +0 -0
- package/components/tryghost-tiers-5.69.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.69.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.69.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.69.0.tgz +0 -0
- package/components/tryghost-webmentions-5.69.0.tgz +0 -0
- package/content/themes/source/assets/built/source.js +1 -1
- package/content/themes/source/assets/built/source.js.map +1 -1
- package/content/themes/source/assets/js/casper.js +1 -1
- package/content/themes/source/package.json +1 -1
- package/core/built/admin/assets/admin-x-settings/{CodeEditorView-1643e910.mjs → CodeEditorView-b2459562.mjs} +275 -273
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-b4c2ef5b.mjs → index-0152ca19.mjs} +4813 -4625
- package/core/built/admin/assets/admin-x-settings/{limit-service-c343d244.mjs → limit-service-b0370dbc.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{modals-8b0aeacd.mjs → modals-4201e074.mjs} +10184 -9731
- package/core/built/admin/assets/{chunk.143.da2301d9edb6ad0b5496.js → chunk.143.6e2a218178c3c44fac33.js} +5 -5
- package/core/built/admin/assets/{chunk.178.efec05ab07e1e216d657.js → chunk.178.a7169a78c2b1e9582f2e.js} +4 -4
- package/core/built/admin/assets/{chunk.940.b09a2c2049ed9c373dd0.js → chunk.940.159ed6caf77114942465.js} +34 -34
- package/core/built/admin/assets/{ghost-193c98abe66483968da753e54917e87a.js → ghost-a012143b70f1e3bffed74252f7a736df.js} +189 -188
- package/core/built/admin/assets/ghost-a1d3bcea0bd360af258a27e29323ba35.css +1 -0
- package/core/built/admin/assets/ghost-dark-638883e1af34829a72578aad50905f7c.css +1 -0
- package/core/built/admin/assets/koenig-lexical/Koenig-editor-1.png +0 -0
- package/core/built/admin/assets/koenig-lexical/Koenig-editor-2.png +0 -0
- package/core/built/admin/assets/koenig-lexical/assets/fonts/Inter.ttf +0 -0
- package/core/built/admin/assets/koenig-lexical/index.css +6 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +129021 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +1442 -0
- package/core/built/admin/index.html +5 -5
- package/core/frontend/helpers/img_url.js +6 -6
- package/core/frontend/helpers/readable_url.js +32 -0
- package/core/frontend/helpers/recommendations.js +1 -1
- package/core/frontend/helpers/tpl/recommendations.hbs +4 -5
- package/core/server/GhostServer.js +9 -0
- package/core/server/api/endpoints/incoming-recommendations.js +20 -0
- package/core/server/api/endpoints/index.js +4 -0
- package/core/server/api/endpoints/posts-public.js +3 -2
- package/core/server/api/endpoints/utils/serializers/output/config.js +0 -1
- package/core/server/data/migrations/utils/schema.js +31 -2
- package/core/server/data/migrations/versions/5.69/2023-10-06-15-06-00-rename-recommendations-reason-to-description.js +3 -0
- package/core/server/data/schema/commands.js +20 -0
- package/core/server/data/schema/schema.js +1 -1
- package/core/server/models/product.js +1 -1
- package/core/server/services/newsletters/NewslettersService.js +2 -2
- package/core/server/services/oembed/TwitterOEmbedProvider.js +5 -1
- package/core/server/services/recommendations/RecommendationServiceWrapper.js +10 -0
- package/core/server/services/settings/SettingsBREADService.js +1 -5
- package/core/server/web/api/endpoints/admin/routes.js +3 -0
- package/core/shared/config/defaults.json +0 -4
- package/core/shared/labs.js +3 -4
- package/package.json +157 -157
- package/yarn.lock +147 -141
- package/components/tryghost-adapter-cache-memory-ttl-5.67.0.tgz +0 -0
- package/components/tryghost-adapter-cache-redis-5.67.0.tgz +0 -0
- package/components/tryghost-adapter-manager-5.67.0.tgz +0 -0
- package/components/tryghost-announcement-bar-settings-5.67.0.tgz +0 -0
- package/components/tryghost-api-framework-5.67.0.tgz +0 -0
- package/components/tryghost-api-version-compatibility-service-5.67.0.tgz +0 -0
- package/components/tryghost-audience-feedback-5.67.0.tgz +0 -0
- package/components/tryghost-bookshelf-repository-5.67.0.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.67.0.tgz +0 -0
- package/components/tryghost-collections-5.67.0.tgz +0 -0
- package/components/tryghost-constants-5.67.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.67.0.tgz +0 -0
- package/components/tryghost-domain-events-5.67.0.tgz +0 -0
- package/components/tryghost-donations-5.67.0.tgz +0 -0
- package/components/tryghost-dynamic-routing-events-5.67.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.67.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.67.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.67.0.tgz +0 -0
- package/components/tryghost-email-events-5.67.0.tgz +0 -0
- package/components/tryghost-email-suppression-list-5.67.0.tgz +0 -0
- package/components/tryghost-event-aware-cache-wrapper-5.67.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.67.0.tgz +0 -0
- package/components/tryghost-external-media-inliner-5.67.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.67.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.67.0.tgz +0 -0
- package/components/tryghost-i18n-5.67.0.tgz +0 -0
- package/components/tryghost-importer-handler-content-files-5.67.0.tgz +0 -0
- package/components/tryghost-importer-revue-5.67.0.tgz +0 -0
- package/components/tryghost-in-memory-repository-5.67.0.tgz +0 -0
- package/components/tryghost-job-manager-5.67.0.tgz +0 -0
- package/components/tryghost-link-redirects-5.67.0.tgz +0 -0
- package/components/tryghost-link-replacer-5.67.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.67.0.tgz +0 -0
- package/components/tryghost-magic-link-5.67.0.tgz +0 -0
- package/components/tryghost-mail-events-5.67.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.67.0.tgz +0 -0
- package/components/tryghost-member-attribution-5.67.0.tgz +0 -0
- package/components/tryghost-member-events-5.67.0.tgz +0 -0
- package/components/tryghost-members-csv-5.67.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.67.0.tgz +0 -0
- package/components/tryghost-members-importer-5.67.0.tgz +0 -0
- package/components/tryghost-members-offers-5.67.0.tgz +0 -0
- package/components/tryghost-members-payments-5.67.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.67.0.tgz +0 -0
- package/components/tryghost-mentions-email-report-5.67.0.tgz +0 -0
- package/components/tryghost-milestones-5.67.0.tgz +0 -0
- package/components/tryghost-minifier-5.67.0.tgz +0 -0
- package/components/tryghost-model-to-domain-event-interceptor-5.67.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.67.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.67.0.tgz +0 -0
- package/components/tryghost-mw-error-handler-5.67.0.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.67.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.67.0.tgz +0 -0
- package/components/tryghost-mw-version-match-5.67.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.67.0.tgz +0 -0
- package/components/tryghost-nql-filter-expansions-5.67.0.tgz +0 -0
- package/components/tryghost-oembed-service-5.67.0.tgz +0 -0
- package/components/tryghost-package-json-5.67.0.tgz +0 -0
- package/components/tryghost-post-events-5.67.0.tgz +0 -0
- package/components/tryghost-post-revisions-5.67.0.tgz +0 -0
- package/components/tryghost-posts-service-5.67.0.tgz +0 -0
- package/components/tryghost-recommendations-5.67.0.tgz +0 -0
- package/components/tryghost-referrers-5.67.0.tgz +0 -0
- package/components/tryghost-security-5.67.0.tgz +0 -0
- package/components/tryghost-session-service-5.67.0.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.67.0.tgz +0 -0
- package/components/tryghost-slack-notifications-5.67.0.tgz +0 -0
- package/components/tryghost-staff-service-5.67.0.tgz +0 -0
- package/components/tryghost-stats-service-5.67.0.tgz +0 -0
- package/components/tryghost-tiers-5.67.0.tgz +0 -0
- package/components/tryghost-update-check-service-5.67.0.tgz +0 -0
- package/components/tryghost-verification-trigger-5.67.0.tgz +0 -0
- package/components/tryghost-version-notifications-data-service-5.67.0.tgz +0 -0
- package/components/tryghost-webmentions-5.67.0.tgz +0 -0
- package/core/built/admin/assets/ghost-9e454659c8c0896ed10336b640d1b1eb.css +0 -1
- package/core/built/admin/assets/ghost-dark-1d24186396fe74946c926faa9faa247b.css +0 -1
- /package/core/built/admin/assets/{chunk.940.b09a2c2049ed9c373dd0.js.LICENSE.txt → chunk.940.159ed6caf77114942465.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.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22cdnUrl%22%3A%22%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.69%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%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%22a38c847eaf%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%226c3c017a49%22%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
<link integrity="" rel="stylesheet" href="assets/vendor-3e6947aa681f0fb82b193090e520dc73.css">
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-a1d3bcea0bd360af258a27e29323ba35.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
58
58
|
|
|
59
59
|
<script src="assets/vendor-71d23939faaa5d1478ead73d917111b9.js"></script>
|
|
60
|
-
<script src="assets/chunk.940.
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
60
|
+
<script src="assets/chunk.940.159ed6caf77114942465.js"></script>
|
|
61
|
+
<script src="assets/chunk.143.6e2a218178c3c44fac33.js"></script>
|
|
62
|
+
<script src="assets/ghost-a012143b70f1e3bffed74252f7a736df.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -54,7 +54,7 @@ module.exports = function imgUrl(requestedImageUrl, options) {
|
|
|
54
54
|
// ignore errors and just return the original URL
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
return requestedImageUrl;
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -117,7 +117,7 @@ function getUnsplashImage(imagePath, sizeOptions) {
|
|
|
117
117
|
const parsedUrl = new URL(imagePath);
|
|
118
118
|
const {requestedSize, imageSizes, requestedFormat} = sizeOptions;
|
|
119
119
|
|
|
120
|
-
if (requestedFormat) {
|
|
120
|
+
if (requestedFormat) {
|
|
121
121
|
const supportedFormats = ['avif', 'gif', 'jpg', 'png', 'webp'];
|
|
122
122
|
if (supportedFormats.includes(requestedFormat)) {
|
|
123
123
|
parsedUrl.searchParams.set('fm', requestedFormat);
|
|
@@ -150,13 +150,13 @@ function getUnsplashImage(imagePath, sizeOptions) {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
|
-
*
|
|
154
|
-
* @param {string} imagePath
|
|
155
|
-
* @param {Object} sizeOptions
|
|
153
|
+
*
|
|
154
|
+
* @param {string} imagePath
|
|
155
|
+
* @param {Object} sizeOptions
|
|
156
156
|
* @param {string} sizeOptions.requestedSize
|
|
157
157
|
* @param {Object[]} sizeOptions.imageSizes
|
|
158
158
|
* @param {string} [sizeOptions.requestedFormat]
|
|
159
|
-
* @returns
|
|
159
|
+
* @returns
|
|
160
160
|
*/
|
|
161
161
|
function getImageWithSize(imagePath, sizeOptions) {
|
|
162
162
|
const hasLeadingSlash = imagePath[0] === '/';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// # Readable URL helper
|
|
2
|
+
// Usage: `{{readable_url "https://google.com"}}`
|
|
3
|
+
//
|
|
4
|
+
// Returns a human readable URL for the given URL, e.g. google.com for https://www.google.com?query=1#section
|
|
5
|
+
|
|
6
|
+
const logging = require('@tryghost/logging');
|
|
7
|
+
const sentry = require('../../shared/sentry');
|
|
8
|
+
const errors = require('@tryghost/errors');
|
|
9
|
+
const {SafeString} = require('../services/handlebars');
|
|
10
|
+
|
|
11
|
+
function captureError(message) {
|
|
12
|
+
const error = new errors.IncorrectUsageError({message});
|
|
13
|
+
sentry.captureException(error);
|
|
14
|
+
logging.error(error);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = function readableUrl(inputUrl) {
|
|
18
|
+
if (!inputUrl || typeof inputUrl !== 'string') {
|
|
19
|
+
captureError(`Expected a string, received ${inputUrl}.`);
|
|
20
|
+
return new SafeString('');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(inputUrl);
|
|
25
|
+
const readable = url.hostname.replace(/^www\./, '') + url.pathname.replace(/\/$/, '');
|
|
26
|
+
|
|
27
|
+
return new SafeString(readable);
|
|
28
|
+
} catch (e) {
|
|
29
|
+
captureError(`The string "${inputUrl}" could not be parsed as URL.`);
|
|
30
|
+
return new SafeString(inputUrl);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -63,7 +63,7 @@ async function fetchRecommendations(apiOptions) {
|
|
|
63
63
|
*/
|
|
64
64
|
function parseOptions(options) {
|
|
65
65
|
let limit = options.limit ?? 5;
|
|
66
|
-
let order = options.order ?? '
|
|
66
|
+
let order = options.order ?? 'created_at desc';
|
|
67
67
|
let filter = options.filter ?? '';
|
|
68
68
|
let page = options.page ?? 1;
|
|
69
69
|
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
<ul class="recommendations">
|
|
3
3
|
{{#each recommendations as |rec|}}
|
|
4
4
|
<li class="recommendation">
|
|
5
|
-
<a href="{{rec.url}}">
|
|
5
|
+
<a href="{{rec.url}}" data-recommendation="{{rec.id}}" target="_blank" rel="noopener">
|
|
6
6
|
<img class="recommendation-favicon" src="{{rec.favicon}}" alt="{{rec.title}}">
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
</div>
|
|
7
|
+
<span class="recommendation-url">{{readable_url rec.url}}</span>
|
|
8
|
+
<h5 class="recommendation-title">{{rec.title}}</h5>
|
|
9
|
+
<p class="recommendation-description">{{rec.description}}</p>
|
|
11
10
|
</a>
|
|
12
11
|
</li>
|
|
13
12
|
{{/each}}
|
|
@@ -4,6 +4,7 @@ const debug = require('@tryghost/debug')('server');
|
|
|
4
4
|
const errors = require('@tryghost/errors');
|
|
5
5
|
const tpl = require('@tryghost/tpl');
|
|
6
6
|
const logging = require('@tryghost/logging');
|
|
7
|
+
const metrics = require('@tryghost/metrics');
|
|
7
8
|
const notify = require('./notify');
|
|
8
9
|
const moment = require('moment');
|
|
9
10
|
const stoppable = require('stoppable');
|
|
@@ -169,8 +170,16 @@ class GhostServer {
|
|
|
169
170
|
try {
|
|
170
171
|
// If we never fully started, there's nothing to stop
|
|
171
172
|
if (this.httpServer && this.httpServer.listening) {
|
|
173
|
+
// Time how long it takes to close all in-flight requests
|
|
174
|
+
const startTime = Date.now();
|
|
175
|
+
|
|
172
176
|
// We stop the server first so that no new long running requests or processes can be started
|
|
173
177
|
await this._stopServer();
|
|
178
|
+
|
|
179
|
+
const shutdownDuration = Date.now() - startTime;
|
|
180
|
+
if (shutdownDuration > 15000) {
|
|
181
|
+
metrics.metric('long-shutdown', shutdownDuration);
|
|
182
|
+
}
|
|
174
183
|
}
|
|
175
184
|
// Do all of the cleanup tasks
|
|
176
185
|
await this._cleanup();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const recommendations = require('../../services/recommendations');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
docName: 'recommendations',
|
|
5
|
+
|
|
6
|
+
browse: {
|
|
7
|
+
headers: {
|
|
8
|
+
cacheInvalidate: false
|
|
9
|
+
},
|
|
10
|
+
options: [
|
|
11
|
+
'limit',
|
|
12
|
+
'page'
|
|
13
|
+
],
|
|
14
|
+
permissions: true,
|
|
15
|
+
validation: {},
|
|
16
|
+
async query(frame) {
|
|
17
|
+
return await recommendations.incomingRecommendationController.browse(frame);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -209,6 +209,10 @@ module.exports = {
|
|
|
209
209
|
return apiFramework.pipeline(require('./recommendations'), localUtils);
|
|
210
210
|
},
|
|
211
211
|
|
|
212
|
+
get incomingRecommendations() {
|
|
213
|
+
return apiFramework.pipeline(require('./incoming-recommendations'), localUtils);
|
|
214
|
+
},
|
|
215
|
+
|
|
212
216
|
/**
|
|
213
217
|
* Content API Controllers
|
|
214
218
|
*
|
|
@@ -26,8 +26,9 @@ const rejectPrivateFieldsTransformer = input => mapQuery(input, function (value,
|
|
|
26
26
|
function generateOptionsData(frame, options) {
|
|
27
27
|
return options.reduce((memo, option) => {
|
|
28
28
|
let value = frame.options?.[option];
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
|
|
30
|
+
if (['include', 'fields', 'formats'].includes(option) && typeof value === 'string') {
|
|
31
|
+
value = value.split(',').sort();
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
if (option === 'page') {
|
|
@@ -91,7 +91,35 @@ function createSetNullableMigration(table, column, options = {}) {
|
|
|
91
91
|
if (options.disableForeignKeyChecks) {
|
|
92
92
|
await knex.raw('SET FOREIGN_KEY_CHECKS=1;').transacting(knex);
|
|
93
93
|
}
|
|
94
|
-
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {string} table
|
|
101
|
+
* @param {string} from
|
|
102
|
+
* @param {string} to
|
|
103
|
+
*
|
|
104
|
+
* @returns {Migration}
|
|
105
|
+
*/
|
|
106
|
+
function createRenameColumnMigration(table, from, to) {
|
|
107
|
+
return createNonTransactionalMigration(
|
|
108
|
+
async function up(knex) {
|
|
109
|
+
const hasColumn = await knex.schema.hasColumn(table, to);
|
|
110
|
+
if (hasColumn) {
|
|
111
|
+
logging.warn(`Renaming ${table}.${from} to ${table}.${to} column - skipping as column ${table}.${to} already exists`);
|
|
112
|
+
} else {
|
|
113
|
+
await commands.renameColumn(table, from, to, knex);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
async function down(knex) {
|
|
117
|
+
const hasColumn = await knex.schema.hasColumn(table, from);
|
|
118
|
+
if (hasColumn) {
|
|
119
|
+
logging.warn(`Renaming ${table}.${to} to ${table}.${from} column - skipping as column ${table}.${from} already exists`);
|
|
120
|
+
} else {
|
|
121
|
+
await commands.renameColumn(table, to, from, knex);
|
|
122
|
+
}
|
|
95
123
|
}
|
|
96
124
|
);
|
|
97
125
|
}
|
|
@@ -134,7 +162,8 @@ module.exports = {
|
|
|
134
162
|
createAddColumnMigration,
|
|
135
163
|
createDropColumnMigration,
|
|
136
164
|
createSetNullableMigration,
|
|
137
|
-
createDropNullableMigration
|
|
165
|
+
createDropNullableMigration,
|
|
166
|
+
createRenameColumnMigration
|
|
138
167
|
};
|
|
139
168
|
|
|
140
169
|
/**
|
|
@@ -156,6 +156,25 @@ async function dropColumn(tableName, column, transaction = db.knex, columnSpec =
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* @param {string} tableName
|
|
161
|
+
* @param {string} from
|
|
162
|
+
* @param {string} to
|
|
163
|
+
* @param {import('knex').Knex.Transaction} [transaction]
|
|
164
|
+
*/
|
|
165
|
+
async function renameColumn(tableName, from, to, transaction = db.knex) {
|
|
166
|
+
logging.info(`Renaming column '${from}' to '${to}' in table '${tableName}'`);
|
|
167
|
+
|
|
168
|
+
if (DatabaseInfo.isMySQL(transaction)) {
|
|
169
|
+
// The knex helper does a lot of interesting things with foreign keys that are slow on bigger MySQL clusters
|
|
170
|
+
return await transaction.raw(`ALTER TABLE \`${tableName}\` RENAME COLUMN \`${from}\` TO \`${to}\`;`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return await transaction.schema.table(tableName, function (table) {
|
|
174
|
+
table.renameColumn(from, to);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
159
178
|
/**
|
|
160
179
|
* Adds an unique index to a table over the given columns.
|
|
161
180
|
*
|
|
@@ -520,6 +539,7 @@ module.exports = {
|
|
|
520
539
|
addForeign,
|
|
521
540
|
dropForeign,
|
|
522
541
|
addColumn,
|
|
542
|
+
renameColumn,
|
|
523
543
|
dropColumn,
|
|
524
544
|
setNullable,
|
|
525
545
|
dropNullable,
|
|
@@ -1076,7 +1076,7 @@ module.exports = {
|
|
|
1076
1076
|
excerpt: {type: 'string', maxlength: 2000, nullable: true},
|
|
1077
1077
|
featured_image: {type: 'string', maxlength: 2000, nullable: true},
|
|
1078
1078
|
favicon: {type: 'string', maxlength: 2000, nullable: true},
|
|
1079
|
-
|
|
1079
|
+
description: {type: 'string', maxlength: 2000, nullable: true},
|
|
1080
1080
|
one_click_subscribe: {type: 'boolean', nullable: false, defaultTo: false},
|
|
1081
1081
|
created_at: {type: 'dateTime', nullable: false},
|
|
1082
1082
|
updated_at: {type: 'dateTime', nullable: true}
|
|
@@ -82,7 +82,7 @@ const Product = ghostBookshelf.Model.extend({
|
|
|
82
82
|
return benefitToSave.name.toLowerCase() === existingBenefit.get('name').toLowerCase();
|
|
83
83
|
});
|
|
84
84
|
if (existingBenefitModel) {
|
|
85
|
-
benefitToSave.
|
|
85
|
+
benefitToSave.id = existingBenefitModel.id;
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
|
|
@@ -199,7 +199,7 @@ class NewslettersService {
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
let updatedNewsletter;
|
|
202
|
-
|
|
202
|
+
|
|
203
203
|
try {
|
|
204
204
|
updatedNewsletter = await this.NewsletterModel.edit(cleanedAttrs, options);
|
|
205
205
|
} catch (error) {
|
|
@@ -215,7 +215,7 @@ class NewslettersService {
|
|
|
215
215
|
|
|
216
216
|
// Load relations correctly in the response
|
|
217
217
|
updatedNewsletter = await this.NewsletterModel.findOne({id: updatedNewsletter.id}, {...options, require: true});
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
await this.respondWithEmailVerification(updatedNewsletter, emailsToVerify);
|
|
220
220
|
return updatedNewsletter;
|
|
221
221
|
}
|
|
@@ -23,7 +23,7 @@ class TwitterOEmbedProvider {
|
|
|
23
23
|
* @returns {Promise<boolean>}
|
|
24
24
|
*/
|
|
25
25
|
async canSupportRequest(url) {
|
|
26
|
-
return url.host === 'twitter.com' && TWITTER_PATH_REGEX.test(url.pathname);
|
|
26
|
+
return (url.host === 'twitter.com' || url.host === 'x.com') && TWITTER_PATH_REGEX.test(url.pathname);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -33,6 +33,10 @@ class TwitterOEmbedProvider {
|
|
|
33
33
|
* @returns {Promise<object>}
|
|
34
34
|
*/
|
|
35
35
|
async getOEmbedData(url, externalRequest) {
|
|
36
|
+
if (url.host === 'x.com') { // api is still at twitter.com... also not certain how people are getting x urls because twitter currently redirects every x host to twitter
|
|
37
|
+
url = new URL('https://twitter.com' + url.pathname);
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
const [match, tweetId] = url.pathname.match(TWITTER_PATH_REGEX);
|
|
37
41
|
if (!match) {
|
|
38
42
|
return null;
|
|
@@ -28,6 +28,11 @@ class RecommendationServiceWrapper {
|
|
|
28
28
|
*/
|
|
29
29
|
service;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @type {import('@tryghost/recommendations').IncomingRecommendationController}
|
|
33
|
+
*/
|
|
34
|
+
incomingRecommendationController;
|
|
35
|
+
|
|
31
36
|
/**
|
|
32
37
|
* @type {import('@tryghost/recommendations').IncomingRecommendationService}
|
|
33
38
|
*/
|
|
@@ -51,6 +56,7 @@ class RecommendationServiceWrapper {
|
|
|
51
56
|
RecommendationController,
|
|
52
57
|
WellknownService,
|
|
53
58
|
BookshelfClickEventRepository,
|
|
59
|
+
IncomingRecommendationController,
|
|
54
60
|
IncomingRecommendationService,
|
|
55
61
|
IncomingRecommendationEmailRenderer
|
|
56
62
|
} = require('@tryghost/recommendations');
|
|
@@ -125,6 +131,10 @@ class RecommendationServiceWrapper {
|
|
|
125
131
|
service: this.service
|
|
126
132
|
});
|
|
127
133
|
|
|
134
|
+
this.incomingRecommendationController = new IncomingRecommendationController({
|
|
135
|
+
service: this.incomingRecommendationService
|
|
136
|
+
});
|
|
137
|
+
|
|
128
138
|
if (labs.isSet('recommendations')) {
|
|
129
139
|
this.service.init().catch(logging.error);
|
|
130
140
|
this.incomingRecommendationService.init().catch(logging.error);
|
|
@@ -65,11 +65,7 @@ class SettingsBREADService {
|
|
|
65
65
|
// @todo: need to make this more generic?
|
|
66
66
|
const adminUrl = urlUtils.urlFor('admin', true);
|
|
67
67
|
const signinURL = new URL(adminUrl);
|
|
68
|
-
signinURL.hash = `/settings/
|
|
69
|
-
// NOTE: to be removed in future, this is to ensure that the new settings are used when enabled
|
|
70
|
-
if (labsService && labsService.isSet('adminXSettings')) {
|
|
71
|
-
signinURL.hash = `/settings-x/portal/edit?verifyEmail=${token}`;
|
|
72
|
-
}
|
|
68
|
+
signinURL.hash = `/settings/portal/edit?verifyEmail=${token}`;
|
|
73
69
|
|
|
74
70
|
return signinURL.href;
|
|
75
71
|
}
|
|
@@ -354,5 +354,8 @@ module.exports = function apiRoutes() {
|
|
|
354
354
|
router.put('/recommendations/:id', mw.authAdminApi, http(api.recommendations.edit));
|
|
355
355
|
router.del('/recommendations/:id', mw.authAdminApi, http(api.recommendations.destroy));
|
|
356
356
|
|
|
357
|
+
// Incoming recommendations
|
|
358
|
+
router.get('/incoming_recommendations', mw.authAdminApi, http(api.incomingRecommendations.browse));
|
|
359
|
+
|
|
357
360
|
return router;
|
|
358
361
|
};
|
|
@@ -197,10 +197,6 @@
|
|
|
197
197
|
"url": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/comments-ui.min.js",
|
|
198
198
|
"version": "0.13"
|
|
199
199
|
},
|
|
200
|
-
"editor": {
|
|
201
|
-
"url": "https://cdn.jsdelivr.net/ghost/koenig-lexical@~{version}/dist/koenig-lexical.umd.js",
|
|
202
|
-
"version": "0.4"
|
|
203
|
-
},
|
|
204
200
|
"signupForm": {
|
|
205
201
|
"url": "https://cdn.jsdelivr.net/ghost/signup-form@~{version}/umd/signup-form.min.js",
|
|
206
202
|
"version": "0.1"
|
package/core/shared/labs.js
CHANGED
|
@@ -19,7 +19,8 @@ const GA_FEATURES = [
|
|
|
19
19
|
'themeErrorsNotification',
|
|
20
20
|
'outboundLinkTagging',
|
|
21
21
|
'announcementBar',
|
|
22
|
-
'signupForm'
|
|
22
|
+
'signupForm',
|
|
23
|
+
'lexicalEditor'
|
|
23
24
|
];
|
|
24
25
|
|
|
25
26
|
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
|
@@ -27,8 +28,7 @@ const GA_FEATURES = [
|
|
|
27
28
|
const BETA_FEATURES = [
|
|
28
29
|
'i18n',
|
|
29
30
|
'activitypub',
|
|
30
|
-
'webmentions'
|
|
31
|
-
'lexicalEditor'
|
|
31
|
+
'webmentions'
|
|
32
32
|
];
|
|
33
33
|
|
|
34
34
|
const ALPHA_FEATURES = [
|
|
@@ -37,7 +37,6 @@ const ALPHA_FEATURES = [
|
|
|
37
37
|
'websockets',
|
|
38
38
|
'stripeAutomaticTax',
|
|
39
39
|
'emailCustomization',
|
|
40
|
-
'adminXSettings',
|
|
41
40
|
'mailEvents',
|
|
42
41
|
'collectionsCard',
|
|
43
42
|
'tipsAndDonations',
|