ghost 4.22.3 → 4.25.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 (230) hide show
  1. package/.c8rc.json +24 -0
  2. package/.eslintrc.js +39 -0
  3. package/Gruntfile.js +0 -1
  4. package/content/public/README.md +3 -0
  5. package/content/themes/casper/assets/built/casper.js +1 -1
  6. package/content/themes/casper/assets/built/casper.js.map +1 -1
  7. package/content/themes/casper/assets/built/global.css +1 -1
  8. package/content/themes/casper/assets/built/global.css.map +1 -1
  9. package/content/themes/casper/assets/built/screen.css +1 -1
  10. package/content/themes/casper/assets/built/screen.css.map +1 -1
  11. package/content/themes/casper/assets/css/global.css +6 -1
  12. package/content/themes/casper/assets/css/screen.css +32 -216
  13. package/content/themes/casper/default.hbs +2 -2
  14. package/content/themes/casper/package.json +3 -2
  15. package/content/themes/casper/post.hbs +1 -1
  16. package/content/themes/casper/yarn.lock +173 -123
  17. package/core/app.js +12 -1
  18. package/core/boot.js +47 -28
  19. package/core/bridge.js +10 -10
  20. package/core/built/assets/{chunk.3.324fd0cc598c73650219.js → chunk.3.8f95b516d88ff4eec64c.js} +18 -18
  21. package/core/built/assets/ghost-dark-d690e732e17ffc794e2e59c1467ca282.css +1 -0
  22. package/core/built/assets/ghost.min-043bb7480a0810109b130f13b2a4235e.css +1 -0
  23. package/core/built/assets/{ghost.min-7da921f6c6cac3fe10da1ba104575440.js → ghost.min-bc72f685c1c9adc9885925c1412435a5.js} +563 -605
  24. package/core/built/assets/icons/audio-upload.svg +8 -0
  25. package/core/built/assets/icons/powered-by-tenor.svg +35 -0
  26. package/core/built/assets/icons/tenor.svg +7 -0
  27. package/core/built/assets/{vendor.min-413f887176a041e6dbf88214ca9a7481.js → vendor.min-d1234c632a54502777c34e50752fa3fc.js} +4622 -3631
  28. package/core/frontend/apps/amp/lib/helpers/amp_content.js +2 -2
  29. package/core/frontend/apps/amp/lib/views/amp.hbs +112 -0
  30. package/core/frontend/apps/private-blogging/index.js +1 -1
  31. package/core/frontend/apps/private-blogging/lib/router.js +1 -1
  32. package/core/frontend/services/apps/index.js +1 -1
  33. package/core/frontend/services/apps/loader.js +3 -3
  34. package/core/frontend/services/card-assets/index.js +0 -12
  35. package/core/frontend/services/card-assets/service.js +29 -28
  36. package/core/frontend/services/helpers/handlebars.js +1 -1
  37. package/core/frontend/services/routing/CollectionRouter.js +4 -5
  38. package/core/frontend/services/routing/EmailRouter.js +1 -1
  39. package/core/frontend/services/routing/ParentRouter.js +0 -8
  40. package/core/frontend/services/routing/PreviewRouter.js +1 -1
  41. package/core/frontend/services/routing/StaticPagesRouter.js +1 -1
  42. package/core/frontend/services/routing/StaticRoutesRouter.js +4 -4
  43. package/core/frontend/services/routing/TaxonomyRouter.js +3 -3
  44. package/core/frontend/services/routing/{middlewares → middleware}/index.js +0 -0
  45. package/core/frontend/services/routing/{middlewares → middleware}/page-param.js +0 -0
  46. package/core/frontend/services/routing/router-manager.js +7 -2
  47. package/core/frontend/services/rss/generate-feed.js +2 -1
  48. package/core/frontend/services/theme-engine/middleware/ensure-active-theme.js +34 -0
  49. package/core/frontend/services/theme-engine/middleware/index.js +6 -0
  50. package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +116 -0
  51. package/core/frontend/services/theme-engine/middleware/update-local-template-data.js +9 -0
  52. package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +57 -0
  53. package/core/frontend/src/cards/css/bookmark.css +72 -47
  54. package/core/frontend/src/cards/css/button.css +4 -0
  55. package/core/frontend/src/cards/css/callout.css +40 -3
  56. package/core/frontend/src/cards/css/gallery.css +15 -10
  57. package/core/frontend/src/cards/css/nft.css +20 -11
  58. package/core/frontend/src/cards/css/toggle.css +58 -0
  59. package/core/frontend/src/cards/js/toggle.js +16 -0
  60. package/core/frontend/web/middleware/error-handler.js +93 -0
  61. package/core/frontend/web/middleware/handle-image-sizes.js +3 -6
  62. package/core/frontend/web/middleware/index.js +1 -0
  63. package/core/frontend/web/middleware/serve-public-file.js +39 -16
  64. package/core/frontend/web/site.js +11 -14
  65. package/core/server/adapters/scheduling/SchedulingDefault.js +2 -2
  66. package/core/server/adapters/storage/LocalStorageBase.js +2 -2
  67. package/core/server/api/canary/authentication.js +1 -1
  68. package/core/server/api/canary/db.js +2 -2
  69. package/core/server/api/canary/media.js +3 -2
  70. package/core/server/api/canary/oembed.js +16 -1
  71. package/core/server/api/canary/session.js +1 -1
  72. package/core/server/api/canary/slugs.js +1 -1
  73. package/core/server/api/canary/utils/permissions.js +2 -2
  74. package/core/server/api/canary/utils/serializers/output/config.js +2 -6
  75. package/core/server/api/v2/authentication.js +1 -1
  76. package/core/server/api/v2/db.js +2 -2
  77. package/core/server/api/v2/session.js +1 -1
  78. package/core/server/api/v2/slugs.js +1 -1
  79. package/core/server/api/v2/utils/permissions.js +2 -2
  80. package/core/server/api/v3/authentication.js +1 -1
  81. package/core/server/api/v3/db.js +2 -2
  82. package/core/server/api/v3/session.js +1 -1
  83. package/core/server/api/v3/slugs.js +1 -1
  84. package/core/server/api/v3/utils/permissions.js +2 -2
  85. package/core/server/data/db/connection.js +7 -0
  86. package/core/server/data/db/state-manager.js +4 -4
  87. package/core/server/data/exporter/export-filename.js +1 -1
  88. package/core/server/data/importer/handlers/json.js +1 -1
  89. package/core/server/data/importer/import-manager.js +1 -1
  90. package/core/server/data/importer/importers/data/base.js +1 -1
  91. package/core/server/data/importer/importers/data/data-importer.js +3 -3
  92. package/core/server/data/migrations/init/2-create-fixtures.js +3 -20
  93. package/core/server/data/migrations/utils.js +2 -2
  94. package/core/server/data/migrations/versions/1.21/1-add-contributor-role.js +5 -5
  95. package/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +1 -0
  96. package/core/server/data/migrations/versions/2.15/2-insert-zapier-integration.js +3 -3
  97. package/core/server/data/migrations/versions/2.2/3-insert-admin-integration-role.js +5 -5
  98. package/core/server/data/migrations/versions/2.27/1-insert-ghost-db-backup-role.js +5 -6
  99. package/core/server/data/migrations/versions/2.27/2-insert-db-backup-integration.js +3 -4
  100. package/core/server/data/migrations/versions/2.28/3-insert-ghost-scheduler-role.js +7 -7
  101. package/core/server/data/migrations/versions/2.28/4-insert-scheduler-integration.js +3 -3
  102. package/core/server/data/migrations/versions/3.1/08-add-uuid-values-to-members.js +1 -0
  103. package/core/server/data/migrations/versions/3.22/02-settings-key-renames.js +2 -0
  104. package/core/server/data/migrations/versions/3.22/05-migrate-members-subscription-settings.js +3 -0
  105. package/core/server/data/migrations/versions/3.22/06-migrate-stripe-connect-settings.js +2 -0
  106. package/core/server/data/migrations/versions/3.23/01-migrate-bulk-email-settings.js +1 -0
  107. package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -0
  108. package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -0
  109. package/core/server/data/migrations/versions/3.38/04-populate-recipient-filter-column.js +2 -0
  110. package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +2 -0
  111. package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +4 -0
  112. package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -0
  113. package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -0
  114. package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +5 -0
  115. package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -0
  116. package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -0
  117. package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +2 -1
  118. package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +1 -0
  119. package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +3 -0
  120. package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +1 -0
  121. package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +2 -0
  122. package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +59 -0
  123. package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -0
  124. package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +1 -0
  125. package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +1 -0
  126. package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -0
  127. package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -0
  128. package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +1 -0
  129. package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +1 -0
  130. package/core/server/data/schema/commands.js +2 -2
  131. package/core/server/data/schema/fixtures/fixture-manager.js +340 -0
  132. package/core/server/data/schema/fixtures/index.js +8 -2
  133. package/core/server/ghost-server.js +2 -2
  134. package/core/server/lib/image/image-size.js +2 -2
  135. package/core/server/models/base/listeners.js +2 -2
  136. package/core/server/models/member-email-change-event.js +2 -2
  137. package/core/server/models/member-login-event.js +2 -2
  138. package/core/server/models/member-paid-subscription-event.js +3 -3
  139. package/core/server/models/member-payment-event.js +3 -3
  140. package/core/server/models/member-product-event.js +6 -6
  141. package/core/server/models/member-status-event.js +5 -3
  142. package/core/server/models/member-subscribe-event.js +9 -3
  143. package/core/server/models/relations/authors.js +1 -1
  144. package/core/server/models/settings.js +1 -1
  145. package/core/server/services/auth/passwordreset.js +1 -1
  146. package/core/server/services/auth/setup.js +1 -1
  147. package/core/server/services/email-analytics/jobs/index.js +1 -1
  148. package/core/server/services/mega/mega.js +6 -4
  149. package/core/server/services/mega/post-email-serializer.js +5 -1
  150. package/core/server/services/mega/segment-parser.js +1 -2
  151. package/core/server/services/mega/template.js +52 -37
  152. package/core/server/services/members/api.js +22 -0
  153. package/core/server/services/members/config.js +1 -1
  154. package/core/server/services/members/emails/signup-paid.js +168 -0
  155. package/core/server/services/members/service.js +6 -2
  156. package/core/server/services/members/stripe-connect.js +4 -2
  157. package/core/server/services/nft-oembed.js +13 -22
  158. package/core/server/services/oembed.js +28 -24
  159. package/core/server/services/permissions/can-this.js +1 -1
  160. package/core/server/services/public-config/config.js +1 -1
  161. package/core/server/services/redirects/api.js +20 -25
  162. package/core/server/services/redirects/index.js +18 -10
  163. package/core/server/services/redirects/utils.js +14 -0
  164. package/core/server/services/redirects/validation.js +10 -0
  165. package/core/server/services/route-settings/default-settings-manager.js +1 -1
  166. package/core/server/services/route-settings/index.js +40 -17
  167. package/core/server/services/route-settings/route-settings.js +120 -115
  168. package/core/server/services/route-settings/settings-loader.js +18 -36
  169. package/core/server/services/route-settings/yaml-parser.js +1 -1
  170. package/core/server/services/slack.js +1 -1
  171. package/core/server/services/themes/activation-bridge.js +3 -3
  172. package/core/server/services/themes/storage.js +2 -2
  173. package/core/server/services/twitter-embed.js +80 -0
  174. package/core/server/services/url/LocalFileCache.js +75 -0
  175. package/core/server/services/url/Resources.js +8 -2
  176. package/core/server/services/url/UrlGenerator.js +23 -20
  177. package/core/server/services/url/UrlService.js +75 -63
  178. package/core/server/services/url/index.js +17 -3
  179. package/core/server/services/xmlrpc.js +2 -2
  180. package/core/server/web/admin/app.js +7 -10
  181. package/core/server/web/admin/controller.js +35 -12
  182. package/core/server/web/admin/middleware/redirect-admin-urls.js +15 -0
  183. package/core/server/web/admin/views/default-prod.html +4 -4
  184. package/core/server/web/admin/views/default.html +4 -4
  185. package/core/server/web/api/app.js +1 -1
  186. package/core/server/web/api/canary/admin/app.js +3 -6
  187. package/core/server/web/api/canary/admin/middleware.js +7 -7
  188. package/core/server/web/api/canary/admin/routes.js +5 -5
  189. package/core/server/web/api/canary/content/app.js +3 -6
  190. package/core/server/web/api/canary/content/middleware.js +3 -3
  191. package/core/server/web/api/v2/admin/app.js +3 -6
  192. package/core/server/web/api/v2/admin/middleware.js +7 -7
  193. package/core/server/web/api/v2/admin/routes.js +5 -5
  194. package/core/server/web/api/v2/content/app.js +3 -6
  195. package/core/server/web/api/v2/content/middleware.js +3 -3
  196. package/core/server/web/api/v3/admin/app.js +3 -6
  197. package/core/server/web/api/v3/admin/middleware.js +7 -7
  198. package/core/server/web/api/v3/admin/routes.js +5 -5
  199. package/core/server/web/api/v3/content/app.js +3 -6
  200. package/core/server/web/api/v3/content/middleware.js +3 -3
  201. package/core/server/web/members/app.js +6 -9
  202. package/core/server/web/oauth/app.js +0 -4
  203. package/core/server/web/parent/app.js +17 -9
  204. package/core/server/web/parent/frontend.js +1 -1
  205. package/core/server/web/shared/index.js +2 -2
  206. package/core/server/web/shared/{middlewares → middleware}/api/index.js +0 -0
  207. package/core/server/web/shared/{middlewares → middleware}/api/spam-prevention.js +0 -0
  208. package/core/server/web/shared/{middlewares → middleware}/brute.js +0 -0
  209. package/core/server/web/shared/{middlewares → middleware}/cache-control.js +0 -0
  210. package/core/server/web/shared/middleware/error-handler.js +224 -0
  211. package/core/server/web/shared/{middlewares → middleware}/index.js +0 -4
  212. package/core/server/web/shared/{middlewares → middleware}/pretty-urls.js +0 -0
  213. package/core/server/web/shared/{middlewares → middleware}/uncapitalise.js +0 -0
  214. package/core/server/web/shared/{middlewares → middleware}/url-redirects.js +0 -0
  215. package/core/shared/config/defaults.json +13 -1
  216. package/core/shared/config/helpers.js +42 -0
  217. package/core/shared/config/loader.js +1 -1
  218. package/core/shared/labs.js +9 -5
  219. package/core/shared/sentry.js +1 -1
  220. package/loggingrc.js +19 -20
  221. package/package.json +38 -37
  222. package/yarn.lock +1064 -892
  223. package/content/themes/casper/assets/js/gallery-card.js +0 -24
  224. package/core/built/assets/ghost-dark-39fb496d051565531062d7e047d1c0b1.css +0 -1
  225. package/core/built/assets/ghost.min-4207edfc1ae0a3f9f6505ca00d20b0c0.css +0 -1
  226. package/core/frontend/services/theme-engine/middleware.js +0 -209
  227. package/core/server/data/schema/fixtures/utils.js +0 -321
  228. package/core/server/web/parent/vhost-utils.js +0 -39
  229. package/core/server/web/shared/middlewares/error-handler.js +0 -329
  230. package/core/server/web/shared/middlewares/maintenance.js +0 -25
@@ -143,7 +143,7 @@ function getAmperizeHTML(html, post) {
143
143
  if (err) {
144
144
  if (err.src) {
145
145
  // This is a valid 500 GhostError because it means the amperize parser is unable to handle some Ghost HTML.
146
- logging.error(new errors.GhostError({
146
+ logging.error(new errors.InternalServerError({
147
147
  message: `AMP HTML couldn't be parsed: ${err.src}`,
148
148
  code: 'AMP_PARSER_ERROR',
149
149
  err: err,
@@ -151,7 +151,7 @@ function getAmperizeHTML(html, post) {
151
151
  help: 'Please share this error on GitHub or https://forum.ghost.org'
152
152
  }));
153
153
  } else {
154
- logging.error(new errors.GhostError({err, code: 'AMP_PARSER_ERROR'}));
154
+ logging.error(new errors.InternalServerError({err, code: 'AMP_PARSER_ERROR'}));
155
155
  }
156
156
 
157
157
  // save it in cache to prevent multiple calls to Amperize until
@@ -457,6 +457,118 @@
457
457
  margin: 0 .5em;
458
458
  }
459
459
 
460
+ .kg-toggle-card-icon {
461
+ display: none;
462
+ }
463
+
464
+ .kg-toggle-content {
465
+ margin-top: 0.8rem;
466
+ }
467
+
468
+ .kg-nft-card-container {
469
+ position: relative;
470
+ display: flex;
471
+ flex: auto;
472
+ flex-direction: column;
473
+ text-decoration: none;
474
+ font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;
475
+ font-size: 1.4rem;
476
+ font-weight: 400;
477
+ box-shadow: 0 2px 6px -2px rgb(0 0 0 / 10%), 0 0 1px rgb(0 0 0 / 40%);
478
+ width: 100%;
479
+ max-width: 512px;
480
+ color: #15212A;
481
+ background: #fff;
482
+ border-radius: 5px;
483
+ transition: none;
484
+ margin: 0 auto;
485
+ }
486
+
487
+ .kg-nft-metadata {
488
+ padding: 2.0rem;
489
+ }
490
+
491
+ .kg-nft-image-container {
492
+ position: relative;
493
+ }
494
+
495
+ .kg-nft-image {
496
+ display: flex;
497
+ border-radius: 5px 5px 0 0;
498
+ }
499
+
500
+ .kg-nft-header {
501
+ display: flex;
502
+ justify-content: space-between;
503
+ align-items: flex-start;
504
+ gap: 20px;
505
+ }
506
+
507
+ .kg-nft-header h4.kg-nft-title {
508
+ font-size: 1.9rem;
509
+ font-weight: 700;
510
+ margin: 0;
511
+ color: #15212A;
512
+ }
513
+
514
+ .kg-nft-header amp-img {
515
+ max-width: 114px;
516
+ max-height: 26px;
517
+ }
518
+
519
+ .kg-nft-opensea-logo {
520
+ margin-top: 2px;
521
+ width: 100px;
522
+ }
523
+
524
+ .kg-nft-creator {
525
+ font-family: inherit;
526
+ color: #95A1AD;
527
+ }
528
+
529
+ .kg-nft-creator span {
530
+ font-weight: 500;
531
+ color: #15212A;
532
+ }
533
+
534
+ .kg-nft-card p.kg-nft-description {
535
+ font-size: 1.4rem;
536
+ line-height: 1.4em;
537
+ margin: 2.0rem 0 0;
538
+ color: #222;
539
+ }
540
+
541
+ .kg-button-card {
542
+ display: flex;
543
+ position: static;
544
+ align-items: center;
545
+ width: 100%;
546
+ justify-content: center;
547
+ }
548
+
549
+ .kg-btn {
550
+ display: flex;
551
+ position: static;
552
+ align-items: center;
553
+ padding: 0 2.0rem;
554
+ height: 4.0rem;
555
+ line-height: 4.0rem;
556
+ font-size: 1.65rem;
557
+ font-weight: 600;
558
+ text-decoration: none;
559
+ border-radius: 5px;
560
+ transition: opacity 0.2s ease-in-out;
561
+ }
562
+
563
+ .kg-btn:hover {
564
+ opacity: 0.85;
565
+ }
566
+
567
+ .kg-btn-accent {
568
+ background-color: var(--ghost-accent-color, #1292EE);
569
+ color: #fff;
570
+ }
571
+
460
572
  .kg-width-full.kg-card-hascaption {
461
573
  display: grid;
462
574
  grid-template-columns: inherit;
@@ -24,7 +24,7 @@ let checkSubdir = function checkSubdir() {
24
24
  paths = urlUtils.getSubdir().split('/');
25
25
 
26
26
  if (paths.pop() === PRIVATE_KEYWORD) {
27
- logging.error(new errors.GhostError({
27
+ logging.error(new errors.InternalServerError({
28
28
  message: tpl(messages.urlCannotContainPrivateSubdir.error),
29
29
  context: tpl(messages.urlCannotContainPrivateSubdir.description),
30
30
  help: tpl(messages.urlCannotContainPrivateSubdir.help)
@@ -36,7 +36,7 @@ privateRouter
36
36
  .post(
37
37
  bodyParser.urlencoded({extended: true}),
38
38
  middleware.redirectPrivateToHomeIfLoggedIn,
39
- web.shared.middlewares.brute.privateBlog,
39
+ web.shared.middleware.brute.privateBlog,
40
40
  middleware.doLoginToPrivateSite,
41
41
  _renderer
42
42
  );
@@ -18,7 +18,7 @@ module.exports = {
18
18
 
19
19
  return Promise.map(appsToLoad, appName => loader.activateAppByName(appName))
20
20
  .catch(function (err) {
21
- logging.error(new errors.GhostError({
21
+ logging.error(new errors.InternalServerError({
22
22
  err: err,
23
23
  context: tpl(messages.appWillNotBeLoadedError),
24
24
  help: tpl(messages.appWillNotBeLoadedHelp)
@@ -40,9 +40,9 @@ module.exports = {
40
40
 
41
41
  // Check for an activate() method on the app.
42
42
  if (!_.isFunction(app.activate)) {
43
- return Promise.reject(new errors.IncorrectUsageError(
44
- tpl(messages.noActivateMethodLoadingAppError, {name: name})
45
- ));
43
+ return Promise.reject(new errors.IncorrectUsageError({
44
+ message: tpl(messages.noActivateMethodLoadingAppError, {name: name})
45
+ }));
46
46
  }
47
47
 
48
48
  // Wrapping the activate() with a when because it's possible
@@ -1,16 +1,4 @@
1
- const debug = require('@tryghost/debug')('card-assets');
2
- const themeEngine = require('../theme-engine');
3
-
4
1
  const CardAssetService = require('./service');
5
2
  let cardAssetService = new CardAssetService();
6
3
 
7
- const initFn = async () => {
8
- const cardAssetConfig = themeEngine.getActive().config('card_assets');
9
- debug('initialising with config', cardAssetConfig);
10
-
11
- await cardAssetService.load(cardAssetConfig);
12
- };
13
-
14
4
  module.exports = cardAssetService;
15
-
16
- module.exports.init = initFn;
@@ -1,14 +1,15 @@
1
+ const debug = require('@tryghost/debug')('card-assets');
1
2
  const Minifier = require('@tryghost/minifier');
2
3
  const _ = require('lodash');
3
4
  const path = require('path');
4
5
  const fs = require('fs').promises;
5
6
  const logging = require('@tryghost/logging');
7
+ const config = require('../../../shared/config');
6
8
 
7
9
  class CardAssetService {
8
10
  constructor(options = {}) {
9
- // @TODO: use our config paths concept
10
- this.src = options.src || path.join(__dirname, '../../src/cards');
11
- this.dest = options.dest || path.join(__dirname, '../../public');
11
+ this.src = options.src || path.join(config.get('paths').assetSrc, 'cards');
12
+ this.dest = options.dest || config.getContentPath('public');
12
13
  this.minifier = new Minifier({src: this.src, dest: this.dest});
13
14
 
14
15
  if ('config' in options) {
@@ -52,35 +53,31 @@ class CardAssetService {
52
53
  async minify(globs) {
53
54
  try {
54
55
  return await this.minifier.minify(globs);
55
- } catch (err) {
56
- // @TODO: Convert this back to a proper error once the underlying bug is fixed
57
- if (err.code === 'EACCES') {
58
- logging.warn('Ghost was not able to write card asset files due to permissions.');
56
+ } catch (error) {
57
+ if (error.code === 'EACCES') {
58
+ logging.error('Ghost was not able to write card asset files due to permissions.');
59
+ return;
59
60
  }
61
+
62
+ throw error;
60
63
  }
61
64
  }
62
65
 
63
66
  async clearFiles() {
64
67
  this.files = [];
65
68
 
66
- // @deprecated switch this to use fs.rm when we drop support for Node v12
67
- try {
68
- await fs.unlink(path.join(this.dest, 'cards.min.css'));
69
- } catch (error) {
70
- // Don't worry if the file didn't exist or we don't have perms here
71
- if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
72
- throw error;
73
- }
74
- }
69
+ const rmFile = async (name) => {
70
+ await fs.unlink(path.join(this.dest, name));
71
+ };
75
72
 
76
- try {
77
- await fs.unlink(path.join(this.dest, 'cards.min.js'));
78
- } catch (error) {
79
- // Don't worry if the file didn't exist or we don't have perms here
80
- if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
81
- throw error;
82
- }
83
- }
73
+ let promises = [
74
+ // @deprecated switch this to use fs.rm when we drop support for Node v12
75
+ rmFile('cards.min.css'),
76
+ rmFile('cards.min.js')
77
+ ];
78
+
79
+ // We don't care if removing these files fails as it's valid for them to not exist
80
+ return Promise.allSettled(promises);
84
81
  }
85
82
 
86
83
  hasFile(type) {
@@ -90,18 +87,22 @@ class CardAssetService {
90
87
  /**
91
88
  * A theme can declare which cards it supports, and we'll do the rest
92
89
  *
93
- * @param {Array|boolean} config
90
+ * @param {Array|boolean} cardAssetConfig
94
91
  * @returns
95
92
  */
96
- async load(config) {
97
- if (config) {
98
- this.config = config;
93
+ async load(cardAssetConfig) {
94
+ if (cardAssetConfig) {
95
+ this.config = cardAssetConfig;
99
96
  }
100
97
 
98
+ debug('loading with config', cardAssetConfig);
99
+
101
100
  await this.clearFiles();
102
101
 
103
102
  const globs = this.generateGlobs();
104
103
 
104
+ debug('globs', globs);
105
+
105
106
  this.files = await this.minify(globs) || [];
106
107
  }
107
108
  }
@@ -17,7 +17,7 @@ function asyncHelperWrapper(hbsInstance, name, fn) {
17
17
  Promise.resolve(fn.call(this, context, options)).then(function asyncHelperSuccess(result) {
18
18
  cb(result);
19
19
  }).catch(function asyncHelperError(err) {
20
- const wrappedErr = err instanceof errors.GhostError ? err : new errors.IncorrectUsageError({
20
+ const wrappedErr = errors.utils.isGhostError(err) ? err : new errors.IncorrectUsageError({
21
21
  err: err,
22
22
  context: 'registerAsyncThemeHelper: ' + name,
23
23
  errorDetails: {
@@ -3,7 +3,7 @@ const urlUtils = require('../../../shared/url-utils');
3
3
  const ParentRouter = require('./ParentRouter');
4
4
 
5
5
  const controllers = require('./controllers');
6
- const middlewares = require('./middlewares');
6
+ const middleware = require('./middleware');
7
7
  const RSSRouter = require('./RSSRouter');
8
8
 
9
9
  /**
@@ -27,7 +27,6 @@ class CollectionRouter extends ParentRouter {
27
27
  this.rss = object.rss !== false;
28
28
 
29
29
  this.permalinks = {
30
- originalValue: object.permalink,
31
30
  value: object.permalink
32
31
  };
33
32
 
@@ -71,7 +70,7 @@ class CollectionRouter extends ParentRouter {
71
70
  this.mountRoute(this.route.value, controllers.collection);
72
71
 
73
72
  // REGISTER: enable pagination by default
74
- this.router().param('page', middlewares.pageParam);
73
+ this.router().param('page', middleware.pageParam);
75
74
  this.mountRoute(urlUtils.urlJoin(this.route.value, 'page', ':page(\\d+)'), controllers.collection);
76
75
 
77
76
  // REGISTER: is rss enabled?
@@ -93,7 +92,7 @@ class CollectionRouter extends ParentRouter {
93
92
  }
94
93
 
95
94
  /**
96
- * @description Prepare index context for further middlewares/controllers.
95
+ * @description Prepare index context for further middleware/controllers.
97
96
  */
98
97
  _prepareEntriesContext(req, res, next) {
99
98
  res.routerOptions = {
@@ -116,7 +115,7 @@ class CollectionRouter extends ParentRouter {
116
115
  }
117
116
 
118
117
  /**
119
- * @description Prepare entry context for further middlewares/controllers.
118
+ * @description Prepare entry context for further middleware/controllers.
120
119
  */
121
120
  _prepareEntryContext(req, res, next) {
122
121
  res.routerOptions.context = ['post'];
@@ -30,7 +30,7 @@ class EmailRouter extends ParentRouter {
30
30
  }
31
31
 
32
32
  /**
33
- * @description Prepare context for further middlewares/controllers.
33
+ * @description Prepare context for further middleware/controllers.
34
34
  * @param {Object} req
35
35
  * @param {Object} res
36
36
  * @param {Function} next
@@ -180,14 +180,6 @@ class ParentRouter {
180
180
  return this.permalinks;
181
181
  }
182
182
 
183
- /**
184
- * @description Get configured filter of this router.
185
- * @returns {String}
186
- */
187
- getFilter() {
188
- return this.filter;
189
- }
190
-
191
183
  /**
192
184
  * @description Get main route of this router.
193
185
  *
@@ -30,7 +30,7 @@ class PreviewRouter extends ParentRouter {
30
30
  }
31
31
 
32
32
  /**
33
- * @description Prepare context for further middlewares/controllers.
33
+ * @description Prepare context for further middleware/controllers.
34
34
  * @param {Object} req
35
35
  * @param {Object} res
36
36
  * @param {Function} next
@@ -52,7 +52,7 @@ class StaticPagesRouter extends ParentRouter {
52
52
  }
53
53
 
54
54
  /**
55
- * @description Prepare context for futher middlewares/controllers.
55
+ * @description Prepare context for futher middleware/controllers.
56
56
  * @param {Object} req
57
57
  * @param {Object} res
58
58
  * @param {Function} next
@@ -3,7 +3,7 @@ const errors = require('@tryghost/errors');
3
3
  const urlUtils = require('../../../shared/url-utils');
4
4
  const RSSRouter = require('./RSSRouter');
5
5
  const controllers = require('./controllers');
6
- const middlewares = require('./middlewares');
6
+ const middleware = require('./middleware');
7
7
  const ParentRouter = require('./ParentRouter');
8
8
 
9
9
  /**
@@ -59,14 +59,14 @@ class StaticRoutesRouter extends ParentRouter {
59
59
  this.mountRoute(this.route.value, controllers[this.controller]);
60
60
 
61
61
  // REGISTER: pagination
62
- this.router().param('page', middlewares.pageParam);
62
+ this.router().param('page', middleware.pageParam);
63
63
  this.mountRoute(urlUtils.urlJoin(this.route.value, 'page', ':page(\\d+)'), controllers[this.controller]);
64
64
 
65
65
  this.routerCreated(this);
66
66
  }
67
67
 
68
68
  /**
69
- * @description Prepare channel context for further middlewares/controllers.
69
+ * @description Prepare channel context for further middleware/controllers.
70
70
  * @param {Object} req
71
71
  * @param {Object} res
72
72
  * @param {Function} next
@@ -102,7 +102,7 @@ class StaticRoutesRouter extends ParentRouter {
102
102
  }
103
103
 
104
104
  /**
105
- * @description Prepare static route context for further middlewares/controllers.
105
+ * @description Prepare static route context for further middleware/controllers.
106
106
  * @param {Object} req
107
107
  * @param {Object} res
108
108
  * @param {Function} next
@@ -4,7 +4,7 @@ const ParentRouter = require('./ParentRouter');
4
4
  const RSSRouter = require('./RSSRouter');
5
5
  const urlUtils = require('../../../shared/url-utils');
6
6
  const controllers = require('./controllers');
7
- const middlewares = require('./middlewares');
7
+ const middleware = require('./middleware');
8
8
 
9
9
  /**
10
10
  * @description Taxonomies are groupings of posts based on a common relation.
@@ -51,7 +51,7 @@ class TaxonomyRouter extends ParentRouter {
51
51
  this.mountRoute(this.permalinks.getValue(), controllers.channel);
52
52
 
53
53
  // REGISTER: enable pagination for each taxonomy by default
54
- this.router().param('page', middlewares.pageParam);
54
+ this.router().param('page', middleware.pageParam);
55
55
  this.mountRoute(urlUtils.urlJoin(this.permalinks.value, 'page', ':page(\\d+)'), controllers.channel);
56
56
 
57
57
  // REGISTER: edit redirect to admin client e.g. /tag/:slug/edit
@@ -63,7 +63,7 @@ class TaxonomyRouter extends ParentRouter {
63
63
  }
64
64
 
65
65
  /**
66
- * @description Prepare context for routing middlewares/controllers.
66
+ * @description Prepare context for routing middleware/controllers.
67
67
  * @param {Object} req
68
68
  * @param {Object} res
69
69
  * @param {Function} next
@@ -43,7 +43,12 @@ class RouterManager {
43
43
  return;
44
44
  }
45
45
 
46
- this.urlService.onRouterAddedType(router);
46
+ this.urlService.onRouterAddedType(
47
+ router.identifier,
48
+ router.filter,
49
+ router.getResourceType(),
50
+ router.getPermalinks().getValue()
51
+ );
47
52
  }
48
53
 
49
54
  /**
@@ -179,7 +184,7 @@ class RouterManager {
179
184
  if (collectionRouter.getPermalinks().getValue().match(/:year|:month|:day/)) {
180
185
  debug('handleTimezoneEdit: trigger regeneration');
181
186
 
182
- this.urlService.onRouterUpdated(collectionRouter);
187
+ this.urlService.onRouterUpdated(collectionRouter.identifier);
183
188
  }
184
189
  }
185
190
  }
@@ -1,6 +1,5 @@
1
1
  const downsize = require('downsize');
2
2
  const Promise = require('bluebird');
3
- const cheerio = require('cheerio');
4
3
  const RSS = require('rss');
5
4
  const urlUtils = require('../../../shared/url-utils');
6
5
  const {routerManager} = require('../routing');
@@ -19,6 +18,8 @@ const generateTags = function generateTags(data) {
19
18
  };
20
19
 
21
20
  const generateItem = function generateItem(post, secure) {
21
+ const cheerio = require('cheerio');
22
+
22
23
  const itemUrl = routerManager.getUrlByResourceId(post.id, {secure, absolute: true});
23
24
  const htmlContent = cheerio.load(post.html || '');
24
25
  const item = {
@@ -0,0 +1,34 @@
1
+ const errors = require('@tryghost/errors');
2
+ const tpl = require('@tryghost/tpl');
3
+
4
+ const activeTheme = require('../active');
5
+ const settingsCache = require('../../../../shared/settings-cache');
6
+
7
+ const messages = {
8
+ missingTheme: 'The currently active theme "{theme}" is missing.'
9
+ };
10
+
11
+ // ### Ensure Active Theme
12
+ // Ensure there's a properly set & mounted active theme before attempting to serve a site request
13
+ // If there is no active theme, throw an error
14
+ // Else, ensure the active theme is mounted
15
+ function ensureActiveTheme(req, res, next) {
16
+ // CASE: this means that the theme hasn't been loaded yet i.e. there is no active theme
17
+ if (!activeTheme.get()) {
18
+ // This is the one place we ACTUALLY throw an error for a missing theme as it's a request we cannot serve
19
+ return next(new errors.InternalServerError({
20
+ // We use the settingsCache here, because the setting will be set,
21
+ // even if the theme itself is not usable because it is invalid or missing.
22
+ message: tpl(messages.missingTheme, {theme: settingsCache.get('active_theme')})
23
+ }));
24
+ }
25
+
26
+ // If the active theme has not yet been mounted, mount it into express
27
+ if (!activeTheme.get().mounted) {
28
+ activeTheme.get().mount(req.app);
29
+ }
30
+
31
+ next();
32
+ }
33
+
34
+ module.exports = ensureActiveTheme;
@@ -0,0 +1,6 @@
1
+ module.exports = [
2
+ require('./ensure-active-theme'),
3
+ require('./update-global-template-options'),
4
+ require('./update-local-template-data'),
5
+ require('./update-local-template-options')
6
+ ];
@@ -0,0 +1,116 @@
1
+ const hbs = require('../engine');
2
+ const urlUtils = require('../../../../shared/url-utils');
3
+ const {api} = require('../../proxy');
4
+ const settingsCache = require('../../../../shared/settings-cache');
5
+ const customThemeSettingsCache = require('../../../../shared/custom-theme-settings-cache');
6
+ const labs = require('../../../../shared/labs');
7
+ const activeTheme = require('../active');
8
+
9
+ function calculateLegacyPriceData(products) {
10
+ const defaultPrice = {
11
+ amount: 0,
12
+ currency: 'usd',
13
+ interval: 'year',
14
+ nickname: ''
15
+ };
16
+
17
+ function makePriceObject(price) {
18
+ const numberAmount = 0 + price.amount;
19
+ const dollarAmount = numberAmount ? Math.round(numberAmount / 100) : 0;
20
+ return {
21
+ valueOf() {
22
+ return dollarAmount;
23
+ },
24
+ amount: numberAmount,
25
+ currency: price.currency,
26
+ nickname: price.name,
27
+ interval: price.interval
28
+ };
29
+ }
30
+
31
+ const defaultProduct = products[0] || {};
32
+
33
+ const monthlyPrice = makePriceObject(defaultProduct.monthly_price || defaultPrice);
34
+
35
+ const yearlyPrice = makePriceObject(defaultProduct.yearly_price || defaultPrice);
36
+
37
+ const priceData = {
38
+ monthly: monthlyPrice,
39
+ yearly: yearlyPrice,
40
+ currency: monthlyPrice ? monthlyPrice.currency : defaultPrice.currency
41
+ };
42
+
43
+ return priceData;
44
+ }
45
+
46
+ async function getProductAndPricesData() {
47
+ try {
48
+ const page = await api.canary.productsPublic.browse({
49
+ include: ['monthly_price', 'yearly_price'],
50
+ limit: 'all'
51
+ });
52
+
53
+ return page.products;
54
+ } catch (err) {
55
+ return [];
56
+ }
57
+ }
58
+
59
+ function getSiteData() {
60
+ let siteData = settingsCache.getPublic();
61
+
62
+ // theme-only computed property added to @site
63
+ if (settingsCache.get('members_signup_access') === 'none') {
64
+ const escapedUrl = encodeURIComponent(urlUtils.urlFor({relativeUrl: '/rss/'}, true));
65
+ siteData.signup_url = `https://feedly.com/i/subscription/feed/${escapedUrl}`;
66
+ } else {
67
+ siteData.signup_url = '#/portal';
68
+ }
69
+
70
+ return siteData;
71
+ }
72
+
73
+ async function updateGlobalTemplateOptions(req, res, next) {
74
+ // Static information, same for every request unless the settings change
75
+ // @TODO: bind this once and then update based on events?
76
+ // @TODO: decouple theme layer from settings cache using the Content API
77
+ const siteData = getSiteData();
78
+ const labsData = labs.getAll();
79
+
80
+ const themeData = {
81
+ posts_per_page: activeTheme.get().config('posts_per_page'),
82
+ image_sizes: activeTheme.get().config('image_sizes')
83
+ };
84
+ const themeSettingsData = customThemeSettingsCache.getAll();
85
+ const productData = await getProductAndPricesData();
86
+ const priceData = calculateLegacyPriceData(productData);
87
+
88
+ let products = null;
89
+ let product = null;
90
+ if (productData.length === 1) {
91
+ product = productData[0];
92
+ } else {
93
+ products = productData;
94
+ }
95
+
96
+ // @TODO: only do this if something changed?
97
+ // @TODO: remove blog in a major where we are happy to break more themes
98
+ {
99
+ hbs.updateTemplateOptions({
100
+ data: {
101
+ blog: siteData,
102
+ site: siteData,
103
+ labs: labsData,
104
+ config: themeData,
105
+ price: priceData,
106
+ product,
107
+ products,
108
+ custom: themeSettingsData
109
+ }
110
+ });
111
+ }
112
+
113
+ next();
114
+ }
115
+
116
+ module.exports = updateGlobalTemplateOptions;