ghost 4.24.0 → 4.26.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 (147) hide show
  1. package/.eslintrc.js +39 -0
  2. package/content/themes/casper/assets/built/casper.js +1 -1
  3. package/content/themes/casper/assets/built/casper.js.map +1 -1
  4. package/content/themes/casper/assets/built/global.css +1 -1
  5. package/content/themes/casper/assets/built/global.css.map +1 -1
  6. package/content/themes/casper/assets/built/screen.css +1 -1
  7. package/content/themes/casper/assets/built/screen.css.map +1 -1
  8. package/content/themes/casper/assets/css/global.css +6 -1
  9. package/content/themes/casper/assets/css/screen.css +50 -215
  10. package/content/themes/casper/default.hbs +2 -2
  11. package/content/themes/casper/package.json +3 -2
  12. package/content/themes/casper/post.hbs +1 -1
  13. package/content/themes/casper/yarn.lock +173 -123
  14. package/core/app.js +8 -4
  15. package/core/boot.js +52 -25
  16. package/core/built/assets/ghost-dark-ef86e3bc7f0fb83d39d3d6a49bff8dd5.css +1 -0
  17. package/core/built/assets/ghost.min-57c1e677f42d596942d317ce93e8a62c.css +1 -0
  18. package/core/built/assets/{ghost.min-d5595f9c71ebc534ccf9ac78483d357c.js → ghost.min-f3c6886e191d34450e9ffca0c8fa056e.js} +504 -579
  19. package/core/built/assets/icons/audio-upload.svg +8 -0
  20. package/core/built/assets/{vendor.min-1a84ac3ef74edf31c6e86810b45221cc.js → vendor.min-b6b8d2a31d61830c2d8f65c5ba54236a.js} +2614 -2149
  21. package/core/frontend/apps/amp/lib/helpers/amp_content.js +2 -2
  22. package/core/frontend/apps/amp/lib/views/amp.hbs +75 -0
  23. package/core/frontend/apps/private-blogging/index.js +1 -1
  24. package/core/frontend/helpers/url.js +18 -1
  25. package/core/frontend/services/apps/index.js +1 -1
  26. package/core/frontend/services/apps/loader.js +3 -3
  27. package/core/frontend/services/helpers/handlebars.js +1 -1
  28. package/core/frontend/services/theme-engine/middleware/ensure-active-theme.js +34 -0
  29. package/core/frontend/services/theme-engine/middleware/index.js +6 -0
  30. package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +116 -0
  31. package/core/frontend/services/theme-engine/middleware/update-local-template-data.js +9 -0
  32. package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +57 -0
  33. package/core/frontend/src/cards/css/audio.css +186 -0
  34. package/core/frontend/src/cards/css/blockquote.css +27 -0
  35. package/core/frontend/src/cards/css/button.css +4 -0
  36. package/core/frontend/src/cards/css/callout.css +23 -14
  37. package/core/frontend/src/cards/css/toggle.css +42 -20
  38. package/core/frontend/src/cards/js/audio.js +137 -0
  39. package/core/frontend/web/middleware/error-handler.js +93 -0
  40. package/core/frontend/web/middleware/handle-image-sizes.js +3 -6
  41. package/core/frontend/web/middleware/index.js +1 -0
  42. package/core/frontend/web/site.js +2 -2
  43. package/core/server/adapters/scheduling/SchedulingDefault.js +2 -2
  44. package/core/server/adapters/storage/LocalStorageBase.js +2 -2
  45. package/core/server/api/canary/db.js +2 -2
  46. package/core/server/api/canary/media.js +3 -2
  47. package/core/server/api/canary/oembed.js +16 -1
  48. package/core/server/api/canary/session.js +1 -1
  49. package/core/server/api/canary/slugs.js +1 -1
  50. package/core/server/api/canary/utils/permissions.js +2 -2
  51. package/core/server/api/canary/utils/serializers/output/config.js +2 -6
  52. package/core/server/api/v2/db.js +2 -2
  53. package/core/server/api/v2/session.js +1 -1
  54. package/core/server/api/v2/slugs.js +1 -1
  55. package/core/server/api/v2/utils/permissions.js +2 -2
  56. package/core/server/api/v3/db.js +2 -2
  57. package/core/server/api/v3/session.js +1 -1
  58. package/core/server/api/v3/slugs.js +1 -1
  59. package/core/server/api/v3/utils/permissions.js +2 -2
  60. package/core/server/data/db/state-manager.js +4 -4
  61. package/core/server/data/exporter/export-filename.js +1 -1
  62. package/core/server/data/importer/handlers/json.js +1 -1
  63. package/core/server/data/importer/import-manager.js +1 -1
  64. package/core/server/data/importer/importers/data/base.js +1 -1
  65. package/core/server/data/migrations/utils.js +2 -2
  66. package/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +1 -0
  67. package/core/server/data/migrations/versions/3.1/08-add-uuid-values-to-members.js +1 -0
  68. package/core/server/data/migrations/versions/3.22/02-settings-key-renames.js +2 -0
  69. package/core/server/data/migrations/versions/3.22/05-migrate-members-subscription-settings.js +3 -0
  70. package/core/server/data/migrations/versions/3.22/06-migrate-stripe-connect-settings.js +2 -0
  71. package/core/server/data/migrations/versions/3.23/01-migrate-bulk-email-settings.js +1 -0
  72. package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -0
  73. package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -0
  74. package/core/server/data/migrations/versions/3.38/04-populate-recipient-filter-column.js +2 -0
  75. package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +2 -0
  76. package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +4 -0
  77. package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -0
  78. package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -0
  79. package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +5 -0
  80. package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -0
  81. package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -0
  82. package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +2 -1
  83. package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +1 -0
  84. package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +3 -0
  85. package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +1 -0
  86. package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +2 -0
  87. package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +1 -0
  88. package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -0
  89. package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +1 -0
  90. package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +1 -0
  91. package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -0
  92. package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -0
  93. package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +1 -0
  94. package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +1 -0
  95. package/core/server/data/schema/commands.js +2 -2
  96. package/core/server/ghost-server.js +2 -2
  97. package/core/server/lib/image/image-size.js +2 -2
  98. package/core/server/models/base/listeners.js +2 -2
  99. package/core/server/models/member-email-change-event.js +2 -2
  100. package/core/server/models/member-login-event.js +2 -2
  101. package/core/server/models/member-paid-subscription-event.js +3 -3
  102. package/core/server/models/member-payment-event.js +3 -3
  103. package/core/server/models/member-product-event.js +6 -6
  104. package/core/server/models/member-status-event.js +5 -3
  105. package/core/server/models/member-subscribe-event.js +9 -3
  106. package/core/server/models/relations/authors.js +1 -1
  107. package/core/server/models/settings.js +1 -1
  108. package/core/server/notify.js +1 -2
  109. package/core/server/services/auth/passwordreset.js +1 -1
  110. package/core/server/services/auth/setup.js +1 -1
  111. package/core/server/services/mega/mega.js +6 -4
  112. package/core/server/services/mega/template.js +43 -17
  113. package/core/server/services/members/api.js +22 -0
  114. package/core/server/services/members/config.js +1 -1
  115. package/core/server/services/members/emails/signup-paid.js +168 -0
  116. package/core/server/services/members/service.js +6 -2
  117. package/core/server/services/members/stripe-connect.js +4 -2
  118. package/core/server/services/nft-oembed.js +7 -2
  119. package/core/server/services/oembed.js +15 -3
  120. package/core/server/services/permissions/can-this.js +1 -1
  121. package/core/server/services/redirects/api.js +2 -2
  122. package/core/server/services/route-settings/default-settings-manager.js +1 -1
  123. package/core/server/services/route-settings/route-settings.js +4 -12
  124. package/core/server/services/route-settings/settings-loader.js +4 -4
  125. package/core/server/services/route-settings/yaml-parser.js +1 -1
  126. package/core/server/services/slack.js +1 -1
  127. package/core/server/services/themes/storage.js +2 -2
  128. package/core/server/services/twitter-embed.js +81 -0
  129. package/core/server/services/xmlrpc.js +2 -2
  130. package/core/server/web/admin/views/default-prod.html +4 -4
  131. package/core/server/web/admin/views/default.html +4 -4
  132. package/core/server/web/api/canary/admin/middleware.js +1 -1
  133. package/core/server/web/api/v2/admin/middleware.js +1 -1
  134. package/core/server/web/api/v3/admin/middleware.js +1 -1
  135. package/core/server/web/parent/app.js +2 -22
  136. package/core/server/web/parent/backend.js +2 -0
  137. package/core/server/web/shared/middleware/error-handler.js +28 -150
  138. package/core/shared/config/defaults.json +7 -1
  139. package/core/shared/express.js +1 -1
  140. package/core/shared/labs.js +7 -7
  141. package/core/shared/sentry.js +1 -1
  142. package/package.json +38 -38
  143. package/yarn.lock +632 -941
  144. package/content/themes/casper/assets/js/gallery-card.js +0 -24
  145. package/core/built/assets/ghost-dark-e7b57ab951512c5719aee89b16b9a448.css +0 -1
  146. package/core/built/assets/ghost.min-7f3603dbeb5ebf0ec09e207ae82fb4e3.css +0 -1
  147. package/core/frontend/services/theme-engine/middleware.js +0 -209
@@ -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
@@ -281,6 +281,19 @@
281
281
  background: var(--ghost-accent-color, #1292EE);
282
282
  }
283
283
 
284
+ .post-content blockquote.kg-blockquote-alt {
285
+ font-size: 1.2em;
286
+ font-style: italic;
287
+ line-height: 1.6em;
288
+ text-align: center;
289
+ color: #738a94;
290
+ padding: 0.75em 3em 1.25em;
291
+ }
292
+
293
+ .post-content blockquote.kg-blockquote-alt::before {
294
+ display: none;
295
+ }
296
+
284
297
  .post-content :not(.kg-card):not([id]) + .kg-card {
285
298
  margin-top: 40px;
286
299
  }
@@ -457,6 +470,14 @@
457
470
  margin: 0 .5em;
458
471
  }
459
472
 
473
+ .kg-toggle-card-icon {
474
+ display: none;
475
+ }
476
+
477
+ .kg-toggle-content {
478
+ margin-top: 0.8rem;
479
+ }
480
+
460
481
  .kg-nft-card-container {
461
482
  position: relative;
462
483
  display: flex;
@@ -561,6 +582,60 @@
561
582
  color: #fff;
562
583
  }
563
584
 
585
+ .kg-callout-card {
586
+ display: flex;
587
+ padding: 20px 28px;
588
+ border-radius: 3px;
589
+ }
590
+
591
+ .kg-callout-card-grey {
592
+ background: rgba(124, 139, 154, 0.13);
593
+ }
594
+
595
+ .kg-callout-card-white {
596
+ background: transparent;
597
+ box-shadow: inset 0 0 0 1px rgba(124, 139, 154, 0.25);
598
+ }
599
+
600
+ .kg-callout-card-blue {
601
+ background: rgba(33, 172, 232, 0.12);
602
+ }
603
+
604
+ .kg-callout-card-green {
605
+ background: rgba(52, 183, 67, 0.12);
606
+ }
607
+
608
+ .kg-callout-card-yellow {
609
+ background: rgba(240, 165, 15, 0.13);
610
+ }
611
+
612
+ .kg-callout-card-red {
613
+ background: rgba(209, 46, 46, 0.11);
614
+ }
615
+
616
+ .kg-callout-card-pink {
617
+ background: rgba(225, 71, 174, 0.11);
618
+ }
619
+
620
+ .kg-callout-card-purple {
621
+ background: rgba(135, 85, 236, 0.12);
622
+ }
623
+
624
+ .kg-callout-card-accent {
625
+ background: var(--ghost-accent-color);
626
+ color: #fff;
627
+ }
628
+
629
+ .kg-callout-card-accent a {
630
+ color: #fff;
631
+ }
632
+
633
+ .kg-callout-emoji {
634
+ padding-right: 16px;
635
+ line-height: 1.3;
636
+ font-size: 1.25em;
637
+ }
638
+
564
639
  .kg-width-full.kg-card-hascaption {
565
640
  display: grid;
566
641
  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)
@@ -6,6 +6,9 @@
6
6
 
7
7
  const {metaData} = require('../services/proxy');
8
8
  const {SafeString} = require('../services/rendering');
9
+ const logging = require('@tryghost/logging');
10
+ const sentry = require('../../shared/sentry');
11
+ const errors = require('@tryghost/errors');
9
12
 
10
13
  const {getMetaDataUrl} = metaData;
11
14
 
@@ -13,7 +16,21 @@ module.exports = function url(options) {
13
16
  const absolute = options && options.hash.absolute && options.hash.absolute !== 'false';
14
17
  let outputUrl = getMetaDataUrl(this, absolute);
15
18
 
16
- outputUrl = encodeURI(decodeURI(outputUrl));
19
+ try {
20
+ outputUrl = encodeURI(decodeURI(outputUrl));
21
+ } catch (err) {
22
+ // Happens when the outputURL contains an invalid URI character like "%%" or "%80"
23
+
24
+ // Send the error not to be blind to these
25
+ const error = new errors.IncorrectUsageError({
26
+ message: `The url "${outputUrl}" couldn't be escaped correctly`,
27
+ err: err
28
+ });
29
+ sentry.captureException(error);
30
+ logging.error(error);
31
+
32
+ return new SafeString('');
33
+ }
17
34
 
18
35
  return new SafeString(outputUrl);
19
36
  };
@@ -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
@@ -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: {
@@ -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;
@@ -0,0 +1,9 @@
1
+ function updateLocalTemplateData(req, res, next) {
2
+ // Pass 'secure' flag to the view engine
3
+ // so that templates can choose to render https or http 'url', see url utility
4
+ res.locals.secure = req.secure;
5
+
6
+ next();
7
+ }
8
+
9
+ module.exports = updateLocalTemplateData;
@@ -0,0 +1,57 @@
1
+ const _ = require('lodash');
2
+ const hbs = require('../engine');
3
+ const urlUtils = require('../../../../shared/url-utils');
4
+ const customThemeSettingsCache = require('../../../../shared/custom-theme-settings-cache');
5
+ const labs = require('../../../../shared/labs');
6
+ const preview = require('../preview');
7
+
8
+ function updateLocalTemplateOptions(req, res, next) {
9
+ const localTemplateOptions = hbs.getLocalTemplateOptions(res.locals);
10
+
11
+ // adjust @site.url for http/https based on the incoming request
12
+ const siteData = {
13
+ url: urlUtils.urlFor('home', {secure: req.secure, trailingSlash: false}, true)
14
+ };
15
+
16
+ // @TODO: it would be nicer if this was proper middleware somehow...
17
+ const previewData = preview.handle(req, Object.keys(customThemeSettingsCache.getAll()));
18
+
19
+ // strip custom off of preview data so it doesn't get merged into @site
20
+ const customThemeSettingsPreviewData = previewData.custom;
21
+ delete previewData.custom;
22
+ let customData = {};
23
+ if (labs.isSet('customThemeSettings')) {
24
+ customData = customThemeSettingsPreviewData;
25
+ }
26
+
27
+ // update site data with any preview values from the request
28
+ Object.assign(siteData, previewData);
29
+
30
+ const member = req.member ? {
31
+ uuid: req.member.uuid,
32
+ email: req.member.email,
33
+ name: req.member.name,
34
+ firstname: req.member.name && req.member.name.split(' ')[0],
35
+ avatar_image: req.member.avatar_image,
36
+ subscriptions: req.member.subscriptions && req.member.subscriptions.map((sub) => {
37
+ return Object.assign({}, sub, {
38
+ default_payment_card_last4: sub.default_payment_card_last4 || '****'
39
+ });
40
+ }),
41
+ paid: req.member.status !== 'free'
42
+ } : null;
43
+
44
+ hbs.updateLocalTemplateOptions(res.locals, _.merge({}, localTemplateOptions, {
45
+ data: {
46
+ member: member,
47
+ site: siteData,
48
+ custom: customData,
49
+ // @deprecated: a gscan warning for @blog was added before 3.0 which replaced it with @site
50
+ blog: siteData
51
+ }
52
+ }));
53
+
54
+ next();
55
+ }
56
+
57
+ module.exports = updateLocalTemplateOptions;
@@ -0,0 +1,186 @@
1
+ .kg-audio-card {
2
+ margin-top: 7vmin;
3
+ }
4
+
5
+ .kg-audio-player-container button {
6
+ padding: 0;
7
+ border: 0;
8
+ background: transparent;
9
+ cursor: pointer;
10
+ outline: none;
11
+ width: 40px;
12
+ height: 40px;
13
+ float: left;
14
+ }
15
+
16
+ .kg-audio-player-container {
17
+ position: relative;
18
+ /* margin: 0 2.5% auto 2.5%; */
19
+ width: 100%;
20
+ /* max-width: 500px; */
21
+ height: 132px;
22
+ background: #fff;
23
+ font-family: Arial, Helvetica, sans-serif;
24
+ --seek-before-width: 0%;
25
+ --volume-before-width: 100%;
26
+ --buffered-width: 0%;
27
+ letter-spacing: -0.5px;
28
+ }
29
+ .kg-audio-player-container::before {
30
+ position: absolute;
31
+ content: '';
32
+ width: calc(100% + 4px);
33
+ height: calc(100% + 4px);
34
+ left: -2px;
35
+ top: -2px;
36
+ background: linear-gradient(to left, #007db5, #ff8a00);
37
+ z-index: -1;
38
+ }
39
+ .kg-audio-player-container p {
40
+ position: absolute;
41
+ top: -18px;
42
+ right: 5%;
43
+ padding: 0 5px;
44
+ margin: 0;
45
+ font-size: 24px;
46
+ background: #fff;
47
+ }
48
+ .kg-audio-play-icon {
49
+ margin: 20px 2.5% 10px 2.5%;
50
+ }
51
+ .kg-audio-player-container path {
52
+ stroke: #007db5;
53
+ }
54
+ .kg-audio-duration,
55
+ .kg-audio-current-time {
56
+ display: inline-block;
57
+ width: 37px;
58
+ text-align: center;
59
+ font-size: 20px;
60
+ margin: 28.5px 0 18.5px 0;
61
+ float: left;
62
+ }
63
+ .kg-audio-player-container output {
64
+ display: inline-block;
65
+ width: 32px;
66
+ text-align: center;
67
+ font-size: 20px;
68
+ margin: 10px 2.5% 0 5%;
69
+ float: left;
70
+ clear: left;
71
+ }
72
+ .kg-audio-volume-slider {
73
+ margin: 10px 2.5% !important;
74
+ width: 58% !important;
75
+ }
76
+ .kg-audio-volume-slider::-webkit-slider-runnable-track {
77
+ background: rgba(0, 125, 181, 0.6) !important;
78
+ }
79
+ .kg-audio-volume-slider::-moz-range-track {
80
+ background: rgba(0, 125, 181, 0.6) !important;
81
+ }
82
+ .kg-audio-volume-slider::-ms-fill-upper {
83
+ background: rgba(0, 125, 181, 0.6) !important;
84
+ }
85
+ .kg-audio-volume-slider::before {
86
+ width: var(--volume-before-width) !important;
87
+ }
88
+ .kg-audio-mute-icon {
89
+ margin: 0 2.5%;
90
+ }
91
+ .kg-audio-player-container input[type="range"] {
92
+ position: relative;
93
+ -webkit-appearance: none;
94
+ width: 48%;
95
+ margin: 0;
96
+ padding: 0;
97
+ height: 19px;
98
+ margin: 30px 2.5% 20px 2.5%;
99
+ float: left;
100
+ outline: none;
101
+ }
102
+ .kg-audio-player-container input[type="range"]::-webkit-slider-runnable-track {
103
+ width: 100%;
104
+ height: 3px;
105
+ cursor: pointer;
106
+ background: linear-gradient(to right, rgba(0, 125, 181, 0.6) var(--buffered-width), rgba(0, 125, 181, 0.2) var(--buffered-width));
107
+ }
108
+ .kg-audio-player-container input[type="range"]::before {
109
+ position: absolute;
110
+ content: "";
111
+ top: 8px;
112
+ left: 0;
113
+ width: var(--seek-before-width);
114
+ height: 3px;
115
+ background-color: #007db5;
116
+ cursor: pointer;
117
+ }
118
+ .kg-audio-player-container input[type="range"]::-webkit-slider-thumb {
119
+ position: relative;
120
+ -webkit-appearance: none;
121
+ box-sizing: content-box;
122
+ border: 1px solid #007db5;
123
+ height: 15px;
124
+ width: 15px;
125
+ border-radius: 50%;
126
+ background-color: #fff;
127
+ cursor: pointer;
128
+ margin: -7px 0 0 0;
129
+ }
130
+ .kg-audio-player-container input[type="range"]:active::-webkit-slider-thumb {
131
+ transform: scale(1.2);
132
+ background: #007db5;
133
+ }
134
+ .kg-audio-player-container input[type="range"]::-moz-range-track {
135
+ width: 100%;
136
+ height: 3px;
137
+ cursor: pointer;
138
+ background: linear-gradient(to right, rgba(0, 125, 181, 0.6) var(--buffered-width), rgba(0, 125, 181, 0.2) var(--buffered-width));
139
+ }
140
+ .kg-audio-player-container input[type="range"]::-moz-range-progress {
141
+ background-color: #007db5;
142
+ }
143
+ .kg-audio-player-container input[type="range"]::-moz-focus-outer {
144
+ border: 0;
145
+ }
146
+ .kg-audio-player-container input[type="range"]::-moz-range-thumb {
147
+ box-sizing: content-box;
148
+ border: 1px solid #007db5;
149
+ height: 15px;
150
+ width: 15px;
151
+ border-radius: 50%;
152
+ background-color: #fff;
153
+ cursor: pointer;
154
+ }
155
+ .kg-audio-player-container input[type="range"]:active::-moz-range-thumb {
156
+ transform: scale(1.2);
157
+ background: #007db5;
158
+ }
159
+
160
+ .kg-audio-player-container input[type="range"]::-ms-track {
161
+ width: 100%;
162
+ height: 3px;
163
+ cursor: pointer;
164
+ background: transparent;
165
+ border: solid transparent;
166
+ color: transparent;
167
+ }
168
+ .kg-audio-player-container input[type="range"]::-ms-fill-lower {
169
+ background-color: #007db5;
170
+ }
171
+ .kg-audio-player-container input[type="range"]::-ms-fill-upper {
172
+ background: linear-gradient(to right, rgba(0, 125, 181, 0.6) var(--buffered-width), rgba(0, 125, 181, 0.2) var(--buffered-width));
173
+ }
174
+ .kg-audio-player-container input[type="range"]::-ms-thumb {
175
+ box-sizing: content-box;
176
+ border: 1px solid #007db5;
177
+ height: 15px;
178
+ width: 15px;
179
+ border-radius: 50%;
180
+ background-color: #fff;
181
+ cursor: pointer;
182
+ }
183
+ .kg-audio-player-container input[type="range"]:active::-ms-thumb {
184
+ transform: scale(1.2);
185
+ background: #007db5;
186
+ }
@@ -0,0 +1,27 @@
1
+ blockquote.kg-blockquote-alt {
2
+ background: none;
3
+ font-size: 1.6em;
4
+ line-height: 1.7em;
5
+ text-align: center;
6
+ padding: 0.5em 2.5em 0.75em;
7
+ }
8
+
9
+ blockquote.kg-blockquote-alt::before {
10
+ display: none;
11
+ }
12
+
13
+ @media (max-width: 800px) {
14
+ blockquote.kg-blockquote-alt {
15
+ font-size: 1.4em;
16
+ padding-left: 2em;
17
+ padding-right: 2em;
18
+ }
19
+ }
20
+
21
+ @media (max-width: 600px) {
22
+ blockquote.kg-blockquote-alt {
23
+ font-size: 1.2em;
24
+ padding-left: 1.75em;
25
+ padding-right: 1.75em;
26
+ }
27
+ }
@@ -6,6 +6,10 @@
6
6
  justify-content: center;
7
7
  }
8
8
 
9
+ .kg-button-card.kg-align-left {
10
+ justify-content: flex-start;
11
+ }
12
+
9
13
  a.kg-btn {
10
14
  display: flex;
11
15
  position: static;