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.
- package/core/built/assets/{ghost.min-a2a47979b0b61d15a3914042a7b1cd20.js → ghost.min-10ba0de3ecb93d63d62bb43ac11c740f.js} +2 -2
- package/core/server/api/canary/members.js +3 -11
- package/core/server/api/canary/utils/serializers/output/members.js +3 -8
- package/core/server/data/exporter/index.js +4 -1
- package/core/server/data/exporter/table-lists.js +13 -12
- package/core/server/services/members/exporter/query.js +88 -0
- package/core/server/services/members/service.js +2 -2
- package/core/server/web/admin/views/default-prod.html +2 -2
- package/core/server/web/admin/views/default.html +2 -2
- package/package.json +1 -1
|
@@ -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.
|
|
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-
|
|
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
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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:
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
+
};
|
|
@@ -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.
|
|
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-
|
|
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.
|
|
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-
|
|
60
|
+
<script src="assets/ghost.min-10ba0de3ecb93d63d62bb43ac11c740f.js"></script>
|
|
61
61
|
|
|
62
62
|
</body>
|
|
63
63
|
</html>
|