ghost 4.29.0 → 4.32.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 (79) 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 +110 -43
  4. package/content/themes/casper/package.json +1 -1
  5. package/core/boot.js +3 -5
  6. package/core/built/assets/{chunk.3.e54be01b5124e4e27958.js → chunk.3.8f95b516d88ff4eec64c.js} +7 -7
  7. package/core/built/assets/ghost-dark-43f5faa616791819b3ae91e128ec41f0.css +1 -0
  8. package/core/built/assets/{ghost.min-ba8ef5d16b8a6f9072564a368154df72.js → ghost.min-2b20489c79323b165909749382adc158.js} +4 -4
  9. package/core/built/assets/ghost.min-c3f7cbabcc1a69476534453c6c747ee3.css +1 -0
  10. package/core/built/assets/icons/file-upload.svg +1 -1
  11. package/core/built/assets/{vendor.min-c450359ad46217cd48c00b277ae54a09.js → vendor.min-992a9b07f7d0a67b5a4afd91319edf8b.js} +615 -579
  12. package/core/frontend/apps/amp/lib/views/amp.hbs +134 -0
  13. package/core/frontend/helpers/get.js +4 -0
  14. package/core/frontend/services/theme-engine/i18n/i18n.js +11 -12
  15. package/core/frontend/services/theme-engine/i18n/index.js +1 -2
  16. package/core/frontend/services/theme-engine/i18n/theme-i18n.js +5 -4
  17. package/core/frontend/src/cards/css/audio.css +4 -2
  18. package/core/frontend/src/cards/css/before-after.css +45 -11
  19. package/core/frontend/src/cards/css/bookmark.css +3 -2
  20. package/core/frontend/src/cards/css/button.css +4 -3
  21. package/core/frontend/src/cards/css/callout.css +10 -5
  22. package/core/frontend/src/cards/css/file.css +107 -48
  23. package/core/frontend/src/cards/css/gallery.css +5 -3
  24. package/core/frontend/src/cards/css/header.css +228 -0
  25. package/core/frontend/src/cards/css/nft.css +1 -7
  26. package/core/frontend/src/cards/css/product.css +24 -26
  27. package/core/frontend/src/cards/css/toggle.css +32 -22
  28. package/core/frontend/src/cards/css/video.css +103 -44
  29. package/core/frontend/src/cards/js/before-after.js +10 -15
  30. package/core/frontend/src/cards/js/video.js +81 -13
  31. package/core/frontend/web/middleware/error-handler.js +2 -2
  32. package/core/frontend/web/site.js +2 -1
  33. package/core/server/api/canary/oembed.js +1 -2
  34. package/core/server/data/db/state-manager.js +5 -8
  35. package/core/server/lib/image/cached-image-size-from-url.js +3 -4
  36. package/core/server/lib/image/image-utils.js +2 -2
  37. package/core/server/lib/image/index.js +1 -2
  38. package/core/server/run-update-check.js +1 -13
  39. package/core/server/services/email-analytics/index.js +2 -4
  40. package/core/server/services/email-analytics/jobs/fetch-latest.js +2 -21
  41. package/core/server/services/integrations/integrations-service.js +2 -2
  42. package/core/server/services/invites/index.js +0 -2
  43. package/core/server/services/invites/invites.js +3 -3
  44. package/core/server/services/jobs/job-service.js +1 -1
  45. package/core/server/services/mail/templates/invite-user.html +1 -1
  46. package/core/server/services/mail/templates/reset-password.html +1 -1
  47. package/core/server/services/mail/templates/welcome.html +1 -1
  48. package/core/server/services/mega/template.js +109 -1
  49. package/core/server/services/members/api.js +0 -1
  50. package/core/server/services/members/config.js +4 -7
  51. package/core/server/services/members/emails/signin.js +1 -1
  52. package/core/server/services/members/emails/signup-paid.js +1 -1
  53. package/core/server/services/members/emails/signup.js +1 -1
  54. package/core/server/services/members/emails/subscribe.js +1 -1
  55. package/core/server/services/members/emails/updateEmail.js +1 -1
  56. package/core/server/services/members/service.js +1 -4
  57. package/core/server/services/notifications/index.js +0 -2
  58. package/core/server/services/notifications/notifications.js +4 -5
  59. package/core/server/services/stripe/index.js +0 -2
  60. package/core/server/services/twitter-embed.js +2 -1
  61. package/core/server/web/admin/app.js +4 -2
  62. package/core/server/web/admin/views/default-prod.html +4 -4
  63. package/core/server/web/admin/views/default.html +4 -4
  64. package/core/server/web/api/app.js +3 -2
  65. package/core/server/web/api/canary/admin/app.js +4 -2
  66. package/core/server/web/api/canary/content/app.js +4 -2
  67. package/core/server/web/api/v2/admin/app.js +4 -2
  68. package/core/server/web/api/v2/content/app.js +4 -2
  69. package/core/server/web/api/v3/admin/app.js +4 -2
  70. package/core/server/web/api/v3/content/app.js +4 -2
  71. package/core/server/web/members/app.js +6 -4
  72. package/core/server/web/shared/middleware/index.js +0 -4
  73. package/core/shared/config/overrides.json +11 -1
  74. package/core/shared/labs.js +6 -6
  75. package/package.json +22 -21
  76. package/yarn.lock +212 -111
  77. package/core/built/assets/ghost-dark-b93e2e2dcc4c0e1efa981314f45f8cda.css +0 -1
  78. package/core/built/assets/ghost.min-578a1b7e68d1f7ae0003ebe421b7c182.css +0 -1
  79. package/core/server/web/shared/middleware/error-handler.js +0 -224
@@ -2,35 +2,30 @@
2
2
  const beforeAfterCards = [...document.querySelectorAll('.kg-before-after-card')];
3
3
 
4
4
  for (let card of beforeAfterCards) {
5
- const isFullWidth = card.classList.contains('kg-width-full');
6
5
  const input = card.querySelector('input');
7
6
  const overlay = card.querySelector('.kg-before-after-card-image-before');
8
- const orientation = card.querySelector('div').getAttribute('data-orientation');
7
+ const button = card.querySelector('.kg-before-after-card-slider-button');
9
8
  const images = [...card.querySelectorAll('img')];
10
- const smallestImageWidth = Math.min(
11
- ...images.map(img => parseInt(img.getAttribute('width')))
12
- );
13
9
 
14
10
  function updateSlider() {
15
- if (orientation === 'vertical') {
16
- overlay.setAttribute('style', `height: ${input.value}%`);
17
- } else {
18
- overlay.setAttribute('style', `width: ${input.value}%`);
19
- }
11
+ overlay.setAttribute('style', `width: ${input.value}%`);
12
+ button.setAttribute('style', `left: calc(${input.value}% - 18px`);
20
13
  }
21
14
 
22
15
  function updateDimensions() {
23
- const containerWidth = parseInt(getComputedStyle(card).getPropertyValue('width'));
24
- const width = isFullWidth ? containerWidth : Math.min(smallestImageWidth, containerWidth);
25
- for (let image of images) {
26
- image.setAttribute('style', `width: ${width.toString()}px;`);
27
- }
16
+ const imageWidth = getComputedStyle(images[0]).getPropertyValue('width');
17
+
18
+ images[1].setAttribute('style', `width: ${imageWidth}`);
28
19
  }
29
20
 
30
21
  input.addEventListener('input', function () {
31
22
  updateSlider();
32
23
  });
33
24
 
25
+ input.addEventListener('change', function () {
26
+ input.blur();
27
+ });
28
+
34
29
  window.addEventListener('resize', function () {
35
30
  updateDimensions();
36
31
  });
@@ -1,6 +1,7 @@
1
1
  (function() {
2
2
  const handleVideoPlayer = function (videoElementContainer) {
3
- const videoPlayerContainer = videoElementContainer.querySelector('.kg-video-player');
3
+ const videoPlayer = videoElementContainer.querySelector('.kg-video-player');
4
+ const videoPlayerContainer = videoElementContainer.querySelector('.kg-video-player-container');
4
5
  const playIconContainer = videoElementContainer.querySelector('.kg-video-play-icon');
5
6
  const pauseIconContainer = videoElementContainer.querySelector('.kg-video-pause-icon');
6
7
  const seekSlider = videoElementContainer.querySelector('.kg-video-seek-slider');
@@ -11,6 +12,8 @@
11
12
  const videoEl = videoElementContainer.querySelector('video');
12
13
  const durationContainer = videoElementContainer.querySelector('.kg-video-duration');
13
14
  const currentTimeContainer = videoElementContainer.querySelector('.kg-video-current-time');
15
+ const largePlayIcon = videoElementContainer.querySelector('.kg-video-large-play-icon');
16
+ const videoOverlay = videoElementContainer.querySelector('.kg-video-overlay');
14
17
  let playbackRates = [{
15
18
  rate: 0.75,
16
19
  label: '0.7×'
@@ -30,20 +33,23 @@
30
33
 
31
34
  let raf = null;
32
35
  let currentPlaybackRateIdx = 1;
33
-
36
+ if (!!videoEl.loop) {
37
+ largePlayIcon.classList.add("kg-video-hide-animated");
38
+ videoOverlay.classList.add("kg-video-hide-animated");
39
+ }
34
40
  const whilePlaying = () => {
35
41
  seekSlider.value = Math.floor(videoEl.currentTime);
36
42
  currentTimeContainer.textContent = calculateTime(seekSlider.value);
37
- videoPlayerContainer.style.setProperty('--seek-before-width', `${seekSlider.value / seekSlider.max * 100}%`);
43
+ videoPlayer.style.setProperty('--seek-before-width', `${seekSlider.value / seekSlider.max * 100}%`);
38
44
  raf = requestAnimationFrame(whilePlaying);
39
45
  }
40
46
 
41
47
  const showRangeProgress = (rangeInput) => {
42
48
  if (rangeInput === seekSlider) {
43
- videoPlayerContainer.style.setProperty('--seek-before-width', rangeInput.value / rangeInput.max * 100 + '%');
49
+ videoPlayer.style.setProperty('--seek-before-width', rangeInput.value / rangeInput.max * 100 + '%');
44
50
  }
45
51
  else {
46
- videoPlayerContainer.style.setProperty('--volume-before-width', rangeInput.value / rangeInput.max * 100 + '%');
52
+ videoPlayer.style.setProperty('--volume-before-width', rangeInput.value / rangeInput.max * 100 + '%');
47
53
  }
48
54
  }
49
55
 
@@ -65,7 +71,7 @@
65
71
  const displayBufferedAmount = () => {
66
72
  if (videoEl.buffered.length > 0) {
67
73
  const bufferedAmount = Math.floor(videoEl.buffered.end(videoEl.buffered.length - 1));
68
- videoPlayerContainer.style.setProperty('--buffered-width', `${(bufferedAmount / seekSlider.max) * 100}%`);
74
+ videoPlayer.style.setProperty('--buffered-width', `${(bufferedAmount / seekSlider.max) * 100}%`);
69
75
  }
70
76
  }
71
77
 
@@ -99,33 +105,84 @@
99
105
  });
100
106
  }
101
107
 
102
- playIconContainer.addEventListener('click', () => {
108
+ videoElementContainer.onmouseover = () => {
109
+ if (!videoEl.loop) {
110
+ videoPlayerContainer.classList.remove("kg-video-hide-animated");
111
+ }
112
+ }
113
+
114
+ videoElementContainer.onmouseleave = () => {
115
+ const isPlaying = !!(videoEl.currentTime > 0 && !videoEl.paused && !videoEl.ended && videoEl.readyState > 2);
116
+ if (isPlaying) {
117
+ videoPlayerContainer.classList.add("kg-video-hide-animated");
118
+ }
119
+ }
120
+
121
+ videoElementContainer.addEventListener('click', () => {
122
+ if (!videoEl.loop) {
123
+ const isPlaying = !!(videoEl.currentTime > 0 && !videoEl.paused && !videoEl.ended && videoEl.readyState > 2);
124
+ if (isPlaying) {
125
+ handleOnPause();
126
+ } else {
127
+ handleOnPlay();
128
+ }
129
+ }
130
+ });
131
+
132
+ videoEl.onplay = () => {
133
+ largePlayIcon.classList.add("kg-video-hide-animated");
134
+ videoOverlay.classList.add("kg-video-hide-animated");
135
+ playIconContainer.classList.add("kg-video-hide");
136
+ pauseIconContainer.classList.remove("kg-video-hide");
137
+ };
138
+
139
+ const handleOnPlay = () => {
140
+ largePlayIcon.classList.add("kg-video-hide-animated");
141
+ videoOverlay.classList.add("kg-video-hide-animated");
103
142
  playIconContainer.classList.add("kg-video-hide");
104
143
  pauseIconContainer.classList.remove("kg-video-hide");
105
144
  videoEl.play();
106
145
  raf = requestAnimationFrame(whilePlaying);
107
- });
146
+ }
108
147
 
109
- pauseIconContainer.addEventListener('click', () => {
148
+ const handleOnPause = () => {
110
149
  pauseIconContainer.classList.add("kg-video-hide");
111
150
  playIconContainer.classList.remove("kg-video-hide");
112
151
  videoEl.pause();
113
152
  cancelAnimationFrame(raf);
153
+ }
154
+
155
+ largePlayIcon.addEventListener('click', (event) => {
156
+ event.stopPropagation();
157
+ handleOnPlay();
158
+ });
159
+
160
+ playIconContainer.addEventListener('click', (event) => {
161
+ event.stopPropagation();
162
+ handleOnPlay();
114
163
  });
115
164
 
116
- muteIconContainer.addEventListener('click', () => {
165
+ pauseIconContainer.addEventListener('click', (event) => {
166
+ event.stopPropagation();
167
+ handleOnPause();
168
+ });
169
+
170
+ muteIconContainer.addEventListener('click', (event) => {
171
+ event.stopPropagation();
117
172
  muteIconContainer.classList.add("kg-video-hide");
118
173
  unmuteIconContainer.classList.remove("kg-video-hide");
119
174
  videoEl.muted = false;
120
175
  });
121
176
 
122
- unmuteIconContainer.addEventListener('click', () => {
177
+ unmuteIconContainer.addEventListener('click', (event) => {
178
+ event.stopPropagation();
123
179
  unmuteIconContainer.classList.add("kg-video-hide");
124
180
  muteIconContainer.classList.remove("kg-video-hide");
125
181
  videoEl.muted = true;
126
182
  });
127
183
 
128
- playbackRateContainer.addEventListener('click', () => {
184
+ playbackRateContainer.addEventListener('click', (event) => {
185
+ event.stopPropagation();
129
186
  let nextPlaybackRate = playbackRates[(currentPlaybackRateIdx + 1) % 5];
130
187
  currentPlaybackRateIdx = currentPlaybackRateIdx + 1;
131
188
  videoEl.playbackRate = nextPlaybackRate.rate;
@@ -135,6 +192,7 @@
135
192
  videoEl.addEventListener('progress', displayBufferedAmount);
136
193
 
137
194
  seekSlider.addEventListener('input', (e) => {
195
+ e.stopPropagation();
138
196
  showRangeProgress(e.target);
139
197
  currentTimeContainer.textContent = calculateTime(seekSlider.value);
140
198
  if (!videoEl.paused) {
@@ -142,14 +200,24 @@
142
200
  }
143
201
  });
144
202
 
145
- seekSlider.addEventListener('change', () => {
203
+ seekSlider.addEventListener('change', (event) => {
204
+ event.stopPropagation();
146
205
  videoEl.currentTime = seekSlider.value;
147
206
  if (!videoEl.paused) {
148
207
  requestAnimationFrame(whilePlaying);
149
208
  }
150
209
  });
151
210
 
211
+ volumeSlider.addEventListener('click', (event) => {
212
+ event.stopPropagation();
213
+ });
214
+
215
+ seekSlider.addEventListener('click', (event) => {
216
+ event.stopPropagation();
217
+ });
218
+
152
219
  volumeSlider.addEventListener('input', (e) => {
220
+ e.stopPropagation();
153
221
  const value = e.target.value;
154
222
  showRangeProgress(e.target);
155
223
  videoEl.volume = value / 100;
@@ -7,7 +7,7 @@ const config = require('../../../shared/config');
7
7
  const helpers = require('../../services/routing/helpers');
8
8
 
9
9
  // @TODO: make this properly shared code
10
- const shared = require('../../../server/web/shared/middleware/error-handler');
10
+ const {prepareError} = require('@tryghost/mw-error-handler');
11
11
 
12
12
  const messages = {
13
13
  oopsErrorTemplateHasError: 'Oops, seems there is an error in the error template.',
@@ -85,7 +85,7 @@ const themeErrorRenderer = (err, req, res, next) => {
85
85
 
86
86
  module.exports.handleThemeResponse = [
87
87
  // Make sure the error can be served
88
- shared.prepareError,
88
+ prepareError,
89
89
  // Handle the error in Sentry
90
90
  sentry.errorHandler,
91
91
  // Render the error using theme template
@@ -20,6 +20,7 @@ const offersService = require('../../server/services/offers');
20
20
  const customRedirects = require('../../server/services/redirects');
21
21
  const siteRoutes = require('./routes');
22
22
  const shared = require('../../server/web/shared');
23
+ const errorHandler = require('@tryghost/mw-error-handler');
23
24
  const mw = require('./middleware');
24
25
  const labs = require('../../shared/labs');
25
26
 
@@ -176,7 +177,7 @@ module.exports = function setupSiteApp(options = {}) {
176
177
  siteApp.use(SiteRouter);
177
178
 
178
179
  // ### Error handlers
179
- siteApp.use(shared.middleware.errorHandler.pageNotFound);
180
+ siteApp.use(errorHandler.pageNotFound);
180
181
  config.get('apps:internal').forEach((appName) => {
181
182
  const app = require(path.join(config.get('paths').internalAppPath, appName));
182
183
 
@@ -15,8 +15,7 @@ const Twitter = require('../../services/twitter-embed');
15
15
  const twitter = new Twitter({
16
16
  config: {
17
17
  bearerToken: config.get('twitter').privateReadOnlyToken
18
- },
19
- logging: require('@tryghost/logging')
18
+ }
20
19
  });
21
20
 
22
21
  oembed.registerProvider(nft);
@@ -1,5 +1,6 @@
1
1
  const KnexMigrator = require('knex-migrator');
2
2
  const errors = require('@tryghost/errors');
3
+ const logging = require('@tryghost/logging');
3
4
 
4
5
  const states = {
5
6
  READY: 0,
@@ -8,7 +9,7 @@ const states = {
8
9
  ERROR: 3
9
10
  };
10
11
 
11
- const printState = ({state, logging}) => {
12
+ const printState = ({state}) => {
12
13
  if (state === states.READY) {
13
14
  logging.info('Database is in a ready state.');
14
15
  }
@@ -67,13 +68,11 @@ class DatabaseStateManager {
67
68
  }
68
69
  }
69
70
 
70
- async makeReady({logging}) {
71
+ async makeReady() {
71
72
  try {
72
73
  let state = await this.getState();
73
74
 
74
- if (logging) {
75
- printState({state, logging});
76
- }
75
+ printState({state});
77
76
 
78
77
  if (state === states.READY) {
79
78
  return;
@@ -89,9 +88,7 @@ class DatabaseStateManager {
89
88
 
90
89
  state = await this.getState();
91
90
 
92
- if (logging) {
93
- printState({state, logging});
94
- }
91
+ printState({state});
95
92
  } catch (error) {
96
93
  let errorToThrow = error;
97
94
  if (!errors.utils.isGhostError(error)) {
@@ -1,9 +1,8 @@
1
1
  const debug = require('@tryghost/debug')('utils:image-size-cache');
2
2
  const errors = require('@tryghost/errors');
3
-
3
+ const logging = require('@tryghost/logging');
4
4
  class CachedImageSizeFromUrl {
5
- constructor({logging, imageSize}) {
6
- this.logging = logging;
5
+ constructor({imageSize}) {
7
6
  this.imageSize = imageSize;
8
7
  this.cache = new Map();
9
8
  }
@@ -37,7 +36,7 @@ class CachedImageSizeFromUrl {
37
36
  return this.cache.get(url);
38
37
  }).catch((err) => {
39
38
  debug('Cached image (error):', url);
40
- this.logging.error(err);
39
+ logging.error(err);
41
40
 
42
41
  // in case of error we just attach the url
43
42
  this.cache.set(url, url);
@@ -4,10 +4,10 @@ const Gravatar = require('./gravatar');
4
4
  const ImageSize = require('./image-size');
5
5
 
6
6
  class ImageUtils {
7
- constructor({config, logging, urlUtils, settingsCache, storageUtils, storage, validator, request}) {
7
+ constructor({config, urlUtils, settingsCache, storageUtils, storage, validator, request}) {
8
8
  this.blogIcon = new BlogIcon({config, urlUtils, settingsCache, storageUtils});
9
9
  this.imageSize = new ImageSize({config, storage, storageUtils, validator, urlUtils, request});
10
- this.cachedImageSizeFromUrl = new CachedImageSizeFromUrl({logging, imageSize: this.imageSize});
10
+ this.cachedImageSizeFromUrl = new CachedImageSizeFromUrl({imageSize: this.imageSize});
11
11
  this.gravatar = new Gravatar({config, request});
12
12
  }
13
13
  }
@@ -4,8 +4,7 @@ const storage = require('../../adapters/storage');
4
4
  const storageUtils = require('../../adapters/storage/utils');
5
5
  const validator = require('@tryghost/validator');
6
6
  const config = require('../../../shared/config');
7
- const logging = require('@tryghost/logging');
8
7
  const settingsCache = require('../../../shared/settings-cache');
9
8
  const ImageUtils = require('./image-utils');
10
9
 
11
- module.exports = new ImageUtils({config, logging, urlUtils, settingsCache, storageUtils, storage, validator, request});
10
+ module.exports = new ImageUtils({config, urlUtils, settingsCache, storageUtils, storage, validator, request});
@@ -31,18 +31,6 @@ if (parentPort) {
31
31
  (async () => {
32
32
  const updateCheck = require('./update-check');
33
33
 
34
- const logging = {
35
- info(message) {
36
- postParentPortMessage(message);
37
- },
38
- warn(message) {
39
- postParentPortMessage(message);
40
- },
41
- error(message) {
42
- postParentPortMessage(message);
43
- }
44
- };
45
-
46
34
  // INIT required services
47
35
  const models = require('./models');
48
36
  models.init();
@@ -54,7 +42,7 @@ if (parentPort) {
54
42
  await settings.init();
55
43
  // Finished INIT
56
44
 
57
- await updateCheck({logging});
45
+ await updateCheck();
58
46
 
59
47
  postParentPortMessage(`Ran update check`);
60
48
 
@@ -1,5 +1,4 @@
1
1
  const config = require('../../../shared/config');
2
- const logging = require('@tryghost/logging');
3
2
  const db = require('../../data/db');
4
3
  const settings = require('../../../shared/settings-cache');
5
4
  const {EmailAnalyticsService} = require('@tryghost/email-analytics-service');
@@ -9,11 +8,10 @@ const queries = require('./lib/queries');
9
8
 
10
9
  module.exports = new EmailAnalyticsService({
11
10
  config,
12
- logging,
13
11
  settings,
14
- eventProcessor: new EventProcessor({db, logging}),
12
+ eventProcessor: new EventProcessor({db}),
15
13
  providers: [
16
- new MailgunProvider({config, settings, logging})
14
+ new MailgunProvider({config, settings})
17
15
  ],
18
16
  queries
19
17
  });
@@ -28,24 +28,6 @@ if (parentPort) {
28
28
  const config = require('../../../../shared/config');
29
29
  const db = require('../../../data/db');
30
30
 
31
- const logging = {
32
- info(message) {
33
- if (parentPort) {
34
- parentPort.postMessage(message);
35
- }
36
- },
37
- warn(message) {
38
- if (parentPort) {
39
- parentPort.postMessage(message);
40
- }
41
- },
42
- error(message) {
43
- if (parentPort) {
44
- parentPort.postMessage(message);
45
- }
46
- }
47
- };
48
-
49
31
  const settingsRows = await db.knex('settings')
50
32
  .whereIn('key', ['mailgun_api_key', 'mailgun_domain', 'mailgun_base_url']);
51
33
 
@@ -69,10 +51,9 @@ if (parentPort) {
69
51
  const emailAnalyticsService = new EmailAnalyticsService({
70
52
  config,
71
53
  settings,
72
- logging,
73
- eventProcessor: new EventProcessor({db, logging}),
54
+ eventProcessor: new EventProcessor({db}),
74
55
  providers: [
75
- new MailgunProvider({config, settings, logging})
56
+ new MailgunProvider({config, settings})
76
57
  ],
77
58
  queries
78
59
  });
@@ -1,4 +1,4 @@
1
- const {NotFoundError, GhostError} = require('@tryghost/errors');
1
+ const {NotFoundError, InternalServerError} = require('@tryghost/errors');
2
2
  const tpl = require('@tryghost/tpl');
3
3
 
4
4
  const messages = {
@@ -29,7 +29,7 @@ class IntegrationsService {
29
29
  withRelated: ['api_keys', 'webhooks']
30
30
  });
31
31
  } catch (err) {
32
- throw new GhostError({
32
+ throw new InternalServerError({
33
33
  err: err
34
34
  });
35
35
  }
@@ -1,12 +1,10 @@
1
1
  const settingsCache = require('../../../shared/settings-cache');
2
2
  const mailService = require('../../services/mail');
3
- const logging = require('@tryghost/logging');
4
3
  const urlUtils = require('../../../shared/url-utils');
5
4
  const Invites = require('./invites');
6
5
 
7
6
  module.exports = new Invites({
8
7
  settingsCache,
9
- logging,
10
8
  mailService,
11
9
  urlUtils
12
10
  });
@@ -1,5 +1,6 @@
1
1
  const security = require('@tryghost/security');
2
2
  const tpl = require('@tryghost/tpl');
3
+ const logging = require('@tryghost/logging');
3
4
 
4
5
  const messages = {
5
6
  invitedByName: '{invitedByName} has invited you to join {blogName}',
@@ -10,9 +11,8 @@ const messages = {
10
11
  };
11
12
 
12
13
  class Invites {
13
- constructor({settingsCache, logging, mailService, urlUtils}) {
14
+ constructor({settingsCache, mailService, urlUtils}) {
14
15
  this.settingsCache = settingsCache;
15
- this.logging = logging;
16
16
  this.mailService = mailService;
17
17
  this.urlUtils = urlUtils;
18
18
  }
@@ -80,7 +80,7 @@ class Invites {
80
80
  });
81
81
  const helpText = tpl(messages.errorSendingEmail.help);
82
82
  err.message = `${errorMessage} ${helpText}`;
83
- this.logging.warn(err.message);
83
+ logging.warn(err.message);
84
84
  }
85
85
 
86
86
  return Promise.reject(err);
@@ -17,6 +17,6 @@ const workerMessageHandler = ({name, message}) => {
17
17
  logging.info(`Worker for job ${name} sent a message: ${message}`);
18
18
  };
19
19
 
20
- const jobManager = new JobManager({logging, errorHandler, workerMessageHandler});
20
+ const jobManager = new JobManager({errorHandler, workerMessageHandler});
21
21
 
22
22
  module.exports = jobManager;
@@ -101,7 +101,7 @@ a {
101
101
  }
102
102
  </style>
103
103
  </head>
104
- <body class="" style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
104
+ <body style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
105
105
 
106
106
  <table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
107
107
  <tr>
@@ -101,7 +101,7 @@ a {
101
101
  }
102
102
  </style>
103
103
  </head>
104
- <body class="" style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
104
+ <body style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
105
105
 
106
106
  <table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
107
107
  <tr>
@@ -101,7 +101,7 @@ a {
101
101
  }
102
102
  </style>
103
103
  </head>
104
- <body class="" style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
104
+ <body style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
105
105
 
106
106
  <table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
107
107
  <tr>
@@ -638,6 +638,114 @@ figure blockquote p {
638
638
  font-size: 20px;
639
639
  }
640
640
 
641
+ .kg-header-card {
642
+ margin: 0 0 1.5em 0;
643
+ padding: 110px 35px 110px 35px;
644
+ text-align: center;
645
+ }
646
+
647
+ .kg-header-card.kg-size-small {
648
+ padding-top: 75px;
649
+ padding-bottom: 75px;
650
+ }
651
+
652
+ .kg-header-card.kg-size-large {
653
+ padding-top: 140px;
654
+ padding-bottom: 140px;
655
+ }
656
+
657
+ .kg-header-card.kg-align-left {
658
+ text-align: left;
659
+ }
660
+
661
+ .kg-header-card.kg-style-dark {
662
+ background: #15171a;
663
+ color: #ffffff;
664
+ }
665
+
666
+ .kg-header-card.kg-style-light {
667
+ background-color: #F9F9FA;
668
+ }
669
+
670
+ .kg-header-card.kg-style-accent {
671
+ background: ${templateSettings.accentColor || '#15171a'};
672
+ color: #ffffff;
673
+ }
674
+
675
+ .kg-header-card.kg-style-image {
676
+ background-color: #e7e7eb;
677
+ background-size: cover;
678
+ background-position: center center;
679
+ color: #ffffff;
680
+ }
681
+
682
+ .kg-header-card h2 {
683
+ font-size: 3em;
684
+ font-weight: 700;
685
+ line-height: 1.1em;
686
+ margin: 0 0 0.125em;
687
+ }
688
+
689
+ .kg-header-card h2 strong {
690
+ font-weight: 800;
691
+ }
692
+
693
+ .kg-header-card.kg-size-small h2 {
694
+ font-size: 2.5em;
695
+ }
696
+
697
+ .kg-header-card.kg-size-large h2 {
698
+ font-size: 3.5em;
699
+ }
700
+
701
+ .kg-header-card h3 {
702
+ font-size: 1.125em;
703
+ font-weight: 500;
704
+ line-height: 1.3em;
705
+ margin: 0;
706
+ }
707
+
708
+ .kg-header-card h3 strong {
709
+ font-weight: 600;
710
+ }
711
+
712
+ .kg-header-card.kg-size-large h3 {
713
+ font-size: 1.25em;
714
+ }
715
+
716
+ .kg-header-card.kg-size-small h3 {
717
+ font-size: 1em;
718
+ }
719
+
720
+ .kg-header-card-button {
721
+ margin-top: 1.75em;
722
+ background: #ffffff;
723
+ border-radius: 5px;
724
+ box-sizing: border-box;
725
+ cursor: pointer;
726
+ display: inline-block;
727
+ font-size: 14px;
728
+ font-weight: bold;
729
+ margin: 0;
730
+ padding: 8px 16px;
731
+ text-decoration: none !important;
732
+ color: #15171A !important;
733
+ }
734
+
735
+ .kg-size-large .kg-header-card-button {
736
+ margin-top: 2em;
737
+ }
738
+
739
+ .kg-size-small .kg-header-card-button {
740
+ margin-top: 1.5em;
741
+ }
742
+
743
+ .kg-style-light .kg-header-card-button {
744
+ background: ${templateSettings.accentColor || '#15212A'} !important;
745
+ color: #ffffff !important;
746
+ }
747
+
748
+
641
749
  /* -------------------------------------
642
750
  HEADER, FOOTER, MAIN
643
751
  ------------------------------------- */
@@ -1003,7 +1111,7 @@ ${ templateSettings.showBadge ? `
1003
1111
  </style>
1004
1112
  </head>
1005
1113
 
1006
- <body class="">
1114
+ <body>
1007
1115
  <span class="preheader">${ post.excerpt ? post.excerpt : `${post.title} – ` }</span>
1008
1116
  <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body" width="100%">
1009
1117