0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-24 23:48:13 -05:00

Added support for exportign posts with email recipients to specific tiers

no issue

Support for tiers was missing when traslating the email recipients NQL filter to a string. This is fixed now. It also improved some copy.
This commit is contained in:
Simon Backx 2023-03-28 12:03:17 +02:00
parent 2dd94d4300
commit 1f39040649
3 changed files with 74 additions and 26 deletions

View file

@ -19,7 +19,8 @@ const getPostServiceInstance = () => {
models: { models: {
Post: models.Post, Post: models.Post,
Newsletter: models.Newsletter, Newsletter: models.Newsletter,
Label: models.Label Label: models.Label,
Product: models.Product
}, },
getPostUrl(post) { getPostUrl(post) {
const jsonModel = post.toJSON(); const jsonModel = post.toJSON();

View file

@ -13,6 +13,7 @@ class PostsExporter {
* @param {Object} dependencies.models.Post * @param {Object} dependencies.models.Post
* @param {Object} dependencies.models.Newsletter * @param {Object} dependencies.models.Newsletter
* @param {Object} dependencies.models.Label * @param {Object} dependencies.models.Label
* @param {Object} dependencies.models.Product
* @param {Object} dependencies.getPostUrl * @param {Object} dependencies.getPostUrl
* @param {Object} dependencies.settingsCache * @param {Object} dependencies.settingsCache
* @param {Object} dependencies.settingsHelpers * @param {Object} dependencies.settingsHelpers
@ -51,6 +52,7 @@ class PostsExporter {
const newsletters = (await this.#models.Newsletter.findAll()).models; const newsletters = (await this.#models.Newsletter.findAll()).models;
const labels = (await this.#models.Label.findAll()).models; const labels = (await this.#models.Label.findAll()).models;
const tiers = (await this.#models.Product.findAll()).models;
const membersEnabled = this.#settingsHelpers.isMembersEnabled(); const membersEnabled = this.#settingsHelpers.isMembersEnabled();
const membersTrackSources = membersEnabled && this.#settingsCache.get('members_track_sources'); const membersTrackSources = membersEnabled && this.#settingsCache.get('members_track_sources');
@ -82,7 +84,7 @@ class PostsExporter {
featured: post.get('featured'), featured: post.get('featured'),
tags: post.related('tags').map(tag => tag.get('name')).join(', '), tags: post.related('tags').map(tag => tag.get('name')).join(', '),
post_access: this.postAccessToString(post), post_access: this.postAccessToString(post),
email_recipients: email ? this.humanReadableEmailRecipientFilter(email?.get('recipient_filter'), labels) : null, email_recipients: email ? this.humanReadableEmailRecipientFilter(email?.get('recipient_filter'), labels, tiers) : null,
newsletter_name: newsletters.length > 1 && post.get('newsletter_id') && email ? newsletters.find(newsletter => newsletter.get('id') === post.get('newsletter_id'))?.get('name') : null, newsletter_name: newsletters.length > 1 && post.get('newsletter_id') && email ? newsletters.find(newsletter => newsletter.get('id') === post.get('newsletter_id'))?.get('name') : null,
sends: email?.get('email_count') ?? null, sends: email?.get('email_count') ?? null,
opens: trackOpens ? (email?.get('opened_count') ?? null) : null, opens: trackOpens ? (email?.get('opened_count') ?? null) : null,
@ -184,17 +186,18 @@ class PostsExporter {
* @private Convert an email filter to a human readable string * @private Convert an email filter to a human readable string
* @param {string} recipientFilter * @param {string} recipientFilter
* @param {*} allLabels * @param {*} allLabels
* @param {*} allTiers
* @returns * @returns
*/ */
humanReadableEmailRecipientFilter(recipientFilter, allLabels) { humanReadableEmailRecipientFilter(recipientFilter, allLabels, allTiers) {
// Examples: "label:test"; "label:test,label:batch1"; "status:-free,label:test", "all" // Examples: "label:test"; "label:test,label:batch1"; "status:-free,label:test", "all"
if (recipientFilter === 'all') { if (recipientFilter === 'all') {
return 'all'; return 'All subscribers';
} }
try { try {
const parsed = nql(recipientFilter).parse(); const parsed = nql(recipientFilter).parse();
const strings = this.filterToString(parsed, allLabels); const strings = this.filterToString(parsed, allLabels, allTiers);
return strings.join(', '); return strings.join(', ');
} catch (e) { } catch (e) {
logging.error(e); logging.error(e);
@ -208,17 +211,17 @@ class PostsExporter {
* @param {*} allLabels All available member labels * @param {*} allLabels All available member labels
* @returns * @returns
*/ */
filterToString(filter, allLabels) { filterToString(filter, allLabels, allTiers) {
const strings = []; const strings = [];
if (filter.$and) { if (filter.$and) {
// Not supported // Not supported
} else if (filter.$or) { } else if (filter.$or) {
for (const subfilter of filter.$or) { for (const subfilter of filter.$or) {
strings.push(...this.filterToString(subfilter, allLabels)); strings.push(...this.filterToString(subfilter, allLabels, allTiers));
} }
} else if (filter.yg) { } else if (filter.yg) {
// Single filter grouped in brackets // Single filter grouped in brackets
strings.push(...this.filterToString(filter.yg, allLabels)); strings.push(...this.filterToString(filter.yg, allLabels, allTiers));
} else { } else {
for (const key of Object.keys(filter)) { for (const key of Object.keys(filter)) {
if (key === 'label') { if (key === 'label') {
@ -232,22 +235,33 @@ class PostsExporter {
} }
} }
} }
if (key === 'tier') {
if (typeof filter.tier === 'string') {
const tierSlug = filter.tier;
const tier = allTiers.find(l => l.get('slug') === tierSlug);
if (tier) {
strings.push(tier.get('name'));
} else {
strings.push(tierSlug);
}
}
}
if (key === 'status') { if (key === 'status') {
if (typeof filter.status === 'string') { if (typeof filter.status === 'string') {
if (filter.status === 'free') { if (filter.status === 'free') {
strings.push('free members'); strings.push('Free subscribers');
} else if (filter.status === 'paid') { } else if (filter.status === 'paid') {
strings.push('paid members'); strings.push('Paid subscribers');
} else if (filter.status === 'comped') { } else if (filter.status === 'comped') {
strings.push('complimentary members'); strings.push('Complimentary subscribers');
} }
} else { } else {
if (filter.status.$ne === 'free') { if (filter.status.$ne === 'free') {
strings.push('paid members'); strings.push('Paid subscribers');
} }
if (filter.status.$ne === 'paid') { if (filter.status.$ne === 'paid') {
strings.push('free members'); strings.push('Free subscribers');
} }
} }
} }

View file

@ -89,6 +89,9 @@ describe('PostsExporter', function () {
}), }),
Label: createModelClass({ Label: createModelClass({
findAll: [] findAll: []
}),
Product: createModelClass({
findAll: []
}) })
}; };
@ -394,6 +397,7 @@ describe('PostsExporter', function () {
describe('humanReadableEmailRecipientFilter', function () { describe('humanReadableEmailRecipientFilter', function () {
const exporter = new PostsExporter({}); const exporter = new PostsExporter({});
let labels; let labels;
let tiers;
beforeEach(function () { beforeEach(function () {
labels = [ labels = [
@ -406,12 +410,22 @@ describe('PostsExporter', function () {
name: 'VIP' name: 'VIP'
}) })
]; ];
tiers = [
createModel({
slug: 'silver',
name: 'Silver'
}),
createModel({
slug: 'gold',
name: 'Gold'
})
];
}); });
it('Returns all', function () { it('Returns all', function () {
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('all'), exporter.humanReadableEmailRecipientFilter('all'),
'all' 'All subscribers'
); );
}); });
@ -424,19 +438,38 @@ describe('PostsExporter', function () {
it('Returns labels', function () { it('Returns labels', function () {
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('label:imported', labels), exporter.humanReadableEmailRecipientFilter('label:imported', labels, tiers),
'Imported' 'Imported'
); );
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('label:imported,label:vip', labels), exporter.humanReadableEmailRecipientFilter('label:imported,label:vip', labels, tiers),
'Imported, VIP' 'Imported, VIP'
); );
}); });
it('Returns invalid labels', function () { it('Returns invalid labels', function () {
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('label:invalidone', labels), exporter.humanReadableEmailRecipientFilter('label:invalidone', labels, tiers),
'invalidone'
);
});
it('Returns tiers', function () {
assert.equal(
exporter.humanReadableEmailRecipientFilter('tier:silver', labels, tiers),
'Silver'
);
assert.equal(
exporter.humanReadableEmailRecipientFilter('tier:silver,tier:gold', labels, tiers),
'Silver, Gold'
);
});
it('Returns invalid tiers', function () {
assert.equal(
exporter.humanReadableEmailRecipientFilter('tier:invalidone', labels, tiers),
'invalidone' 'invalidone'
); );
}); });
@ -444,47 +477,47 @@ describe('PostsExporter', function () {
it('Returns status', function () { it('Returns status', function () {
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('status:free'), exporter.humanReadableEmailRecipientFilter('status:free'),
'free members' 'Free subscribers'
); );
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('status:-free'), exporter.humanReadableEmailRecipientFilter('status:-free'),
'paid members' 'Paid subscribers'
); );
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('status:paid'), exporter.humanReadableEmailRecipientFilter('status:paid'),
'paid members' 'Paid subscribers'
); );
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('status:comped'), exporter.humanReadableEmailRecipientFilter('status:comped'),
'complimentary members' 'Complimentary subscribers'
); );
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('status:-paid'), exporter.humanReadableEmailRecipientFilter('status:-paid'),
'free members' 'Free subscribers'
); );
}); });
it('Ignores AND', function () { it('Ignores AND', function () {
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('status:free+status:paid', labels), exporter.humanReadableEmailRecipientFilter('status:free+status:paid', labels, tiers),
'' ''
); );
}); });
it('Single brackets filter', function () { it('Single brackets filter', function () {
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('(status:free)', labels), exporter.humanReadableEmailRecipientFilter('(status:free)', labels, tiers),
'free members' 'Free subscribers'
); );
}); });
it('Ignores invalid filters', function () { it('Ignores invalid filters', function () {
assert.equal( assert.equal(
exporter.humanReadableEmailRecipientFilter('sdgsdgsdg sdg sdg sdgs dgs', labels), exporter.humanReadableEmailRecipientFilter('sdgsdgsdg sdg sdg sdgs dgs', labels, tiers),
'sdgsdgsdg sdg sdg sdgs dgs' 'sdgsdgsdg sdg sdg sdgs dgs'
); );
}); });