diff --git a/ghost/core/core/server/services/staff/index.js b/ghost/core/core/server/services/staff/index.js index 8ad4632d63..2b57d20de6 100644 --- a/ghost/core/core/server/services/staff/index.js +++ b/ghost/core/core/server/services/staff/index.js @@ -12,6 +12,7 @@ class StaffServiceWrapper { const logging = require('@tryghost/logging'); const models = require('../../models'); + const memberAttribution = require('../member-attribution'); const {GhostMailer} = require('../mail'); const mailer = new GhostMailer(); const settingsCache = require('../../../shared/settings-cache'); @@ -26,6 +27,7 @@ class StaffServiceWrapper { settingsCache, urlUtils, DomainEvents, + memberAttributionService: memberAttribution.service, labs }); diff --git a/ghost/staff-service/lib/email-templates/new-free-signup.hbs b/ghost/staff-service/lib/email-templates/new-free-signup.hbs index 26eba02ddd..c2a1007267 100644 --- a/ghost/staff-service/lib/email-templates/new-free-signup.hbs +++ b/ghost/staff-service/lib/email-templates/new-free-signup.hbs @@ -37,21 +37,18 @@ - -
+
{{memberData.initials}}
+

{{memberData.name}}

{{#if memberData.showEmail}} {{/if}}

Created on {{memberData.createdAt}}{{#if memberData.location}} • {{memberData.location}} {{/if}}

- {{#if referrerSource}} - - {{/if}}
@@ -60,6 +57,25 @@ + + + + +
+ {{#if referrerSource}} +

Signup info

+
+

Source + - {{referrerSource}} +

+ {{#if attributionTitle}} +

Page + - {{attributionTitle}} +

+ {{/if}} + {{/if}} +
+ diff --git a/ghost/staff-service/lib/email-templates/new-paid-started.hbs b/ghost/staff-service/lib/email-templates/new-paid-started.hbs index 10b2aec16a..d3b7674207 100644 --- a/ghost/staff-service/lib/email-templates/new-paid-started.hbs +++ b/ghost/staff-service/lib/email-templates/new-paid-started.hbs @@ -37,45 +37,58 @@ - - - - {{#if offerData}} - - - - {{/if}}
- -
+
{{memberData.initials}}
+

{{memberData.name}}

{{#if memberData.showEmail}} {{/if}}

Subscription started on {{subscriptionData.startedOn}}

- {{#if referrerSource}} - - {{/if}}
-
-

Tier

-

{{tierData.name}} - {{#if tierData.details}} - {{tierData.details}}{{/if}} -

-
-

Offer

-

{{offerData.name}} - {{offerData.details}}

-
+ + + + +
+ +

Tier

+
+

{{tierData.name}} + {{#if tierData.details}} - {{tierData.details}}{{/if}} +

+ + {{#if offerData}} +

Offer

+
+

{{offerData.name}} - {{offerData.details}}

+ {{/if}} + + {{#if referrerSource}} +

Signup info

+
+

Source + - {{referrerSource}} +

+ {{#if attributionTitle}} +

Page + - {{attributionTitle}} +

+ {{/if}} + {{/if}} + +
+ diff --git a/ghost/staff-service/lib/emails.js b/ghost/staff-service/lib/emails.js index 089b965a01..4fe62a44a4 100644 --- a/ghost/staff-service/lib/emails.js +++ b/ghost/staff-service/lib/emails.js @@ -27,8 +27,16 @@ class StaffServiceEmails { const subject = `🥳 Free member signup: ${memberData.name}`; + let attributionTitle = attribution?.title || ''; + // In case of a homepage attribution, we want to show the title as "Homepage" on email + if (attributionTitle === 'homepage') { + attributionTitle = 'Homepage'; + } + const templateData = { memberData, + attributionTitle, + attributionUrl: attribution?.url || '', referrerSource: attribution?.referrerSource, siteTitle: this.settingsCache.get('title'), siteUrl: this.urlUtils.getSiteUrl(), @@ -73,8 +81,16 @@ class StaffServiceEmails { let offerData = this.getOfferData(offer); + let attributionTitle = attribution?.title || ''; + // In case of a homepage attribution, we want to show the title as "Homepage" on email + if (attributionTitle === 'homepage') { + attributionTitle = 'Homepage'; + } + const templateData = { memberData, + attributionTitle, + attributionUrl: attribution?.url || '', referrerSource: attribution?.referrerSource, tierData, offerData, diff --git a/ghost/staff-service/lib/staff-service.js b/ghost/staff-service/lib/staff-service.js index 47fc642e71..8502a24d61 100644 --- a/ghost/staff-service/lib/staff-service.js +++ b/ghost/staff-service/lib/staff-service.js @@ -5,13 +5,14 @@ const {MilestoneCreatedEvent} = require('@tryghost/milestones'); // @NOTE: 'StaffService' is a vague name that does not describe what it's actually doing. // Possibly, "StaffNotificationService" or "StaffEventNotificationService" would be a more accurate name class StaffService { - constructor({logging, models, mailer, settingsCache, settingsHelpers, urlUtils, DomainEvents, labs}) { + constructor({logging, models, mailer, settingsCache, settingsHelpers, urlUtils, DomainEvents, labs, memberAttributionService}) { this.logging = logging; this.labs = labs; /** @private */ this.settingsCache = settingsCache; this.models = models; this.DomainEvents = DomainEvents; + this.memberAttributionService = memberAttributionService; const Emails = require('./emails'); @@ -98,17 +99,29 @@ class StaffService { }); if (type === MemberCreatedEvent && member.status === 'free') { + let attribution; + try { + attribution = await this.memberAttributionService.getMemberCreatedAttribution(event.data.memberId); + } catch (e) { + this.logging.warn(`Failed to get attribution for member - ${event?.data?.memberId}`); + } await this.emails.notifyFreeMemberSignup({ member, - attribution: event?.data?.attribution + attribution }); } else if (type === SubscriptionActivatedEvent) { + let attribution; + try { + attribution = await this.memberAttributionService.getSubscriptionCreatedAttribution(event.data.subscriptionId); + } catch (e) { + this.logging.warn(`Failed to get attribution for member - ${event?.data?.memberId}`); + } await this.emails.notifyPaidSubscriptionStarted({ member, offer, tier, subscription, - attribution: event?.data?.attribution + attribution }); } else if (type === SubscriptionCancelledEvent) { subscription.canceledAt = event.timestamp; diff --git a/ghost/staff-service/test/staff-service.test.js b/ghost/staff-service/test/staff-service.test.js index cc339ed3c9..4a312a9e4e 100644 --- a/ghost/staff-service/test/staff-service.test.js +++ b/ghost/staff-service/test/staff-service.test.js @@ -429,7 +429,9 @@ describe('StaffService', function () { }; const attribution = { - referrerSource: 'Twitter' + referrerSource: 'Twitter', + title: 'Welcome Post', + url: 'https://example.com/welcome' }; await service.emails.notifyFreeMemberSignup({member, attribution}, options); @@ -447,8 +449,23 @@ describe('StaffService', function () { mailStub.calledWith( sinon.match.has('html', sinon.match('Created on 1 Aug 2022 • France')) ).should.be.true(); + mailStub.calledWith( - sinon.match.has('html', sinon.match('Source: Twitter')) + sinon.match.has('html', sinon.match('Source')) + ).should.be.true(); + + mailStub.calledWith( + sinon.match.has('html', sinon.match('Twitter')) + ).should.be.true(); + + // check attribution page + mailStub.calledWith( + sinon.match.has('html', sinon.match('Welcome Post')) + ).should.be.true(); + + // check attribution url + mailStub.calledWith( + sinon.match.has('html', sinon.match('https://example.com/welcome')) ).should.be.true(); }); }); @@ -486,7 +503,9 @@ describe('StaffService', function () { it('sends paid subscription start alert with attribution', async function () { const attribution = { - referrerSource: 'Twitter' + referrerSource: 'Twitter', + title: 'Welcome Post', + url: 'https://example.com/welcome' }; await service.emails.notifyPaidSubscriptionStarted({member, offer: null, tier, subscription, attribution}, options); @@ -495,7 +514,22 @@ describe('StaffService', function () { // check attribution text mailStub.calledWith( - sinon.match.has('html', sinon.match('Source: Twitter')) + sinon.match.has('html', sinon.match('Twitter')) + ).should.be.true(); + + // check attribution text + mailStub.calledWith( + sinon.match.has('html', sinon.match('Source')) + ).should.be.true(); + + // check attribution page + mailStub.calledWith( + sinon.match.has('html', sinon.match('Welcome Post')) + ).should.be.true(); + + // check attribution url + mailStub.calledWith( + sinon.match.has('html', sinon.match('https://example.com/welcome')) ).should.be.true(); });