ghost 4.23.0 → 4.26.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 (175) 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 +20 -5
  15. package/core/boot.js +77 -36
  16. package/core/bridge.js +10 -10
  17. package/core/built/assets/ghost-dark-ef86e3bc7f0fb83d39d3d6a49bff8dd5.css +1 -0
  18. package/core/built/assets/ghost.min-57c1e677f42d596942d317ce93e8a62c.css +1 -0
  19. package/core/built/assets/{ghost.min-cccc107e881b74c7aaf1a73e1e5e0dee.js → ghost.min-f3c6886e191d34450e9ffca0c8fa056e.js} +543 -618
  20. package/core/built/assets/icons/audio-upload.svg +8 -0
  21. package/core/built/assets/{vendor.min-c9002845b6c30ac978abdadde9f33d7c.js → vendor.min-b6b8d2a31d61830c2d8f65c5ba54236a.js} +2656 -2118
  22. package/core/frontend/apps/amp/lib/helpers/amp_content.js +2 -2
  23. package/core/frontend/apps/amp/lib/views/amp.hbs +75 -0
  24. package/core/frontend/apps/private-blogging/index.js +1 -1
  25. package/core/frontend/helpers/url.js +18 -1
  26. package/core/frontend/services/apps/index.js +1 -1
  27. package/core/frontend/services/apps/loader.js +3 -3
  28. package/core/frontend/services/card-assets/index.js +0 -12
  29. package/core/frontend/services/card-assets/service.js +22 -21
  30. package/core/frontend/services/helpers/handlebars.js +1 -1
  31. package/core/frontend/services/theme-engine/middleware/ensure-active-theme.js +34 -0
  32. package/core/frontend/services/theme-engine/middleware/index.js +6 -0
  33. package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +116 -0
  34. package/core/frontend/services/theme-engine/middleware/update-local-template-data.js +9 -0
  35. package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +57 -0
  36. package/core/frontend/src/cards/css/audio.css +186 -0
  37. package/core/frontend/src/cards/css/blockquote.css +27 -0
  38. package/core/frontend/src/cards/css/bookmark.css +7 -0
  39. package/core/frontend/src/cards/css/button.css +4 -0
  40. package/core/frontend/src/cards/css/callout.css +23 -15
  41. package/core/frontend/src/cards/css/gallery.css +13 -3
  42. package/core/frontend/src/cards/css/toggle.css +48 -15
  43. package/core/frontend/src/cards/js/audio.js +137 -0
  44. package/core/frontend/web/middleware/error-handler.js +93 -0
  45. package/core/frontend/web/middleware/handle-image-sizes.js +3 -6
  46. package/core/frontend/web/middleware/index.js +1 -0
  47. package/core/frontend/web/middleware/serve-public-file.js +25 -8
  48. package/core/frontend/web/site.js +2 -5
  49. package/core/server/adapters/scheduling/SchedulingDefault.js +2 -2
  50. package/core/server/adapters/storage/LocalStorageBase.js +2 -2
  51. package/core/server/api/canary/db.js +2 -2
  52. package/core/server/api/canary/media.js +3 -2
  53. package/core/server/api/canary/oembed.js +16 -1
  54. package/core/server/api/canary/session.js +1 -1
  55. package/core/server/api/canary/slugs.js +1 -1
  56. package/core/server/api/canary/utils/permissions.js +2 -2
  57. package/core/server/api/canary/utils/serializers/output/config.js +2 -6
  58. package/core/server/api/v2/db.js +2 -2
  59. package/core/server/api/v2/session.js +1 -1
  60. package/core/server/api/v2/slugs.js +1 -1
  61. package/core/server/api/v2/utils/permissions.js +2 -2
  62. package/core/server/api/v3/db.js +2 -2
  63. package/core/server/api/v3/session.js +1 -1
  64. package/core/server/api/v3/slugs.js +1 -1
  65. package/core/server/api/v3/utils/permissions.js +2 -2
  66. package/core/server/data/db/state-manager.js +4 -4
  67. package/core/server/data/exporter/export-filename.js +1 -1
  68. package/core/server/data/importer/handlers/json.js +1 -1
  69. package/core/server/data/importer/import-manager.js +1 -1
  70. package/core/server/data/importer/importers/data/base.js +1 -1
  71. package/core/server/data/migrations/utils.js +2 -2
  72. package/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +1 -0
  73. package/core/server/data/migrations/versions/3.1/08-add-uuid-values-to-members.js +1 -0
  74. package/core/server/data/migrations/versions/3.22/02-settings-key-renames.js +2 -0
  75. package/core/server/data/migrations/versions/3.22/05-migrate-members-subscription-settings.js +3 -0
  76. package/core/server/data/migrations/versions/3.22/06-migrate-stripe-connect-settings.js +2 -0
  77. package/core/server/data/migrations/versions/3.23/01-migrate-bulk-email-settings.js +1 -0
  78. package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -0
  79. package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -0
  80. package/core/server/data/migrations/versions/3.38/04-populate-recipient-filter-column.js +2 -0
  81. package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +2 -0
  82. package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +4 -0
  83. package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -0
  84. package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -0
  85. package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +5 -0
  86. package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -0
  87. package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -0
  88. package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +2 -1
  89. package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +1 -0
  90. package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +3 -0
  91. package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +1 -0
  92. package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +2 -0
  93. package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +1 -0
  94. package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -0
  95. package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +1 -0
  96. package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +1 -0
  97. package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -0
  98. package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -0
  99. package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +1 -0
  100. package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +1 -0
  101. package/core/server/data/schema/commands.js +2 -2
  102. package/core/server/ghost-server.js +2 -2
  103. package/core/server/lib/image/image-size.js +2 -2
  104. package/core/server/models/base/listeners.js +2 -2
  105. package/core/server/models/member-email-change-event.js +2 -2
  106. package/core/server/models/member-login-event.js +2 -2
  107. package/core/server/models/member-paid-subscription-event.js +3 -3
  108. package/core/server/models/member-payment-event.js +3 -3
  109. package/core/server/models/member-product-event.js +6 -6
  110. package/core/server/models/member-status-event.js +5 -3
  111. package/core/server/models/member-subscribe-event.js +9 -3
  112. package/core/server/models/relations/authors.js +1 -1
  113. package/core/server/models/settings.js +1 -1
  114. package/core/server/notify.js +1 -2
  115. package/core/server/services/auth/passwordreset.js +1 -1
  116. package/core/server/services/auth/setup.js +1 -1
  117. package/core/server/services/email-analytics/jobs/index.js +1 -1
  118. package/core/server/services/mega/mega.js +6 -4
  119. package/core/server/services/mega/template.js +47 -17
  120. package/core/server/services/members/api.js +22 -0
  121. package/core/server/services/members/config.js +1 -1
  122. package/core/server/services/members/emails/signup-paid.js +168 -0
  123. package/core/server/services/members/service.js +6 -2
  124. package/core/server/services/members/stripe-connect.js +4 -2
  125. package/core/server/services/nft-oembed.js +7 -2
  126. package/core/server/services/oembed.js +15 -3
  127. package/core/server/services/permissions/can-this.js +1 -1
  128. package/core/server/services/redirects/api.js +20 -25
  129. package/core/server/services/redirects/index.js +18 -10
  130. package/core/server/services/redirects/utils.js +14 -0
  131. package/core/server/services/redirects/validation.js +10 -0
  132. package/core/server/services/route-settings/default-settings-manager.js +1 -1
  133. package/core/server/services/route-settings/index.js +40 -17
  134. package/core/server/services/route-settings/route-settings.js +120 -115
  135. package/core/server/services/route-settings/settings-loader.js +18 -36
  136. package/core/server/services/route-settings/yaml-parser.js +1 -1
  137. package/core/server/services/slack.js +1 -1
  138. package/core/server/services/themes/activation-bridge.js +3 -3
  139. package/core/server/services/themes/storage.js +2 -2
  140. package/core/server/services/twitter-embed.js +81 -0
  141. package/core/server/services/url/LocalFileCache.js +75 -0
  142. package/core/server/services/url/UrlService.js +15 -47
  143. package/core/server/services/url/index.js +17 -4
  144. package/core/server/services/xmlrpc.js +2 -2
  145. package/core/server/web/admin/app.js +2 -5
  146. package/core/server/web/admin/controller.js +35 -12
  147. package/core/server/web/admin/middleware/redirect-admin-urls.js +15 -0
  148. package/core/server/web/admin/views/default-prod.html +4 -4
  149. package/core/server/web/admin/views/default.html +4 -4
  150. package/core/server/web/api/canary/admin/app.js +0 -3
  151. package/core/server/web/api/canary/admin/middleware.js +1 -1
  152. package/core/server/web/api/canary/content/app.js +0 -3
  153. package/core/server/web/api/v2/admin/app.js +0 -3
  154. package/core/server/web/api/v2/admin/middleware.js +1 -1
  155. package/core/server/web/api/v2/content/app.js +0 -3
  156. package/core/server/web/api/v3/admin/app.js +0 -3
  157. package/core/server/web/api/v3/admin/middleware.js +1 -1
  158. package/core/server/web/api/v3/content/app.js +0 -3
  159. package/core/server/web/members/app.js +0 -3
  160. package/core/server/web/oauth/app.js +0 -4
  161. package/core/server/web/parent/app.js +2 -13
  162. package/core/server/web/parent/backend.js +2 -0
  163. package/core/server/web/shared/middleware/error-handler.js +57 -162
  164. package/core/server/web/shared/middleware/index.js +0 -4
  165. package/core/shared/config/defaults.json +7 -1
  166. package/core/shared/express.js +1 -1
  167. package/core/shared/labs.js +12 -7
  168. package/core/shared/sentry.js +1 -1
  169. package/package.json +41 -40
  170. package/yarn.lock +799 -948
  171. package/content/themes/casper/assets/js/gallery-card.js +0 -24
  172. package/core/built/assets/ghost-dark-42cf6e0c730578940ec069bda45aea41.css +0 -1
  173. package/core/built/assets/ghost.min-fcf6a0738421f86c47c55f20d00c5ba9.css +0 -1
  174. package/core/frontend/services/theme-engine/middleware.js +0 -209
  175. package/core/server/web/shared/middleware/maintenance.js +0 -25
@@ -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
+ }
@@ -21,6 +21,7 @@
21
21
  align-items: flex-start;
22
22
  justify-content: flex-start;
23
23
  padding: 20px;
24
+ overflow: hidden;
24
25
  }
25
26
 
26
27
  .kg-bookmark-title {
@@ -49,6 +50,7 @@
49
50
  width: 100%;
50
51
  font-size: 1.4rem;
51
52
  font-weight: 500;
53
+ white-space: nowrap;
52
54
  }
53
55
 
54
56
  .kg-bookmark-metadata > *:not(img) {
@@ -84,6 +86,11 @@
84
86
  margin: 0 6px;
85
87
  }
86
88
 
89
+ .kg-bookmark-metadata > span:last-of-type {
90
+ overflow: hidden;
91
+ text-overflow: ellipsis;
92
+ }
93
+
87
94
  .kg-bookmark-thumbnail {
88
95
  position: relative;
89
96
  flex-grow: 1;
@@ -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;
@@ -1,50 +1,58 @@
1
- .kg-card-callout {
1
+ .kg-callout-card {
2
2
  display: flex;
3
- align-items: center;
4
- padding: 20px 28px;
3
+ padding: 1.2em 1.6em;
5
4
  border-radius: 3px;
6
5
  }
7
6
 
8
- .kg-card-callout-grey {
7
+ .kg-callout-card-grey {
9
8
  background: rgba(124, 139, 154, 0.13);
10
9
  }
11
10
 
12
- .kg-card-callout-white {
11
+ .kg-callout-card-white {
13
12
  background: transparent;
14
13
  box-shadow: inset 0 0 0 1px rgba(124, 139, 154, 0.25);
15
14
  }
16
15
 
17
- .kg-card-callout-blue {
16
+ .kg-callout-card-blue {
18
17
  background: rgba(33, 172, 232, 0.12);
19
18
  }
20
19
 
21
- .kg-card-callout-green {
20
+ .kg-callout-card-green {
22
21
  background: rgba(52, 183, 67, 0.12);
23
22
  }
24
23
 
25
- .kg-card-callout-yellow {
24
+ .kg-callout-card-yellow {
26
25
  background: rgba(240, 165, 15, 0.13);
27
26
  }
28
27
 
29
- .kg-card-callout-red {
28
+ .kg-callout-card-red {
30
29
  background: rgba(209, 46, 46, 0.11);
31
30
  }
32
31
 
33
- .kg-card-callout-pink {
32
+ .kg-callout-card-pink {
34
33
  background: rgba(225, 71, 174, 0.11);
35
34
  }
36
35
 
37
- .kg-card-callout-purple {
36
+ .kg-callout-card-purple {
38
37
  background: rgba(135, 85, 236, 0.12);
39
38
  }
40
39
 
41
- .kg-card-callout-accent {
40
+ .kg-callout-card-accent {
42
41
  background: var(--ghost-accent-color);
43
42
  color: #fff;
44
43
  }
45
44
 
45
+ .kg-callout-card-accent a {
46
+ color: #fff;
47
+ }
48
+
46
49
  .kg-callout-emoji {
47
- padding-right: 12px;
48
- line-height: 1.6;
49
- font-size: 2rem;
50
+ padding-right: .8em;
51
+ line-height: 1.3em;
52
+ font-size: 1.2em;
50
53
  }
54
+
55
+ .kg-callout-text {
56
+ font-size: .95em;
57
+ line-height: 1.5em;
58
+ }
@@ -1,8 +1,12 @@
1
+ .kg-gallery-card {
2
+ --gap: 1.2rem;
3
+ }
4
+
1
5
  .kg-image-card:not(.kg-card-hascaption) + .kg-image-card,
2
6
  .kg-image-card:not(.kg-card-hascaption) + .kg-gallery-card,
3
7
  .kg-gallery-card:not(.kg-card-hascaption) + .kg-image-card,
4
8
  .kg-gallery-card:not(.kg-card-hascaption) + .kg-gallery-card {
5
- margin-top: 1.2rem;
9
+ margin-top: var(--gap);
6
10
  }
7
11
 
8
12
  .kg-gallery-container {
@@ -23,9 +27,15 @@
23
27
  }
24
28
 
25
29
  .kg-gallery-row:not(:first-of-type) {
26
- margin: 1.2rem 0 0;
30
+ margin: var(--gap) 0 0;
27
31
  }
28
32
 
29
33
  .kg-gallery-image:not(:first-of-type) {
30
- margin: 0 0 0 1.2rem;
34
+ margin: 0 0 0 var(--gap);
31
35
  }
36
+
37
+ @media (max-width: 600px) {
38
+ .kg-gallery-card {
39
+ --gap: 0.6rem;
40
+ }
41
+ }
@@ -1,8 +1,30 @@
1
+ .kg-toggle-card+.kg-toggle-card {
2
+ margin-top: 1em;
3
+ }
4
+
1
5
  .kg-toggle-card[data-kg-toggle-state="close"] .kg-toggle-content{
2
- visibility: hidden;
3
- opacity: 0;
6
+ height: auto;
7
+ opacity: 1;
8
+ transition: opacity 1s ease, top .35s ease;
9
+ top: 0;
10
+ position: relative;
11
+ }
12
+
13
+ .kg-toggle-content {
4
14
  height: 0;
5
- padding: 0;
15
+ overflow: hidden;
16
+ transition: opacity .5s ease, top .35s ease;
17
+ opacity: 0;
18
+ top: -0.5em;
19
+ position: relative;
20
+ }
21
+
22
+ .kg-toggle-content p:first-of-type {
23
+ margin-top: 0.5em;
24
+ }
25
+
26
+ .kg-toggle-content p {
27
+ font-size: 0.95em;
6
28
  }
7
29
 
8
30
  .kg-toggle-card[data-kg-toggle-state="close"] svg {
@@ -10,14 +32,13 @@
10
32
  }
11
33
 
12
34
  .kg-toggle-card {
13
- border: 1px solid rgba(127, 127, 127, 0.15);
35
+ background: transparent;
36
+ box-shadow: inset 0 0 0 1px rgba(124, 139, 154, 0.25);
14
37
  border-radius: 4px;
15
- padding: 20px;
38
+ padding: 1.2em;
16
39
  }
17
40
 
18
41
  .kg-toggle-heading {
19
- font-size: 2rem;
20
- font-weight: 600;
21
42
  cursor: pointer;
22
43
  display: flex;
23
44
  justify-content: space-between;
@@ -30,18 +51,30 @@
30
51
  display: flex;
31
52
  justify-content: center;
32
53
  align-items: center;
33
- margin-left: 16px;
54
+ margin-left: 1em;
55
+ background: none;
56
+ border: 0;
34
57
  }
35
58
 
36
59
  .kg-toggle-heading svg {
37
60
  width: 14px;
38
- color: rgba(127, 127, 127, 0.4);
39
- transition: transform 0.3s;
40
- transform: rotate(180deg);
61
+ color: rgba(124, 139, 154, 0.5);
62
+ transition: all 0.3s;
63
+ transform: rotate(-180deg);
41
64
  }
42
65
 
43
- .kg-toggle-content {
44
- display: flex;
45
- transition: opacity 0.3s;
46
- padding-top: 8px;
66
+ .kg-toggle-heading path {
67
+ fill: none;
68
+ stroke: currentcolor;
69
+ stroke-linecap: round;
70
+ stroke-linejoin: round;
71
+ stroke-width: 1.5;
72
+ fill-rule: evenodd;
47
73
  }
74
+
75
+ .kg-toggle-heading-text {
76
+ margin-top: 0;
77
+ margin-bottom: 0;
78
+ line-height: 1.3em;
79
+ font-size: 1.1em;
80
+ }
@@ -0,0 +1,137 @@
1
+ const handleAudioPlayer = function (audioElementContainer) {
2
+ const audioPlayerContainer = audioElementContainer.querySelector('.kg-audio-player-container');
3
+ const playIconContainer = audioElementContainer.querySelector('.kg-audio-play-icon');
4
+ const seekSlider = audioElementContainer.querySelector('.kg-audio-seek-slider');
5
+ const volumeSlider = audioElementContainer.querySelector('.kg-audio-volume-slider');
6
+ const muteIconContainer = audioElementContainer.querySelector('.kg-audio-mute-icon');
7
+ const playbackRateContainer = audioElementContainer.querySelector('.kg-audio-playback-rate');
8
+ const audio = audioElementContainer.querySelector('audio');
9
+ const durationContainer = audioElementContainer.querySelector('.kg-audio-duration');
10
+ const currentTimeContainer = audioElementContainer.querySelector('.kg-audio-current-time');
11
+ const outputContainer = audioElementContainer.querySelector('.kg-audio-volume-output');
12
+ let playState = 'play';
13
+ let muteState = 'unmute';
14
+ let playbackRate = 1.0;
15
+ let raf = null;
16
+
17
+ audio.src = audioElementContainer.getAttribute('data-kg-audio-src');
18
+
19
+ const whilePlaying = () => {
20
+ seekSlider.value = Math.floor(audio.currentTime);
21
+ currentTimeContainer.textContent = calculateTime(seekSlider.value);
22
+ audioPlayerContainer.style.setProperty('--seek-before-width', `${seekSlider.value / seekSlider.max * 100}%`);
23
+ raf = requestAnimationFrame(whilePlaying);
24
+ }
25
+
26
+ const showRangeProgress = (rangeInput) => {
27
+ if (rangeInput === seekSlider) {
28
+ audioPlayerContainer.style.setProperty('--seek-before-width', rangeInput.value / rangeInput.max * 100 + '%');
29
+ }
30
+ else {
31
+ audioPlayerContainer.style.setProperty('--volume-before-width', rangeInput.value / rangeInput.max * 100 + '%');
32
+ }
33
+ }
34
+
35
+ const calculateTime = (secs) => {
36
+ const minutes = Math.floor(secs / 60);
37
+ const seconds = Math.floor(secs % 60);
38
+ const returnedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`;
39
+ return `${minutes}:${returnedSeconds}`;
40
+ }
41
+
42
+ const displayDuration = () => {
43
+ durationContainer.textContent = calculateTime(audio.duration);
44
+ }
45
+
46
+ const setSliderMax = () => {
47
+ seekSlider.max = Math.floor(audio.duration);
48
+ }
49
+
50
+ const displayBufferedAmount = () => {
51
+ if (audio.buffered.length > 0) {
52
+ const bufferedAmount = Math.floor(audio.buffered.end(audio.buffered.length - 1));
53
+ audioPlayerContainer.style.setProperty('--buffered-width', `${(bufferedAmount / seekSlider.max) * 100}%`);
54
+ }
55
+ }
56
+
57
+ if (audio.readyState > 0) {
58
+ displayDuration();
59
+ setSliderMax();
60
+ displayBufferedAmount();
61
+ } else {
62
+ audio.addEventListener('loadedmetadata', () => {
63
+ displayDuration();
64
+ setSliderMax();
65
+ displayBufferedAmount();
66
+ });
67
+ }
68
+
69
+ playIconContainer.addEventListener('click', () => {
70
+ if (playState === 'play') {
71
+ audio.play();
72
+ requestAnimationFrame(whilePlaying);
73
+ playState = 'pause';
74
+ playIconContainer.textContent = '||';
75
+ } else {
76
+ audio.pause();
77
+ cancelAnimationFrame(raf);
78
+ playState = 'play';
79
+ playIconContainer.textContent = '>';
80
+ }
81
+ });
82
+
83
+ muteIconContainer.addEventListener('click', () => {
84
+ if (muteState === 'unmute') {
85
+ audio.muted = true;
86
+ muteState = 'mute';
87
+ muteIconContainer.textContent = 'UM';
88
+ } else {
89
+ audio.muted = false;
90
+ muteState = 'unmute';
91
+ muteIconContainer.textContent = 'M';
92
+ }
93
+ });
94
+
95
+ playbackRateContainer.addEventListener('click', () => {
96
+ if (playbackRate === 1.0) {
97
+ audio.playbackRate = 2;
98
+ playbackRate = 2;
99
+ playbackRateContainer.textContent = '2x';
100
+ } else {
101
+ audio.playbackRate = 1.0;
102
+ playbackRate = 1.0;
103
+ playbackRateContainer.textContent = '1x';
104
+ }
105
+ });
106
+
107
+ audio.addEventListener('progress', displayBufferedAmount);
108
+
109
+ seekSlider.addEventListener('input', (e) => {
110
+ showRangeProgress(e.target);
111
+ currentTimeContainer.textContent = calculateTime(seekSlider.value);
112
+ if (!audio.paused) {
113
+ cancelAnimationFrame(raf);
114
+ }
115
+ });
116
+
117
+ seekSlider.addEventListener('change', () => {
118
+ audio.currentTime = seekSlider.value;
119
+ if (!audio.paused) {
120
+ requestAnimationFrame(whilePlaying);
121
+ }
122
+ });
123
+
124
+ volumeSlider.addEventListener('input', (e) => {
125
+ const value = e.target.value;
126
+ showRangeProgress(e.target);
127
+ outputContainer.textContent = value;
128
+ audio.volume = value / 100;
129
+ });
130
+ }
131
+
132
+ const audioCardElements = document.querySelectorAll('.kg-audio-card');
133
+
134
+ for (let i = 0; i < audioCardElements.length; i++) {
135
+ handleAudioPlayer(audioCardElements[i]);
136
+ }
137
+
@@ -0,0 +1,93 @@
1
+ const hbs = require('express-hbs');
2
+ const _ = require('lodash');
3
+ const tpl = require('@tryghost/tpl');
4
+ const sentry = require('../../../shared/sentry');
5
+
6
+ const config = require('../../../shared/config');
7
+ const helpers = require('../../services/routing/helpers');
8
+
9
+ // @TODO: make this properly shared code
10
+ const shared = require('../../../server/web/shared/middleware/error-handler');
11
+
12
+ const messages = {
13
+ oopsErrorTemplateHasError: 'Oops, seems there is an error in the error template.',
14
+ encounteredError: 'Encountered the error: ',
15
+ whilstTryingToRender: 'whilst trying to render an error page for the error: '
16
+ };
17
+
18
+ const escapeExpression = hbs.Utils.escapeExpression;
19
+
20
+ /**
21
+ * This is a bare minimum setup, which allows us to render the error page
22
+ * It uses the {{asset}} helper, and nothing more
23
+ */
24
+ const createHbsEngine = () => {
25
+ const engine = hbs.create();
26
+ engine.registerHelper('asset', require('../../helpers/asset'));
27
+
28
+ return engine.express4();
29
+ };
30
+
31
+ const errorFallbackMessage = err => `<h1>${tpl(messages.oopsErrorTemplateHasError)}</h1>
32
+ <p>${tpl(messages.encounteredError)}</p>
33
+ <pre>${escapeExpression(err.message || err)}</pre>
34
+ <br ><p>${tpl(messages.whilstTryingToRender)}</p>
35
+ ${err.statusCode} <pre>${escapeExpression(err.message || err)}</pre>`;
36
+
37
+ const themeErrorRenderer = (err, req, res, next) => {
38
+ // If the error code is explicitly set to STATIC_FILE_NOT_FOUND,
39
+ // Skip trying to render an HTML error, and move on to the basic error renderer
40
+ // We do this because customised 404 templates could reference the image that's missing
41
+ // A better long term solution might be to do this based on extension
42
+ if (err.code === 'STATIC_FILE_NOT_FOUND') {
43
+ return next(err);
44
+ }
45
+
46
+ // Renderer begin
47
+ // Format Data
48
+ const data = {
49
+ message: err.message,
50
+ // @deprecated Remove in Ghost 5.0
51
+ code: err.statusCode,
52
+ statusCode: err.statusCode,
53
+ errorDetails: err.errorDetails || []
54
+ };
55
+
56
+ // Template
57
+ // @TODO: very dirty !!!!!!
58
+ helpers.templates.setTemplate(req, res);
59
+
60
+ // It can be that something went wrong with the theme or otherwise loading handlebars
61
+ // This ensures that no matter what res.render will work here
62
+ // @TODO: split the error handler for assets, admin & theme to refactor this away
63
+ if (_.isEmpty(req.app.engines)) {
64
+ res._template = 'error';
65
+ req.app.engine('hbs', createHbsEngine());
66
+ req.app.set('view engine', 'hbs');
67
+ req.app.set('views', config.get('paths').defaultViews);
68
+ }
69
+
70
+ // @TODO use renderer here?!
71
+ // Render Call - featuring an error handler for what happens if rendering fails
72
+ res.render(res._template, data, (_err, html) => {
73
+ if (!_err) {
74
+ return res.send(html);
75
+ }
76
+
77
+ // re-attach new error e.g. error template has syntax error or misusage
78
+ req.err = _err;
79
+
80
+ // And then try to explain things to the user...
81
+ // Cheat and output the error using handlebars escapeExpression
82
+ return res.status(500).send(errorFallbackMessage(_err));
83
+ });
84
+ };
85
+
86
+ module.exports.handleThemeResponse = [
87
+ // Make sure the error can be served
88
+ shared.prepareError,
89
+ // Handle the error in Sentry
90
+ sentry.errorHandler,
91
+ // Render the error using theme template
92
+ themeErrorRenderer
93
+ ];