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.
- package/.eslintrc.js +39 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/casper.js.map +1 -1
- package/content/themes/casper/assets/built/global.css +1 -1
- package/content/themes/casper/assets/built/global.css.map +1 -1
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/built/screen.css.map +1 -1
- package/content/themes/casper/assets/css/global.css +6 -1
- package/content/themes/casper/assets/css/screen.css +50 -215
- package/content/themes/casper/default.hbs +2 -2
- package/content/themes/casper/package.json +3 -2
- package/content/themes/casper/post.hbs +1 -1
- package/content/themes/casper/yarn.lock +173 -123
- package/core/app.js +8 -4
- package/core/boot.js +52 -25
- package/core/built/assets/ghost-dark-ef86e3bc7f0fb83d39d3d6a49bff8dd5.css +1 -0
- package/core/built/assets/ghost.min-57c1e677f42d596942d317ce93e8a62c.css +1 -0
- package/core/built/assets/{ghost.min-d5595f9c71ebc534ccf9ac78483d357c.js → ghost.min-f3c6886e191d34450e9ffca0c8fa056e.js} +504 -579
- package/core/built/assets/icons/audio-upload.svg +8 -0
- package/core/built/assets/{vendor.min-1a84ac3ef74edf31c6e86810b45221cc.js → vendor.min-b6b8d2a31d61830c2d8f65c5ba54236a.js} +2614 -2149
- package/core/frontend/apps/amp/lib/helpers/amp_content.js +2 -2
- package/core/frontend/apps/amp/lib/views/amp.hbs +75 -0
- package/core/frontend/apps/private-blogging/index.js +1 -1
- package/core/frontend/helpers/url.js +18 -1
- package/core/frontend/services/apps/index.js +1 -1
- package/core/frontend/services/apps/loader.js +3 -3
- package/core/frontend/services/helpers/handlebars.js +1 -1
- package/core/frontend/services/theme-engine/middleware/ensure-active-theme.js +34 -0
- package/core/frontend/services/theme-engine/middleware/index.js +6 -0
- package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +116 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-data.js +9 -0
- package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +57 -0
- package/core/frontend/src/cards/css/audio.css +186 -0
- package/core/frontend/src/cards/css/blockquote.css +27 -0
- package/core/frontend/src/cards/css/button.css +4 -0
- package/core/frontend/src/cards/css/callout.css +23 -14
- package/core/frontend/src/cards/css/toggle.css +42 -20
- package/core/frontend/src/cards/js/audio.js +137 -0
- package/core/frontend/web/middleware/error-handler.js +93 -0
- package/core/frontend/web/middleware/handle-image-sizes.js +3 -6
- package/core/frontend/web/middleware/index.js +1 -0
- package/core/frontend/web/site.js +2 -2
- package/core/server/adapters/scheduling/SchedulingDefault.js +2 -2
- package/core/server/adapters/storage/LocalStorageBase.js +2 -2
- package/core/server/api/canary/db.js +2 -2
- package/core/server/api/canary/media.js +3 -2
- package/core/server/api/canary/oembed.js +16 -1
- package/core/server/api/canary/session.js +1 -1
- package/core/server/api/canary/slugs.js +1 -1
- package/core/server/api/canary/utils/permissions.js +2 -2
- package/core/server/api/canary/utils/serializers/output/config.js +2 -6
- package/core/server/api/v2/db.js +2 -2
- package/core/server/api/v2/session.js +1 -1
- package/core/server/api/v2/slugs.js +1 -1
- package/core/server/api/v2/utils/permissions.js +2 -2
- package/core/server/api/v3/db.js +2 -2
- package/core/server/api/v3/session.js +1 -1
- package/core/server/api/v3/slugs.js +1 -1
- package/core/server/api/v3/utils/permissions.js +2 -2
- package/core/server/data/db/state-manager.js +4 -4
- package/core/server/data/exporter/export-filename.js +1 -1
- package/core/server/data/importer/handlers/json.js +1 -1
- package/core/server/data/importer/import-manager.js +1 -1
- package/core/server/data/importer/importers/data/base.js +1 -1
- package/core/server/data/migrations/utils.js +2 -2
- package/core/server/data/migrations/versions/1.25/1-update-koenig-beta-html.js +1 -0
- package/core/server/data/migrations/versions/3.1/08-add-uuid-values-to-members.js +1 -0
- package/core/server/data/migrations/versions/3.22/02-settings-key-renames.js +2 -0
- package/core/server/data/migrations/versions/3.22/05-migrate-members-subscription-settings.js +3 -0
- package/core/server/data/migrations/versions/3.22/06-migrate-stripe-connect-settings.js +2 -0
- package/core/server/data/migrations/versions/3.23/01-migrate-bulk-email-settings.js +1 -0
- package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -0
- package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -0
- package/core/server/data/migrations/versions/3.38/04-populate-recipient-filter-column.js +2 -0
- package/core/server/data/migrations/versions/4.0/01-update-mobiledoc.js +2 -0
- package/core/server/data/migrations/versions/4.0/03-populate-status-column-for-members.js +4 -0
- package/core/server/data/migrations/versions/4.0/06-populate-members-subscribe-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/17-populate-members-status-events-table.js +1 -0
- package/core/server/data/migrations/versions/4.0/18-transform-urls-absolute-to-transform-ready.js +5 -0
- package/core/server/data/migrations/versions/4.0/22-solve-orphaned-webhooks.js +1 -0
- package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -0
- package/core/server/data/migrations/versions/4.0/25-populate-members-paid-subscription-events-table.js +2 -1
- package/core/server/data/migrations/versions/4.12/02-fix-member-statuses.js +1 -0
- package/core/server/data/migrations/versions/4.14/01-fix-comped-member-statuses.js +3 -0
- package/core/server/data/migrations/versions/4.14/02-fix-free-members-status-events.js +1 -0
- package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +2 -0
- package/core/server/data/migrations/versions/4.23/01-truncate-offer-names.js +1 -0
- package/core/server/data/migrations/versions/4.3/04-attach-members-to-product.js +1 -0
- package/core/server/data/migrations/versions/4.4/01-restore-free-members-signup-setting-from-backup.js +1 -0
- package/core/server/data/migrations/versions/4.6/01-remove-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.8/04-migrate-show-newsletter-header-setting.js +1 -0
- package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -0
- package/core/server/data/migrations/versions/4.9/06-add-comped-status.js +1 -0
- package/core/server/data/migrations/versions/4.9/07-update-comped-members-status-events.js +1 -0
- package/core/server/data/schema/commands.js +2 -2
- package/core/server/ghost-server.js +2 -2
- package/core/server/lib/image/image-size.js +2 -2
- package/core/server/models/base/listeners.js +2 -2
- package/core/server/models/member-email-change-event.js +2 -2
- package/core/server/models/member-login-event.js +2 -2
- package/core/server/models/member-paid-subscription-event.js +3 -3
- package/core/server/models/member-payment-event.js +3 -3
- package/core/server/models/member-product-event.js +6 -6
- package/core/server/models/member-status-event.js +5 -3
- package/core/server/models/member-subscribe-event.js +9 -3
- package/core/server/models/relations/authors.js +1 -1
- package/core/server/models/settings.js +1 -1
- package/core/server/notify.js +1 -2
- package/core/server/services/auth/passwordreset.js +1 -1
- package/core/server/services/auth/setup.js +1 -1
- package/core/server/services/mega/mega.js +6 -4
- package/core/server/services/mega/template.js +43 -17
- package/core/server/services/members/api.js +22 -0
- package/core/server/services/members/config.js +1 -1
- package/core/server/services/members/emails/signup-paid.js +168 -0
- package/core/server/services/members/service.js +6 -2
- package/core/server/services/members/stripe-connect.js +4 -2
- package/core/server/services/nft-oembed.js +7 -2
- package/core/server/services/oembed.js +15 -3
- package/core/server/services/permissions/can-this.js +1 -1
- package/core/server/services/redirects/api.js +2 -2
- package/core/server/services/route-settings/default-settings-manager.js +1 -1
- package/core/server/services/route-settings/route-settings.js +4 -12
- package/core/server/services/route-settings/settings-loader.js +4 -4
- package/core/server/services/route-settings/yaml-parser.js +1 -1
- package/core/server/services/slack.js +1 -1
- package/core/server/services/themes/storage.js +2 -2
- package/core/server/services/twitter-embed.js +81 -0
- package/core/server/services/xmlrpc.js +2 -2
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/api/canary/admin/middleware.js +1 -1
- package/core/server/web/api/v2/admin/middleware.js +1 -1
- package/core/server/web/api/v3/admin/middleware.js +1 -1
- package/core/server/web/parent/app.js +2 -22
- package/core/server/web/parent/backend.js +2 -0
- package/core/server/web/shared/middleware/error-handler.js +28 -150
- package/core/shared/config/defaults.json +7 -1
- package/core/shared/express.js +1 -1
- package/core/shared/labs.js +7 -7
- package/core/shared/sentry.js +1 -1
- package/package.json +38 -38
- package/yarn.lock +632 -941
- package/content/themes/casper/assets/js/gallery-card.js +0 -24
- package/core/built/assets/ghost-dark-e7b57ab951512c5719aee89b16b9a448.css +0 -1
- package/core/built/assets/ghost.min-7f3603dbeb5ebf0ec09e207ae82fb4e3.css +0 -1
- package/core/frontend/services/theme-engine/middleware.js +0 -209
|
@@ -1,49 +1,58 @@
|
|
|
1
|
-
.kg-card
|
|
1
|
+
.kg-callout-card {
|
|
2
2
|
display: flex;
|
|
3
|
-
padding:
|
|
3
|
+
padding: 1.2em 1.6em;
|
|
4
4
|
border-radius: 3px;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
.kg-card-
|
|
7
|
+
.kg-callout-card-grey {
|
|
8
8
|
background: rgba(124, 139, 154, 0.13);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
.kg-card-
|
|
11
|
+
.kg-callout-card-white {
|
|
12
12
|
background: transparent;
|
|
13
13
|
box-shadow: inset 0 0 0 1px rgba(124, 139, 154, 0.25);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
.kg-card-
|
|
16
|
+
.kg-callout-card-blue {
|
|
17
17
|
background: rgba(33, 172, 232, 0.12);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
.kg-card-
|
|
20
|
+
.kg-callout-card-green {
|
|
21
21
|
background: rgba(52, 183, 67, 0.12);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
.kg-card-
|
|
24
|
+
.kg-callout-card-yellow {
|
|
25
25
|
background: rgba(240, 165, 15, 0.13);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
.kg-card-
|
|
28
|
+
.kg-callout-card-red {
|
|
29
29
|
background: rgba(209, 46, 46, 0.11);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
.kg-card-
|
|
32
|
+
.kg-callout-card-pink {
|
|
33
33
|
background: rgba(225, 71, 174, 0.11);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
.kg-card-
|
|
36
|
+
.kg-callout-card-purple {
|
|
37
37
|
background: rgba(135, 85, 236, 0.12);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
.kg-card-
|
|
40
|
+
.kg-callout-card-accent {
|
|
41
41
|
background: var(--ghost-accent-color);
|
|
42
42
|
color: #fff;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
.kg-callout-card-accent a {
|
|
46
|
+
color: #fff;
|
|
47
|
+
}
|
|
48
|
+
|
|
45
49
|
.kg-callout-emoji {
|
|
46
|
-
padding-right:
|
|
47
|
-
line-height: 1.
|
|
48
|
-
font-size:
|
|
50
|
+
padding-right: .8em;
|
|
51
|
+
line-height: 1.3em;
|
|
52
|
+
font-size: 1.2em;
|
|
49
53
|
}
|
|
54
|
+
|
|
55
|
+
.kg-callout-text {
|
|
56
|
+
font-size: .95em;
|
|
57
|
+
line-height: 1.5em;
|
|
58
|
+
}
|
|
@@ -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
|
-
|
|
6
|
+
height: 0;
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
transition: opacity .5s ease, top .35s ease;
|
|
3
9
|
opacity: 0;
|
|
4
|
-
|
|
5
|
-
|
|
10
|
+
top: -0.5em;
|
|
11
|
+
position: relative;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.kg-toggle-content {
|
|
15
|
+
height: auto;
|
|
16
|
+
opacity: 1;
|
|
17
|
+
transition: opacity 1s ease, top .35s ease;
|
|
18
|
+
top: 0;
|
|
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 {
|
|
@@ -13,11 +35,10 @@
|
|
|
13
35
|
background: transparent;
|
|
14
36
|
box-shadow: inset 0 0 0 1px rgba(124, 139, 154, 0.25);
|
|
15
37
|
border-radius: 4px;
|
|
16
|
-
padding:
|
|
38
|
+
padding: 1.2em;
|
|
17
39
|
}
|
|
18
40
|
|
|
19
41
|
.kg-toggle-heading {
|
|
20
|
-
font-weight: 600;
|
|
21
42
|
cursor: pointer;
|
|
22
43
|
display: flex;
|
|
23
44
|
justify-content: space-between;
|
|
@@ -30,29 +51,30 @@
|
|
|
30
51
|
display: flex;
|
|
31
52
|
justify-content: center;
|
|
32
53
|
align-items: center;
|
|
33
|
-
margin-left:
|
|
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
61
|
color: rgba(124, 139, 154, 0.5);
|
|
39
|
-
transition:
|
|
62
|
+
transition: all 0.3s;
|
|
40
63
|
transform: rotate(-180deg);
|
|
41
64
|
}
|
|
42
65
|
|
|
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;
|
|
73
|
+
}
|
|
74
|
+
|
|
43
75
|
.kg-toggle-heading-text {
|
|
44
76
|
margin-top: 0;
|
|
45
77
|
margin-bottom: 0;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
transition: all .3s;
|
|
50
|
-
padding-top: 1.5rem;
|
|
51
|
-
max-height: 100vh;
|
|
52
|
-
overflow: hidden;
|
|
53
|
-
opacity: 1;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.kg-toggle-content > *+* {
|
|
57
|
-
|
|
58
|
-
}
|
|
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
|
+
];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const {
|
|
3
|
+
const {NoContentError} = require('@tryghost/errors');
|
|
4
4
|
const imageTransform = require('@tryghost/image-transform');
|
|
5
5
|
const storage = require('../../../server/adapters/storage');
|
|
6
6
|
const activeTheme = require('../../services/theme-engine/active');
|
|
@@ -102,10 +102,7 @@ module.exports = function (req, res, next) {
|
|
|
102
102
|
})
|
|
103
103
|
.then((originalImageBuffer) => {
|
|
104
104
|
if (originalImageBuffer.length <= 0) {
|
|
105
|
-
throw new
|
|
106
|
-
errorType: 'NoContentError',
|
|
107
|
-
statusCode: 204
|
|
108
|
-
});
|
|
105
|
+
throw new NoContentError();
|
|
109
106
|
}
|
|
110
107
|
return imageTransform.resizeFromBuffer(originalImageBuffer, imageDimensionConfig);
|
|
111
108
|
})
|
|
@@ -115,7 +112,7 @@ module.exports = function (req, res, next) {
|
|
|
115
112
|
}).then(() => {
|
|
116
113
|
next();
|
|
117
114
|
}).catch(function (err) {
|
|
118
|
-
if (err.code === 'SHARP_INSTALLATION' || err.errorType === 'NoContentError') {
|
|
115
|
+
if (err.code === 'SHARP_INSTALLATION' || err.code === 'IMAGE_PROCESSING' || err.errorType === 'NoContentError') {
|
|
119
116
|
return redirectToOriginal();
|
|
120
117
|
}
|
|
121
118
|
next(err);
|
|
@@ -112,7 +112,7 @@ module.exports = function setupSiteApp(options = {}) {
|
|
|
112
112
|
|
|
113
113
|
// Card assets
|
|
114
114
|
siteApp.use(mw.servePublicFile('built', 'public/cards.min.css', 'text/css', constants.ONE_YEAR_S));
|
|
115
|
-
siteApp.use(mw.servePublicFile('built', 'public/cards.min.js', '
|
|
115
|
+
siteApp.use(mw.servePublicFile('built', 'public/cards.min.js', 'application/javascript', constants.ONE_YEAR_S));
|
|
116
116
|
|
|
117
117
|
// Serve blog images using the storage adapter
|
|
118
118
|
siteApp.use(STATIC_IMAGE_URL_PREFIX, mw.handleImageSizes, storage.getStorage('images').serve());
|
|
@@ -184,7 +184,7 @@ module.exports = function setupSiteApp(options = {}) {
|
|
|
184
184
|
app.setupErrorHandling(siteApp);
|
|
185
185
|
}
|
|
186
186
|
});
|
|
187
|
-
siteApp.use(
|
|
187
|
+
siteApp.use(mw.errorHandler.handleThemeResponse);
|
|
188
188
|
|
|
189
189
|
debug('Site setup end');
|
|
190
190
|
|
|
@@ -307,7 +307,7 @@ SchedulingDefault.prototype._pingUrl = function (object) {
|
|
|
307
307
|
this._pingUrl(object);
|
|
308
308
|
}, this.retryTimeoutInMs);
|
|
309
309
|
|
|
310
|
-
logging.error(new errors.
|
|
310
|
+
logging.error(new errors.InternalServerError({
|
|
311
311
|
err,
|
|
312
312
|
context: 'Retrying...',
|
|
313
313
|
level: 'normal'
|
|
@@ -316,7 +316,7 @@ SchedulingDefault.prototype._pingUrl = function (object) {
|
|
|
316
316
|
return;
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
logging.error(new errors.
|
|
319
|
+
logging.error(new errors.InternalServerError({
|
|
320
320
|
err,
|
|
321
321
|
level: 'critical'
|
|
322
322
|
}));
|
|
@@ -147,7 +147,7 @@ class LocalStorageBase extends StorageBase {
|
|
|
147
147
|
return next(new errors.NoPermissionError({err: err}));
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
return next(new errors.
|
|
150
|
+
return next(new errors.InternalServerError({err: err}));
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
next();
|
|
@@ -196,7 +196,7 @@ class LocalStorageBase extends StorageBase {
|
|
|
196
196
|
return reject(new errors.NoPermissionError({err: err}));
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
return reject(new errors.
|
|
199
|
+
return reject(new errors.InternalServerError({
|
|
200
200
|
err: err,
|
|
201
201
|
message: tpl(this.errorMessages.cannotRead, {file: options.path})
|
|
202
202
|
}));
|
|
@@ -62,7 +62,7 @@ module.exports = {
|
|
|
62
62
|
return Promise.resolve()
|
|
63
63
|
.then(() => exporter.doExport({include: frame.options.withRelated}))
|
|
64
64
|
.catch((err) => {
|
|
65
|
-
return Promise.reject(new errors.
|
|
65
|
+
return Promise.reject(new errors.InternalServerError({err: err}));
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
},
|
|
@@ -124,7 +124,7 @@ module.exports = {
|
|
|
124
124
|
}, {concurrency: 100});
|
|
125
125
|
})
|
|
126
126
|
.catch((err) => {
|
|
127
|
-
throw new errors.
|
|
127
|
+
throw new errors.InternalServerError({
|
|
128
128
|
err: err
|
|
129
129
|
});
|
|
130
130
|
});
|
|
@@ -3,9 +3,24 @@ const externalRequest = require('../../lib/request-external');
|
|
|
3
3
|
|
|
4
4
|
const OEmbed = require('../../services/oembed');
|
|
5
5
|
const oembed = new OEmbed({config, externalRequest});
|
|
6
|
+
|
|
6
7
|
const NFT = require('../../services/nft-oembed');
|
|
7
|
-
const nft = new NFT(
|
|
8
|
+
const nft = new NFT({
|
|
9
|
+
config: {
|
|
10
|
+
apiKey: config.get('opensea').privateReadOnlyApiKey
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const Twitter = require('../../services/twitter-embed');
|
|
15
|
+
const twitter = new Twitter({
|
|
16
|
+
config: {
|
|
17
|
+
bearerToken: config.get('twitter').privateReadOnlyToken
|
|
18
|
+
},
|
|
19
|
+
logging: require('@tryghost/logging')
|
|
20
|
+
});
|
|
21
|
+
|
|
8
22
|
oembed.registerProvider(nft);
|
|
23
|
+
oembed.registerProvider(twitter);
|
|
9
24
|
|
|
10
25
|
module.exports = {
|
|
11
26
|
docName: 'oembed',
|
|
@@ -43,7 +43,7 @@ module.exports = {
|
|
|
43
43
|
return models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all'})
|
|
44
44
|
.then((slug) => {
|
|
45
45
|
if (!slug) {
|
|
46
|
-
return Promise.reject(new errors.
|
|
46
|
+
return Promise.reject(new errors.InternalServerError({
|
|
47
47
|
message: tpl(messages.couldNotGenerateSlug)
|
|
48
48
|
}));
|
|
49
49
|
}
|
|
@@ -66,11 +66,11 @@ const nonePublicAuth = (apiConfig, frame) => {
|
|
|
66
66
|
return Promise.reject(err);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
if (errors.utils.
|
|
69
|
+
if (errors.utils.isGhostError(err)) {
|
|
70
70
|
return Promise.reject(err);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
return Promise.reject(new errors.
|
|
73
|
+
return Promise.reject(new errors.InternalServerError({
|
|
74
74
|
err: err
|
|
75
75
|
}));
|
|
76
76
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
-
const labs = require('../../../../../../shared/labs');
|
|
3
2
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:config');
|
|
4
3
|
|
|
5
4
|
module.exports = {
|
|
@@ -18,13 +17,10 @@ module.exports = {
|
|
|
18
17
|
'stripeDirect',
|
|
19
18
|
'mailgunIsConfigured',
|
|
20
19
|
'emailAnalytics',
|
|
21
|
-
'hostSettings'
|
|
20
|
+
'hostSettings',
|
|
21
|
+
'tenor'
|
|
22
22
|
];
|
|
23
23
|
|
|
24
|
-
if (labs.isSet('gifsCard')) {
|
|
25
|
-
keys.push('tenor');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
24
|
frame.response = {
|
|
29
25
|
config: _.pick(data, keys)
|
|
30
26
|
};
|
package/core/server/api/v2/db.js
CHANGED
|
@@ -51,7 +51,7 @@ module.exports = {
|
|
|
51
51
|
return Promise.resolve()
|
|
52
52
|
.then(() => exporter.doExport({include: frame.options.withRelated}))
|
|
53
53
|
.catch((err) => {
|
|
54
|
-
return Promise.reject(new errors.
|
|
54
|
+
return Promise.reject(new errors.InternalServerError({err: err}));
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
},
|
|
@@ -107,7 +107,7 @@ module.exports = {
|
|
|
107
107
|
}, {concurrency: 100});
|
|
108
108
|
})
|
|
109
109
|
.catch((err) => {
|
|
110
|
-
throw new errors.
|
|
110
|
+
throw new errors.InternalServerError({
|
|
111
111
|
err: err
|
|
112
112
|
});
|
|
113
113
|
});
|
|
@@ -40,7 +40,7 @@ module.exports = {
|
|
|
40
40
|
return models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all'})
|
|
41
41
|
.then((slug) => {
|
|
42
42
|
if (!slug) {
|
|
43
|
-
return Promise.reject(new errors.
|
|
43
|
+
return Promise.reject(new errors.InternalServerError({
|
|
44
44
|
message: tpl(messages.couldNotGenerateSlug)
|
|
45
45
|
}));
|
|
46
46
|
}
|
|
@@ -62,11 +62,11 @@ const nonePublicAuth = (apiConfig, frame) => {
|
|
|
62
62
|
return Promise.reject(err);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
if (errors.utils.
|
|
65
|
+
if (errors.utils.isGhostError(err)) {
|
|
66
66
|
return Promise.reject(err);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
return Promise.reject(new errors.
|
|
69
|
+
return Promise.reject(new errors.InternalServerError({
|
|
70
70
|
err: err
|
|
71
71
|
}));
|
|
72
72
|
});
|
package/core/server/api/v3/db.js
CHANGED
|
@@ -62,7 +62,7 @@ module.exports = {
|
|
|
62
62
|
return Promise.resolve()
|
|
63
63
|
.then(() => exporter.doExport({include: frame.options.withRelated}))
|
|
64
64
|
.catch((err) => {
|
|
65
|
-
return Promise.reject(new errors.
|
|
65
|
+
return Promise.reject(new errors.InternalServerError({err: err}));
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
},
|
|
@@ -118,7 +118,7 @@ module.exports = {
|
|
|
118
118
|
}, {concurrency: 100});
|
|
119
119
|
})
|
|
120
120
|
.catch((err) => {
|
|
121
|
-
throw new errors.
|
|
121
|
+
throw new errors.InternalServerError({
|
|
122
122
|
err: err
|
|
123
123
|
});
|
|
124
124
|
});
|