IONOS(sharebymail): add BeforeShare*MailSentEvent dispatched before each mail send (HDNEXT-1010)#262
Conversation
0dfb4f0 to
05ca5da
Compare
05ca5da to
e5d9cd6
Compare
450ed8c to
ccc0f83
Compare
0d58932 to
6b86f4e
Compare
Code reviewFound 2 issues:
In both nc-server/apps/sharebymail/lib/ShareByMailProvider.php Lines 499 to 514 in 6b86f4e nc-server/apps/sharebymail/lib/ShareByMailProvider.php Lines 641 to 650 in 6b86f4e
The PR adds test expectations for nc-server/apps/sharebymail/tests/ShareByMailProviderTest.php Lines 24 to 26 in 6b86f4e nc-server/apps/sharebymail/tests/ShareByMailProviderTest.php Lines 363 to 370 in 6b86f4e 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
2d0c98c to
c0d9b40
Compare
There was a problem hiding this comment.
Pull request overview
Adds PSR-14 “BeforeShare*MailSentEvent” hooks to apps/sharebymail so app-layer listeners can intercept share-by-mail outgoing messages and optionally suppress the native mailer->send() path.
Changes:
- Dispatch
BeforeShareMailSentEvent,BeforeSharePasswordMailSentEvent, andBeforeShareNoteMailSentEventimmediately before sending share-by-mail notifications. - Refactor mail template input into
$templateDataarrays that are passed to bothcreateEMailTemplate()and the dispatched events. - Extend unit tests to assert event dispatch and “handled” behavior.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/sharebymail/lib/ShareByMailProvider.php | Dispatches new events before mailer->send() in share, password, note, and owner-password flows. |
| apps/sharebymail/lib/Event/AbstractBeforeShareMailSentEvent.php | Introduces shared event base with message, recipients, and “handled” flag. |
| apps/sharebymail/lib/Event/BeforeShareMailSentEvent.php | Adds event for share-link notification email sends. |
| apps/sharebymail/lib/Event/BeforeSharePasswordMailSentEvent.php | Adds event for password email sends (recipient + owner flows). |
| apps/sharebymail/lib/Event/BeforeShareNoteMailSentEvent.php | Adds event for note-update email sends. |
| apps/sharebymail/tests/ShareByMailProviderTest.php | Updates expectations for added dispatches and adds tests for note + handled password scenarios. |
| apps/sharebymail/composer/composer/autoload_static.php | Updates classmap to include new event classes. |
| apps/sharebymail/composer/composer/autoload_classmap.php | Updates classmap to include new event classes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…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.
c0d9b40 to
71f4973
Compare
…ach 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.
Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
…ssmap autoload_real.php sets setClassMapAuthoritative(true), which disables PSR-4 fallback entirely. Any class not listed in $classMap is never found, regardless of whether PSR-4 would resolve it. Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
…a and event refactors - Add senderUserId to the expected RecipientNotification templateData in 3 tests (added by the createEMailTemplate params refactor in 242b896) - Update dispatchTyped expectation from once(GenerateSecurePasswordEvent) to exactly(3) with withConsecutive for GenerateSecurePasswordEvent, BeforeShareMailSentEvent, and BeforeSharePasswordMailSentEvent (sendEmail() and sendPassword() now each dispatch a Before*Event) Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
…t dispatch Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
…s suppressed sendPassword() and sendPasswordToOwner() called createPasswordSendActivity() unconditionally outside the isMailHandled() guard. A listener intercepting the BeforeSharePasswordMailSentEvent would suppress the SMTP send but still cause a false activity-log entry for a mail that was never delivered. Apply the same early-return pattern used by sendEmail(): return immediately when isMailHandled() is true so no side effects run after the flag is set. Signed-off-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
…r "file request") https://github.com/IONOS-Productivity/nc-ionos-processes/releases/tag/1.0.0-9501a5e Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de> Co-authored-by: Tatjana Kaschperko Lindt <kaschperko-lindt@strato.de>
71f4973 to
e97f706
Compare
Summary
Introduces a PSR-14 event hook inside
ShareByMailProviderthat allows app-layer listeners to replace the native Nextcloud SMTP path for share-by-mail notifications.New event classes in
apps/sharebymail/lib/Event/AbstractBeforeShareMailSentEvent— abstract base carrying the fully-preparedIMessage, validated recipient list, pre-computed mail data, and amarkMailHandled()flagBeforeShareMailSentEvent— fired bysendEmail()before the share-link notification sendBeforeSharePasswordMailSentEvent— fired bysendPassword()andsendPasswordToOwner()before password emailsBeforeShareNoteMailSentEvent— fired bysendNote()before note-update emailsHow the replacement mechanism works
When a listener calls
$event->markMailHandled(), the nativemailer->send()is skipped. If the listener's own send throws, the exception propagates — native send is also skipped. No silent fallback. When no listener is active, native SMTP runs as before.mailDataarrayEach send method passes the data it already computed before building the
IMessage. Listeners read$event->getMailData()instead of re-deriving from the share, avoiding duplicate user/node lookups.BeforeShareMailSentEventcarries:senderUserId,fileName,resourceUrl,note,expiration(\DateTime|null).Dispatch point
Events fire inside each private send method immediately before
mailer->send()— covering bothManager::createShare()andsendShareEmail()Controller paths without touching either.Related PRs
Test plan
apps/sharebymail/unit tests pass