ghost 5.129.2 → 6.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/components/tryghost-i18n-6.0.0-alpha.1.tgz +0 -0
  2. package/core/boot.js +0 -2
  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-B12913rO.mjs → index-BhgdXgH_.mjs} +2 -2
  5. package/core/built/admin/assets/admin-x-activitypub/{index-B7EmcyVj.mjs → index-rDFm98Ub.mjs} +15498 -15418
  6. package/core/built/admin/assets/admin-x-settings/{CodeEditorView-l2Ex2555.mjs → CodeEditorView-CA2VVtOE.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-C6P_16OJ.mjs → index-Dl3F40x5.mjs} +2 -2
  9. package/core/built/admin/assets/admin-x-settings/index-KA2tjCkS.mjs +30462 -0
  10. package/core/built/admin/assets/admin-x-settings/{modals-CY1xx4Em.mjs → modals-B0zezufO.mjs} +2185 -2183
  11. package/core/built/admin/assets/{chunk.524.c8313bccd308920abf9c.js → chunk.524.996c1c4d269fa6a50e90.js} +7 -7
  12. package/core/built/admin/assets/{chunk.582.e4feab981886cfc91835.js → chunk.582.75cf44e5d1b925adf16d.js} +9 -9
  13. package/core/built/admin/assets/{chunk.728.214803966b81ffdb1acd.js → chunk.728.985c45ad584b4b91ca60.js} +124 -124
  14. package/core/built/admin/assets/{ghost-db9fcb8c1f65776f3ee11c39f19a660b.js → ghost-5a5b2112df68dfaf6813ce38cad16847.js} +29 -30
  15. package/core/built/admin/assets/posts/posts.js +6641 -6621
  16. package/core/built/admin/assets/stats/stats.js +12770 -12724
  17. package/core/built/admin/index.html +4 -4
  18. package/core/frontend/helpers/get.js +4 -2
  19. package/core/frontend/helpers/ghost_head.js +71 -77
  20. package/core/frontend/helpers/match.js +3 -0
  21. package/core/frontend/meta/canonical-url.js +1 -7
  22. package/core/frontend/meta/context-object.js +1 -1
  23. package/core/frontend/meta/get-meta.js +1 -4
  24. package/core/frontend/meta/og-image.js +1 -1
  25. package/core/frontend/meta/og-type.js +0 -2
  26. package/core/frontend/meta/schema.js +20 -1
  27. package/core/frontend/meta/twitter-image.js +1 -1
  28. package/core/frontend/meta/url.js +1 -12
  29. package/core/frontend/services/rendering/context.js +0 -8
  30. package/core/server/api/endpoints/index.js +0 -4
  31. package/core/server/api/endpoints/session.js +0 -9
  32. package/core/server/api/endpoints/utils/serializers/input/settings.js +3 -3
  33. package/core/server/api/endpoints/utils/serializers/input/utils/settings-filter-type-group-mapper.js +0 -1
  34. package/core/server/api/endpoints/utils/serializers/input/utils/settings-key-group-mapper.js +3 -2
  35. package/core/server/api/endpoints/utils/serializers/input/utils/settings-key-type-mapper.js +3 -2
  36. package/core/server/api/endpoints/utils/serializers/output/all.js +1 -1
  37. package/core/server/api/endpoints/utils/serializers/output/config.js +2 -1
  38. package/core/server/api/endpoints/utils/serializers/output/index.js +0 -4
  39. package/core/server/api/endpoints/utils/serializers/output/mappers/snippets.js +1 -5
  40. package/core/server/api/endpoints/utils/serializers/output/members.js +0 -2
  41. package/core/server/api/endpoints/utils/validators/input/index.js +0 -4
  42. package/core/server/data/importer/importers/data/Base.js +1 -3
  43. package/core/server/data/importer/importers/data/SettingsImporter.js +1 -3
  44. package/core/server/data/migrations/utils/index.js +1 -4
  45. package/core/server/data/migrations/utils/permissions.js +14 -6
  46. package/core/server/data/migrations/utils/settings.js +39 -22
  47. package/core/server/data/migrations/versions/4.47/2022-05-03-15-30-final-v4.js +2 -0
  48. package/core/server/data/migrations/versions/4.47/2022-05-04-10-03-no-op.js +6 -0
  49. package/core/server/data/migrations/versions/5.100/2024-11-06-04-45-15-add-activitypub-integration.js +4 -2
  50. package/core/server/data/migrations/versions/5.113/2025-03-07-12-24-00-add-super-editor.js +4 -2
  51. package/core/server/data/migrations/versions/5.130/2025-07-11-14-14-54-add-explore-settings.js +16 -0
  52. package/core/server/data/migrations/versions/5.3/2022-07-06-07-58-add-ghost-explore-integration-role.js +4 -2
  53. package/core/server/data/migrations/versions/5.3/2022-07-06-09-17-add-ghost-explore-integration.js +4 -2
  54. package/core/server/data/migrations/versions/5.3/2022-07-06-09-26-add-ghost-explore-integration-api-key.js +4 -2
  55. package/core/server/data/migrations/versions/5.40/2023-03-21-18-42-add-self-serve-integration-role.js +4 -2
  56. package/core/server/data/migrations/versions/5.40/2023-03-21-18-52-add-self-serve-integration.js +4 -2
  57. package/core/server/data/migrations/versions/5.40/2023-03-21-19-02-add-self-serve-integration-api-key.js +4 -2
  58. package/core/server/data/migrations/versions/5.63/2023-09-13-13-03-10-add-ghost-core-content-integration.js +4 -2
  59. package/core/server/data/migrations/versions/5.63/2023-09-13-13-34-11-add-ghost-core-content-integration-key.js +4 -2
  60. package/core/server/data/migrations/versions/6.0/2025-06-20-01-41-54-remove-updated-by-column.js +46 -0
  61. package/core/server/data/migrations/versions/6.0/2025-06-20-13-41-55-remove-created-by-column.js +47 -0
  62. package/core/server/data/migrations/versions/6.0/2025-06-23-09-49-25-add-missing-member-uuids.js +22 -0
  63. package/core/server/data/migrations/versions/6.0/2025-06-23-10-03-26-members-nullable-uuid.js +5 -0
  64. package/core/server/data/migrations/versions/6.0/2025-06-24-09-19-42-use-object-id-for-hardcoded-user-id.js +95 -0
  65. package/core/server/data/migrations/versions/6.0/2025-06-25-15-03-29-remove-amp-from-settings.js +6 -0
  66. package/core/server/data/migrations/versions/6.0/2025-06-30-13-59-10-remove-mail-events-table.js +3 -0
  67. package/core/server/data/migrations/versions/6.0/2025-06-30-14-00-00-update-feature-image-alt-length.js +25 -0
  68. package/core/server/data/schema/default-settings/default-settings.json +18 -13
  69. package/core/server/data/schema/fixtures/FixtureManager.js +128 -5
  70. package/core/server/data/schema/fixtures/fixtures.json +4 -6
  71. package/core/server/data/schema/fixtures/index.js +3 -1
  72. package/core/server/data/schema/schema.js +20 -65
  73. package/core/server/data/seeders/DataGenerator.js +11 -2
  74. package/core/server/data/seeders/importers/EmailsImporter.js +1 -3
  75. package/core/server/data/seeders/importers/LabelsImporter.js +1 -3
  76. package/core/server/data/seeders/importers/MembersImporter.js +0 -1
  77. package/core/server/data/seeders/importers/MembersStripeCustomersImporter.js +1 -2
  78. package/core/server/data/seeders/importers/MembersStripeCustomersSubscriptionsImporter.js +0 -1
  79. package/core/server/data/seeders/importers/PostsImporter.js +0 -1
  80. package/core/server/data/seeders/importers/RolesUsersImporter.js +6 -1
  81. package/core/server/data/seeders/importers/TagsImporter.js +1 -2
  82. package/core/server/data/seeders/importers/UsersImporter.js +1 -2
  83. package/core/server/data/tinybird/README.md +9 -14
  84. package/core/server/models/base/bookshelf.js +8 -1
  85. package/core/server/models/base/plugins/events.js +0 -28
  86. package/core/server/models/base/plugins/user-type.js +10 -36
  87. package/core/server/models/post.js +25 -10
  88. package/core/server/models/relations/authors.js +2 -2
  89. package/core/server/models/settings.js +1 -14
  90. package/core/server/models/user.js +33 -6
  91. package/core/server/services/activitypub/ActivityPubService.js +23 -4
  92. package/core/server/services/activitypub/ActivityPubService.ts +27 -8
  93. package/core/server/services/email-service/email-templates/partials/styles.hbs +0 -14
  94. package/core/server/services/explore-ping/ExplorePingService.js +44 -33
  95. package/core/server/services/link-redirection/README.md +1 -1
  96. package/core/server/services/mentions/MentionSendingService.js +1 -1
  97. package/core/server/services/public-config/config.js +4 -0
  98. package/core/server/services/themes/installer.js +17 -3
  99. package/core/server/services/update-check/UpdateCheckService.js +18 -2
  100. package/core/server/services/url/config.js +0 -2
  101. package/core/server/web/api/app.js +4 -0
  102. package/core/server/web/api/endpoints/admin/middleware.js +8 -9
  103. package/core/server/web/api/endpoints/admin/routes.js +0 -2
  104. package/core/server/web/comments/routes.js +3 -0
  105. package/core/server/web/shared/middleware/index.js +4 -0
  106. package/core/server/web/shared/middleware/max-limit-cap.js +27 -0
  107. package/core/server/web/shared/middleware/pretty-urls.js +3 -1
  108. package/core/server/web/shared/middleware/redirect-amp-urls.js +36 -0
  109. package/core/shared/config/defaults.json +2 -0
  110. package/core/shared/config/env/config.production.json +4 -0
  111. package/core/shared/config/overrides.json +1 -4
  112. package/core/shared/labs.js +5 -6
  113. package/core/shared/max-limit-cap.js +61 -0
  114. package/package.json +7 -8
  115. package/tsconfig.tsbuildinfo +1 -1
  116. package/yarn.lock +203 -261
  117. package/components/tryghost-i18n-5.129.2.tgz +0 -0
  118. package/core/built/admin/assets/admin-x-settings/index-DoLRADbr.mjs +0 -30308
  119. package/core/built/admin/assets/img/amp-d7b72aae3315fda95921fb575dfca100.svg +0 -4
  120. package/core/frontend/apps/amp/index.js +0 -30
  121. package/core/frontend/apps/amp/lib/helpers/amp_analytics.js +0 -32
  122. package/core/frontend/apps/amp/lib/helpers/amp_components.js +0 -48
  123. package/core/frontend/apps/amp/lib/helpers/amp_content.js +0 -214
  124. package/core/frontend/apps/amp/lib/helpers/amp_style.js +0 -8
  125. package/core/frontend/apps/amp/lib/router.js +0 -95
  126. package/core/frontend/apps/amp/lib/views/amp.hbs +0 -1046
  127. package/core/frontend/meta/amp-url.js +0 -14
  128. package/core/server/api/endpoints/mail-events.js +0 -17
  129. package/core/server/api/endpoints/utils/serializers/output/mail-events.js +0 -9
  130. package/core/server/api/endpoints/utils/validators/input/mail-events.js +0 -7
  131. package/core/server/data/migrations/utils/constants.js +0 -3
  132. package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +0 -61
  133. package/core/server/data/migrations/versions/4.0/02-add-status-column-to-members.js +0 -11
  134. package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +0 -81
  135. package/core/server/data/migrations/versions/4.0/04-drop-apps-related-tables.js +0 -10
  136. package/core/server/data/migrations/versions/4.0/05-add-members-subscribe-events-table.js +0 -9
  137. package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +0 -53
  138. package/core/server/data/migrations/versions/4.0/07-alter-unique-constraint-for-posts-slug.js +0 -7
  139. package/core/server/data/migrations/versions/4.0/08-add-members-login-events-table.js +0 -7
  140. package/core/server/data/migrations/versions/4.0/09-add-members-email-change-events-table.js +0 -9
  141. package/core/server/data/migrations/versions/4.0/10-add-members-status-events-table.js +0 -9
  142. package/core/server/data/migrations/versions/4.0/11-add-members-paid-subscription-events-table.js +0 -12
  143. package/core/server/data/migrations/versions/4.0/12-delete-apps-related-settings-keys.js +0 -16
  144. package/core/server/data/migrations/versions/4.0/13-add-members-payment-events-table.js +0 -10
  145. package/core/server/data/migrations/versions/4.0/14-remove-orphaned-stripe-records.js +0 -36
  146. package/core/server/data/migrations/versions/4.0/15-add-frontmatter-column-to-meta.js +0 -7
  147. package/core/server/data/migrations/versions/4.0/16-refactor-slack-setting.js +0 -96
  148. package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +0 -41
  149. package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +0 -201
  150. package/core/server/data/migrations/versions/4.0/19-remove-labs-members-setting.js +0 -10
  151. package/core/server/data/migrations/versions/4.0/20-refactor-unsplash-setting.js +0 -41
  152. package/core/server/data/migrations/versions/4.0/21-sanitize-email-batches-provider-id.js +0 -8
  153. package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +0 -87
  154. package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +0 -66
  155. package/core/server/data/migrations/versions/4.0/24-add-missing-email-permissions.js +0 -36
  156. package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +0 -129
  157. package/core/server/data/migrations/versions/4.0/26-add-cascade-on-delete.js +0 -76
  158. package/core/server/data/migrations/versions/4.0/27-add-primary-key-brute-migrations-lock.js +0 -9
  159. package/core/server/data/migrations/versions/4.0/28-add-webhook-intergrations-foreign-key.js +0 -16
  160. package/core/server/data/migrations/versions/4.0/29-fix-foreign-key-for-members-stripe-customers-subscriptions.js +0 -35
  161. package/core/server/data/migrations/versions/4.0/30-set-default-accent-color.js +0 -21
  162. package/core/server/data/migrations/versions/4.1/01-fix-backup-content-permission-typo.js +0 -15
  163. package/core/server/data/migrations/versions/4.1/02-add-unique-constraint-for-member-stripe-tables.js +0 -21
  164. package/core/server/data/migrations/versions/4.11/01-add-oauth-user-data.js +0 -12
  165. package/core/server/data/migrations/versions/4.11/02-add-email-verification-required-setting.js +0 -43
  166. package/core/server/data/migrations/versions/4.12/01-add-email-only-column-to-posts-meta-table.js +0 -7
  167. package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +0 -39
  168. package/core/server/data/migrations/versions/4.13/01-add-members-stripe-connect-auth-permission-to-administrators.js +0 -6
  169. package/core/server/data/migrations/versions/4.13/02-add-members-products-events-table.js +0 -33
  170. package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +0 -73
  171. package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +0 -61
  172. package/core/server/data/migrations/versions/4.15/01-add-temp-members-analytic-events-table.js +0 -12
  173. package/core/server/data/migrations/versions/4.16/01-add-custom-theme-settings-table.js +0 -9
  174. package/core/server/data/migrations/versions/4.17/01-add-custom-theme-settings-permissions.js +0 -21
  175. package/core/server/data/migrations/versions/4.17/02-add-offers-table.js +0 -19
  176. package/core/server/data/migrations/versions/4.17/03-add-offers-permissions.js +0 -35
  177. package/core/server/data/migrations/versions/4.19/01-add-active-column-to-offers.js +0 -7
  178. package/core/server/data/migrations/versions/4.19/02-add-offer-redemptions-table.js +0 -8
  179. package/core/server/data/migrations/versions/4.2/01-fix-incorrect-mrr-delta-events.js +0 -13
  180. package/core/server/data/migrations/versions/4.20/01-remove-offer-redemptions-table.js +0 -19
  181. package/core/server/data/migrations/versions/4.20/02-remove-offers-table.js +0 -30
  182. package/core/server/data/migrations/versions/4.20/03-add-offers-table.js +0 -21
  183. package/core/server/data/migrations/versions/4.20/04-add-offer-redemptions-table.js +0 -9
  184. package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +0 -44
  185. package/core/server/data/migrations/versions/4.22/01-add-is-launch-complete-setting.js +0 -8
  186. package/core/server/data/migrations/versions/4.22/02-update-launch-complete-setting-from-user-data.js +0 -39
  187. package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +0 -59
  188. package/core/server/data/migrations/versions/4.3/01-add-products-table.js +0 -9
  189. package/core/server/data/migrations/versions/4.3/02-add-members-products-table.js +0 -8
  190. package/core/server/data/migrations/versions/4.3/03-add-default-product.js +0 -39
  191. package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +0 -50
  192. package/core/server/data/migrations/versions/4.3/05-add-stripe-products-table.js +0 -9
  193. package/core/server/data/migrations/versions/4.3/06-add-stripe-prices-table.js +0 -15
  194. package/core/server/data/migrations/versions/4.3/07-add-products-permissions.js +0 -29
  195. package/core/server/data/migrations/versions/4.3/08-migrate-members-signup-setting.js +0 -109
  196. package/core/server/data/migrations/versions/4.3/09-add-price-id-column-to-subscriptions-table.js +0 -10
  197. package/core/server/data/migrations/versions/4.3/10-populate-stripe-price-id-in-subscriptions.js +0 -20
  198. package/core/server/data/migrations/versions/4.33/2022-01-14-11-50-add-type-column-to-products.js +0 -12
  199. package/core/server/data/migrations/versions/4.33/2022-01-14-11-51-add-default-free-tier.js +0 -40
  200. package/core/server/data/migrations/versions/4.33/2022-01-18-09-07-remove-duplicate-offer-redemptions.js +0 -46
  201. package/core/server/data/migrations/versions/4.33/2022-01-19-10-43-add-active-column-to-products-table.js +0 -7
  202. package/core/server/data/migrations/versions/4.34/2022-01-25-13-53-add-welcome-page-url-column-to-products.js +0 -7
  203. package/core/server/data/migrations/versions/4.35/2022-01-20-05-55-add-post-products-table.js +0 -8
  204. package/core/server/data/migrations/versions/4.35/2022-01-30-15-17-set-welcome-page-url-from-settings.js +0 -45
  205. package/core/server/data/migrations/versions/4.35/2022-02-01-11-48-update-email-recipient-filter-column-type.js +0 -19
  206. package/core/server/data/migrations/versions/4.35/2022-02-01-12-03-update-recipient-filter-column-type.js +0 -19
  207. package/core/server/data/migrations/versions/4.35/2022-02-02-10-38-add-default-content-visibility-tiers-setting.js +0 -8
  208. package/core/server/data/migrations/versions/4.35/2022-02-02-13-10-transform-specific-tiers-default-content-visibility.js +0 -147
  209. package/core/server/data/migrations/versions/4.35/2022-02-04-04-34-populate-empty-portal-products.js +0 -60
  210. package/core/server/data/migrations/versions/4.36/2022-02-07-14-34-add-last-seen-at-column-to-members.js +0 -10
  211. package/core/server/data/migrations/versions/4.37/2022-02-21-09-53-backfill-members-last-seen-at-column.js +0 -32
  212. package/core/server/data/migrations/versions/4.38/2022-03-01-08-46-add-visibility-to-tiers.js +0 -11
  213. package/core/server/data/migrations/versions/4.38/2022-03-03-16-12-add-visibility-to-tiers.js +0 -8
  214. package/core/server/data/migrations/versions/4.38/2022-03-03-16-17-drop-tiers-visible-column.js +0 -7
  215. package/core/server/data/migrations/versions/4.39/2022-03-07-10-57-update-free-products-visibility-column.js +0 -66
  216. package/core/server/data/migrations/versions/4.39/2022-03-07-10-57-update-products-visibility-column.js +0 -36
  217. package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +0 -99
  218. package/core/server/data/migrations/versions/4.4/02-migrate-members-signup-access.js +0 -126
  219. package/core/server/data/migrations/versions/4.40/2022-03-07-14-37-add-members-cancel-events-table.js +0 -8
  220. package/core/server/data/migrations/versions/4.40/2022-03-15-06-40-add-offers-admin-integration-permission-roles.js +0 -23
  221. package/core/server/data/migrations/versions/4.40/2022-03-15-06-40-add-tiers-admin-integration-permission-roles.js +0 -20
  222. package/core/server/data/migrations/versions/4.42/2022-03-21-17-17-add.js +0 -25
  223. package/core/server/data/migrations/versions/4.42/2022-03-30-15-44-add-newsletter-permissions.js +0 -28
  224. package/core/server/data/migrations/versions/4.43/2022-03-28-19-26-recreate-newsletter-table.js +0 -29
  225. package/core/server/data/migrations/versions/4.43/2022-03-29-14-45-add-members-newsletters-table.js +0 -7
  226. package/core/server/data/migrations/versions/4.43/2022-04-01-10-13-add-post-newsletter-relation.js +0 -108
  227. package/core/server/data/migrations/versions/4.43/2022-04-06-09-47-add-type-column-to-paid-subscription-events.js +0 -7
  228. package/core/server/data/migrations/versions/4.43/2022-04-06-14-56-add-email-newsletter-relation.js +0 -8
  229. package/core/server/data/migrations/versions/4.43/2022-04-08-10-45-add-subscription-id-to-mrr-events.js +0 -7
  230. package/core/server/data/migrations/versions/4.44/2022-04-06-15-22-populate-type-column-for-paid-subscription-events.js +0 -21
  231. package/core/server/data/migrations/versions/4.44/2022-04-08-11-54-add-cancelled-events.js +0 -51
  232. package/core/server/data/migrations/versions/4.44/2022-04-11-08-24-add-newsletter-permissions.js +0 -33
  233. package/core/server/data/migrations/versions/4.44/2022-04-11-10-54-add-mrr-to-subscriptions.js +0 -8
  234. package/core/server/data/migrations/versions/4.44/2022-04-12-07-33-fill-mrr.js +0 -29
  235. package/core/server/data/migrations/versions/4.44/2022-04-13-12-00-remove-newsletter-sender-name-not-null-constraint.js +0 -33
  236. package/core/server/data/migrations/versions/4.44/2022-04-15-07-53-add-offer-id-to-subscriptions.js +0 -9
  237. package/core/server/data/migrations/versions/4.45/2022-04-19-12-23-backfill-subscriptions-offers.js +0 -60
  238. package/core/server/data/migrations/versions/4.45/2022-04-20-11-25-add-newsletter-read-permission.js +0 -9
  239. package/core/server/data/migrations/versions/4.45/2022-04-21-02-55-add-notifications-key-entry-to-settings-table.js +0 -8
  240. package/core/server/data/migrations/versions/4.46/2022-04-13-12-00-add-created-at-newsletters.js +0 -6
  241. package/core/server/data/migrations/versions/4.46/2022-04-13-12-01-add-updated-at-newsletters.js +0 -6
  242. package/core/server/data/migrations/versions/4.46/2022-04-13-12-02-fill-created-at-newsletters.js +0 -19
  243. package/core/server/data/migrations/versions/4.46/2022-04-13-12-03-drop-nullable-created-at-newsletters.js +0 -3
  244. package/core/server/data/migrations/versions/4.46/2022-04-13-12-08-newsletters-show-header-name.js +0 -7
  245. package/core/server/data/migrations/versions/4.46/2022-04-13-12-57-add-uuid-column-to-newsletters.js +0 -8
  246. package/core/server/data/migrations/versions/4.46/2022-04-13-12-58-fill-uuid-for-newsletters.js +0 -19
  247. package/core/server/data/migrations/versions/4.46/2022-04-13-12-59-drop-nullable-uuid-newsletters.js +0 -3
  248. package/core/server/data/migrations/versions/4.46/2022-04-13-13-00-add-default-newsletter.js +0 -92
  249. package/core/server/data/migrations/versions/4.46/2022-04-20-08-39-map-subscribers-to-default-newsletter.js +0 -66
  250. package/core/server/data/migrations/versions/4.46/2022-04-22-07-43-add-newsletter-id-to-subscribe-events.js +0 -9
  251. package/core/server/data/migrations/versions/4.46/2022-04-27-07-59-set-newsletter-id-subscribe-events.js +0 -31
  252. package/core/server/data/migrations/versions/4.47/2022-05-03-15-30-update-newsletter-sending-options.js +0 -34
  253. package/core/server/data/migrations/versions/4.47/2022-05-04-10-03-transform-newsletter-header-image.js +0 -26
  254. package/core/server/data/migrations/versions/4.5/01-add-stripe-price-description-column.js +0 -7
  255. package/core/server/data/migrations/versions/4.5/02-add-product-description-column.js +0 -7
  256. package/core/server/data/migrations/versions/4.5/03-give-label-read-permissions-to-editors.js +0 -14
  257. package/core/server/data/migrations/versions/4.5/04-remove-unique-constraint-from-product-name.js +0 -13
  258. package/core/server/data/migrations/versions/4.5/05-rename-default-product-to-site-title.js +0 -38
  259. package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +0 -47
  260. package/core/server/data/migrations/versions/4.7/01-add-monthly-price-column-to-products.js +0 -7
  261. package/core/server/data/migrations/versions/4.7/02-add-yearly-price-column-to-products.js +0 -7
  262. package/core/server/data/migrations/versions/4.7/03-add-labs-setting.js +0 -42
  263. package/core/server/data/migrations/versions/4.8/01-add-feature-image-alt-column-to-posts-meta.js +0 -7
  264. package/core/server/data/migrations/versions/4.8/02-add-feature-image-caption-column-to-posts-meta.js +0 -7
  265. package/core/server/data/migrations/versions/4.8/03-add-default-product-portal-products.js +0 -69
  266. package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +0 -124
  267. package/core/server/data/migrations/versions/4.9/01-add-reset-all-passwords-permission.js +0 -11
  268. package/core/server/data/migrations/versions/4.9/02-add-benefits-table.js +0 -9
  269. package/core/server/data/migrations/versions/4.9/03-add-products-benefits-table.js +0 -8
  270. package/core/server/data/migrations/versions/4.9/04-add-member-segment-to-email-batches.js +0 -7
  271. package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +0 -87
  272. package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +0 -47
  273. package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +0 -39
  274. package/core/server/models/mail-event.js +0 -12
  275. package/core/server/services/mail-events/BookshelfMailEventRepository.js +0 -40
  276. package/core/server/services/mail-events/InMemoryMailEventRepository.js +0 -10
  277. package/core/server/services/mail-events/InMemoryMailEventRepository.ts +0 -8
  278. package/core/server/services/mail-events/MailEvent.js +0 -20
  279. package/core/server/services/mail-events/MailEvent.ts +0 -10
  280. package/core/server/services/mail-events/MailEventRepository.js +0 -2
  281. package/core/server/services/mail-events/MailEventRepository.ts +0 -5
  282. package/core/server/services/mail-events/MailEventService.js +0 -124
  283. package/core/server/services/mail-events/MailEventService.ts +0 -169
  284. package/core/server/services/mail-events/index.js +0 -21
  285. package/core/server/services/mail-events/libraries.d.ts +0 -2
  286. /package/core/built/admin/assets/{chunk.728.214803966b81ffdb1acd.js.LICENSE.txt → chunk.728.985c45ad584b4b91ca60.js.LICENSE.txt} +0 -0
@@ -138,26 +138,12 @@ module.exports = function (Bookshelf) {
138
138
 
139
139
  /**
140
140
  * Adding resources implies setting these properties on the server side
141
- * - set `created_by` based on the context
142
- * - set `updated_by` based on the context
143
141
  * - the bookshelf `timestamps` plugin sets `created_at` and `updated_at`
144
142
  * - if plugin is disabled (e.g. import) we have a fallback condition
145
143
  *
146
144
  * Exceptions: internal context or importing
147
145
  */
148
146
  onCreating: function onCreating(model, attr, options) {
149
- if (Object.prototype.hasOwnProperty.call(schema.tables[this.tableName], 'created_by')) {
150
- if (!options.importing || (options.importing && !this.get('created_by'))) {
151
- this.set('created_by', String(this.contextUser(options)));
152
- }
153
- }
154
-
155
- if (Object.prototype.hasOwnProperty.call(schema.tables[this.tableName], 'updated_by')) {
156
- if (!options.importing) {
157
- this.set('updated_by', String(this.contextUser(options)));
158
- }
159
- }
160
-
161
147
  if (Object.prototype.hasOwnProperty.call(schema.tables[this.tableName], 'created_at')) {
162
148
  if (!model.get('created_at')) {
163
149
  model.set('created_at', new Date());
@@ -199,9 +185,7 @@ module.exports = function (Bookshelf) {
199
185
 
200
186
  /**
201
187
  * Changing resources implies setting these properties on the server side
202
- * - set `updated_by` based on the context
203
188
  * - ensure `created_at` never changes
204
- * - ensure `created_by` never changes
205
189
  * - the bookshelf `timestamps` plugin sets `updated_at` automatically
206
190
  *
207
191
  * Exceptions:
@@ -216,24 +200,12 @@ module.exports = function (Bookshelf) {
216
200
  model.changed = _.omit(model.changed, this.relationships);
217
201
  }
218
202
 
219
- if (Object.prototype.hasOwnProperty.call(schema.tables[this.tableName], 'updated_by')) {
220
- if (!options.importing && !options.migrating) {
221
- this.set('updated_by', String(this.contextUser(options)));
222
- }
223
- }
224
-
225
203
  if (options && options.context && !options.context.internal && !options.importing) {
226
204
  if (Object.prototype.hasOwnProperty.call(schema.tables[this.tableName], 'created_at')) {
227
205
  if (model.hasDateChanged('created_at', {beforeWrite: true})) {
228
206
  model.set('created_at', this.previous('created_at'));
229
207
  }
230
208
  }
231
-
232
- if (Object.prototype.hasOwnProperty.call(schema.tables[this.tableName], 'created_by')) {
233
- if (model.hasChanged('created_by')) {
234
- model.set('created_by', String(this.previous('created_by')));
235
- }
236
- }
237
209
  }
238
210
 
239
211
  // CASE: do not allow setting only the `updated_at` field, exception: importing
@@ -8,7 +8,7 @@ const messages = {
8
8
  /**
9
9
  * @param {import('bookshelf')} Bookshelf
10
10
  */
11
- module.exports = function (Bookshelf) {
11
+ module.exports = function (Bookshelf, pluginOptions) {
12
12
  Bookshelf.Model = Bookshelf.Model.extend({
13
13
  getActor(options = {context: {}}) {
14
14
  if (options.context && options.context.integration) {
@@ -29,37 +29,22 @@ module.exports = function (Bookshelf) {
29
29
  },
30
30
 
31
31
  // Get the user from the options object
32
- contextUser: function contextUser(options) {
32
+ contextUser: async function contextUser(options) {
33
33
  options = options || {};
34
34
  options.context = options.context || {};
35
35
 
36
36
  if (options.context.user) {
37
37
  return options.context.user;
38
38
  } else if (options.context.integration) {
39
- /**
40
- * @NOTE:
41
- *
42
- * This is a dirty fix until we get rid of all the x_by columns
43
- * @deprecated x_by columns are deprecated as of v1.0 - instead we should use the actions table
44
- * see https://github.com/TryGhost/Ghost/issues/10286.
45
- *
46
- * We return the owner ID '1' in case an integration updates or creates resources.
47
- *
48
- * ---
49
- *
50
- * Why using ID '1'? WAIT. What???????
51
- *
52
- * See https://github.com/TryGhost/Ghost/issues/9299.
53
- *
54
- * We currently don't read the correct owner ID from the database and assume it's '1'.
55
- * This is a leftover from switching from auto increment ID's to Object ID's.
56
- * But this takes too long to refactor out now. If an internal update happens, we also
57
- * use ID '1'. This logic exists for a LONG while now. The owner ID only changes from '1' to something else,
58
- * if you transfer ownership.
59
- */
60
- return Bookshelf.Model.internalUser;
39
+ return pluginOptions.resolveIntegrationUserId({
40
+ transacting: options.transacting,
41
+ context: options.context
42
+ });
61
43
  } else if (options.context.internal) {
62
- return Bookshelf.Model.internalUser;
44
+ return pluginOptions.resolveInternalUserId({
45
+ transacting: options.transacting,
46
+ context: options.context
47
+ });
63
48
  } else if (this.get('id')) {
64
49
  return this.get('id');
65
50
  } else {
@@ -69,16 +54,5 @@ module.exports = function (Bookshelf) {
69
54
  });
70
55
  }
71
56
  }
72
- }, {
73
- /**
74
- * please use these static definitions when comparing id's
75
- * we keep type Number, because we have too many check's where we rely on Number
76
- * context.user ? true : false (if context.user is 0 as number, this condition is false)
77
- */
78
- internalUser: 1,
79
-
80
- isInternalUser: function isInternalUser(id) {
81
- return id === Bookshelf.Model.internalUser || id === Bookshelf.Model.internalUser.toString();
82
- }
83
57
  });
84
58
  };
@@ -601,6 +601,28 @@ Post = ghostBookshelf.Model.extend({
601
601
  }
602
602
  }
603
603
 
604
+ // CASE: Force a change for scheduled posts within 2 minutes of
605
+ // publishing. This ensures the scheduler can detect last-minute
606
+ // touches to the post
607
+ const isScheduled = newStatus === 'scheduled';
608
+ const isUpdate = options.method === 'update';
609
+ const isWithin2Minutes = publishedAt && moment(publishedAt).diff(moment(), 'minutes') <= 2;
610
+ const isNotImporting = !options.importing;
611
+ const isNotMigrating = !options.migrating;
612
+
613
+ if (isScheduled && isUpdate && isWithin2Minutes && isNotImporting && isNotMigrating) {
614
+ // Check if no actual changes have been made
615
+ if (!this.changed || Object.keys(this.changed).length === 0) {
616
+ // Force a "touch" by setting a dummy property that will be stored in _changed
617
+ this.set('_touch', true);
618
+ // Immediately unset it so it doesn't get saved to the database
619
+ this.unset('_touch');
620
+ // But ensure the changed object still has it for event detection
621
+ this.changed = this.changed || {};
622
+ this.changed._touch = true;
623
+ }
624
+ }
625
+
604
626
  // CASE: detect lowercase/uppercase tag slugs
605
627
  if (!_.isUndefined(this.get('tags')) && !_.isNull(this.get('tags'))) {
606
628
  tagsToSave = [];
@@ -744,7 +766,8 @@ Post = ghostBookshelf.Model.extend({
744
766
  if ((newStatus === 'published' || newStatus === 'sent') && this.hasChanged('status')) {
745
767
  // unless published_by is set and we're importing, set published_by to contextUser
746
768
  if (!(this.get('published_by') && options.importing)) {
747
- this.set('published_by', String(this.contextUser(options)));
769
+ const userId = await this.contextUser(options);
770
+ this.set('published_by', String(userId));
748
771
  }
749
772
  } else {
750
773
  // In any other case (except import), `published_by` should not be changed
@@ -888,7 +911,7 @@ Post = ghostBookshelf.Model.extend({
888
911
  revision_interval_ms: POST_REVISIONS_INTERVAL_MS
889
912
  }
890
913
  });
891
- let authorId = this.contextUser(options);
914
+ let authorId = await this.contextUser(options);
892
915
  const authorExists = await ghostBookshelf.model('User').findOne({id: authorId}, {transacting: options.transacting});
893
916
  if (!authorExists) {
894
917
  authorId = await ghostBookshelf.model('User').getOwnerUser().get('id');
@@ -954,14 +977,6 @@ Post = ghostBookshelf.Model.extend({
954
977
  return sequence(ops);
955
978
  },
956
979
 
957
- created_by: function createdBy() {
958
- return this.belongsTo('User', 'created_by');
959
- },
960
-
961
- updated_by: function updatedBy() {
962
- return this.belongsTo('User', 'updated_by');
963
- },
964
-
965
980
  published_by: function publishedBy() {
966
981
  return this.belongsTo('User', 'published_by');
967
982
  },
@@ -75,10 +75,10 @@ module.exports.extendModel = function extendModel(Post, Posts, ghostBookshelf) {
75
75
  return proto.onFetchedCollection.call(this, collection, attrs, options);
76
76
  },
77
77
 
78
- onCreating: function onCreating(model, attrs, options) {
78
+ onCreating: async function onCreating(model, attrs, options) {
79
79
  if (!model.get('authors')) {
80
80
  model.set('authors', [{
81
- id: this.contextUser(options)
81
+ id: await this.contextUser(options)
82
82
  }]);
83
83
  }
84
84
 
@@ -292,25 +292,12 @@ Settings = ghostBookshelf.Model.extend({
292
292
 
293
293
  // fetch other data that is used when inserting new settings
294
294
  const date = ghostBookshelf.knex.raw('CURRENT_TIMESTAMP');
295
- let owner;
296
- try {
297
- owner = await ghostBookshelf.model('User').getOwnerUser();
298
- } catch (e) {
299
- // in some tests the owner is deleted and not recreated before setup
300
- if (e.errorType === 'NotFoundError') {
301
- owner = {id: 1};
302
- } else {
303
- throw e;
304
- }
305
- }
306
295
 
307
296
  const settingsDataToInsert = settingsToInsert.map((setting) => {
308
297
  const settingValues = Object.assign({}, setting, {
309
298
  id: ObjectID().toHexString(),
310
299
  created_at: date,
311
- created_by: owner.id,
312
- updated_at: date,
313
- updated_by: owner.id
300
+ updated_at: date
314
301
  });
315
302
 
316
303
  return _.pick(settingValues, columns);
@@ -76,7 +76,7 @@ User = ghostBookshelf.Model.extend({
76
76
  },
77
77
 
78
78
  format(options) {
79
- if (options.website &&
79
+ if (options.website &&
80
80
  !validator.isURL(options.website, {
81
81
  require_protocol: true,
82
82
  protocols: ['http', 'https']
@@ -295,10 +295,6 @@ User = ghostBookshelf.Model.extend({
295
295
  return attrs;
296
296
  },
297
297
 
298
- posts: function posts() {
299
- return this.hasMany('Posts', 'created_by');
300
- },
301
-
302
298
  sessions: function sessions() {
303
299
  return this.hasMany('Session');
304
300
  },
@@ -778,9 +774,38 @@ User = ghostBookshelf.Model.extend({
778
774
  });
779
775
  },
780
776
 
777
+ ownerIdCache: {
778
+ value: null,
779
+ set(value) {
780
+ this.value = value;
781
+ },
782
+ get() {
783
+ return this.value;
784
+ },
785
+ clear() {
786
+ this.value = null;
787
+ }
788
+ },
789
+
790
+ getOwnerId: function getOwnerId(options) {
791
+ if (this.ownerIdCache.value !== null) {
792
+ return Promise.resolve(this.ownerIdCache.value);
793
+ }
794
+
795
+ return this.getOwnerUser(options).then((owner) => {
796
+ this.ownerIdCache.set(owner.id);
797
+
798
+ return owner.id;
799
+ });
800
+ },
801
+
802
+ generateId: function generateId() {
803
+ return ObjectId().toHexString();
804
+ },
805
+
781
806
  /**
782
807
  * Checks if a user has permission to perform an action on another user
783
- *
808
+ *
784
809
  * @param {Object|string|number} userModelOrId - The user model or ID being acted upon
785
810
  * @param {'edit'|'destroy'} action - The action being performed:
786
811
  * - 'edit': Edit user details, status, or role
@@ -1112,6 +1137,8 @@ User = ghostBookshelf.Model.extend({
1112
1137
  ]);
1113
1138
  })
1114
1139
  .then((results) => {
1140
+ this.ownerIdCache.clear();
1141
+
1115
1142
  return Users.forge()
1116
1143
  .query('whereIn', 'id', [contextUser.id, results[2]])
1117
1144
  .fetch({withRelated: ['roles']});
@@ -18,12 +18,32 @@ class ActivityPubService {
18
18
  this.identityTokenService = identityTokenService;
19
19
  }
20
20
  getExpectedWebhooks(secret) {
21
- return [{
21
+ return [
22
+ {
22
23
  event: 'post.published',
23
24
  target_url: new URL('.ghost/activitypub/v1/webhooks/post/published', this.siteUrl),
24
25
  api_version: 'v5.100.0',
25
26
  secret
26
- }];
27
+ },
28
+ {
29
+ event: 'post.deleted',
30
+ target_url: new URL('.ghost/activitypub/v1/webhooks/post/deleted', this.siteUrl),
31
+ api_version: 'v5.100.0',
32
+ secret
33
+ },
34
+ {
35
+ event: 'post.unpublished',
36
+ target_url: new URL('.ghost/activitypub/v1/webhooks/post/unpublished', this.siteUrl),
37
+ api_version: 'v5.100.0',
38
+ secret
39
+ },
40
+ {
41
+ event: 'post.published.edited',
42
+ target_url: new URL('.ghost/activitypub/v1/webhooks/post/updated', this.siteUrl),
43
+ api_version: 'v5.100.0',
44
+ secret
45
+ }
46
+ ];
27
47
  }
28
48
  async checkWebhookState(expectedWebhooks, integration) {
29
49
  this.logging.info(`Checking ActivityPub Webhook state`);
@@ -104,8 +124,7 @@ class ActivityPubService {
104
124
  name: `ActivityPub ${expectedWebhook.event} Webhook`,
105
125
  secret: secret,
106
126
  integration_id: integration.id,
107
- created_at: this.knex.raw('current_timestamp'),
108
- created_by: '1'
127
+ created_at: this.knex.raw('current_timestamp')
109
128
  };
110
129
  });
111
130
  await this.knex
@@ -25,12 +25,32 @@ export class ActivityPubService {
25
25
  ) {}
26
26
 
27
27
  getExpectedWebhooks(secret: string): ExpectedWebhook[] {
28
- return [{
29
- event: 'post.published',
30
- target_url: new URL('.ghost/activitypub/v1/webhooks/post/published', this.siteUrl),
31
- api_version: 'v5.100.0',
32
- secret
33
- }];
28
+ return [
29
+ {
30
+ event: 'post.published',
31
+ target_url: new URL('.ghost/activitypub/v1/webhooks/post/published', this.siteUrl),
32
+ api_version: 'v5.100.0',
33
+ secret
34
+ },
35
+ {
36
+ event: 'post.deleted',
37
+ target_url: new URL('.ghost/activitypub/v1/webhooks/post/deleted', this.siteUrl),
38
+ api_version: 'v5.100.0',
39
+ secret
40
+ },
41
+ {
42
+ event: 'post.unpublished',
43
+ target_url: new URL('.ghost/activitypub/v1/webhooks/post/unpublished', this.siteUrl),
44
+ api_version: 'v5.100.0',
45
+ secret
46
+ },
47
+ {
48
+ event: 'post.published.edited',
49
+ target_url: new URL('.ghost/activitypub/v1/webhooks/post/updated', this.siteUrl),
50
+ api_version: 'v5.100.0',
51
+ secret
52
+ }
53
+ ];
34
54
  }
35
55
 
36
56
  async checkWebhookState(expectedWebhooks: ExpectedWebhook[], integration: {id: string}) {
@@ -127,8 +147,7 @@ export class ActivityPubService {
127
147
  name: `ActivityPub ${expectedWebhook.event} Webhook`,
128
148
  secret: secret,
129
149
  integration_id: integration.id,
130
- created_at: this.knex.raw('current_timestamp'),
131
- created_by: '1'
150
+ created_at: this.knex.raw('current_timestamp')
132
151
  };
133
152
  });
134
153
 
@@ -381,7 +381,6 @@ h6 + .kg-paywall .kg-paywall-hr td {
381
381
  }
382
382
 
383
383
  /* Exclude CTA cards with colored backgrounds from custom text color, but allow transparent ones */
384
- {{#hasFeature "emailCustomization"}}
385
384
  {{#each ctaBgColors}}
386
385
  .post-content-row .kg-cta-bg-{{this}} .kg-cta-text p,
387
386
  .post-content-row .kg-cta-bg-{{this}} .kg-cta-text ul,
@@ -391,17 +390,6 @@ h6 + .kg-paywall .kg-paywall-hr td {
391
390
  color: inherit !important;
392
391
  }
393
392
  {{/each}}
394
- {{else}}
395
- .post-content-row .kg-cta-bg-grey .kg-cta-text p,
396
- .post-content-row .kg-cta-bg-blue .kg-cta-text p,
397
- .post-content-row .kg-cta-bg-green .kg-cta-text p,
398
- .post-content-row .kg-cta-bg-yellow .kg-cta-text p,
399
- .post-content-row .kg-cta-bg-red .kg-cta-text p,
400
- .post-content-row .kg-cta-bg-pink .kg-cta-text p,
401
- .post-content-row .kg-cta-bg-purple .kg-cta-text p {
402
- color: inherit !important;
403
- }
404
- {{/hasFeature}}
405
393
 
406
394
  .kg-cta-bg-none .kg-cta-sponsor-label span,
407
395
  .kg-cta-bg-white .kg-cta-sponsor-label span {
@@ -2542,10 +2530,8 @@ table.btn-accent a {
2542
2530
  {{/if}}
2543
2531
 
2544
2532
  </style>
2545
- {{#hasFeature "emailCustomization"}}
2546
2533
  <!--[if mso]>
2547
2534
  <style type="text/css">
2548
2535
  ul, ol { margin-left: 1.5em !important; } {{!-- fix bullets/numbers not appearing for lists in older Outlook versions --}}
2549
2536
  </style>
2550
2537
  <![endif]-->
2551
- {{/hasFeature}}
@@ -1,7 +1,7 @@
1
1
  module.exports = class ExplorePingService {
2
2
  /**
3
3
  * @param {object} deps
4
- * @param {{getPublic: () => import('../../../shared/settings-cache/CacheManager').PublicSettingsCache}} deps.settingsCache
4
+ * @param {{get: (string) => string}} deps.settingsCache
5
5
  * @param {object} deps.config
6
6
  * @param {object} deps.labs
7
7
  * @param {object} deps.logging
@@ -14,6 +14,7 @@ module.exports = class ExplorePingService {
14
14
  * }}} deps.posts
15
15
  * @param {{stats: {
16
16
  * getTotalMembers: () => Promise<number>
17
+ * getMRRHistory: () => Promise<number>
17
18
  * }}} deps.members
18
19
  */
19
20
  constructor({settingsCache, config, labs, logging, ghostVersion, request, posts, members}) {
@@ -28,45 +29,50 @@ module.exports = class ExplorePingService {
28
29
  }
29
30
 
30
31
  async constructPayload() {
31
- /* eslint-disable camelcase */
32
- const {title, description, icon, locale, accent_color, twitter, facebook, site_uuid} = this.settingsCache.getPublic();
33
-
34
- // Get post statistics
35
- const [totalPosts, lastPublishedAt, firstPublishedAt] = await Promise.all([
36
- this.posts.stats.getTotalPostsPublished(),
37
- this.posts.stats.getMostRecentlyPublishedPostDate(),
38
- this.posts.stats.getFirstPublishedPostDate()
39
- ]);
32
+ const payload = {
33
+ ghost: this.ghostVersion.full,
34
+ site_uuid: this.settingsCache.get('site_uuid'),
35
+ url: this.config.get('url'),
36
+ theme: this.settingsCache.get('active_theme')
37
+ };
40
38
 
41
- // Get member statistics with error handling
42
- let totalMembers = null;
43
39
  try {
44
- totalMembers = await this.members.stats.getTotalMembers();
40
+ const [totalPosts, lastPublishedAt, firstPublishedAt] = await Promise.all([
41
+ this.posts.stats.getTotalPostsPublished(),
42
+ this.posts.stats.getMostRecentlyPublishedPostDate(),
43
+ this.posts.stats.getFirstPublishedPostDate()
44
+ ]);
45
+
46
+ payload.posts_total = totalPosts;
47
+ payload.posts_last = lastPublishedAt ? lastPublishedAt.toISOString() : null;
48
+ payload.posts_first = firstPublishedAt ? firstPublishedAt.toISOString() : null;
45
49
  } catch (err) {
46
- this.logging.warn('Failed to fetch member statistics', {
50
+ this.logging.warn('Failed to fetch post statistics', {
47
51
  error: err.message,
48
52
  context: 'explore-ping-service'
49
53
  });
50
- // Continue without member statistics
54
+ payload.posts_total = null;
55
+ payload.posts_last = null;
56
+ payload.posts_first = null;
51
57
  }
52
58
 
53
- return {
54
- ghost: this.ghostVersion.full,
55
- site_uuid,
56
- url: this.config.get('url'),
57
- title,
58
- description,
59
- icon,
60
- locale,
61
- accent_color,
62
- twitter,
63
- facebook,
64
- posts_first: firstPublishedAt ? firstPublishedAt.toISOString() : null,
65
- posts_last: lastPublishedAt ? lastPublishedAt.toISOString() : null,
66
- posts_total: totalPosts,
67
- members_total: totalMembers
68
- };
69
- /* eslint-enable camelcase */
59
+ if (this.settingsCache.get('explore_ping_growth')) {
60
+ try {
61
+ const totalMembers = await this.members.stats.getTotalMembers();
62
+ const mrr = await this.members.stats.getMRRHistory();
63
+ payload.members_total = totalMembers;
64
+ payload.mrr = mrr;
65
+ } catch (err) {
66
+ this.logging.warn('Failed to fetch member statistics', {
67
+ error: err.message,
68
+ context: 'explore-ping-service'
69
+ });
70
+ payload.members_total = null;
71
+ payload.mrr = null;
72
+ }
73
+ }
74
+
75
+ return payload;
70
76
  }
71
77
 
72
78
  async makeRequest(exploreUrl, payload) {
@@ -95,12 +101,17 @@ module.exports = class ExplorePingService {
95
101
  return;
96
102
  }
97
103
 
98
- const exploreUrl = this.config.get('explore:url');
104
+ const exploreUrl = this.config.get('explore:update_url');
99
105
  if (!exploreUrl) {
100
106
  this.logging.warn('Explore URL not set');
101
107
  return;
102
108
  }
103
109
 
110
+ if (!this.settingsCache.get('explore_ping')) {
111
+ this.logging.info('Explore ping disabled');
112
+ return;
113
+ }
114
+
104
115
  const payload = await this.constructPayload();
105
116
  await this.makeRequest(exploreUrl, payload);
106
117
  }
@@ -77,7 +77,7 @@ select `newsletters`.*, `members_newsletters`.`member_id` as `_pivot_member_id`,
77
77
 
78
78
  Then we update the member:
79
79
  ```
80
- update `members` set `uuid` = ?, `transient_id` = ?, `email` = ?, `status` = ?, `name` = ?, `expertise` = ?, `note` = ?, `geolocation` = ?, `enable_comment_notifications` = ?, `email_count` = ?, `email_opened_count` = ?, `email_open_rate` = ?, `email_disabled` = ?, `last_seen_at` = ?, `last_commented_at` = ?, `created_at` = ?, `created_by` = ?, `updated_at` = ?, `updated_by` = ? where `id` = ? trx34
80
+ update `members` set `uuid` = ?, `transient_id` = ?, `email` = ?, `status` = ?, `name` = ?, `expertise` = ?, `note` = ?, `geolocation` = ?, `enable_comment_notifications` = ?, `email_count` = ?, `email_opened_count` = ?, `email_open_rate` = ?, `email_disabled` = ?, `last_seen_at` = ?, `last_commented_at` = ?, `created_at` = ?, `updated_at` = ? where `id` = ? trx34
81
81
  ```
82
82
 
83
83
  Then we select the member by ID again to get the freshly updated values from the DB:
@@ -45,7 +45,7 @@ module.exports = class MentionSendingService {
45
45
  // for now we don't want to evaluate mentions when importing data (at least needs queueing set up)
46
46
  // we do not want to evaluate mentions with fixture (internal) data, e.g. generating posts
47
47
  // TODO: real solution is likely suppressing event emission when building fixture data
48
- if (options && (options.importing || options.context.internal)) {
48
+ if (options && (options.importing || (options.context && options.context.internal))) {
49
49
  return;
50
50
  }
51
51
 
@@ -25,6 +25,10 @@ module.exports = function getConfigProperties() {
25
25
  security: config.get('security')
26
26
  };
27
27
 
28
+ if (config.get('explore') && config.get('explore:testimonials_url')) {
29
+ configProperties.exploreTestimonialsUrl = config.get('explore:testimonials_url');
30
+ }
31
+
28
32
  // WIP tinybird stats feature - it's entirely config driven instead of using an alpha flag for now
29
33
  if (config.get('tinybird') && config.get('tinybird:stats')) {
30
34
  const statsConfig = config.get('tinybird:stats');
@@ -19,9 +19,18 @@ const messages = {
19
19
  const installFromGithub = async (ref) => {
20
20
  const [org, repo] = ref.toLowerCase().split('/');
21
21
 
22
- //TODO: move the organization check to config
23
- if (limitService.isLimited('customThemes') && org.toLowerCase() !== 'tryghost') {
24
- await limitService.errorIfWouldGoOverLimit('customThemes', {value: repo.toLowerCase()});
22
+ if (limitService.isLimited('customThemes')) {
23
+ // The custom theme limit might consist of only one single theme, so we can't rely on
24
+ // the org alone to determine if the request is allowed or not.
25
+ const noOtherThemesAllowed = limitService.limits.customThemes?.allowlist?.length === 1;
26
+ //TODO: move the organization check to config
27
+ const isNotOfficialThemeRequest = org.toLowerCase() !== 'tryghost';
28
+
29
+ const checkThemeLimit = noOtherThemesAllowed || isNotOfficialThemeRequest;
30
+
31
+ if (checkThemeLimit) {
32
+ await limitService.errorIfWouldGoOverLimit('customThemes', {value: repo.toLowerCase()});
33
+ }
25
34
  }
26
35
 
27
36
  // omit /:ref so we fetch the default branch
@@ -62,6 +71,11 @@ const installFromGithub = async (ref) => {
62
71
  }));
63
72
  }
64
73
 
74
+ if (e instanceof errors.HostLimitError) {
75
+ // If the error is a HostLimitError, we can assume that the theme name is not allowed
76
+ return Promise.reject(e);
77
+ }
78
+
65
79
  throw e;
66
80
  } finally {
67
81
  // clean up tmp dir with downloaded file
@@ -116,7 +116,10 @@ class UpdateCheckService {
116
116
  const hash = (await this.api.settings.read(_.extend({key: 'db_hash'}, internal))).settings[0];
117
117
  const theme = (await this.api.settings.read(_.extend({key: 'active_theme'}, internal))).settings[0];
118
118
  const posts = await this.api.posts.browse();
119
- const users = await this.api.users.browse(internal);
119
+ const users = await this.api.users.browse({
120
+ ...internal,
121
+ include: ['roles']
122
+ });
120
123
  const npm = await util.promisify(exec)('npm -v');
121
124
 
122
125
  const blogUrl = this.config.siteUrl;
@@ -128,7 +131,20 @@ class UpdateCheckService {
128
131
  data.theme = theme ? theme.value : '';
129
132
  data.post_count = posts && posts.meta && posts.meta.pagination ? posts.meta.pagination.total : 0;
130
133
  data.user_count = users && users.users && users.users.length ? users.users.length : 0;
131
- data.blog_created_at = users && users.users && users.users[0] && users.users[0].created_at ? moment(users.users[0].created_at).unix() : '';
134
+
135
+ let blogCreatedAt = null;
136
+
137
+ if (users && users.users && users.users.length > 0) {
138
+ const ownerUser = users.users.find(user => user.roles.some(role => role.name === 'Owner'));
139
+
140
+ if (ownerUser) {
141
+ blogCreatedAt = ownerUser.created_at;
142
+ } else {
143
+ blogCreatedAt = users.users[0].created_at;
144
+ }
145
+ }
146
+
147
+ data.blog_created_at = blogCreatedAt ? moment(blogCreatedAt).unix() : '';
132
148
  data.npm_version = npm.stdout.trim();
133
149
 
134
150
  return data;