ghost 4.26.0 → 4.27.2

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 (36) hide show
  1. package/content/themes/casper/assets/built/screen.css +1 -1
  2. package/content/themes/casper/assets/built/screen.css.map +1 -1
  3. package/content/themes/casper/assets/css/screen.css +29 -4
  4. package/content/themes/casper/package.json +1 -1
  5. package/core/built/assets/{chunk.3.8f95b516d88ff4eec64c.js → chunk.3.e54be01b5124e4e27958.js} +7 -7
  6. package/core/built/assets/ghost-dark-485081d359d3ce1412ccd09a8f5b83d9.css +1 -0
  7. package/core/built/assets/{ghost.min-f3c6886e191d34450e9ffca0c8fa056e.js → ghost.min-3224fe43ca0ca4d6184f579d165ad801.js} +132 -129
  8. package/core/built/assets/ghost.min-e272e8cd67577087cee1c853694e2852.css +1 -0
  9. package/core/built/assets/icons/audio-file.svg +5 -0
  10. package/core/built/assets/icons/film-camera.svg +4 -0
  11. package/core/built/assets/icons/mute.svg +3 -0
  12. package/core/built/assets/icons/pause.svg +4 -0
  13. package/core/built/assets/icons/play.svg +3 -0
  14. package/core/built/assets/icons/unmute.svg +3 -0
  15. package/core/built/assets/{vendor.min-b6b8d2a31d61830c2d8f65c5ba54236a.js → vendor.min-08ca94a205555a22db64e8d595062c63.js} +680 -591
  16. package/core/frontend/helpers/ghost_head.js +1 -1
  17. package/core/frontend/src/cards/css/audio.css +228 -128
  18. package/core/frontend/src/cards/css/blockquote.css +6 -10
  19. package/core/frontend/src/cards/css/button.css +4 -4
  20. package/core/frontend/src/cards/css/nft.css +7 -6
  21. package/core/frontend/src/cards/css/product.css +109 -0
  22. package/core/frontend/src/cards/css/toggle.css +8 -8
  23. package/core/frontend/src/cards/css/video.css +4 -0
  24. package/core/frontend/src/cards/js/audio.js +120 -111
  25. package/core/frontend/src/cards/js/gallery.js +10 -8
  26. package/core/frontend/src/cards/js/toggle.js +16 -14
  27. package/core/server/services/mega/post-email-serializer.js +3 -1
  28. package/core/server/services/mega/template.js +2 -2
  29. package/core/server/services/twitter-embed.js +2 -1
  30. package/core/server/web/admin/views/default-prod.html +4 -4
  31. package/core/server/web/admin/views/default.html +4 -4
  32. package/core/shared/labs.js +5 -3
  33. package/package.json +20 -17
  34. package/yarn.lock +2561 -2120
  35. package/core/built/assets/ghost-dark-ef86e3bc7f0fb83d39d3d6a49bff8dd5.css +0 -1
  36. package/core/built/assets/ghost.min-57c1e677f42d596942d317ce93e8a62c.css +0 -1
@@ -1,137 +1,146 @@
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 + '%');
1
+ (function() {
2
+ const handleAudioPlayer = function (audioElementContainer) {
3
+ const audioPlayerContainer = audioElementContainer.querySelector('.kg-player-container');
4
+ const playIconContainer = audioElementContainer.querySelector('.kg-audio-play-icon');
5
+ const pauseIconContainer = audioElementContainer.querySelector('.kg-audio-pause-icon');
6
+ const seekSlider = audioElementContainer.querySelector('.kg-audio-seek-slider');
7
+ const playbackRateContainer = audioElementContainer.querySelector('.kg-audio-playback-rate');
8
+ const muteIconContainer = audioElementContainer.querySelector('.kg-audio-mute-icon');
9
+ const unmuteIconContainer = audioElementContainer.querySelector('.kg-audio-unmute-icon');
10
+ const volumeSlider = audioElementContainer.querySelector('.kg-audio-volume-slider');
11
+ const audio = audioElementContainer.querySelector('audio');
12
+ const durationContainer = audioElementContainer.querySelector('.kg-audio-duration');
13
+ const currentTimeContainer = audioElementContainer.querySelector('.kg-audio-current-time');
14
+ let playbackRates = [{
15
+ rate: 0.75,
16
+ label: '0.7×'
17
+ }, {
18
+ rate: 1.0,
19
+ label: '1×'
20
+ }, {
21
+ rate: 1.25,
22
+ label: '1.'
23
+ }, {
24
+ rate: 1.75,
25
+ label: '1.7×'
26
+ }, {
27
+ rate: 2.0,
28
+ label: ''
29
+ }];
30
+
31
+ let raf = null;
32
+ let currentPlaybackRateIdx = 1;
33
+
34
+ const whilePlaying = () => {
35
+ seekSlider.value = Math.floor(audio.currentTime);
36
+ currentTimeContainer.textContent = calculateTime(seekSlider.value);
37
+ audioPlayerContainer.style.setProperty('--seek-before-width', `${seekSlider.value / seekSlider.max * 100}%`);
38
+ raf = requestAnimationFrame(whilePlaying);
29
39
  }
30
- else {
31
- audioPlayerContainer.style.setProperty('--volume-before-width', rangeInput.value / rangeInput.max * 100 + '%');
40
+
41
+ const showRangeProgress = (rangeInput) => {
42
+ if (rangeInput === seekSlider) {
43
+ audioPlayerContainer.style.setProperty('--seek-before-width', rangeInput.value / rangeInput.max * 100 + '%');
44
+ }
45
+ else {
46
+ audioPlayerContainer.style.setProperty('--volume-before-width', rangeInput.value / rangeInput.max * 100 + '%');
47
+ }
32
48
  }
33
- }
34
49
 
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
- }
50
+ const calculateTime = (secs) => {
51
+ const minutes = Math.floor(secs / 60);
52
+ const seconds = Math.floor(secs % 60);
53
+ const returnedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`;
54
+ return `${minutes}:${returnedSeconds}`;
55
+ }
41
56
 
42
- const displayDuration = () => {
43
- durationContainer.textContent = calculateTime(audio.duration);
44
- }
57
+ const displayDuration = () => {
58
+ durationContainer.textContent = calculateTime(audio.duration);
59
+ }
45
60
 
46
- const setSliderMax = () => {
47
- seekSlider.max = Math.floor(audio.duration);
48
- }
61
+ const setSliderMax = () => {
62
+ seekSlider.max = Math.floor(audio.duration);
63
+ }
49
64
 
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}%`);
65
+ const displayBufferedAmount = () => {
66
+ if (audio.buffered.length > 0) {
67
+ const bufferedAmount = Math.floor(audio.buffered.end(audio.buffered.length - 1));
68
+ audioPlayerContainer.style.setProperty('--buffered-width', `${(bufferedAmount / seekSlider.max) * 100}%`);
69
+ }
54
70
  }
55
- }
56
71
 
57
- if (audio.readyState > 0) {
58
- displayDuration();
59
- setSliderMax();
60
- displayBufferedAmount();
61
- } else {
62
- audio.addEventListener('loadedmetadata', () => {
72
+ if (audio.readyState > 0) {
63
73
  displayDuration();
64
74
  setSliderMax();
65
75
  displayBufferedAmount();
66
- });
67
- }
76
+ } else {
77
+ audio.addEventListener('loadedmetadata', () => {
78
+ displayDuration();
79
+ setSliderMax();
80
+ displayBufferedAmount();
81
+ });
82
+ }
68
83
 
69
- playIconContainer.addEventListener('click', () => {
70
- if (playState === 'play') {
84
+ playIconContainer.addEventListener('click', () => {
85
+ playIconContainer.classList.add("kg-audio-hide");
86
+ pauseIconContainer.classList.remove("kg-audio-hide");
71
87
  audio.play();
72
88
  requestAnimationFrame(whilePlaying);
73
- playState = 'pause';
74
- playIconContainer.textContent = '||';
75
- } else {
89
+ });
90
+
91
+ pauseIconContainer.addEventListener('click', () => {
92
+ pauseIconContainer.classList.add("kg-audio-hide");
93
+ playIconContainer.classList.remove("kg-audio-hide");
76
94
  audio.pause();
77
95
  cancelAnimationFrame(raf);
78
- playState = 'play';
79
- playIconContainer.textContent = '>';
80
- }
81
- });
96
+ });
82
97
 
83
- muteIconContainer.addEventListener('click', () => {
84
- if (muteState === 'unmute') {
85
- audio.muted = true;
86
- muteState = 'mute';
87
- muteIconContainer.textContent = 'UM';
88
- } else {
98
+ muteIconContainer.addEventListener('click', () => {
99
+ muteIconContainer.classList.add("kg-audio-hide");
100
+ unmuteIconContainer.classList.remove("kg-audio-hide");
89
101
  audio.muted = false;
90
- muteState = 'unmute';
91
- muteIconContainer.textContent = 'M';
92
- }
93
- });
102
+ });
94
103
 
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
- });
104
+ unmuteIconContainer.addEventListener('click', () => {
105
+ unmuteIconContainer.classList.add("kg-audio-hide");
106
+ muteIconContainer.classList.remove("kg-audio-hide");
107
+ audio.muted = true;
108
+ });
106
109
 
107
- audio.addEventListener('progress', displayBufferedAmount);
110
+ playbackRateContainer.addEventListener('click', () => {
111
+ let nextPlaybackRate = playbackRates[(currentPlaybackRateIdx + 1) % 5];
112
+ currentPlaybackRateIdx = currentPlaybackRateIdx + 1;
113
+ audio.playbackRate = nextPlaybackRate.rate;
114
+ playbackRateContainer.textContent = nextPlaybackRate.label;
115
+ });
108
116
 
109
- seekSlider.addEventListener('input', (e) => {
110
- showRangeProgress(e.target);
111
- currentTimeContainer.textContent = calculateTime(seekSlider.value);
112
- if (!audio.paused) {
113
- cancelAnimationFrame(raf);
114
- }
115
- });
117
+ audio.addEventListener('progress', displayBufferedAmount);
116
118
 
117
- seekSlider.addEventListener('change', () => {
118
- audio.currentTime = seekSlider.value;
119
- if (!audio.paused) {
120
- requestAnimationFrame(whilePlaying);
121
- }
122
- });
119
+ seekSlider.addEventListener('input', (e) => {
120
+ showRangeProgress(e.target);
121
+ currentTimeContainer.textContent = calculateTime(seekSlider.value);
122
+ if (!audio.paused) {
123
+ cancelAnimationFrame(raf);
124
+ }
125
+ });
123
126
 
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
- }
127
+ seekSlider.addEventListener('change', () => {
128
+ audio.currentTime = seekSlider.value;
129
+ if (!audio.paused) {
130
+ requestAnimationFrame(whilePlaying);
131
+ }
132
+ });
131
133
 
132
- const audioCardElements = document.querySelectorAll('.kg-audio-card');
134
+ volumeSlider.addEventListener('input', (e) => {
135
+ const value = e.target.value;
136
+ showRangeProgress(e.target);
137
+ audio.volume = value / 100;
138
+ });
139
+ }
133
140
 
134
- for (let i = 0; i < audioCardElements.length; i++) {
135
- handleAudioPlayer(audioCardElements[i]);
136
- }
141
+ const audioCardElements = document.querySelectorAll('.kg-audio-card');
137
142
 
143
+ for (let i = 0; i < audioCardElements.length; i++) {
144
+ handleAudioPlayer(audioCardElements[i]);
145
+ }
146
+ })();
@@ -1,8 +1,10 @@
1
- var images = document.querySelectorAll('.kg-gallery-image img');
2
- images.forEach(function (image) {
3
- var container = image.closest('.kg-gallery-image');
4
- var width = image.attributes.width.value;
5
- var height = image.attributes.height.value;
6
- var ratio = width / height;
7
- container.style.flex = ratio + ' 1 0%';
8
- })
1
+ (function() {
2
+ const images = document.querySelectorAll('.kg-gallery-image img');
3
+ images.forEach(function (image) {
4
+ const container = image.closest('.kg-gallery-image');
5
+ const width = image.attributes.width.value;
6
+ const height = image.attributes.height.value;
7
+ const ratio = width / height;
8
+ container.style.flex = ratio + ' 1 0%';
9
+ })
10
+ })();
@@ -1,16 +1,18 @@
1
- const toggleHeadingElements = document.getElementsByClassName("kg-toggle-heading");
1
+ (function() {
2
+ const toggleHeadingElements = document.getElementsByClassName("kg-toggle-heading");
2
3
 
3
- const toggleFn = function(event) {
4
- const targetElement = event.target;
5
- const parentElement = targetElement.closest('.kg-toggle-card');
6
- var toggleState = parentElement.getAttribute("data-kg-toggle-state");
7
- if (toggleState === 'close') {
8
- parentElement.setAttribute('data-kg-toggle-state', 'open');
9
- } else {
10
- parentElement.setAttribute('data-kg-toggle-state', 'close');
11
- }
12
- };
4
+ const toggleFn = function(event) {
5
+ const targetElement = event.target;
6
+ const parentElement = targetElement.closest('.kg-toggle-card');
7
+ var toggleState = parentElement.getAttribute("data-kg-toggle-state");
8
+ if (toggleState === 'close') {
9
+ parentElement.setAttribute('data-kg-toggle-state', 'open');
10
+ } else {
11
+ parentElement.setAttribute('data-kg-toggle-state', 'close');
12
+ }
13
+ };
13
14
 
14
- for (let i = 0; i < toggleHeadingElements.length; i++) {
15
- toggleHeadingElements[i].addEventListener('click', toggleFn, false);
16
- }
15
+ for (let i = 0; i < toggleHeadingElements.length; i++) {
16
+ toggleHeadingElements[i].addEventListener('click', toggleFn, false);
17
+ }
18
+ })();
@@ -240,7 +240,9 @@ const serialize = async (postModel, options = {isBrowserPreview: false, apiVersi
240
240
  post.excerpt = post.excerpt.replace(/\s\[http(.*?)\]/g, '');
241
241
  }
242
242
 
243
- post.html = mobiledocLib.mobiledocHtmlRenderer.render(JSON.parse(post.mobiledoc), {target: 'email'});
243
+ post.html = mobiledocLib.mobiledocHtmlRenderer.render(
244
+ JSON.parse(post.mobiledoc), {target: 'email', postUrl: post.url}
245
+ );
244
246
 
245
247
  // perform any email specific adjustments to the mobiledoc->HTML render output
246
248
  // body wrapper is required so we can get proper top-level selections
@@ -915,8 +915,8 @@ figure blockquote p {
915
915
 
916
916
  table.body blockquote.kg-blockquote-alt {
917
917
  border-left: 0 none !important;
918
- margin: 0 0 2.5em 0;
919
- padding: 0 50px 0 50px;
918
+ margin: 0 0 2.5em 0 !important;
919
+ padding: 0 50px 0 50px !important;
920
920
  font-size: 1.2em;
921
921
  }
922
922
 
@@ -1,4 +1,5 @@
1
1
  const {extract} = require('oembed-parser');
2
+ const labs = require('../../shared/labs');
2
3
 
3
4
  /**
4
5
  * @typedef {import('./oembed').ICustomProvider} ICustomProvider
@@ -41,7 +42,7 @@ class TwitterOEmbedProvider {
41
42
  /** @type {object} */
42
43
  const oembedData = await extract(url.href);
43
44
 
44
- if (this.dependencies.config.bearerToken) {
45
+ if (this.dependencies.config.bearerToken && labs.isSet('richTwitterNewsletters')) {
45
46
  const query = {
46
47
  expansions: ['attachments.poll_ids', 'attachments.media_keys', 'author_id', 'entities.mentions.username', 'geo.place_id', 'in_reply_to_user_id', 'referenced_tweets.id', 'referenced_tweets.id.author_id'],
47
48
  'media.fields': ['duration_ms', 'height', 'media_key', 'preview_image_url', 'type', 'url', 'width', 'public_metrics', 'alt_text'],
@@ -8,7 +8,7 @@
8
8
  <title>Ghost Admin</title>
9
9
 
10
10
 
11
- <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%224.26%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%22%3Atrue%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
11
+ <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%224.27%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%22%3Atrue%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
12
12
 
13
13
  <meta name="HandheldFriendly" content="True" />
14
14
  <meta name="MobileOptimized" content="320" />
@@ -41,7 +41,7 @@
41
41
 
42
42
 
43
43
  <link rel="stylesheet" href="assets/vendor.min-987af30228885bce50f05c4723fe6f53.css">
44
- <link rel="stylesheet" href="assets/ghost.min-57c1e677f42d596942d317ce93e8a62c.css" title="light">
44
+ <link rel="stylesheet" href="assets/ghost.min-e272e8cd67577087cee1c853694e2852.css" title="light">
45
45
 
46
46
 
47
47
 
@@ -59,8 +59,8 @@
59
59
  <div id="ember-basic-dropdown-wormhole"></div>
60
60
 
61
61
 
62
- <script src="assets/vendor.min-b6b8d2a31d61830c2d8f65c5ba54236a.js"></script>
63
- <script src="assets/ghost.min-f3c6886e191d34450e9ffca0c8fa056e.js"></script>
62
+ <script src="assets/vendor.min-08ca94a205555a22db64e8d595062c63.js"></script>
63
+ <script src="assets/ghost.min-3224fe43ca0ca4d6184f579d165ad801.js"></script>
64
64
 
65
65
  </body>
66
66
  </html>
@@ -8,7 +8,7 @@
8
8
  <title>Ghost Admin</title>
9
9
 
10
10
 
11
- <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%224.26%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%22%3Atrue%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
11
+ <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%224.27%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22emberKeyboard%22%3A%7B%22disableInputsInitializer%22%3Atrue%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
12
12
 
13
13
  <meta name="HandheldFriendly" content="True" />
14
14
  <meta name="MobileOptimized" content="320" />
@@ -41,7 +41,7 @@
41
41
 
42
42
 
43
43
  <link rel="stylesheet" href="assets/vendor.min-987af30228885bce50f05c4723fe6f53.css">
44
- <link rel="stylesheet" href="assets/ghost.min-57c1e677f42d596942d317ce93e8a62c.css" title="light">
44
+ <link rel="stylesheet" href="assets/ghost.min-e272e8cd67577087cee1c853694e2852.css" title="light">
45
45
 
46
46
 
47
47
 
@@ -59,8 +59,8 @@
59
59
  <div id="ember-basic-dropdown-wormhole"></div>
60
60
 
61
61
 
62
- <script src="assets/vendor.min-b6b8d2a31d61830c2d8f65c5ba54236a.js"></script>
63
- <script src="assets/ghost.min-f3c6886e191d34450e9ffca0c8fa056e.js"></script>
62
+ <script src="assets/vendor.min-08ca94a205555a22db64e8d595062c63.js"></script>
63
+ <script src="assets/ghost.min-3224fe43ca0ca4d6184f579d165ad801.js"></script>
64
64
 
65
65
  </body>
66
66
  </html>
@@ -18,7 +18,8 @@ const GA_FEATURES = [
18
18
  'customThemeSettings',
19
19
  'nftCard',
20
20
  'calloutCard',
21
- 'accordionCard'
21
+ 'accordionCard',
22
+ 'richTwitterNewsletters'
22
23
  ];
23
24
 
24
25
  // NOTE: this allowlist is meant to be used to filter out any unexpected
@@ -40,8 +41,9 @@ const ALPHA_FEATURES = [
40
41
  'audioCard',
41
42
  'videoCard',
42
43
  'productCard',
43
- 'quoteStyles',
44
- 'beforeAfterCard'
44
+ 'beforeAfterCard',
45
+ 'tweetGridCard',
46
+ 'headerCard'
45
47
  ];
46
48
 
47
49
  module.exports.GA_KEYS = [...GA_FEATURES];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghost",
3
- "version": "4.26.0",
3
+ "version": "4.27.2",
4
4
  "description": "The professional publishing platform",
5
5
  "author": "Ghost Foundation",
6
6
  "homepage": "https://ghost.org",
@@ -32,7 +32,7 @@
32
32
  "test:unit": "c8 --reporter text-summary --reporter cobertura mocha --require=./test/utils/overrides.js --exit --trace-warnings --recursive --extension=test.js './test/unit' --timeout=2000",
33
33
  "test:integration": "mocha --require=./test/utils/overrides.js --exit --trace-warnings --recursive --extension=test.js './test/integration' --timeout=5000",
34
34
  "test:e2e": "mocha --require=./test/utils/overrides.js --exit --trace-warnings --recursive --extension=test.js './test/e2e-api' './test/e2e-frontend' './test/e2e-server' --timeout=10000",
35
- "test:regression": "mocha --require=./test/utils/overrides.js --exit --trace-warnings --recursive --extension=test.js './test/regression' --timeout=60000",
35
+ "test:regression": "mocha --require=./test/utils/overrides.js --require=./test/utils/snapshots.js --exit --trace-warnings --recursive --extension=test.js './test/regression' --timeout=60000",
36
36
  "test:unit:slow": "yarn test:unit --reporter=mocha-slow-test-reporter",
37
37
  "test:int:slow": "yarn test:integration --reporter=mocha-slow-test-reporter",
38
38
  "test:e2e:slow": "yarn test:e2e --reporter=mocha-slow-test-reporter",
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@nexes/nql": "0.6.0",
57
- "@sentry/node": "6.15.0",
57
+ "@sentry/node": "6.16.0",
58
58
  "@tryghost/adapter-manager": "0.2.24",
59
59
  "@tryghost/admin-api-schema": "2.6.1",
60
60
  "@tryghost/bookshelf-plugins": "0.3.5",
@@ -73,7 +73,7 @@
73
73
  "@tryghost/job-manager": "0.8.16",
74
74
  "@tryghost/kg-card-factory": "3.1.0",
75
75
  "@tryghost/kg-default-atoms": "3.1.0",
76
- "@tryghost/kg-default-cards": "5.9.5",
76
+ "@tryghost/kg-default-cards": "5.13.1",
77
77
  "@tryghost/kg-markdown-html-renderer": "5.1.0",
78
78
  "@tryghost/kg-mobiledoc-html-renderer": "5.3.1",
79
79
  "@tryghost/limit-service": "1.0.6",
@@ -130,7 +130,7 @@
130
130
  "ghost-storage-base": "1.0.0",
131
131
  "glob": "7.2.0",
132
132
  "got": "9.6.0",
133
- "gscan": "4.15.2",
133
+ "gscan": "4.16.0",
134
134
  "html-to-text": "5.1.1",
135
135
  "image-size": "1.0.0",
136
136
  "intl": "1.2.5",
@@ -145,18 +145,18 @@
145
145
  "lodash": "4.17.21",
146
146
  "luxon": "2.1.1",
147
147
  "mailgun-js": "0.22.0",
148
- "metascraper": "5.25.4",
149
- "metascraper-author": "5.25.4",
150
- "metascraper-description": "5.25.4",
151
- "metascraper-image": "5.25.4",
152
- "metascraper-logo": "5.25.4",
153
- "metascraper-logo-favicon": "5.25.4",
154
- "metascraper-publisher": "5.25.4",
155
- "metascraper-title": "5.25.4",
156
- "metascraper-url": "5.25.4",
148
+ "metascraper": "5.25.5",
149
+ "metascraper-author": "5.25.5",
150
+ "metascraper-description": "5.25.5",
151
+ "metascraper-image": "5.25.5",
152
+ "metascraper-logo": "5.25.5",
153
+ "metascraper-logo-favicon": "5.25.5",
154
+ "metascraper-publisher": "5.25.5",
155
+ "metascraper-title": "5.25.5",
156
+ "metascraper-url": "5.25.5",
157
157
  "moment": "2.24.0",
158
158
  "moment-timezone": "0.5.23",
159
- "multer": "1.4.3",
159
+ "multer": "1.4.4",
160
160
  "mysql": "2.18.1",
161
161
  "nconf": "0.11.3",
162
162
  "node-jose": "2.0.0",
@@ -166,7 +166,7 @@
166
166
  "path-match": "1.2.4",
167
167
  "probe-image-size": "5.0.0",
168
168
  "rss": "1.2.2",
169
- "sanitize-html": "2.6.0",
169
+ "sanitize-html": "2.6.1",
170
170
  "semver": "7.3.5",
171
171
  "stoppable": "1.1.0",
172
172
  "tough-cookie": "4.0.0",
@@ -174,16 +174,19 @@
174
174
  "xml": "1.0.1"
175
175
  },
176
176
  "optionalDependencies": {
177
- "@tryghost/html-to-mobiledoc": "1.4.1",
177
+ "@tryghost/html-to-mobiledoc": "1.5.2",
178
178
  "sqlite3": "5.0.2"
179
179
  },
180
180
  "devDependencies": {
181
+ "@ethanresnick/chai-jest-snapshot": "3.0.0",
181
182
  "@lodder/grunt-postcss": "3.1.1",
182
183
  "c8": "7.10.0",
184
+ "chai": "4.3.4",
183
185
  "coffeescript": "2.6.1",
184
186
  "cssnano": "5.0.12",
185
187
  "eslint": "7.32.0",
186
188
  "eslint-plugin-ghost": "2.11.0",
189
+ "expect": "27.4.2",
187
190
  "grunt": "1.4.1",
188
191
  "grunt-bg-shell": "2.3.3",
189
192
  "grunt-contrib-clean": "2.0.0",