Skip to content

🎨 Changed email verification error messages to be configurable via hostSettings#26631

Merged
9larsons merged 2 commits intoTryGhost:mainfrom
magicpages:configurable-email-verification-messages
Mar 16, 2026
Merged

🎨 Changed email verification error messages to be configurable via hostSettings#26631
9larsons merged 2 commits intoTryGhost:mainfrom
magicpages:configurable-email-verification-messages

Conversation

@betschki
Copy link
Contributor

Managed hosting providers other than Ghost(Pro) currently see hardcoded "support@ghost.org" and "account is currently in review" messages that don't apply to their platform. This adds hostSettings configuration keys (emailSendingDisabledMessage, emailVerificationNeededMessage) under the existing emailVerification namespace, with the current Ghost(Pro) messages kept as defaults. Also replaced fragile regex-based error detection in publish-limit modal with proper error code checking.

Got some code for us? Awesome 🎊!

Please take a minute to explain the change you're making:

  • Why are you making it?
  • What does it do?
  • Why is this something Ghost users or developers need?

Please check your PR against these items:

  • I've read and followed the Contributor Guide
  • I've explained my change
  • I've written an automated test to prove my change works

We appreciate your contribution! 🙏

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

Caution

Review failed

The head commit changed during the review from abed0b6 to dd117ed.

Walkthrough

This pull request implements configurable error messages for email verification workflows across the email and publishing systems. A new error code EMAIL_VERIFICATION_NEEDED is introduced and threaded through multiple services. The changes allow custom messages for email sending disabled scenarios and email verification needed scenarios to be sourced from host settings configuration, replacing hard-coded defaults. These messages propagate from config through EmailServiceWrapper, EmailService, VerificationTrigger, and ultimately to the PublishLimitModal UI component, which now selects header text based on the error code rather than message content inspection. Test coverage is added for the new verification code paths.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: making email verification error messages configurable via hostSettings, which is the core objective of this PR.
Description check ✅ Passed The description clearly explains the rationale (hardcoded Ghost(Pro) messages don't apply to other managed hosts), the solution (configurable hostSettings keys), and confirms tests were written.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes with custom instructions.

Set the reviews.auto_title_instructions setting to generate a title for your PR based on the changes in the PR with custom instructions.

@codecov
Copy link

codecov bot commented Feb 28, 2026

Codecov Report

❌ Patch coverage is 63.63636% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.10%. Comparing base (462000e) to head (dd117ed).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...ore/server/services/email-service/email-service.js 62.50% 3 Missing ⚠️
ghost/admin/app/utils/publish-options.js 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #26631   +/-   ##
=======================================
  Coverage   73.09%   73.10%           
=======================================
  Files        1535     1535           
  Lines      121261   121268    +7     
  Branches    14648    14649    +1     
=======================================
+ Hits        88641    88647    +6     
+ Misses      31613    31593   -20     
- Partials     1007     1028   +21     
Flag Coverage Δ
admin-tests 54.32% <0.00%> (+<0.01%) ⬆️
e2e-tests 73.10% <63.63%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JohnONolan JohnONolan self-requested a review March 2, 2026 09:44
Copy link
Member

@JohnONolan JohnONolan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea

@betschki
Copy link
Contributor Author

@JohnONolan any chance we could get a review on this?

@9larsons 9larsons self-requested a review March 16, 2026 14:12
Copy link
Contributor

@9larsons 9larsons left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more general comment: I'd love for us to define the available hostSettings (and more generally, our config options) somewhere. The fact that this is totally ambiguous is troublesome and hard to reason about.

I think I'd like to see a more generic pattern for passing the messages, although the DI pattern is the right one. Perhaps something like hostSettings.messages.emailVerification.<items>?

Then DI would/could still pass in hostSettings.messages.emailVerification, or if we use other hostSettings/config, the more broad item that's appropriate.

emailAnalyticsJobs,
domainWarmingService
domainWarmingService,
emailSendingDisabledMessage: configService.get('hostSettings:emailVerification:emailSendingDisabledMessage')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike passing a message through the wrapper vs passing config, as this feels too 'low level' and an anti pattern if we were to have several messages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Switched to passing the full config service via DI, same pattern as DomainWarmingService.

amountTriggered: amount
});

if (throwOnTrigger) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, aside from tests, we have no callers that pass throwOnTrigger = true. This would then be dead code. Could you let me know why you updated this path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right! Reverted it back.

…stSettings

Pass config service to EmailService via DI (matching DomainWarmingService pattern)
instead of threading resolved message strings through the wrapper.

Removed dead configurable message from VerificationTrigger's throwOnTrigger
path — no production callers use throwOnTrigger = true.
@betschki betschki force-pushed the configurable-email-verification-messages branch from af205f3 to abed0b6 Compare March 16, 2026 19:30
@betschki betschki requested a review from 9larsons March 16, 2026 19:33
Copy link
Contributor

@9larsons 9larsons left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super clean! I am annoyed we pass config: configService, as I think simply passing configService is more clear... but we do this almost everywhere in core so it's better to be consistent.

@9larsons 9larsons merged commit bea537d into TryGhost:main Mar 16, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants