From ea8cc47ee27733cedf5050fb6fb0e8f0216d25d1 Mon Sep 17 00:00:00 2001
From: Jessica Lawshe <5312836+lawshe@users.noreply.github.com>
Date: Wed, 10 Jun 2026 09:36:35 -0500
Subject: [PATCH] Merge pull request #34330 from
overleaf/jel-web-save-captured_by_group
[web] Send email alert when domain is captured or not
GitOrigin-RevId: 5554d82653be3226e5d72a0e42ca8aaa9a9b8f41
---
.../app/src/Features/Email/EmailBuilder.mjs | 30 +++++++++++++
.../test/unit/src/Email/EmailBuilder.test.mjs | 45 +++++++++++++++++++
2 files changed, 75 insertions(+)
diff --git a/services/web/app/src/Features/Email/EmailBuilder.mjs b/services/web/app/src/Features/Email/EmailBuilder.mjs
index f87f5903f8..3de026eb6d 100644
--- a/services/web/app/src/Features/Email/EmailBuilder.mjs
+++ b/services/web/app/src/Features/Email/EmailBuilder.mjs
@@ -1040,6 +1040,36 @@ templates.groupMemberLimitWarning = ctaTemplate({
},
})
+templates.groupDomainCapturedByGroupChanged = ctaTemplate({
+ subject(opts) {
+ return opts.domainCapturedByGroup
+ ? `Domain capture now active for ${opts.domain}`
+ : `Domain capture now inactive for ${opts.domain}`
+ },
+ title(opts) {
+ return opts.domainCapturedByGroup
+ ? `Domain capture is active for ${_.escape(opts.domain)}`
+ : `Domain capture is inactive for ${_.escape(opts.domain)}`
+ },
+ message(opts) {
+ if (opts.domainCapturedByGroup) {
+ return [
+ `Users with a ${_.escape(opts.domain)} email address on their account will be able to join your group through domain capture.`,
+ ]
+ }
+ return [
+ `Users with a ${_.escape(opts.domain)} email address on their account will no longer be able to join your group through domain capture. Anyone already in your group is unaffected.`,
+ `If you didn't expect this or want to re-enable it, please contact ${settings.adminEmail}.`,
+ ]
+ },
+ ctaText() {
+ return 'Manage domains'
+ },
+ ctaURL(opts) {
+ return `${settings.siteUrl}/manage/groups/${opts.groupId}/settings`
+ },
+})
+
function _formatUserNameAndEmail(user, placeholder) {
if (user.first_name && user.last_name) {
const fullName = `${user.first_name} ${user.last_name}`
diff --git a/services/web/test/unit/src/Email/EmailBuilder.test.mjs b/services/web/test/unit/src/Email/EmailBuilder.test.mjs
index 7e8843f705..508c59fe7f 100644
--- a/services/web/test/unit/src/Email/EmailBuilder.test.mjs
+++ b/services/web/test/unit/src/Email/EmailBuilder.test.mjs
@@ -14,6 +14,7 @@ describe('EmailBuilder', function () {
ctx.settings = {
appName: 'testApp',
siteUrl: 'https://www.overleaf.com',
+ adminEmail: 'admin@overleaf.test',
}
vi.doMock('../../../../app/src/Features/Email/EmailMessageHelper', () => ({
@@ -963,6 +964,50 @@ describe('EmailBuilder', function () {
})
})
})
+
+ describe('groupDomainCapturedByGroupChanged', function () {
+ it('should build active-domain-capture email', function (ctx) {
+ const email = ctx.EmailBuilder.buildEmail(
+ 'groupDomainCapturedByGroupChanged',
+ {
+ to: 'admin@example.com',
+ groupId: 'group-123',
+ domainCapturedByGroup: true,
+ domain: 'example.com',
+ }
+ )
+
+ expect(email.subject).to.equal(
+ 'Domain capture now active for example.com'
+ )
+ expect(email.html).to.contain(
+ 'Domain capture is active for example.com'
+ )
+ expect(email.html).to.contain('/manage/groups/group-123/settings')
+ expect(email.text).to.contain('/manage/groups/group-123/settings')
+ })
+
+ it('should build inactive-domain-capture email including support contact', function (ctx) {
+ const email = ctx.EmailBuilder.buildEmail(
+ 'groupDomainCapturedByGroupChanged',
+ {
+ to: 'admin@example.com',
+ groupId: 'group-123',
+ domainCapturedByGroup: false,
+ domain: 'example.com',
+ }
+ )
+
+ expect(email.subject).to.equal(
+ 'Domain capture now inactive for example.com'
+ )
+ expect(email.html).to.contain(
+ 'Domain capture is inactive for example.com'
+ )
+ expect(email.html).to.contain('admin@overleaf.test')
+ expect(email.text).to.contain('admin@overleaf.test')
+ })
+ })
})
})
})