ghost 4.15.1 → 4.16.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 (47) 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 +1 -1
  4. package/content/themes/casper/default.hbs +2 -2
  5. package/content/themes/casper/package.json +1 -1
  6. package/content/themes/casper/page.hbs +28 -26
  7. package/content/themes/casper/partials/post-card.hbs +2 -2
  8. package/content/themes/casper/post.hbs +67 -65
  9. package/content/themes/casper/tag.hbs +2 -2
  10. package/core/built/assets/ghost-dark-bb2831fc27fcb02893ed0a761207dc63.css +1 -0
  11. package/core/built/assets/{ghost.min-e35cfee26d942c364166f57f3dcc9e75.js → ghost.min-d1d99f3ed6e0f427874b2a11e7078475.js} +228 -187
  12. package/core/built/assets/ghost.min-e7612edfa72b0fe2c201b387923e6fc7.css +1 -0
  13. package/core/built/assets/icons/discount-bubble.svg +1 -0
  14. package/core/built/assets/{vendor.min-ca33abc718f21a51327841d58f8875d0.js → vendor.min-3660ec7864887f1496fe7a27fd23ab76.js} +44 -42
  15. package/core/frontend/helpers/ghost_head.js +7 -1
  16. package/core/frontend/services/settings/loader.js +2 -2
  17. package/core/frontend/services/theme-engine/middleware.js +4 -1
  18. package/core/server/api/canary/settings.js +13 -144
  19. package/core/server/api/canary/utils/validators/input/settings.js +23 -1
  20. package/core/server/api/v3/settings.js +13 -132
  21. package/core/server/api/v3/utils/validators/input/settings.js +23 -1
  22. package/core/server/data/exporter/table-lists.js +1 -0
  23. package/core/server/data/importer/import-manager.js +398 -0
  24. package/core/server/data/importer/importers/data/data-importer.js +162 -0
  25. package/core/server/data/importer/importers/data/index.js +1 -162
  26. package/core/server/data/importer/index.js +1 -379
  27. package/core/server/data/migrations/versions/4.16/01-add-custom-theme-settings-table.js +9 -0
  28. package/core/server/data/schema/schema.js +16 -0
  29. package/core/server/models/custom-theme-setting.js +9 -0
  30. package/core/server/models/index.js +2 -0
  31. package/core/server/services/custom-theme-settings.js +8 -0
  32. package/core/server/services/members/api.js +1 -0
  33. package/core/server/services/settings/index.js +13 -16
  34. package/core/server/services/settings/settings-bread-service.js +188 -0
  35. package/core/server/services/settings/settings-utils.js +32 -0
  36. package/core/server/services/themes/ThemeStorage.js +5 -4
  37. package/core/server/services/themes/activation-bridge.js +14 -0
  38. package/core/server/services/themes/validate.js +5 -2
  39. package/core/server/web/admin/views/default-prod.html +4 -4
  40. package/core/server/web/admin/views/default.html +4 -4
  41. package/core/server/web/members/app.js +2 -0
  42. package/core/shared/custom-theme-settings-cache.js +3 -0
  43. package/core/shared/labs.js +2 -1
  44. package/package.json +28 -27
  45. package/yarn.lock +806 -795
  46. package/core/built/assets/ghost-dark-faf931d90e92535e6c03ca16793cbe7b.css +0 -1
  47. package/core/built/assets/ghost.min-7aa074ad556a8455155ac88ceaca03ab.css +0 -1
@@ -0,0 +1,188 @@
1
+ const _ = require('lodash');
2
+ const tpl = require('@tryghost/tpl');
3
+ const {NotFoundError, NoPermissionError, BadRequestError} = require('@tryghost/errors');
4
+ const {obfuscatedSetting, isSecretSetting, hideValueIfSecret} = require('./settings-utils');
5
+
6
+ const messages = {
7
+ problemFindingSetting: 'Problem finding setting: {key}',
8
+ accessCoreSettingFromExtReq: 'Attempted to access core setting from external request'
9
+ };
10
+
11
+ class SettingsBREADService {
12
+ /**
13
+ *
14
+ * @param {Object} options
15
+ * @param {Object} options.SettingsModel
16
+ * @param {Object} options.settingsCache - SettingsCache instance
17
+ */
18
+ constructor({SettingsModel, settingsCache}) {
19
+ this.SettingsModel = SettingsModel;
20
+ this.settingsCache = settingsCache;
21
+ }
22
+
23
+ /**
24
+ *
25
+ * @param {Object} context ghost API context instance
26
+ * @returns
27
+ */
28
+ browse(context) {
29
+ let settings = this.settingsCache.getAll();
30
+
31
+ // CASE: no context passed (functional call)
32
+ if (!context) {
33
+ return Promise.resolve(settings.filter((setting) => {
34
+ return setting.group === 'site';
35
+ }));
36
+ }
37
+
38
+ if (!context.internal) {
39
+ // CASE: omit core settings unless internal request
40
+ settings = _.filter(settings, (setting) => {
41
+ const isCore = setting.group === 'core';
42
+ return !isCore;
43
+ });
44
+ // CASE: omit secret settings unless internal request
45
+ settings = settings.map(hideValueIfSecret);
46
+ }
47
+
48
+ return settings;
49
+ }
50
+
51
+ /**
52
+ *
53
+ * @param {String} key setting key
54
+ * @param {Object} [context] API context instance
55
+ * @returns {Object} an object with a filled out key that comes in a parameter
56
+ */
57
+ read(key, context) {
58
+ let setting;
59
+
60
+ if (key === 'slack') {
61
+ const slackURL = this.settingsCache.get('slack_url', {resolve: false});
62
+ const slackUsername = this.settingsCache.get('slack_username', {resolve: false});
63
+
64
+ setting = slackURL || slackUsername;
65
+ setting.key = 'slack';
66
+ setting.value = [{
67
+ url: slackURL && slackURL.value,
68
+ username: slackUsername && slackUsername.value
69
+ }];
70
+ } else {
71
+ setting = this.settingsCache.get(key, {resolve: false});
72
+ }
73
+
74
+ if (!setting) {
75
+ return Promise.reject(new NotFoundError({
76
+ message: tpl(messages.problemFindingSetting, {
77
+ key: key
78
+ })
79
+ }));
80
+ }
81
+
82
+ // @TODO: handle in settings model permissible fn
83
+ if (setting.group === 'core' && !(context && context.internal)) {
84
+ return Promise.reject(new NoPermissionError({
85
+ message: tpl(messages.accessCoreSettingFromExtReq)
86
+ }));
87
+ }
88
+
89
+ setting = hideValueIfSecret(setting);
90
+
91
+ return {
92
+ [key]: setting
93
+ };
94
+ }
95
+
96
+ /**
97
+ *
98
+ * @param {Object[]} settings
99
+ * @param {Object} options
100
+ * @param {Object} [options.context]
101
+ * @param {Object} [stripeConnectData]
102
+ * @returns
103
+ */
104
+ async edit(settings, options, stripeConnectData) {
105
+ const filteredSettings = settings.filter((setting) => {
106
+ // The `stripe_connect_integration_token` "setting" is only used to set the `stripe_connect_*` settings.
107
+ return ![
108
+ 'stripe_connect_integration_token',
109
+ 'stripe_connect_publishable_key',
110
+ 'stripe_connect_secret_key',
111
+ 'stripe_connect_livemode',
112
+ 'stripe_connect_account_id',
113
+ 'stripe_connect_display_name'
114
+ ].includes(setting.key)
115
+ // Remove obfuscated settings
116
+ && !(setting.value === obfuscatedSetting && isSecretSetting(setting));
117
+ });
118
+
119
+ const getSetting = setting => this.settingsCache.get(setting.key, {resolve: false});
120
+
121
+ const firstUnknownSetting = filteredSettings.find(setting => !getSetting(setting));
122
+
123
+ if (firstUnknownSetting) {
124
+ throw new NotFoundError({
125
+ message: tpl(messages.problemFindingSetting, {
126
+ key: firstUnknownSetting.key
127
+ })
128
+ });
129
+ }
130
+
131
+ if (!(options.context && options.context.internal)) {
132
+ const firstCoreSetting = filteredSettings.find(setting => getSetting(setting).group === 'core');
133
+
134
+ if (firstCoreSetting) {
135
+ throw new NoPermissionError({
136
+ message: tpl(messages.accessCoreSettingFromExtReq)
137
+ });
138
+ }
139
+ }
140
+
141
+ if (stripeConnectData) {
142
+ filteredSettings.push({
143
+ key: 'stripe_connect_publishable_key',
144
+ value: stripeConnectData.public_key
145
+ });
146
+ filteredSettings.push({
147
+ key: 'stripe_connect_secret_key',
148
+ value: stripeConnectData.secret_key
149
+ });
150
+ filteredSettings.push({
151
+ key: 'stripe_connect_livemode',
152
+ value: stripeConnectData.livemode
153
+ });
154
+ filteredSettings.push({
155
+ key: 'stripe_connect_display_name',
156
+ value: stripeConnectData.display_name
157
+ });
158
+ filteredSettings.push({
159
+ key: 'stripe_connect_account_id',
160
+ value: stripeConnectData.account_id
161
+ });
162
+ }
163
+
164
+ return this.SettingsModel.edit(filteredSettings, options);
165
+ }
166
+
167
+ /**
168
+ *
169
+ * @param {Object} stripeConnectIntegrationToken
170
+ * @param {Function} getSessionProp sync function fetching property from session store
171
+ * @param {Function} getStripeConnectTokenData async function retreiving Stripe Connect data for settings
172
+ * @returns {Promise<Object>} resolves with an object with following keys: public_key, secret_key, livemode, display_name, account_id
173
+ */
174
+ async getStripeConnectData(stripeConnectIntegrationToken, getSessionProp, getStripeConnectTokenData) {
175
+ if (stripeConnectIntegrationToken && stripeConnectIntegrationToken.value) {
176
+ try {
177
+ return await getStripeConnectTokenData(stripeConnectIntegrationToken.value, getSessionProp);
178
+ } catch (err) {
179
+ throw new BadRequestError({
180
+ err,
181
+ message: 'The Stripe Connect token could not be parsed.'
182
+ });
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ module.exports = SettingsBREADService;
@@ -0,0 +1,32 @@
1
+ // The string returned when a setting is set as write-only
2
+ const obfuscatedSetting = '••••••••';
3
+
4
+ /**
5
+ * @description // The function used to decide whether a setting is write-only
6
+ * @param {Object} setting setting record
7
+ * @param {String} setting.key
8
+ * @returns {Boolean}
9
+ */
10
+ function isSecretSetting(setting) {
11
+ return /secret/.test(setting.key);
12
+ }
13
+
14
+ /**
15
+ * @description The function that obfuscates a write-only setting
16
+ * @param {Object} setting setting record
17
+ * @param {String} setting.value
18
+ * @param {String} setting.key
19
+ * @returns {Object} settings record with obfuscated value if it's a secret
20
+ */
21
+ function hideValueIfSecret(setting) {
22
+ if (setting.value && isSecretSetting(setting)) {
23
+ return {...setting, value: obfuscatedSetting};
24
+ }
25
+ return setting;
26
+ }
27
+
28
+ module.exports = {
29
+ obfuscatedSetting,
30
+ isSecretSetting,
31
+ hideValueIfSecret
32
+ };
@@ -60,8 +60,8 @@ class ThemeStorage extends LocalFileStorage {
60
60
  /**
61
61
  * Rename a file / folder
62
62
  *
63
- *
64
- * @param String fileName
63
+ * @param {String} srcName
64
+ * @param {String} destName
65
65
  */
66
66
  rename(srcName, destName) {
67
67
  let src = path.join(this.getTargetDir(), srcName);
@@ -71,9 +71,10 @@ class ThemeStorage extends LocalFileStorage {
71
71
  }
72
72
 
73
73
  /**
74
- * Rename a file / folder
74
+ * Remove a file / folder
75
75
  *
76
- * @param String backupName
76
+ * @param {String} fileName
77
+ * @returns {Promise<void>}
77
78
  */
78
79
  delete(fileName) {
79
80
  return fs.remove(path.join(this.getTargetDir(), fileName));
@@ -1,5 +1,7 @@
1
1
  const debug = require('@tryghost/debug')('themes');
2
2
  const bridge = require('../../../bridge');
3
+ const labs = require('../../../shared/labs');
4
+ const customThemeSettings = require('../custom-theme-settings');
3
5
 
4
6
  /**
5
7
  * These helper methods mean that the bridge is only required in one place
@@ -8,14 +10,26 @@ const bridge = require('../../../bridge');
8
10
  module.exports = {
9
11
  activateFromBoot: (themeName, theme, checkedTheme) => {
10
12
  debug('Activating theme (method A on boot)', themeName);
13
+ // TODO: probably a better place for this to happen - after successful activation / when reloading site?
14
+ if (labs.isSet('customThemeSettings')) {
15
+ customThemeSettings.activateTheme(checkedTheme);
16
+ }
11
17
  bridge.activateTheme(theme, checkedTheme);
12
18
  },
13
19
  activateFromAPI: (themeName, theme, checkedTheme) => {
14
20
  debug('Activating theme (method B on API "activate")', themeName);
21
+ // TODO: probably a better place for this to happen - after successful activation / when reloading site?
22
+ if (labs.isSet('customThemeSettings')) {
23
+ customThemeSettings.activateTheme(checkedTheme);
24
+ }
15
25
  bridge.activateTheme(theme, checkedTheme);
16
26
  },
17
27
  activateFromAPIOverride: (themeName, theme, checkedTheme) => {
18
28
  debug('Activating theme (method C on API "override")', themeName);
29
+ // TODO: probably a better place for this to happen - after successful activation / when reloading site?
30
+ if (labs.isSet('customThemeSettings')) {
31
+ customThemeSettings.activateTheme(checkedTheme);
32
+ }
19
33
  bridge.activateTheme(theme, checkedTheme);
20
34
  }
21
35
  };
@@ -2,6 +2,7 @@ const debug = require('@tryghost/debug')('themes');
2
2
  const _ = require('lodash');
3
3
  const fs = require('fs-extra');
4
4
  const config = require('../../../shared/config');
5
+ const labs = require('../../../shared/labs');
5
6
  const tpl = require('@tryghost/tpl');
6
7
  const errors = require('@tryghost/errors');
7
8
 
@@ -27,12 +28,14 @@ const check = async function check(theme, isZip) {
27
28
  debug('zip mode');
28
29
  checkedTheme = await gscan.checkZip(theme, {
29
30
  keepExtractedDir: true,
30
- checkVersion: 'canary'
31
+ checkVersion: 'canary',
32
+ labs: labs.getAll()
31
33
  });
32
34
  } else {
33
35
  debug('non-zip mode');
34
36
  checkedTheme = await gscan.check(theme.path, {
35
- checkVersion: 'canary'
37
+ checkVersion: 'canary',
38
+ labs: labs.getAll()
36
39
  });
37
40
  }
38
41
 
@@ -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.15%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.16%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-7aa074ad556a8455155ac88ceaca03ab.css" title="light">
44
+ <link rel="stylesheet" href="assets/ghost.min-e7612edfa72b0fe2c201b387923e6fc7.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-ca33abc718f21a51327841d58f8875d0.js"></script>
63
- <script src="assets/ghost.min-e35cfee26d942c364166f57f3dcc9e75.js"></script>
62
+ <script src="assets/vendor.min-3660ec7864887f1496fe7a27fd23ab76.js"></script>
63
+ <script src="assets/ghost.min-d1d99f3ed6e0f427874b2a11e7078475.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.15%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.16%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-7aa074ad556a8455155ac88ceaca03ab.css" title="light">
44
+ <link rel="stylesheet" href="assets/ghost.min-e7612edfa72b0fe2c201b387923e6fc7.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-ca33abc718f21a51327841d58f8875d0.js"></script>
63
- <script src="assets/ghost.min-e35cfee26d942c364166f57f3dcc9e75.js"></script>
62
+ <script src="assets/vendor.min-3660ec7864887f1496fe7a27fd23ab76.js"></script>
63
+ <script src="assets/ghost.min-d1d99f3ed6e0f427874b2a11e7078475.js"></script>
64
64
 
65
65
  </body>
66
66
  </html>
@@ -7,6 +7,7 @@ const urlUtils = require('../../../shared/url-utils');
7
7
  const membersService = require('../../services/members');
8
8
  const middleware = membersService.middleware;
9
9
  const shared = require('../shared');
10
+ const labs = require('../../../shared/labs');
10
11
 
11
12
  module.exports = function setupMembersApp() {
12
13
  debug('Members App setup start');
@@ -44,6 +45,7 @@ module.exports = function setupMembersApp() {
44
45
  membersApp.post('/api/create-stripe-checkout-session', (req, res, next) => membersService.api.middleware.createCheckoutSession(req, res, next));
45
46
  membersApp.post('/api/create-stripe-update-session', (req, res, next) => membersService.api.middleware.createCheckoutSetupSession(req, res, next));
46
47
  membersApp.put('/api/subscriptions/:id', (req, res, next) => membersService.api.middleware.updateSubscription(req, res, next));
48
+ membersApp.post('/api/events', labs.enabledMiddleware('membersActivity'), middleware.loadMemberSession, (req, res, next) => membersService.api.middleware.createEvents(req, res, next));
47
49
 
48
50
  // API error handling
49
51
  membersApp.use('/api', shared.middlewares.errorHandler.resourceNotFound);
@@ -0,0 +1,3 @@
1
+ const {Cache: CustomThemeSettingsCache} = require('@tryghost/custom-theme-settings-service');
2
+
3
+ module.exports = new CustomThemeSettingsCache();
@@ -27,7 +27,8 @@ const ALPHA_FEATURES = [
27
27
  'membersFiltering',
28
28
  'emailOnlyPosts',
29
29
  'customThemeSettings',
30
- 'membersActivity'
30
+ 'membersActivity',
31
+ 'offers'
31
32
  ];
32
33
 
33
34
  module.exports.WRITABLE_KEYS_ALLOWLIST = [...BETA_FEATURES, ...ALPHA_FEATURES];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghost",
3
- "version": "4.15.1",
3
+ "version": "4.16.0",
4
4
  "description": "The professional publishing platform",
5
5
  "author": "Ghost Foundation",
6
6
  "homepage": "https://ghost.org",
@@ -51,50 +51,51 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@nexes/nql": "0.6.0",
54
- "@sentry/node": "6.12.0",
55
- "@tryghost/adapter-manager": "0.2.14",
54
+ "@sentry/node": "6.13.2",
55
+ "@tryghost/adapter-manager": "0.2.15",
56
56
  "@tryghost/admin-api-schema": "2.6.0",
57
57
  "@tryghost/bookshelf-plugins": "0.3.1",
58
- "@tryghost/bootstrap-socket": "0.2.9",
58
+ "@tryghost/bootstrap-socket": "0.2.10",
59
59
  "@tryghost/color-utils": "0.1.2",
60
- "@tryghost/config-url-helpers": "0.1.0",
61
- "@tryghost/constants": "0.1.8",
60
+ "@tryghost/config-url-helpers": "0.1.1",
61
+ "@tryghost/constants": "0.1.9",
62
+ "@tryghost/custom-theme-settings-service": "0.0.2",
62
63
  "@tryghost/debug": "0.1.5",
63
- "@tryghost/email-analytics-provider-mailgun": "1.0.0",
64
- "@tryghost/email-analytics-service": "1.0.0",
65
- "@tryghost/errors": "0.2.13",
64
+ "@tryghost/email-analytics-provider-mailgun": "1.0.1",
65
+ "@tryghost/email-analytics-service": "1.0.1",
66
+ "@tryghost/errors": "0.2.14",
66
67
  "@tryghost/helpers": "1.1.52",
67
- "@tryghost/image-transform": "1.0.13",
68
- "@tryghost/job-manager": "0.8.7",
68
+ "@tryghost/image-transform": "1.0.14",
69
+ "@tryghost/job-manager": "0.8.8",
69
70
  "@tryghost/kg-card-factory": "3.0.4",
70
71
  "@tryghost/kg-default-atoms": "3.0.0",
71
72
  "@tryghost/kg-default-cards": "5.0.7",
72
73
  "@tryghost/kg-markdown-html-renderer": "5.0.5",
73
74
  "@tryghost/kg-mobiledoc-html-renderer": "5.1.1",
74
- "@tryghost/limit-service": "0.6.1",
75
+ "@tryghost/limit-service": "0.6.2",
75
76
  "@tryghost/logging": "0.1.7",
76
- "@tryghost/magic-link": "1.0.11",
77
- "@tryghost/members-api": "1.32.3",
77
+ "@tryghost/magic-link": "1.0.12",
78
+ "@tryghost/members-api": "1.38.1",
78
79
  "@tryghost/members-csv": "1.1.6",
79
80
  "@tryghost/members-importer": "0.3.2",
80
- "@tryghost/members-ssr": "1.0.12",
81
- "@tryghost/mw-session-from-token": "0.1.22",
81
+ "@tryghost/members-ssr": "1.0.13",
82
+ "@tryghost/mw-session-from-token": "0.1.23",
82
83
  "@tryghost/nodemailer": "0.3.2",
83
- "@tryghost/package-json": "1.0.2",
84
- "@tryghost/promise": "0.1.9",
84
+ "@tryghost/package-json": "1.0.3",
85
+ "@tryghost/promise": "0.1.10",
85
86
  "@tryghost/request": "0.1.5",
86
87
  "@tryghost/root-utils": "0.3.4",
87
- "@tryghost/security": "0.2.9",
88
- "@tryghost/session-service": "0.1.24",
88
+ "@tryghost/security": "0.2.10",
89
+ "@tryghost/session-service": "0.1.25",
89
90
  "@tryghost/social-urls": "0.1.26",
90
91
  "@tryghost/string": "0.1.20",
91
- "@tryghost/tpl": "0.1.3",
92
+ "@tryghost/tpl": "0.1.4",
92
93
  "@tryghost/update-check-service": "0.2.2",
93
94
  "@tryghost/url-utils": "2.0.2",
94
95
  "@tryghost/validator": "0.1.5",
95
96
  "@tryghost/version": "0.1.4",
96
- "@tryghost/vhost-middleware": "1.0.15",
97
- "@tryghost/zip": "1.1.14",
97
+ "@tryghost/vhost-middleware": "1.0.16",
98
+ "@tryghost/zip": "1.1.15",
98
99
  "amperize": "0.6.1",
99
100
  "analytics-node": "5.1.0",
100
101
  "bluebird": "3.7.2",
@@ -118,9 +119,9 @@
118
119
  "express-session": "1.17.2",
119
120
  "fs-extra": "10.0.0",
120
121
  "ghost-storage-base": "0.0.6",
121
- "glob": "7.1.7",
122
+ "glob": "7.2.0",
122
123
  "got": "9.6.0",
123
- "gscan": "4.2.1",
124
+ "gscan": "4.3.1",
124
125
  "html-to-text": "5.1.1",
125
126
  "image-size": "1.0.0",
126
127
  "intl": "1.2.5",
@@ -169,7 +170,7 @@
169
170
  "devDependencies": {
170
171
  "@lodder/grunt-postcss": "3.0.1",
171
172
  "c8": "7.9.0",
172
- "coffeescript": "2.5.1",
173
+ "coffeescript": "2.6.0",
173
174
  "cssnano": "5.0.8",
174
175
  "eslint": "7.32.0",
175
176
  "eslint-plugin-ghost": "2.6.0",
@@ -190,7 +191,7 @@
190
191
  "mock-knex": "0.4.10",
191
192
  "nock": "13.1.3",
192
193
  "papaparse": "5.3.1",
193
- "postcss": "8.3.6",
194
+ "postcss": "8.3.7",
194
195
  "rewire": "5.0.0",
195
196
  "should": "13.2.3",
196
197
  "sinon": "11.1.2",