Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
b7afadb
feature: send email to internal users of circles when shared with circle
yemkareems Jun 30, 2025
80d58ff
fix: add FileCopyrightText and License-Identifier
yemkareems Jun 30, 2025
222e3d2
fix: refactored service to mail provider and tests adjusted accordingly
yemkareems Jun 30, 2025
a10022d
fix: refactor ShareByCircleProvider class to implement IShareProvider…
yemkareems Jul 1, 2025
e50f772
fix: move helper functions to provider, remove the helper class and r…
yemkareems Jul 1, 2025
0722d76
fix: composer run cs fix
yemkareems Jul 1, 2025
81e87c0
fix: removed the check sharing.enable_share_mail as it is already done
yemkareems Jul 8, 2025
7d2a2a5
fix: removed the DefaultShareProvider dependency for psalm
yemkareems Jul 8, 2025
787bcb4
Merge branch 'master' into feature/notify-internal-users-on-circle-share
yemkareems Jul 9, 2025
bc31bc1
fix: removed comment, corrected text, used user type constant, if con…
yemkareems Jul 16, 2025
05fb5ae
Merge branch 'feature/notify-internal-users-on-circle-share' of githu…
yemkareems Jul 16, 2025
23db5ad
Merge branch 'master' into feature/notify-internal-users-on-circle-share
yemkareems Jul 16, 2025
82acf7c
fix: note was not getting sent in email
yemkareems Jul 23, 2025
4ad4380
fix: initiator moved outside the loop, member check refactored to not…
yemkareems Aug 3, 2025
bb52de4
fix: moved sending email to ShareCreatedSendMail and removed mail sen…
yemkareems Aug 11, 2025
a9a459c
fix: cs fix
yemkareems Aug 11, 2025
c5828fc
fix: user type added to getInheritedMembers, member isLocal check add…
yemkareems Aug 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/Db/ShareWrapperRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public function save(IShare $share, int $parentId = 0): int {
$qb = $this->getShareInsertSql();
$qb->setValue('attributes', $qb->createNamedParameter($this->formatShareAttributes($share->getAttributes())))
->setValue('share_type', $qb->createNamedParameter($share->getShareType()))
->setValue('mail_send', $qb->createNamedParameter($share->getMailSend()))
->setValue('note', $qb->createNamedParameter($share->getNote()))
->setValue('item_type', $qb->createNamedParameter($share->getNodeType()))
->setValue('item_source', $qb->createNamedParameter($share->getNodeId()))
->setValue('file_source', $qb->createNamedParameter($share->getNodeId()))
Expand Down
42 changes: 42 additions & 0 deletions lib/Listeners/Files/ShareCreatedSendMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
use OCA\Circles\Tools\Traits\TStringTools;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\IRootFolder;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;

/** @template-implements IEventListener<FileShareCreatedEvent|Event> */
Expand Down Expand Up @@ -59,6 +62,10 @@ class ShareCreatedSendMail implements IEventListener {
private $contactService;
/** @var IUserManager */
private $userManager;
/** @var IURLGenerator */
private $urlGenerator;
/** @var IRootFolder */
private $rootFolder;

public function __construct(
ShareWrapperService $shareWrapperService,
Expand All @@ -68,6 +75,8 @@ public function __construct(
ContactService $contactService,
ConfigService $configService,
IUserManager $userManager,
IURLGenerator $urlGenerator,
IRootFolder $rootFolder,
) {
$this->shareWrapperService = $shareWrapperService;
$this->shareTokenService = $shareTokenService;
Expand All @@ -76,6 +85,8 @@ public function __construct(
$this->contactService = $contactService;
$this->configService = $configService;
$this->userManager = $userManager;
$this->urlGenerator = $urlGenerator;
$this->rootFolder = $rootFolder;

$this->setup('app', Application::APP_ID);
}
Expand All @@ -95,8 +106,39 @@ public function handle(Event $event): void {

$circle = $event->getCircle();
$clearPasswords = $event->getFederatedEvent()->getInternal()->gArray('clearPasswords');
/** @var ShareWrapper $wrappedShare */
$wrappedShare = $event->getFederatedEvent()->getParams()->gObj('wrappedShare', ShareWrapper::class);
$iShare = $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
$link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', [
'token' => $iShare->getToken()
]);
$initiator = $iShare->getSharedBy();
$initiatorUser = $this->userManager->get($initiator);
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
$initiatorEmail = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;

foreach ($circle->getInheritedMembers(false, true) as $member) {
if ($member->getUserType() == Member::TYPE_USER && $member->isLocal()) {
$user = $this->userManager->get($member->getUserId());
if ($user === null) {
continue;
}
$email = $user->getEMailAddress();
if ($email === null
|| $email === $initiatorEmail
) {
continue;
}
$this->sendMailService->sendUserShareMail(
$link,
$user->getEMailAddress(),
$initiatorDisplayName,
$circle->getDisplayName(),
$initiatorEmail,
$iShare,
);
}

if ($member->getUserType() !== Member::TYPE_MAIL
&& $member->getUserType() !== Member::TYPE_CONTACT) {
continue;
Expand Down
12 changes: 12 additions & 0 deletions lib/Model/ShareWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class ShareWrapper extends ManagedModel implements IDeserializable, IQueryRow, J
private ?ShareToken $shareToken = null;
private ?IAttributes $attributes = null;
private bool $hideDownload = false;
private bool $mailSend = true;

public function __construct() {
$this->shareTime = new DateTime();
Expand Down Expand Up @@ -374,6 +375,16 @@ public function setHideDownload(bool $hideDownload): self {
return $this;
}

public function setMailSend(bool $mailSend): self {
$this->mailSend = $mailSend;

return $this;
}

public function getMailSend(): bool {
return $this->mailSend;
}


/**
* @throws IllegalIDChangeException
Expand All @@ -396,6 +407,7 @@ public function getShare(
$share->setHideDownload($this->getHideDownload());
$share->setAttributes($this->getAttributes());
$share->setNote($this->getShareNote());
$share->setMailSend($this->getMailSend());
if ($this->hasShareToken()) {
$password = $this->getShareToken()->getPassword();
if ($password !== '') {
Expand Down
78 changes: 78 additions & 0 deletions lib/Service/SendMailService.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use OCP\Mail\IMailer;
use OCP\Security\IHasher;
use OCP\Share\IManager;
use OCP\Share\IShare;
use OCP\Util;

class SendMailService {
Expand Down Expand Up @@ -201,6 +202,83 @@ private function sendMailExistingShares(
$this->mailer->send($message);
}

/**
* Send mail notifications for the user share type
*
* @param string $link link to the file/folder
* @param string $shareWith email address of share receiver
* @param string $initiatorDisplayName name of the share creator
* @param string $circleName name of the circle shared with
* @param string|null $initiatorEmail email of the share creator
* @param IShare $share
* @throws Exception
*/
public function sendUserShareMail(
string $link,
string $shareWith,
string $initiatorDisplayName,
string $circleName,
?string $initiatorEmail,
IShare $share,
): void {

$filename = $share->getNode()->getName();
$expiration = $share->getExpirationDate();
$note = $share->getNote();
$l = $this->l10n;

$message = $this->mailer->createMessage();

$emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
'filename' => $filename,
'link' => $link,
'initiator' => $initiatorDisplayName,
'expiration' => $expiration,
'shareWith' => $shareWith,
]);

$emailTemplate->setSubject($l->t('%1$s shared %2$s with %3$s', [$initiatorDisplayName, $filename, $circleName]));
$emailTemplate->addHeader();
$emailTemplate->addHeading($l->t('%1$s shared %2$s with "%3$s"', [$initiatorDisplayName, $filename, $circleName]), false);

if ($note !== '') {
$emailTemplate->addBodyText(htmlspecialchars($note), $note);
}

$emailTemplate->addBodyButton(
$l->t('Open %s', [$filename]),
$link
);

$message->setTo([$shareWith]);

// The "From" contains the sharers name
$instanceName = $this->defaults->getName();
$senderName = $l->t(
'%1$s via %2$s',
[
$initiatorDisplayName,
$instanceName,
]
);
$message->setFrom([\OCP\Util::getDefaultEmailAddress('noreply') => $senderName]);

// The "Reply-To" is set to the sharer if an mail address is configured
// also the default footer contains a "Do not reply" which needs to be adjusted.
if ($initiatorEmail !== null) {
$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
} else {
$emailTemplate->addFooter();
}

$message->useTemplate($emailTemplate);
$failedRecipients = $this->mailer->send($message);
if (!empty($failedRecipients)) {
return;
}
}


/**
* @param Circle $circle
Expand Down
2 changes: 1 addition & 1 deletion lib/ShareByCircleProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ public function create(IShare $share): IShare {

$circle = $this->circleService->probeCircle($share->getSharedWith(), $circleProbe, $dataProbe);
$share->setToken($this->token(15));
$share->setMailSend(true);
$owner = $circle->getInitiator();
$this->shareWrapperService->save($share);

Expand All @@ -161,7 +162,6 @@ public function create(IShare $share): IShare {
return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
}


/**
* @param IShare $share
*
Expand Down
Loading