feat(sharebymail): dispatch BeforeShare*MailSentEvent before each mail send - #61948
feat(sharebymail): dispatch BeforeShare*MailSentEvent before each mail send#61948printminion-co wants to merge 2 commits into
Conversation
3391f42 to
d928ae7
Compare
d928ae7 to
edc6c9c
Compare
edc6c9c to
9ad378d
Compare
…ateData variables Extract the inline array literals passed to each createEMailTemplate() call into named $templateData variables. For sendNote(), which previously passed no data array, add a minimal array so all four send methods are consistent. Zero behaviour change — $templateData is passed straight through to createEMailTemplate() with identical contents. Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
…ch mail send
Dispatch a typed PSR-14 event immediately before every mailer->send() call
in ShareByMailProvider so that external listeners can intercept and replace
Nextcloud's native SMTP delivery.
Event hierarchy:
AbstractBeforeShareMailSentEvent (base: share, resolvedEmails, message,
markMailHandled / isMailHandled)
├── BeforeShareMailSentEvent – sendEmail()
├── BeforeSharePasswordMailSentEvent – sendPassword() + sendPasswordToOwner()
└── BeforeShareNoteMailSentEvent – sendNote()
Each concrete class holds its own typed $templateData (psalm array-shape)
and exposes named getters (getSenderUserId(), getFileName(), …) instead of
a generic getMailData(): array<string,mixed>. This avoids defensive
is_string() / null guards in listeners.
$templateData reuses the array already passed to createEMailTemplate(), with
one extra key added for sendEmail(): senderUserId (the raw user ID, distinct
from the display name stored under 'initiator').
The native mailer->send() is skipped when a listener calls markMailHandled().
If the listener's own send throws, the exception propagates and the native
send is also skipped — no silent SMTP fallback.
sendEmail() and sendPassword() are flattened from nested
if (!isMailHandled()) { ... } pyramids to early-return style.
createPasswordSendActivity() in sendPassword() and sendPasswordToOwner() now runs
only when the mail was actually sent: it moved inside the isMailHandled() early
return, so a listener that suppresses the SMTP send no longer produces a false
password-share activity entry.
Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
9ad378d to
3ff2742
Compare
|
This is motivated by two problematics:
|
|
Hello there, 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.) |
Summary
ShareByMailProvidersends share-by-mail notifications straight through the native mailer with noextension point. This adds a typed PSR-14 event dispatched immediately before every
mailer->send(), so an app can intercept and replace delivery (e.g. route through a differenttransport) without patching the provider.
A listener calls
markMailHandled()to take ownership of delivery; the nativemailer->send()isthen skipped. If the listener throws, the exception propagates and the native send is still skipped
— no silent SMTP fallback. With no listener registered, behaviour is unchanged.
Before — native SMTP only
sequenceDiagram participant C as Caller (share flow) participant P as ShareByMailProvider participant M as IMailer C->>P: sendEmail / sendPassword / sendPasswordToOwner / sendNote P->>M: mailer->send(message) M-->>P: failedRecipientsAfter — event dispatched before send
sequenceDiagram participant C as Caller (share flow) participant P as ShareByMailProvider participant D as IEventDispatcher participant L as App listener (optional) participant M as IMailer C->>P: sendEmail / sendPassword / sendPasswordToOwner / sendNote P->>D: dispatchTyped(BeforeShare*MailSentEvent) D->>L: handle(event) alt listener took over delivery L->>L: deliver via own transport L->>P: markMailHandled() Note over P,M: native send skipped, activity not logged else no listener / not handled (default) P->>M: mailer->send(message) M-->>P: failedRecipients endNotes
OCA\ShareByMail\Event\*; each concrete event carries typed getters for thedata already computed before building the
IMessage(no re-derivation in listeners).createPasswordSendActivity()now runs only when the mail is actually sent (moved inside theisMailHandled()guard), so a suppressed password mail no longer logs a false activity entry.TODO
Checklist
apps/sharebymail/tests/ShareByMailProviderTest.phpAI (if applicable)