ghost 5.129.1 → 5.130.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 (58) hide show
  1. package/components/tryghost-i18n-5.130.0.tgz +0 -0
  2. package/core/boot.js +6 -5
  3. package/core/built/admin/assets/admin-x-activitypub/admin-x-activitypub.js +2 -2
  4. package/core/built/admin/assets/admin-x-activitypub/{index-CWqPqbZ6.mjs → index-BhgdXgH_.mjs} +2 -2
  5. package/core/built/admin/assets/admin-x-activitypub/{index-t8sCkPyJ.mjs → index-rDFm98Ub.mjs} +15557 -15454
  6. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-FMecMk4J.mjs → CodeEditorView-bO8i1M7l.mjs} +2 -2
  7. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +3 -3
  8. package/core/built/admin/assets/admin-x-settings/{index-CqcRQSMi.mjs → index-BeD9DTp3.mjs} +512 -490
  9. package/core/built/admin/assets/admin-x-settings/index-DIak5kz8.mjs +30462 -0
  10. package/core/built/admin/assets/admin-x-settings/{modals-CfWblo-N.mjs → modals-DLPpqlUq.mjs} +1212 -1210
  11. package/core/built/admin/assets/{chunk.524.5330e1e5569947d7b7f2.js → chunk.524.2443bfd380e6da0cbabd.js} +7 -7
  12. package/core/built/admin/assets/{chunk.582.f6a6d1826e91aafd496b.js → chunk.582.434476dff5ddc79ed054.js} +9 -9
  13. package/core/built/admin/assets/{chunk.383.09219fde42568dd42ed5.js → chunk.728.214803966b81ffdb1acd.js} +6957 -6441
  14. package/core/built/admin/assets/ghost-3d0ad0c58f433d5735532bf25d4fd423.css +1 -0
  15. package/core/built/admin/assets/{ghost-1bce1a4ebfdfc6f6f333a827f40f69a6.js → ghost-5d9c65b5c4ef960a664cd664b2616dea.js} +121 -125
  16. package/core/built/admin/assets/ghost-dark-f19869a3fd0ef48c525149b9c87e4241.css +1 -0
  17. package/core/built/admin/assets/posts/posts.js +6740 -6712
  18. package/core/built/admin/assets/stats/stats.js +13289 -13235
  19. package/core/built/admin/index.html +5 -5
  20. package/core/frontend/helpers/ghost_head.js +3 -4
  21. package/core/frontend/helpers/match.js +3 -0
  22. package/core/frontend/meta/get-meta.js +1 -1
  23. package/core/frontend/meta/schema.js +45 -24
  24. package/core/frontend/services/proxy.js +6 -0
  25. package/core/server/api/endpoints/utils/serializers/input/settings.js +3 -1
  26. package/core/server/api/endpoints/utils/serializers/input/utils/settings-key-group-mapper.js +3 -1
  27. package/core/server/api/endpoints/utils/serializers/input/utils/settings-key-type-mapper.js +3 -1
  28. package/core/server/api/endpoints/utils/serializers/output/config.js +2 -1
  29. package/core/server/data/migrations/versions/5.130/2025-07-11-14-14-54-add-explore-settings.js +16 -0
  30. package/core/server/data/schema/default-settings/default-settings.json +18 -0
  31. package/core/server/data/tinybird/ARCHITECTURE.md +420 -0
  32. package/core/server/data/tinybird/README.md +84 -0
  33. package/core/server/data/tinybird/scripts/configure-ghost.sh +65 -0
  34. package/core/server/services/activitypub/ActivityPubService.js +24 -4
  35. package/core/server/services/activitypub/ActivityPubService.ts +27 -7
  36. package/core/server/services/activitypub/ActivityPubServiceWrapper.js +11 -5
  37. package/core/server/services/email-service/EmailRenderer.js +11 -0
  38. package/core/server/services/email-service/email-templates/partials/styles.hbs +12 -7
  39. package/core/server/services/explore-ping/ExplorePingService.js +44 -33
  40. package/core/server/services/members/members-api/repositories/MemberRepository.js +1 -4
  41. package/core/server/services/public-config/config.js +4 -0
  42. package/core/server/services/settings/settings-service.js +4 -0
  43. package/core/server/services/settings-helpers/SettingsHelpers.js +114 -7
  44. package/core/server/services/settings-helpers/index.js +2 -1
  45. package/core/server/services/stats/StatsService.js +1 -2
  46. package/core/server/services/themes/installer.js +17 -3
  47. package/core/shared/config/env/config.production.json +4 -0
  48. package/core/shared/labs.js +0 -1
  49. package/core/shared/settings-cache/CacheManager.js +2 -0
  50. package/package.json +17 -17
  51. package/tsconfig.tsbuildinfo +1 -1
  52. package/yarn.lock +293 -236
  53. package/components/tryghost-i18n-5.129.1.tgz +0 -0
  54. package/core/built/admin/assets/admin-x-settings/index-DjbkRFc2.mjs +0 -26802
  55. package/core/built/admin/assets/ghost-415f8e3c36dbe0e09f87608628da382d.css +0 -1
  56. package/core/built/admin/assets/ghost-dark-2043bca95512f1fa2ff0bea2f8a632b0.css +0 -1
  57. package/core/server/data/tinybird/readme.md +0 -40
  58. /package/core/built/admin/assets/{chunk.383.09219fde42568dd42ed5.js.LICENSE.txt → chunk.728.214803966b81ffdb1acd.js.LICENSE.txt} +0 -0
@@ -6,7 +6,7 @@
6
6
  <title>Ghost</title>
7
7
 
8
8
 
9
- <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.129%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%2237bd1e3e4d%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%22d9d1575574%22%2C%22adminXActivitypubFilename%22%3A%22admin-x-activitypub.js%22%2C%22adminXActivitypubHash%22%3A%22420f33378b%22%2C%22postsFilename%22%3A%22posts.js%22%2C%22postsHash%22%3A%223c29fa5ebb%22%2C%22statsFilename%22%3A%22stats.js%22%2C%22statsHash%22%3A%224949fe6470%22%2C%22adminXActivitypubCustomUrl%22%3A%22https%3A%2F%2Fcdn.jsdelivr.net%2Fghost%2Fadmin-x-activitypub%400%2Fdist%2Fadmin-x-activitypub.js%22%7D" />
9
+ <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.130%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%2237bd1e3e4d%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%2262b55e2aae%22%2C%22adminXActivitypubFilename%22%3A%22admin-x-activitypub.js%22%2C%22adminXActivitypubHash%22%3A%226d96d71fe8%22%2C%22postsFilename%22%3A%22posts.js%22%2C%22postsHash%22%3A%223ad9cd1892%22%2C%22statsFilename%22%3A%22stats.js%22%2C%22statsHash%22%3A%22701e8fc366%22%2C%22adminXActivitypubCustomUrl%22%3A%22https%3A%2F%2Fcdn.jsdelivr.net%2Fghost%2Fadmin-x-activitypub%400%2Fdist%2Fadmin-x-activitypub.js%22%7D" />
10
10
 
11
11
  <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1, minimal-ui, viewport-fit=cover" />
12
12
  <meta name="pinterest" content="nopin" />
@@ -28,7 +28,7 @@
28
28
  </style>
29
29
 
30
30
  <link integrity="" rel="stylesheet" href="assets/vendor-0ede59da8efb5e28fa929557f7ff7154.css">
31
- <link integrity="" rel="stylesheet" href="assets/ghost-415f8e3c36dbe0e09f87608628da382d.css" title="light">
31
+ <link integrity="" rel="stylesheet" href="assets/ghost-3d0ad0c58f433d5735532bf25d4fd423.css" title="light">
32
32
 
33
33
 
34
34
  </head>
@@ -48,8 +48,8 @@
48
48
  <div id="ember-basic-dropdown-wormhole"></div>
49
49
 
50
50
  <script src="assets/vendor-c89102f24c3d9502e9db741509767580.js"></script>
51
- <script src="assets/chunk.383.09219fde42568dd42ed5.js"></script>
52
- <script src="assets/chunk.524.5330e1e5569947d7b7f2.js"></script>
53
- <script src="assets/ghost-1bce1a4ebfdfc6f6f333a827f40f69a6.js"></script>
51
+ <script src="assets/chunk.728.214803966b81ffdb1acd.js"></script>
52
+ <script src="assets/chunk.524.2443bfd380e6da0cbabd.js"></script>
53
+ <script src="assets/ghost-5d9c65b5c4ef960a664cd664b2616dea.js"></script>
54
54
  </body>
55
55
  </html>
@@ -2,7 +2,7 @@
2
2
  // Usage: `{{ghost_head}}`
3
3
  //
4
4
  // Outputs scripts and other assets at the top of a Ghost theme
5
- const {labs, metaData, settingsCache, config, blogIcon, urlUtils, getFrontendKey} = require('../services/proxy');
5
+ const {labs, metaData, settingsCache, config, blogIcon, urlUtils, getFrontendKey, settingsHelpers} = require('../services/proxy');
6
6
  const {escapeExpression, SafeString} = require('../services/handlebars');
7
7
  const {generateCustomFontCss, isValidCustomFont, isValidCustomHeadingFont} = require('@tryghost/custom-fonts');
8
8
  // BAD REQUIRE
@@ -362,9 +362,8 @@ module.exports = async function ghost_head(options) { // eslint-disable-line cam
362
362
  if (!_.isEmpty(tagCodeInjection)) {
363
363
  head.push(tagCodeInjection);
364
364
  }
365
- const isTbTrackingEnabled = labs.isSet('trafficAnalytics');
366
- const hasTbConfig = config.get('tinybird') && config.get('tinybird:tracker');
367
- if (isTbTrackingEnabled && hasTbConfig) {
365
+ // Use settingsHelpers to check if web analytics is enabled (includes all necessary checks)
366
+ if (settingsHelpers.isWebAnalyticsEnabled()) {
368
367
  head.push(getTinybirdTrackerScript(dataRoot));
369
368
  // Set a flag in response locals to indicate tracking script is being served
370
369
  if (dataRoot._locals) {
@@ -57,6 +57,9 @@ const handleMatch = (data, operator, value) => {
57
57
  case '<=':
58
58
  result = data <= value;
59
59
  break;
60
+ case '~':
61
+ result = _.isString(data) && _.isString(value) && data.includes(value);
62
+ break;
60
63
  case '~^':
61
64
  result = _.isString(data) && _.isString(value) && data.startsWith(value);
62
65
  break;
@@ -21,7 +21,7 @@ const getOgImage = require('./og-image');
21
21
  const getPaginatedUrl = require('./paginated-url');
22
22
  const getPublishedDate = require('./published-date');
23
23
  const getRssUrl = require('./rss-url');
24
- const getSchema = require('./schema');
24
+ const {getSchema} = require('./schema');
25
25
  const getStructuredData = require('./structured-data');
26
26
  const getTitle = require('./title');
27
27
  const getTwitterImage = require('./twitter-image');
@@ -48,34 +48,54 @@ function trimSchema(schema) {
48
48
  return schemaObject;
49
49
  }
50
50
 
51
- function trimSameAs(data, context) {
51
+ // note that website isn't included here
52
+ const SOCIAL_PLATFORMS = ['facebook', 'twitter', 'threads', 'bluesky', 'mastodon', 'tiktok', 'youtube', 'instagram', 'linkedin'];
53
+
54
+ /**
55
+ * Build the `sameAs` array for schema.org Person objects.
56
+ *
57
+ * @param {Object} author either `data.author` or `data.<post|page>.primary_author`.
58
+ * Expected to contain `website` plus any supported social usernames.
59
+ * @returns {string[]} URLs for the author website and each populated social profile.
60
+ */
61
+ function trimSameAs(author) {
62
+ if (!author || Object.keys(author).length === 0) {
63
+ return [];
64
+ }
65
+
52
66
  const sameAs = [];
53
67
 
54
- if (context === 'post' || context === 'page') {
55
- if (data[context].primary_author.website) {
56
- sameAs.push(escapeExpression(data[context].primary_author.website));
57
- }
58
- if (data[context].primary_author.facebook) {
59
- sameAs.push(socialUrls.facebook(data[context].primary_author.facebook));
60
- }
61
- if (data[context].primary_author.twitter) {
62
- sameAs.push(socialUrls.twitter(data[context].primary_author.twitter));
63
- }
64
- } else if (context === 'author') {
65
- if (data.author.website) {
66
- sameAs.push(escapeExpression(data.author.website));
67
- }
68
- if (data.author.facebook) {
69
- sameAs.push(socialUrls.facebook(data.author.facebook));
70
- }
71
- if (data.author.twitter) {
72
- sameAs.push(socialUrls.twitter(data.author.twitter));
73
- }
68
+ if (author.website) {
69
+ sameAs.push(escapeExpression(author.website));
74
70
  }
75
71
 
72
+ SOCIAL_PLATFORMS.forEach((platform) => {
73
+ if (author[platform] && typeof socialUrls[platform] === 'function') {
74
+ sameAs.push(escapeExpression(socialUrls[platform](author[platform])));
75
+ }
76
+ });
77
+
76
78
  return sameAs;
77
79
  }
78
80
 
81
+ /**
82
+ * Build contributor objects for schema.org Article schema.
83
+ *
84
+ * @param {Object[]} authors - Array of author objects (excluding primary author)
85
+ */
86
+ function buildContributorObjects(authors) {
87
+ return authors.map(author => trimSchema({
88
+ '@type': 'Person',
89
+ name: escapeExpression(author.name),
90
+ image: author.profile_image ? schemaImageObject({url: author.profile_image}) : null,
91
+ url: author.url || null,
92
+ sameAs: trimSameAs(author),
93
+ description: author.meta_description ?
94
+ escapeExpression(author.meta_description) :
95
+ null
96
+ }));
97
+ }
98
+
79
99
  function getPostSchema(metaData, data) {
80
100
  // CASE: metaData.excerpt for post context is populated by either the custom excerpt, the meta description,
81
101
  // or the automated excerpt of 50 words. It is empty for any other context.
@@ -94,11 +114,12 @@ function getPostSchema(metaData, data) {
94
114
  name: escapeExpression(data[context].primary_author.name),
95
115
  image: schemaImageObject(metaData.authorImage),
96
116
  url: metaData.authorUrl,
97
- sameAs: trimSameAs(data, context),
117
+ sameAs: trimSameAs(data[context].primary_author),
98
118
  description: data[context].primary_author.metaDescription ?
99
119
  escapeExpression(data[context].primary_author.metaDescription) :
100
120
  null
101
121
  },
122
+ contributor: data[context].authors && data[context].authors.length > 1 ? buildContributorObjects(data[context].authors.slice(1)) : null,
102
123
  headline: escapeExpression(metaData.metaTitle),
103
124
  url: metaData.url,
104
125
  datePublished: metaData.publishedDate,
@@ -150,7 +171,7 @@ function getAuthorSchema(metaData, data) {
150
171
  const schema = {
151
172
  '@context': 'https://schema.org',
152
173
  '@type': 'Person',
153
- sameAs: trimSameAs(data, 'author'),
174
+ sameAs: trimSameAs(data.author),
154
175
  name: escapeExpression(data.author.name),
155
176
  url: metaData.authorUrl,
156
177
  image: schemaImageObject(metaData.authorImage) || schemaImageObject(metaData.coverImage),
@@ -179,4 +200,4 @@ function getSchema(metaData, data) {
179
200
  return null;
180
201
  }
181
202
 
182
- module.exports = getSchema;
203
+ module.exports = {getSchema, SOCIAL_PLATFORMS};
@@ -1,6 +1,7 @@
1
1
  // This file contains everything that the helpers and frontend apps require from the core of Ghost
2
2
  const settingsCache = require('../../shared/settings-cache');
3
3
  const config = require('../../shared/config');
4
+ const settingsHelpers = require('../../server/services/settings-helpers');
4
5
 
5
6
  // Require from the handlebars framework
6
7
  const {SafeString} = require('./handlebars');
@@ -47,6 +48,11 @@ module.exports = {
47
48
  // TODO: Only expose "get"
48
49
  settingsCache: settingsCache,
49
50
 
51
+ // Settings helpers for calculated settings
52
+ settingsHelpers: {
53
+ isWebAnalyticsEnabled: settingsHelpers.isWebAnalyticsEnabled.bind(settingsHelpers)
54
+ },
55
+
50
56
  // TODO: Expose less of the API to make this safe
51
57
  api: require('../../server/api').endpoints,
52
58
 
@@ -78,7 +78,9 @@ const EDITABLE_SETTINGS = [
78
78
  'heading_font',
79
79
  'blocked_email_domains',
80
80
  'require_email_mfa',
81
- 'social_web'
81
+ 'social_web',
82
+ 'explore_ping',
83
+ 'explore_ping_growth'
82
84
  ];
83
85
 
84
86
  module.exports = {
@@ -48,7 +48,9 @@ const keyGroupMapping = {
48
48
  portal_name: 'portal',
49
49
  portal_button: 'portal',
50
50
  portal_plans: 'portal',
51
- require_email_mfa: 'security'
51
+ require_email_mfa: 'security',
52
+ explore_ping: 'explore',
53
+ explore_ping_growth: 'explore'
52
54
  };
53
55
 
54
56
  const mapKeyToGroup = (key) => {
@@ -55,7 +55,9 @@ const keyTypeMapping = {
55
55
  labs: 'object',
56
56
  unsplash: 'object',
57
57
  bulk_email_settings: 'object',
58
- require_email_mfa: 'boolean'
58
+ require_email_mfa: 'boolean',
59
+ explore_ping: 'boolean',
60
+ explore_ping_growth: 'boolean'
59
61
  };
60
62
 
61
63
  const mapKeyToType = (key) => {
@@ -22,7 +22,8 @@ module.exports = {
22
22
  'pintura',
23
23
  'signupForm',
24
24
  'stats',
25
- 'security'
25
+ 'security',
26
+ 'exploreTestimonialsUrl'
26
27
  ];
27
28
 
28
29
  frame.response = {
@@ -0,0 +1,16 @@
1
+ const {combineTransactionalMigrations, addSetting} = require('../../utils');
2
+
3
+ module.exports = combineTransactionalMigrations(
4
+ addSetting({
5
+ key: 'explore_ping',
6
+ value: 'true',
7
+ type: 'boolean',
8
+ group: 'explore'
9
+ }),
10
+ addSetting({
11
+ key: 'explore_ping_growth',
12
+ value: 'false',
13
+ type: 'boolean',
14
+ group: 'explore'
15
+ })
16
+ );
@@ -628,5 +628,23 @@
628
628
  },
629
629
  "type": "boolean"
630
630
  }
631
+ },
632
+ "explore": {
633
+ "explore_ping": {
634
+ "defaultValue": "true",
635
+ "validations": {
636
+ "isEmpty": false,
637
+ "isIn": [["true", "false"]]
638
+ },
639
+ "type": "boolean"
640
+ },
641
+ "explore_ping_growth": {
642
+ "defaultValue": "false",
643
+ "validations": {
644
+ "isEmpty": false,
645
+ "isIn": [["true", "false"]]
646
+ },
647
+ "type": "boolean"
648
+ }
631
649
  }
632
650
  }