feat: Add confirmation email for form respondents#3289
feat: Add confirmation email for form respondents#3289dtretyakov wants to merge 9 commits intonextcloud:mainfrom
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
@Chartman123 I also tried to streamline UX for the feature. Now the form looks like that:
|
|
@dtretyakov looks good :) I'll ping the design specialists to see what they think about it :) |
b111145 to
9452b76
Compare
|
For app code here, |
|
No problem, just ignore this. For the next release we will no longer support NC31 |
363a0cb to
9cb9c09
Compare
|
@Chartman123 the branch is rebased on the fresh main and uses |
Chartman123
left a comment
There was a problem hiding this comment.
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 :)
| ]; | ||
|
|
||
| public const EXTRA_SETTINGS_SHORT = [ | ||
| 'confirmationEmailRecipient' => ['boolean'], |
There was a problem hiding this comment.
Not sure, if we should add the extraSetting to the whole short question type or if we'd better have it only for *_SHORT_EMAIL. Otherwise it could also be set for e.g. number fields.
@susnux @pringelmann what do you think?
There was a problem hiding this comment.
We could also consider an alternative option.
From the domain perspective the form now has the following settings:
replyEmailsubjectbody
In the body we could reference form fields.
What if the same logic will be applied to the replyEmail?
In this case:
- we don't need to use extraSettings at all
- we'll be storing email field id in the replyEmail form field
Reference: #3289 (comment)
There was a problem hiding this comment.
I like this. It's more uniform than the FK approach and removes the special-case extraSetting entirely. Three template strings, one resolver, done.
One thing to think through: validation gets a bit fuzzier. With an FK we can verify at save time that the referenced question exists and is email-validated. With a placeholder string we either resolve lazily at send time (silent failure if the field was renamed/deleted) or eagerly validate the template against the current question set (which then needs to re-validate on every question edit).
I'd lean toward eager validation in the API layer plus a clear UI affordance in the sidebar - e.g. a dropdown that writes the placeholder for the user rather than letting them type {whatever} freehand. That keeps the "free-form template" flexibility for power users while making the common path foolproof.
Worth doing IMO.
There was a problem hiding this comment.
I implemented that change in the last commit, please check whether it look OK.
|
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:
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. |
| $this->eventDispatcher->dispatchTyped(new FormSubmittedEvent($form, $submission)); | ||
|
|
||
| // Send confirmation email if enabled | ||
| $this->sendConfirmationEmail($form, $submission); |
There was a problem hiding this comment.
This runs inline here, so any latency lands directly on the user's submission response. A slow MX, a TLS handshake hiccup, or a transient timeout all become user-visible delays. The try/catch swallows errors but not the time spent. Can lead to poor UX.
Perhaps worth moving into a queued job carrying formId + submissionId and resolving the rest there? Side benefit: it makes the throttling issue for mailbox spamming essentially free, since the job queue is already the right place to pace outbound mail.
There was a problem hiding this comment.
Perhaps worth moving into a queued job carrying
formId+submissionIdand resolving the rest there? Side benefit: it makes the throttling issue for mailbox spamming essentially free, since the job queue is already the right place to pace outbound mail.
Good idea 👍🏻 a background job sounds better
@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) |
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>
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>
15e1a4e to
c7f7327
Compare
Signed-off-by: Dmitry Tretyakov <dtretyakov@gmail.com>
c7f7327 to
4950eb3
Compare
|
To mitigate the risks associated with automated emails now we have implemented a 4-layer defense strategy:
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>
4950eb3 to
33d9af9
Compare

Closes #525 - Send confirmation emails to form respondents after submission.
Features:
Technical changes: