Skip to content

feat: Add confirmation email for form respondents#3289

Open
dtretyakov wants to merge 14 commits intonextcloud:mainfrom
dtretyakov:feat/confirmation-email
Open

feat: Add confirmation email for form respondents#3289
dtretyakov wants to merge 14 commits intonextcloud:mainfrom
dtretyakov:feat/confirmation-email

Conversation

@dtretyakov
Copy link
Copy Markdown

@dtretyakov dtretyakov commented Apr 14, 2026

Closes #525 - Send confirmation emails to form respondents after submission.

Features:

  • Add confirmation email settings (enabled, subject, body) to Form entity.
  • Implement email sending with placeholder replacement:
    • {formTitle}, {formDescription}
    • Field placeholders based on name or label (e.g. {name}, {email}).
  • Allow form creators to explicitly select the recipient email field when multiple email fields are present.
  • Add UI in Settings sidebar to configure confirmation emails and recipient selection.
  • Replace basic email validation with Nextcloud's internal IEmailValidator.
  • Integration with activity notifications and background jobs for file syncing.

Technical changes:

  • Database migration for new Form properties.
  • Enhanced FormsService with email sending logic and validation.
  • Extensive unit and integration tests covering the new functionality.
  • Updated API documentation and OpenAPI spec.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 14, 2026

@dtretyakov dtretyakov marked this pull request as ready for review April 14, 2026 07:10
@dtretyakov
Copy link
Copy Markdown
Author

@Chartman123 I also tried to streamline UX for the feature.

Now the form looks like that:

Снимок экрана 2026-04-14 в 09 04 36

@Chartman123
Copy link
Copy Markdown
Collaborator

@dtretyakov looks good :)

I'll ping the design specialists to see what they think about it :)

@dtretyakov dtretyakov force-pushed the feat/confirmation-email branch from b111145 to 9452b76 Compare April 14, 2026 07:28
@dtretyakov
Copy link
Copy Markdown
Author

OCP\Mail\IEmailValidator breaks backward compatibility for this PR. The interface is not available in nextcloud/ocp on stable31.

For app code here, IMailer::validateMailAddress() is the safer choice: it is already available across the supported server branches and covers the same validation use case without introducing a branch-specific compatibility break.

@Chartman123
Copy link
Copy Markdown
Collaborator

No problem, just ignore this. For the next release we will no longer support NC31

@dtretyakov dtretyakov force-pushed the feat/confirmation-email branch 2 times, most recently from 363a0cb to 9cb9c09 Compare April 21, 2026 17:47
@dtretyakov
Copy link
Copy Markdown
Author

@Chartman123 the branch is rebased on the fresh main and uses OCP\Mail\IEmailValidator.
Please let me know if you're missing something to push it forward and make NC customers happier.

Copy link
Copy Markdown
Collaborator

@Chartman123 Chartman123 left a comment

Choose a reason for hiding this comment

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

I did a quick review and found some parts that should be changed in my opinion.

Please also add the new fields to FormsMigrator.php.

Also not sure about the frontend part, especially in the sidebar as I'm no vue expert. I added @susnux and @pringelmann as reviewers :)

Btw no need to hurry, the next release is not yet directly around the corner. We'll get it merged once it's ready :)

Comment thread lib/Migration/Version050301Date20260413233000.php Outdated
Comment thread lib/Migration/Version050301Date20260413233000.php Outdated
Comment thread lib/Constants.php Outdated
Comment thread lib/ResponseDefinitions.php
Comment thread src/components/SidebarTabs/SettingsSidebarTab.vue Outdated
Comment thread src/components/SidebarTabs/SettingsSidebarTab.vue Outdated
Comment thread src/components/SidebarTabs/SettingsSidebarTab.vue Outdated
@Chartman123 Chartman123 added this to the 5.3 milestone Apr 21, 2026
Comment thread lib/Migration/Version050300Date20260413233000.php
Comment thread lib/Service/FormsService.php Outdated
@pringelmann
Copy link
Copy Markdown
Collaborator

One thing I want to flag before this lands: as written, this turns any public form into an unauthenticated outbound mailer. A submitter controls the recipient address (it's just whatever they typed into the email field), so an attacker can script submissions with a victim's address and use the instance to mailbox-bomb them. Worst case it also burns the operator's MX reputation.

A few options, not mutually exclusive:

  • Rate limit per recipient address and/or per form.
  • Restrict the feature to authenticated submitters only (or at least gate it behind a warning when the form is public).
  • Move sending into a queued job so the queue can be paced.

I'd be more comfortable with at least one of these in place before we ship. Could also be worth a line in the PR description spelling out which mitigations apply, since this is a new outbound channel.

Comment thread lib/Service/FormsService.php Outdated
@Chartman123
Copy link
Copy Markdown
Collaborator

One thing I want to flag before this lands: as written, this turns any public form into an unauthenticated outbound mailer.

@pringelmann Perhaps we should also add an admin setting that can turn the feature on/off and that should be off by default?

Comment thread lib/Migration/Version050300Date20260413233000.php
@pringelmann
Copy link
Copy Markdown
Collaborator

One thing I want to flag before this lands: as written, this turns any public form into an unauthenticated outbound mailer.

@pringelmann Perhaps we should also add an admin setting that can turn the feature on/off and that should be off by default?

Yeah good idea, default-off admin toggle is a sensible safety net and I'd vote for adding it.

That said, I don't think it replaces a per-form/per-recipient mitigation. Once an admin flips it on (which many likely will, because it is a very useful feature for users), the abuse surface is fully open again on every public form on the instance. So it protects the instances that don't need the feature but does nothing for the ones that do.

I'd still suggest at least one of: rate limit per recipient, gate it behind authentication, or pace via a queued job. The admin toggle then becomes a global kill switch on top of that, which is genuinely useful (e.g. an admin can disable the feature instance-wide if they spot abuse in their mail logs)

dtretyakov and others added 4 commits April 23, 2026 22:08
Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
Co-authored-by: Christian Hartmann <chris-hartmann@gmx.de>
Signed-off-by: Dmitry Tretyakov <dtretyakov@users.noreply.github.com>
Co-authored-by: Christian Hartmann <chris-hartmann@gmx.de>
Signed-off-by: Dmitry Tretyakov <dtretyakov@users.noreply.github.com>
Co-authored-by: Christian Hartmann <chris-hartmann@gmx.de>
Signed-off-by: Dmitry Tretyakov <dtretyakov@users.noreply.github.com>
dtretyakov and others added 3 commits April 23, 2026 22:08
Co-authored-by: Christian Hartmann <chris-hartmann@gmx.de>
Signed-off-by: Dmitry Tretyakov <dtretyakov@users.noreply.github.com>
Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
@dtretyakov dtretyakov force-pushed the feat/confirmation-email branch 3 times, most recently from 15e1a4e to c7f7327 Compare April 23, 2026 22:23
Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
@dtretyakov dtretyakov force-pushed the feat/confirmation-email branch from c7f7327 to 4950eb3 Compare April 23, 2026 22:25
@dtretyakov
Copy link
Copy Markdown
Author

To mitigate the risks associated with automated emails now we have implemented a 4-layer defense strategy:

  1. Administrative Control: A global "kill switch" (allowConfirmationEmail) is available in the admin settings (defaulting to off), allowing instance operators to opt-in or disable the feature entirely.
  2. Burst Protection: We utilize Nextcloud's built-in AnonRateLimit and UserRateLimit middleware on the submission endpoint to prevent rapid-fire scripting attacks from a single IP.
  3. Recipient Safeguard: A strict per-recipient rate limit (max 3 emails per form/recipient pair per 24 hours) is enforced using a distributed cache with a "silent skip" strategy. This prevents a malicious actor from using a form to target a specific email address repeatedly, even when using multiple source IPs.
  4. Asynchronous Processing: Email sending is offloaded to a QueuedJob (SendConfirmationMailJob). This decouples the SMTP transaction from the HTTP response, preventing server hangs due to mail server latency and ensuring natural pacing of outgoing traffic.

If you see this combination is excessive please let me know and we could relax it.

- Relocate confirmationEmailRecipient from Question extraSettings to Form
- Centralize email question validation in Question model and FormsService
- Simplify SettingsSidebarTab component logic
- Ensure correct recipient ID mapping during form cloning
- Add comprehensive unit tests for validation and notification logic

Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
@dtretyakov dtretyakov force-pushed the feat/confirmation-email branch from 4950eb3 to 33d9af9 Compare April 24, 2026 08:52
@dtretyakov
Copy link
Copy Markdown
Author

@Chartman123 or @pringelmann please let me know if it requires some extra changes.

Comment thread docs/DataStructure.md Outdated
Comment thread lib/Controller/ApiController.php Outdated
Comment thread lib/Migration/Version050300Date20260413233000.php Outdated
Comment thread lib/Service/FormsService.php Outdated
Comment thread lib/Service/FormsService.php Outdated
Comment thread lib/Service/FormsService.php Outdated
Comment thread lib/Service/FormsService.php Outdated
Comment thread lib/Service/FormsService.php Outdated
Comment thread lib/Controller/ApiController.php Outdated
Comment thread lib/Controller/ApiController.php Outdated
Comment thread lib/Service/ConfigService.php
@github-actions
Copy link
Copy Markdown
Contributor

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
- Add test for globally disabled confirmation emails
- Fix rate limiting on APCu-only installs
- Optimize formMapper updates and refactor sendConfirmationEmail
- Fix PHPDoc, typings and UI validations

Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
- Move confirmation email logic from FormsService to ConfirmationEmailService
- Implement configurable rate limiting for confirmation emails
- Use EMailTemplate in SendConfirmationMailJob for consistent styling
- Improve UI with proper Nextcloud Vue components (NcInputField, NcTextArea)
- Add comprehensive unit tests for the new service
- Clean up Question and ApiController validation logic

Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
@dtretyakov dtretyakov force-pushed the feat/confirmation-email branch from 9e90ee1 to 394afb8 Compare April 29, 2026 08:11
Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
@dtretyakov
Copy link
Copy Markdown
Author

Thank you all for having the detailed review, I applied the fixes.
Also there are refactoring to make simplify the support of new feature:

  • SendConfirmationMailJob now uses email template.
  • ConfirmationEmailService now contains the new logic to process form send requests and triggers the Job.
  • Cleanup in the SettingsSidebarTab to use NC components.
Снимок экрана — 2026-04-29 в 21 17 55

Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
@dtretyakov dtretyakov force-pushed the feat/confirmation-email branch from b2c5303 to 1f60973 Compare April 29, 2026 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Confirmation mail for respondents

4 participants