ghost 4.47.4 → 4.48.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.
@@ -4354,6 +4354,6 @@ var i=t.default.create({properties:["name","event","targetUrl"],name(e){Ember.is
4354
4354
  e.default=i})),define("ghost-admin/views/application",["exports"],(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
4355
4355
  var t=Ember.Component.extend({})
4356
4356
  e.default=t})),define("ghost-admin/config/environment",[],(function(){try{var e="ghost-admin/config/environment",t=document.querySelector('meta[name="'+e+'"]').getAttribute("content"),n={default:JSON.parse(decodeURIComponent(t))}
4357
- return Object.defineProperty(n,"__esModule",{value:!0}),n}catch(i){throw new Error('Could not read config from meta tag with name "'+e+'".')}})),runningTests||require("ghost-admin/app").default.create({version:"4.47",name:"ghost-admin"})
4357
+ return Object.defineProperty(n,"__esModule",{value:!0}),n}catch(i){throw new Error('Could not read config from meta tag with name "'+e+'".')}})),runningTests||require("ghost-admin/app").default.create({version:"4.48",name:"ghost-admin"})
4358
4358
 
4359
- //# sourceMappingURL=ghost.min-a2a47979b0b61d15a3914042a7b1cd20.map
4359
+ //# sourceMappingURL=ghost.min-10ba0de3ecb93d63d62bb43ac11c740f.map
@@ -5,7 +5,6 @@ const moment = require('moment-timezone');
5
5
  const errors = require('@tryghost/errors');
6
6
  const models = require('../../models');
7
7
  const membersService = require('../../services/members');
8
- const labsService = require('../../../shared/labs');
9
8
 
10
9
  const settingsCache = require('../../../shared/settings-cache');
11
10
  const tpl = require('@tryghost/tpl');
@@ -362,16 +361,9 @@ module.exports = {
362
361
  },
363
362
  validation: {},
364
363
  async query(frame) {
365
- frame.options.withRelated = ['labels', 'stripeSubscriptions', 'stripeSubscriptions.customer'];
366
- if (labsService.isSet('multipleProducts')) {
367
- frame.options.withRelated.push('products');
368
- }
369
- if (labsService.isSet('multipleNewsletters')) {
370
- frame.options.withRelated.push('newsletters');
371
- }
372
- const page = await membersService.api.members.list(frame.options);
373
-
374
- return page;
364
+ return {
365
+ data: await membersService.export(frame.options)
366
+ };
375
367
  }
376
368
  },
377
369
 
@@ -79,18 +79,13 @@ function bulkAction(bulkActionResult, _apiConfig, frame) {
79
79
  /**
80
80
  * @template PageMeta
81
81
  *
82
- * @param {{data: import('bookshelf').Model[], meta: PageMeta}} page
83
- * @param {APIConfig} _apiConfig
84
- * @param {Frame} frame
82
+ * @param {{data: any[]}} data
85
83
  *
86
84
  * @returns {string} - A CSV string
87
85
  */
88
- function exportCSV(page, _apiConfig, frame) {
86
+ function exportCSV(data) {
89
87
  debug('exportCSV');
90
-
91
- const members = page.data.map(model => serializeMember(model, frame.options));
92
-
93
- return unparse(members);
88
+ return unparse(data.data);
94
89
  }
95
90
 
96
91
  /**
@@ -1,5 +1,8 @@
1
+ const {BACKUP_TABLES, TABLES_ALLOWLIST} = require('./table-lists');
2
+
1
3
  module.exports = {
2
4
  doExport: require('./exporter'),
3
5
  fileName: require('./export-filename'),
4
- BACKUP_TABLES: require('./table-lists').BACKUP_TABLES
6
+ BACKUP_TABLES,
7
+ TABLES_ALLOWLIST
5
8
  };
@@ -10,25 +10,17 @@ const BACKUP_TABLES = [
10
10
  'members',
11
11
  'members_labels',
12
12
  'members_products',
13
- 'posts_products',
14
13
  'members_stripe_customers',
15
14
  'members_stripe_customers_subscriptions',
16
15
  'migrations',
17
16
  'migrations_lock',
18
- 'newsletters',
19
17
  'oauth',
20
18
  'permissions',
21
19
  'permissions_roles',
22
20
  'permissions_users',
23
- 'products',
24
- 'benefits',
25
- 'products_benefits',
26
21
  'webhooks',
27
- 'snippets',
28
22
  'tokens',
29
23
  'sessions',
30
- 'stripe_products',
31
- 'stripe_prices',
32
24
  'mobiledoc_revisions',
33
25
  'email_batches',
34
26
  'email_recipients',
@@ -40,9 +32,8 @@ const BACKUP_TABLES = [
40
32
  'members_paid_subscription_events',
41
33
  'members_subscribe_events',
42
34
  'members_product_events',
43
- 'members_newsletters',
44
- 'offers',
45
- 'offer_redemptions'
35
+ 'members_newsletters'
36
+
46
37
  ];
47
38
 
48
39
  // NOTE: exposing only tables which are going to be included in a "default" export file
@@ -58,7 +49,17 @@ const TABLES_ALLOWLIST = [
58
49
  'settings',
59
50
  'custom_theme_settings',
60
51
  'tags',
61
- 'users'
52
+ 'users',
53
+ 'products',
54
+ 'stripe_products',
55
+ 'stripe_prices',
56
+ 'posts_products',
57
+ 'newsletters',
58
+ 'benefits',
59
+ 'products_benefits',
60
+ 'offers',
61
+ 'offer_redemptions',
62
+ 'snippets'
62
63
  ];
63
64
 
64
65
  // NOTE: these are settings keys which should never end up in the export file
@@ -0,0 +1,88 @@
1
+ const models = require('../../../models');
2
+ const {knex} = require('../../../data/db');
3
+ const moment = require('moment');
4
+
5
+ module.exports = async function (options) {
6
+ const hasFilter = options.limit !== 'all' || options.filter || options.search;
7
+
8
+ let ids = null;
9
+ if (hasFilter) {
10
+ // do a very minimal query, only to fetch the ids of the filtered values
11
+ // should be quite fast
12
+ options.withRelated = [];
13
+ options.columns = ['id'];
14
+
15
+ const page = await models.Member.findPage(options);
16
+ ids = page.data.map(d => d.id);
17
+
18
+ /*
19
+ const filterOptions = _.pick(options, ['transacting', 'context']);
20
+
21
+ if (all !== true) {
22
+ // Include mongoTransformer to apply subscribed:{true|false} => newsletter relation mapping
23
+ Object.assign(filterOptions, _.pick(options, ['filter', 'search', 'mongoTransformer']));
24
+ }
25
+
26
+ const memberRows = await models.Member.getFilteredCollectionQuery(filterOptions)
27
+ .select('members.id')
28
+ .distinct();
29
+
30
+ ids = memberRows.map(row => row.id);
31
+ */
32
+ }
33
+
34
+ const allProducts = await models.Product.fetchAll();
35
+ const allLabels = await models.Label.fetchAll();
36
+
37
+ let query = knex('members')
38
+ .select('id', 'email', 'name', 'note', 'status', 'created_at')
39
+ .select(knex.raw(`
40
+ (CASE WHEN EXISTS (SELECT 1 FROM members_newsletters n WHERE n.member_id = members.id)
41
+ THEN TRUE ELSE FALSE
42
+ END) as subscribed
43
+ `))
44
+ .select(knex.raw(`
45
+ (SELECT GROUP_CONCAT(product_id) FROM members_products f WHERE f.member_id = members.id) as products
46
+ `))
47
+ .select(knex.raw(`
48
+ (SELECT GROUP_CONCAT(label_id) FROM members_labels f WHERE f.member_id = members.id) as labels
49
+ `))
50
+ .select(knex.raw(`
51
+ (SELECT customer_id FROM members_stripe_customers f WHERE f.member_id = members.id limit 1) as stripe_customer_id
52
+ `));
53
+
54
+ if (hasFilter) {
55
+ query = query.whereIn('id', ids);
56
+ }
57
+
58
+ const rows = await query;
59
+ for (const row of rows) {
60
+ const productIds = row.products ? row.products.split(',') : [];
61
+ const products = productIds.map((id) => {
62
+ const product = allProducts.find(p => p.id === id);
63
+ return {
64
+ name: product.get('name')
65
+ };
66
+ });
67
+ row.products = products;
68
+
69
+ const labelIds = row.labels ? row.labels.split(',') : [];
70
+ const labels = labelIds.map((id) => {
71
+ const label = allLabels.find(l => l.id === id);
72
+ return {
73
+ name: label.get('name')
74
+ };
75
+ });
76
+ row.labels = labels;
77
+ }
78
+
79
+ for (const member of rows) {
80
+ // Note: we don't modify the array or change/duplicate objects
81
+ // to increase performance
82
+ member.subscribed = !!member.subscribed;
83
+ member.comped = member.status === 'comped';
84
+ member.created_at = moment(member.created_at).toISOString();
85
+ }
86
+
87
+ return rows;
88
+ };
@@ -202,7 +202,7 @@ module.exports = {
202
202
 
203
203
  processImport: processImport,
204
204
 
205
- stats: membersStats
206
-
205
+ stats: membersStats,
206
+ export: require('./exporter/query')
207
207
  };
208
208
  module.exports.middleware = require('./middleware');
@@ -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.47%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%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.48%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%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" />
@@ -57,7 +57,7 @@
57
57
 
58
58
 
59
59
  <script src="assets/vendor.min-97fd438f4772c5ec6bb30ad779b8530e.js"></script>
60
- <script src="assets/ghost.min-a2a47979b0b61d15a3914042a7b1cd20.js"></script>
60
+ <script src="assets/ghost.min-10ba0de3ecb93d63d62bb43ac11c740f.js"></script>
61
61
 
62
62
  </body>
63
63
  </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.47%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%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.48%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%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" />
@@ -57,7 +57,7 @@
57
57
 
58
58
 
59
59
  <script src="assets/vendor.min-97fd438f4772c5ec6bb30ad779b8530e.js"></script>
60
- <script src="assets/ghost.min-a2a47979b0b61d15a3914042a7b1cd20.js"></script>
60
+ <script src="assets/ghost.min-10ba0de3ecb93d63d62bb43ac11c740f.js"></script>
61
61
 
62
62
  </body>
63
63
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghost",
3
- "version": "4.47.4",
3
+ "version": "4.48.0",
4
4
  "description": "The professional publishing platform",
5
5
  "author": "Ghost Foundation",
6
6
  "homepage": "https://ghost.org",