Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: mail provider backend #45383

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
82 changes: 57 additions & 25 deletions apps/dav/lib/CalDAV/Schedule/IMipPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use OCP\IConfig;
use OCP\IUserSession;
use OCP\Mail\IMailer;
use OCP\Mail\Provider\IManager as IMailManager;
use OCP\Mail\Provider\IMessageSend;
use OCP\Util;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\Schedule\IMipPlugin as SabreIMipPlugin;
Expand Down Expand Up @@ -55,6 +57,7 @@ class IMipPlugin extends SabreIMipPlugin {
public const METHOD_CANCEL = 'cancel';
public const IMIP_INDENT = 15; // Enough for the length of all body bullet items, in all languages
private EventComparisonService $eventComparisonService;
private IMailManager $mailManager;

public function __construct(IConfig $config,
IMailer $mailer,
Expand All @@ -63,7 +66,8 @@ public function __construct(IConfig $config,
Defaults $defaults,
IUserSession $userSession,
IMipService $imipService,
EventComparisonService $eventComparisonService) {
EventComparisonService $eventComparisonService,
IMailManager $mailManager) {
parent::__construct('');
$this->userSession = $userSession;
$this->config = $config;
Expand All @@ -73,6 +77,7 @@ public function __construct(IConfig $config,
$this->defaults = $defaults;
$this->imipService = $imipService;
$this->eventComparisonService = $eventComparisonService;
$this->mailManager = $mailManager;
}

public function initialize(DAV\Server $server): void {
Expand Down Expand Up @@ -212,21 +217,6 @@ public function schedule(Message $iTipMessage) {
$fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
$fromName = $this->imipService->getFrom($senderName, $this->defaults->getName());

$message = $this->mailer->createMessage()
->setFrom([$fromEMail => $fromName]);

if ($recipientName !== null) {
$message->setTo([$recipient => $recipientName]);
} else {
$message->setTo([$recipient]);
}

if ($senderName !== null) {
$message->setReplyTo([$sender => $senderName]);
} else {
$message->setReplyTo([$sender]);
}

$template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
$template->addHeader();

Expand Down Expand Up @@ -268,18 +258,60 @@ public function schedule(Message $iTipMessage) {
}

$template->addFooter();

$message->useTemplate($template);

// convert iTip Message to string
$itip_msg = $iTipMessage->message->serialize();
$message->attachInline(
$itip_msg,
'event.ics',
'text/calendar; method=' . $iTipMessage->method,
);

$user = null;
$mailService = null;

try {
$failed = $this->mailer->send($message);
// retrieve user object
$user = $this->userSession->getUser();
// evaluate if user object exist
if ($user !== null) {
// retrieve appropriate service with the same address as sender
$mailService = $this->mailManager->findServiceByAddress($user->getUID(), $sender);
}
// evaluate if a mail service was found and has sending capabilities
if ($mailService !== null && $mailService instanceof IMessageSend) {
Fixed Show fixed Hide fixed
SebastianKrupinski marked this conversation as resolved.
Show resolved Hide resolved
SebastianKrupinski marked this conversation as resolved.
Show resolved Hide resolved
// construct mail message and set required parameters
$message = $mailService->initiateMessage();
$message->setFrom(
(new \OCP\Mail\Provider\Address($sender, $fromName))
);
$message->setTo(
(new \OCP\Mail\Provider\Address($recipient, $recipientName))
);
$message->setSubject($template->renderSubject());
$message->setBodyPlain($template->renderText());
$message->setBodyHtml($template->renderHtml());
$message->setAttachments((new \OCP\Mail\Provider\Attachment(
$itip_msg,
'event.ics',
'text/calendar; method=' . $iTipMessage->method,
true
)));
// send message
$mailService->sendMessage($message);
} else {
// construct symfony mailer message and set required parameters
$message = $this->mailer->createMessage();
$message->setFrom([$fromEMail => $fromName]);
$message->setTo(
(($recipientName !== null) ? [$recipient => $recipientName] : [$recipient])
);
$message->setReplyTo(
(($senderName !== null) ? [$sender => $senderName] : [$sender])
Dismissed Show dismissed Hide dismissed
Dismissed Show dismissed Hide dismissed
);
$message->useTemplate($template);
$message->attachInline(
$itip_msg,
'event.ics',
'text/calendar; method=' . $iTipMessage->method
);
$failed = $this->mailer->send($message);
}

$iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
if (!empty($failed)) {
$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
Expand Down
3 changes: 2 additions & 1 deletion apps/dav/lib/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@ public function __construct(IRequest $request, string $baseUri) {
\OC::$server->get(\OCP\Defaults::class),
$userSession,
\OC::$server->get(\OCA\DAV\CalDAV\Schedule\IMipService::class),
\OC::$server->get(\OCA\DAV\CalDAV\EventComparisonService::class)
\OC::$server->get(\OCA\DAV\CalDAV\EventComparisonService::class),
\OC::$server->get(\OCP\Mail\Provider\IManager::class)
));
}
$this->server->addPlugin(new \OCA\DAV\CalDAV\Search\SearchPlugin());
Expand Down
136 changes: 131 additions & 5 deletions apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\Mail\IMessage;
use OCP\Mail\Provider\IManager as IMailManager;
use OCP\Mail\Provider\IMessage as IMailMessageNew;
use OCP\Mail\Provider\IMessageSend as IMailMessageSend;
use OCP\Mail\Provider\IService as IMailService;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Sabre\VObject\Component\VCalendar;
Expand All @@ -26,6 +30,11 @@
use Test\TestCase;
use function array_merge;

interface IMailServiceMock extends IMailService, IMailMessageSend {
// workaround for creating mock class with multiple interfaces
// TODO: remove after phpUnit 10 is supported.
}

class IMipPluginTest extends TestCase {

/** @var IMessage|MockObject */
Expand Down Expand Up @@ -67,6 +76,15 @@ class IMipPluginTest extends TestCase {
/** @var EventComparisonService|MockObject */
private $eventComparisonService;

/** @var IMailManager|MockObject */
private $mailManager;

/** @var IMailService|IMailMessageSend|MockObject */
private $mailService;

/** @var IMailMessageNew|MockObject */
private $mailMessageNew;

protected function setUp(): void {
$this->mailMessage = $this->createMock(IMessage::class);
$this->mailMessage->method('setFrom')->willReturn($this->mailMessage);
Expand All @@ -90,10 +108,6 @@ protected function setUp(): void {
$this->config = $this->createMock(IConfig::class);

$this->user = $this->createMock(IUser::class);
/*
$this->user->method('getUID');
$this->user->method('getDisplayName');
*/

$this->userSession = $this->createMock(IUserSession::class);
$this->userSession->method('getUser')
Expand All @@ -107,6 +121,12 @@ protected function setUp(): void {

$this->eventComparisonService = $this->createMock(EventComparisonService::class);

$this->mailManager = $this->createMock(IMailManager::class);
SebastianKrupinski marked this conversation as resolved.
Show resolved Hide resolved

$this->mailService = $this->createMock(IMailServiceMock::class);

$this->mailMessageNew = $this->createMock(IMailMessageNew::class);

$this->plugin = new IMipPlugin(
$this->config,
$this->mailer,
Expand All @@ -115,7 +135,8 @@ protected function setUp(): void {
$this->defaults,
$this->userSession,
$this->service,
$this->eventComparisonService
$this->eventComparisonService,
$this->mailManager,
);
}

Expand Down Expand Up @@ -582,6 +603,111 @@ public function testFailedDelivery(): void {
$this->assertEquals('5.0', $message->getScheduleStatus());
}

public function testMailProviderSend(): void {
// construct iTip message with event and attendees
$message = new Message();
$message->method = 'REQUEST';
$calendar = new VCalendar();
$event = new VEvent($calendar, 'one', array_merge([
'UID' => 'uid-1234',
'SEQUENCE' => 1,
'SUMMARY' => 'Fellowship meeting without (!) Boromir',
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
], []));
$event->add('ORGANIZER', 'mailto:gandalf@wiz.ard');
$event->add('ATTENDEE', 'mailto:' . 'frodo@hobb.it', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
$message->message = $calendar;
$message->sender = 'mailto:gandalf@wiz.ard';
$message->senderName = 'Mr. Wizard';
$message->recipient = 'mailto:' . 'frodo@hobb.it';
// construct
foreach ($event->select('ATTENDEE') as $entry) {
if (strcasecmp($entry->getValue(), $message->recipient) === 0) {
$attendee = $entry;
}
}
// construct body data return
$data = ['invitee_name' => 'Mr. Wizard',
'meeting_title' => 'Fellowship meeting without (!) Boromir',
'attendee_name' => 'frodo@hobb.it'
];
// construct system config mock returns
$this->config->expects(self::once())
->method('getAppValue')
->with('dav', 'invitation_link_recipients', 'yes')
->willReturn('yes');
// construct user mock returns
$this->user->expects(self::any())
->method('getUID')
->willReturn('user1');
$this->user->expects(self::any())
->method('getDisplayName')
->willReturn('Mr. Wizard');
// construct user session mock returns
$this->userSession->expects(self::any())
->method('getUser')
->willReturn($this->user);
// construct service mock returns
$this->service->expects(self::once())
->method('getLastOccurrence')
->willReturn('1496912700');
$this->service->expects(self::once())
->method('getCurrentAttendee')
->with($message)
->willReturn($attendee);
$this->service->expects(self::once())
->method('isRoomOrResource')
->with($attendee)
->willReturn(false);
$this->service->expects(self::once())
->method('buildBodyData')
->with($event, null)
->willReturn($data);
$this->service->expects(self::once())
->method('getFrom');
$this->service->expects(self::once())
->method('addSubjectAndHeading')
->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Fellowship meeting without (!) Boromir', false);
$this->service->expects(self::once())
->method('addBulletList')
->with($this->emailTemplate, $event, $data);
$this->service->expects(self::once())
->method('getAttendeeRsvpOrReqForParticipant')
->willReturn(true);
$this->service->expects(self::once())
->method('createInvitationToken')
->with($message, $event, '1496912700')
->willReturn('token');
$this->service->expects(self::once())
->method('addResponseButtons')
->with($this->emailTemplate, 'token');
$this->service->expects(self::once())
->method('addMoreOptionsButton')
->with($this->emailTemplate, 'token');
$this->eventComparisonService->expects(self::once())
->method('findModified')
->willReturn(['old' => [] ,'new' => [$event]]);
// construct mail mock returns
$this->mailer->expects(self::once())
->method('validateMailAddress')
->with('frodo@hobb.it')
->willReturn(true);
// construct mail provider mock returns
$this->mailService
->method('initiateMessage')
->willReturn($this->mailMessageNew);
$this->mailService
->method('sendMessage')
->with($this->mailMessageNew);
$this->mailManager
->method('findServiceByAddress')
->with('user1', 'gandalf@wiz.ard')
->willReturn($this->mailService);

$this->plugin->schedule($message);
$this->assertEquals('1.1', $message->getScheduleStatus());
}

public function testNoOldEvent(): void {
$message = new Message();
$message->method = 'REQUEST';
Expand Down
13 changes: 13 additions & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,18 @@
'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php',
'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php',
'OCP\\Mail\\IMessage' => $baseDir . '/lib/public/Mail/IMessage.php',
'OCP\\Mail\\Provider\\Address' => $baseDir . '/lib/public/Mail/Provider/Address.php',
'OCP\\Mail\\Provider\\Attachment' => $baseDir . '/lib/public/Mail/Provider/Attachment.php',
'OCP\\Mail\\Provider\\Exception\\Exception' => $baseDir . '/lib/public/Mail/Provider/Exception/Exception.php',
'OCP\\Mail\\Provider\\Exception\\SendException' => $baseDir . '/lib/public/Mail/Provider/Exception/SendException.php',
'OCP\\Mail\\Provider\\IAddress' => $baseDir . '/lib/public/Mail/Provider/IAddress.php',
'OCP\\Mail\\Provider\\IAttachment' => $baseDir . '/lib/public/Mail/Provider/IAttachment.php',
'OCP\\Mail\\Provider\\IManager' => $baseDir . '/lib/public/Mail/Provider/IManager.php',
'OCP\\Mail\\Provider\\IMessage' => $baseDir . '/lib/public/Mail/Provider/IMessage.php',
'OCP\\Mail\\Provider\\IMessageSend' => $baseDir . '/lib/public/Mail/Provider/IMessageSend.php',
'OCP\\Mail\\Provider\\IProvider' => $baseDir . '/lib/public/Mail/Provider/IProvider.php',
'OCP\\Mail\\Provider\\IService' => $baseDir . '/lib/public/Mail/Provider/IService.php',
'OCP\\Mail\\Provider\\Message' => $baseDir . '/lib/public/Mail/Provider/Message.php',
'OCP\\Migration\\BigIntMigration' => $baseDir . '/lib/public/Migration/BigIntMigration.php',
'OCP\\Migration\\IMigrationStep' => $baseDir . '/lib/public/Migration/IMigrationStep.php',
'OCP\\Migration\\IOutput' => $baseDir . '/lib/public/Migration/IOutput.php',
Expand Down Expand Up @@ -1618,6 +1630,7 @@
'OC\\Mail\\EMailTemplate' => $baseDir . '/lib/private/Mail/EMailTemplate.php',
'OC\\Mail\\Mailer' => $baseDir . '/lib/private/Mail/Mailer.php',
'OC\\Mail\\Message' => $baseDir . '/lib/private/Mail/Message.php',
'OC\\Mail\\Provider\\Manager' => $baseDir . '/lib/private/Mail/Provider/Manager.php',
'OC\\Memcache\\APCu' => $baseDir . '/lib/private/Memcache/APCu.php',
'OC\\Memcache\\ArrayCache' => $baseDir . '/lib/private/Memcache/ArrayCache.php',
'OC\\Memcache\\CADTrait' => $baseDir . '/lib/private/Memcache/CADTrait.php',
Expand Down
13 changes: 13 additions & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,18 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php',
'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php',
'OCP\\Mail\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/IMessage.php',
'OCP\\Mail\\Provider\\Address' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Address.php',
'OCP\\Mail\\Provider\\Attachment' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Attachment.php',
'OCP\\Mail\\Provider\\Exception\\Exception' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Exception/Exception.php',
'OCP\\Mail\\Provider\\Exception\\SendException' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Exception/SendException.php',
'OCP\\Mail\\Provider\\IAddress' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IAddress.php',
'OCP\\Mail\\Provider\\IAttachment' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IAttachment.php',
'OCP\\Mail\\Provider\\IManager' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IManager.php',
'OCP\\Mail\\Provider\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IMessage.php',
'OCP\\Mail\\Provider\\IMessageSend' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IMessageSend.php',
'OCP\\Mail\\Provider\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IProvider.php',
'OCP\\Mail\\Provider\\IService' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IService.php',
'OCP\\Mail\\Provider\\Message' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Message.php',
'OCP\\Migration\\BigIntMigration' => __DIR__ . '/../../..' . '/lib/public/Migration/BigIntMigration.php',
'OCP\\Migration\\IMigrationStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IMigrationStep.php',
'OCP\\Migration\\IOutput' => __DIR__ . '/../../..' . '/lib/public/Migration/IOutput.php',
Expand Down Expand Up @@ -1651,6 +1663,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Mail\\EMailTemplate' => __DIR__ . '/../../..' . '/lib/private/Mail/EMailTemplate.php',
'OC\\Mail\\Mailer' => __DIR__ . '/../../..' . '/lib/private/Mail/Mailer.php',
'OC\\Mail\\Message' => __DIR__ . '/../../..' . '/lib/private/Mail/Message.php',
'OC\\Mail\\Provider\\Manager' => __DIR__ . '/../../..' . '/lib/private/Mail/Provider/Manager.php',
'OC\\Memcache\\APCu' => __DIR__ . '/../../..' . '/lib/private/Memcache/APCu.php',
'OC\\Memcache\\ArrayCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/ArrayCache.php',
'OC\\Memcache\\CADTrait' => __DIR__ . '/../../..' . '/lib/private/Memcache/CADTrait.php',
Expand Down
Loading
Loading