diff --git a/ghost/core/core/server/services/posts/posts-service.js b/ghost/core/core/server/services/posts/posts-service.js index b87c541acb..3fd13141f9 100644 --- a/ghost/core/core/server/services/posts/posts-service.js +++ b/ghost/core/core/server/services/posts/posts-service.js @@ -19,7 +19,8 @@ const getPostServiceInstance = () => { models: { Post: models.Post, Newsletter: models.Newsletter, - Label: models.Label + Label: models.Label, + Product: models.Product }, getPostUrl(post) { const jsonModel = post.toJSON(); diff --git a/ghost/posts-service/lib/PostsExporter.js b/ghost/posts-service/lib/PostsExporter.js index c84c3e9195..5f2508926a 100644 --- a/ghost/posts-service/lib/PostsExporter.js +++ b/ghost/posts-service/lib/PostsExporter.js @@ -13,6 +13,7 @@ class PostsExporter { * @param {Object} dependencies.models.Post * @param {Object} dependencies.models.Newsletter * @param {Object} dependencies.models.Label + * @param {Object} dependencies.models.Product * @param {Object} dependencies.getPostUrl * @param {Object} dependencies.settingsCache * @param {Object} dependencies.settingsHelpers @@ -51,6 +52,7 @@ class PostsExporter { const newsletters = (await this.#models.Newsletter.findAll()).models; const labels = (await this.#models.Label.findAll()).models; + const tiers = (await this.#models.Product.findAll()).models; const membersEnabled = this.#settingsHelpers.isMembersEnabled(); const membersTrackSources = membersEnabled && this.#settingsCache.get('members_track_sources'); @@ -82,7 +84,7 @@ class PostsExporter { featured: post.get('featured'), tags: post.related('tags').map(tag => tag.get('name')).join(', '), 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, sends: email?.get('email_count') ?? 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 * @param {string} recipientFilter * @param {*} allLabels + * @param {*} allTiers * @returns */ - humanReadableEmailRecipientFilter(recipientFilter, allLabels) { + humanReadableEmailRecipientFilter(recipientFilter, allLabels, allTiers) { // Examples: "label:test"; "label:test,label:batch1"; "status:-free,label:test", "all" if (recipientFilter === 'all') { - return 'all'; + return 'All subscribers'; } try { const parsed = nql(recipientFilter).parse(); - const strings = this.filterToString(parsed, allLabels); + const strings = this.filterToString(parsed, allLabels, allTiers); return strings.join(', '); } catch (e) { logging.error(e); @@ -208,17 +211,17 @@ class PostsExporter { * @param {*} allLabels All available member labels * @returns */ - filterToString(filter, allLabels) { + filterToString(filter, allLabels, allTiers) { const strings = []; if (filter.$and) { // Not supported } else if (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) { // Single filter grouped in brackets - strings.push(...this.filterToString(filter.yg, allLabels)); + strings.push(...this.filterToString(filter.yg, allLabels, allTiers)); } else { for (const key of Object.keys(filter)) { 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 (typeof filter.status === 'string') { if (filter.status === 'free') { - strings.push('free members'); + strings.push('Free subscribers'); } else if (filter.status === 'paid') { - strings.push('paid members'); + strings.push('Paid subscribers'); } else if (filter.status === 'comped') { - strings.push('complimentary members'); + strings.push('Complimentary subscribers'); } } else { if (filter.status.$ne === 'free') { - strings.push('paid members'); + strings.push('Paid subscribers'); } if (filter.status.$ne === 'paid') { - strings.push('free members'); + strings.push('Free subscribers'); } } } diff --git a/ghost/posts-service/test/PostsExporter.test.js b/ghost/posts-service/test/PostsExporter.test.js index 84c628d5d1..3a30ef8479 100644 --- a/ghost/posts-service/test/PostsExporter.test.js +++ b/ghost/posts-service/test/PostsExporter.test.js @@ -89,6 +89,9 @@ describe('PostsExporter', function () { }), Label: createModelClass({ findAll: [] + }), + Product: createModelClass({ + findAll: [] }) }; @@ -394,6 +397,7 @@ describe('PostsExporter', function () { describe('humanReadableEmailRecipientFilter', function () { const exporter = new PostsExporter({}); let labels; + let tiers; beforeEach(function () { labels = [ @@ -406,12 +410,22 @@ describe('PostsExporter', function () { name: 'VIP' }) ]; + tiers = [ + createModel({ + slug: 'silver', + name: 'Silver' + }), + createModel({ + slug: 'gold', + name: 'Gold' + }) + ]; }); it('Returns all', function () { assert.equal( exporter.humanReadableEmailRecipientFilter('all'), - 'all' + 'All subscribers' ); }); @@ -424,19 +438,38 @@ describe('PostsExporter', function () { it('Returns labels', function () { assert.equal( - exporter.humanReadableEmailRecipientFilter('label:imported', labels), + exporter.humanReadableEmailRecipientFilter('label:imported', labels, tiers), 'Imported' ); assert.equal( - exporter.humanReadableEmailRecipientFilter('label:imported,label:vip', labels), + exporter.humanReadableEmailRecipientFilter('label:imported,label:vip', labels, tiers), 'Imported, VIP' ); }); it('Returns invalid labels', function () { 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' ); }); @@ -444,47 +477,47 @@ describe('PostsExporter', function () { it('Returns status', function () { assert.equal( exporter.humanReadableEmailRecipientFilter('status:free'), - 'free members' + 'Free subscribers' ); assert.equal( exporter.humanReadableEmailRecipientFilter('status:-free'), - 'paid members' + 'Paid subscribers' ); assert.equal( exporter.humanReadableEmailRecipientFilter('status:paid'), - 'paid members' + 'Paid subscribers' ); assert.equal( exporter.humanReadableEmailRecipientFilter('status:comped'), - 'complimentary members' + 'Complimentary subscribers' ); assert.equal( exporter.humanReadableEmailRecipientFilter('status:-paid'), - 'free members' + 'Free subscribers' ); }); it('Ignores AND', function () { assert.equal( - exporter.humanReadableEmailRecipientFilter('status:free+status:paid', labels), + exporter.humanReadableEmailRecipientFilter('status:free+status:paid', labels, tiers), '' ); }); it('Single brackets filter', function () { assert.equal( - exporter.humanReadableEmailRecipientFilter('(status:free)', labels), - 'free members' + exporter.humanReadableEmailRecipientFilter('(status:free)', labels, tiers), + 'Free subscribers' ); }); it('Ignores invalid filters', function () { assert.equal( - exporter.humanReadableEmailRecipientFilter('sdgsdgsdg sdg sdg sdgs dgs', labels), + exporter.humanReadableEmailRecipientFilter('sdgsdgsdg sdg sdg sdgs dgs', labels, tiers), 'sdgsdgsdg sdg sdg sdgs dgs' ); });