From fc0b694d379efefc50ba46262bd448abd29da6ed Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Thu, 16 May 2024 19:06:34 -0400 Subject: [PATCH] feat: mail provider backend Signed-off-by: SebastianKrupinski --- apps/dav/lib/CalDAV/Schedule/IMipPlugin.php | 82 +++-- apps/dav/lib/Server.php | 3 +- .../unit/CalDAV/Schedule/IMipPluginTest.php | 136 ++++++- lib/composer/composer/autoload_classmap.php | 13 + lib/composer/composer/autoload_static.php | 13 + .../Bootstrap/RegistrationContext.php | 24 ++ lib/private/Mail/Provider/Manager.php | 255 +++++++++++++ lib/private/Server.php | 3 + .../Bootstrap/IRegistrationContext.php | 11 + lib/public/Mail/Provider/Address.php | 85 +++++ lib/public/Mail/Provider/Attachment.php | 139 +++++++ .../Mail/Provider/Exception/Exception.php | 18 + .../Mail/Provider/Exception/SendException.php | 18 + lib/public/Mail/Provider/IAddress.php | 61 ++++ lib/public/Mail/Provider/IAttachment.php | 101 ++++++ lib/public/Mail/Provider/IManager.php | 106 ++++++ lib/public/Mail/Provider/IMessage.php | 232 ++++++++++++ lib/public/Mail/Provider/IMessageSend.php | 31 ++ lib/public/Mail/Provider/IProvider.php | 94 +++++ lib/public/Mail/Provider/IService.php | 119 ++++++ lib/public/Mail/Provider/Message.php | 338 ++++++++++++++++++ tests/lib/Mail/Provider/AddressTest.php | 46 +++ tests/lib/Mail/Provider/AttachmentTest.php | 71 ++++ tests/lib/Mail/Provider/ManagerTest.php | 189 ++++++++++ tests/lib/Mail/Provider/MessageTest.php | 163 +++++++++ 25 files changed, 2320 insertions(+), 31 deletions(-) create mode 100644 lib/private/Mail/Provider/Manager.php create mode 100644 lib/public/Mail/Provider/Address.php create mode 100644 lib/public/Mail/Provider/Attachment.php create mode 100644 lib/public/Mail/Provider/Exception/Exception.php create mode 100644 lib/public/Mail/Provider/Exception/SendException.php create mode 100644 lib/public/Mail/Provider/IAddress.php create mode 100644 lib/public/Mail/Provider/IAttachment.php create mode 100644 lib/public/Mail/Provider/IManager.php create mode 100644 lib/public/Mail/Provider/IMessage.php create mode 100644 lib/public/Mail/Provider/IMessageSend.php create mode 100644 lib/public/Mail/Provider/IProvider.php create mode 100644 lib/public/Mail/Provider/IService.php create mode 100644 lib/public/Mail/Provider/Message.php create mode 100644 tests/lib/Mail/Provider/AddressTest.php create mode 100644 tests/lib/Mail/Provider/AttachmentTest.php create mode 100644 tests/lib/Mail/Provider/ManagerTest.php create mode 100644 tests/lib/Mail/Provider/MessageTest.php diff --git a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php index ef506d1593c..1958531630a 100644 --- a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php +++ b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php @@ -15,6 +15,8 @@ use OCP\Defaults; 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; @@ -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, @@ -63,7 +66,8 @@ class IMipPlugin extends SabreIMipPlugin { Defaults $defaults, IUserSession $userSession, IMipService $imipService, - EventComparisonService $eventComparisonService) { + EventComparisonService $eventComparisonService, + IMailManager $mailManager) { parent::__construct(''); $this->userSession = $userSession; $this->config = $config; @@ -73,6 +77,7 @@ class IMipPlugin extends SabreIMipPlugin { $this->defaults = $defaults; $this->imipService = $imipService; $this->eventComparisonService = $eventComparisonService; + $this->mailManager = $mailManager; } public function initialize(DAV\Server $server): void { @@ -212,21 +217,6 @@ class IMipPlugin extends SabreIMipPlugin { $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(); @@ -268,18 +258,60 @@ class IMipPlugin extends SabreIMipPlugin { } $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) { + // 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]) + ); + $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)]); diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index 9e6d601649f..4fdd70d05c7 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -292,7 +292,8 @@ class Server { \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()); diff --git a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php index eb6bd204bdd..104f7cd7f51 100644 --- a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php +++ b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php @@ -18,6 +18,10 @@ use OCP\Mail\IAttachment; 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; @@ -26,6 +30,11 @@ use Sabre\VObject\ITip\Message; 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 */ @@ -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); @@ -90,10 +108,6 @@ class IMipPluginTest extends TestCase { $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') @@ -107,6 +121,12 @@ class IMipPluginTest extends TestCase { $this->eventComparisonService = $this->createMock(EventComparisonService::class); + $this->mailManager = $this->createMock(IMailManager::class); + + $this->mailService = $this->createMock(IMailServiceMock::class); + + $this->mailMessageNew = $this->createMock(IMailMessageNew::class); + $this->plugin = new IMipPlugin( $this->config, $this->mailer, @@ -115,7 +135,8 @@ class IMipPluginTest extends TestCase { $this->defaults, $this->userSession, $this->service, - $this->eventComparisonService + $this->eventComparisonService, + $this->mailManager, ); } @@ -582,6 +603,111 @@ class IMipPluginTest extends TestCase { $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'; diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 7a2c2c6d227..379a1edbe63 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -568,6 +568,18 @@ return array( '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', @@ -1618,6 +1630,7 @@ return array( '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', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 2f733673312..41109fd5f3d 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -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', @@ -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', diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index df03d59ebfa..f59d5b55706 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -26,6 +26,7 @@ use OCP\Dashboard\IWidget; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Template\ICustomTemplateProvider; use OCP\Http\WellKnown\IHandler; +use OCP\Mail\Provider\IProvider as IMailProvider; use OCP\Notification\INotifier; use OCP\Profile\ILinkAction; use OCP\Search\IProvider; @@ -148,6 +149,9 @@ class RegistrationContext { /** @var ServiceRegistration<\OCP\TaskProcessing\ITaskType>[] */ private array $taskProcessingTaskTypes = []; + + /** @var ServiceRegistration[] */ + private $mailProviders = []; public function __construct(LoggerInterface $logger) { $this->logger = $logger; @@ -411,6 +415,13 @@ class RegistrationContext { $taskProcessingTaskTypeClass ); } + + public function registerMailProvider(string $class): void { + $this->context->registerMailProvider( + $this->appId, + $class + ); + } }; } @@ -603,6 +614,12 @@ class RegistrationContext { public function registerTaskProcessingTaskType(string $appId, string $taskProcessingTaskTypeClass) { $this->taskProcessingTaskTypes[] = new ServiceRegistration($appId, $taskProcessingTaskTypeClass); } + /** + * @psalm-param class-string $migratorClass + */ + public function registerMailProvider(string $appId, string $class): void { + $this->mailProviders[] = new ServiceRegistration($appId, $class); + } /** * @param App[] $apps @@ -948,4 +965,11 @@ class RegistrationContext { public function getTaskProcessingTaskTypes(): array { return $this->taskProcessingTaskTypes; } + + /** + * @return ServiceRegistration[] + */ + public function getMailProviders(): array { + return $this->mailProviders; + } } diff --git a/lib/private/Mail/Provider/Manager.php b/lib/private/Mail/Provider/Manager.php new file mode 100644 index 00000000000..244aa86d68d --- /dev/null +++ b/lib/private/Mail/Provider/Manager.php @@ -0,0 +1,255 @@ +providers()); + + } + + /** + * Retrieve a count of how many mail providers are registered + * + * @since 30.0.0 + * + * @return int + */ + public function count(): int { + + // return count of providers in collection + return count($this->providers()); + + } + + /** + * Retrieve which mail providers are registered + * + * @since 30.0.0 + * + * @return array collection of provider id and label ['jmap' => 'JMap Connector'] + */ + public function types(): array { + + // construct types collection + $types = []; + // extract id and name from providers collection + foreach ($this->providers() as $entry) { + $types[$entry->id()] = $entry->label(); + } + // return types collection + return $types; + + } + + /** + * Retrieve all registered mail providers + * + * @since 30.0.0 + * + * @return array collection of provider id and object ['jmap' => IProviderObject] + */ + public function providers(): array { + + // evaluate if we already have a cached collection of providers and return the collection if we do + if (is_array($this->providersCollection)) { + return $this->providersCollection; + } + // retrieve server registration context + $context = $this->coordinator->getRegistrationContext(); + // evaluate if registration context was returned + if ($context === null) { + return []; + } + // initilize cached collection + $this->providersCollection = []; + // iterate through all registered mail providers + foreach ($context->getMailProviders() as $entry) { + try { + /** @var IProvider $provider */ + // object provider + $provider = $this->container->get($entry->getService()); + // add provider to cache collection + $this->providersCollection[$provider->id()] = $provider; + } catch (Throwable $e) { + $this->logger->error( + 'Could not load mail provider ' . $entry->getService() . ': ' . $e->getMessage(), + ['exception' => $e] + ); + } + } + // return mail provider collection + return $this->providersCollection; + + } + + /** + * Retrieve a provider with a specific id + * + * @since 30.0.0 + * + * @param string $providerId provider id + * + * @return IProvider|null + */ + public function findProviderById(string $providerId): IProvider | null { + + // evaluate if we already have a cached collection of providers + if (!is_array($this->providersCollection)) { + $this->providers(); + } + + if (isset($this->providersCollection[$providerId])) { + return $this->providersCollection[$providerId]; + } + // return null if provider was not found + return null; + + } + + /** + * Retrieve all services for all registered mail providers + * + * @since 30.0.0 + * + * @param string $userId user id + * + * @return array> collection of provider id, service id and object ['jmap' => ['Service1' => IServiceObject]] + */ + public function services(string $userId): array { + + // initilize collection + $services = []; + // retrieve and iterate through mail providers + foreach ($this->providers() as $entry) { + // retrieve collection of services + $mailServices = $entry->listServices($userId); + // evaluate if mail services collection is not empty and add results to services collection + if (!empty($mailServices)) { + $services[$entry->id()] = $mailServices; + } + } + // return collection + return $services; + + } + + /** + * Retrieve a service with a specific id + * + * @since 30.0.0 + * + * @param string $userId user id + * @param string $serviceId service id + * @param string $providerId provider id + * + * @return IService|null returns service object or null if none found + */ + public function findServiceById(string $userId, string $serviceId, ?string $providerId = null): IService | null { + + // evaluate if provider id was specified + if ($providerId !== null) { + // find provider + $provider = $this->findProviderById($providerId); + // evaluate if provider was found + if ($provider instanceof IProvider) { + // find service with specific id + $service = $provider->findServiceById($userId, $serviceId); + // evaluate if mail service was found + if ($service instanceof IService) { + return $service; + } + } + } else { + // retrieve and iterate through mail providers + foreach ($this->providers() as $provider) { + // find service with specific id + $service = $provider->findServiceById($userId, $serviceId); + // evaluate if mail service was found + if ($service instanceof IService) { + return $service; + } + } + } + + // return null if no match was found + return null; + + } + + /** + * Retrieve a service for a specific mail address + * returns first service with specific primary address + * + * @since 30.0.0 + * + * @param string $userId user id + * @param string $address mail address (e.g. test@example.com) + * @param string $providerId provider id + * + * @return IService|null returns service object or null if none found + */ + public function findServiceByAddress(string $userId, string $address, ?string $providerId = null): IService | null { + + // evaluate if provider id was specified + if ($providerId !== null) { + // find provider + $provider = $this->findProviderById($providerId); + // evaluate if provider was found + if ($provider instanceof IProvider) { + // find service with specific mail address + $service = $provider->findServiceByAddress($userId, $address); + // evaluate if mail service was found + if ($service instanceof IService) { + return $service; + } + } + } else { + // retrieve and iterate through mail providers + foreach ($this->providers() as $provider) { + // find service with specific mail address + $service = $provider->findServiceByAddress($userId, $address); + // evaluate if mail service was found + if ($service instanceof IService) { + return $service; + } + } + } + // return null if no match was found + return null; + + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index cfbb6dc317f..3d156454920 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -1026,6 +1026,9 @@ class Server extends ServerContainer implements IServerContainer { /** @deprecated 19.0.0 */ $this->registerDeprecatedAlias('Mailer', IMailer::class); + /** @since 30.0.0 */ + $this->registerAlias(\OCP\Mail\Provider\IManager::class, \OC\Mail\Provider\Manager::class); + /** @deprecated 21.0.0 */ $this->registerDeprecatedAlias('LDAPProvider', ILDAPProvider::class); diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index b86f7bcd76d..57e76f268d9 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -17,6 +17,7 @@ use OCP\Collaboration\Reference\IReferenceProvider; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Template\ICustomTemplateProvider; use OCP\IContainer; +use OCP\Mail\Provider\IProvider as IMailProvider; use OCP\Notification\INotifier; use OCP\Preview\IProviderV2; use OCP\SpeechToText\ISpeechToTextProvider; @@ -412,4 +413,14 @@ interface IRegistrationContext { * @since 30.0.0 */ public function registerTaskProcessingTaskType(string $taskProcessingTaskTypeClass): void; + + /** + * Register a mail provider + * + * @param string $class + * @psalm-param class-string $class + * @since 30.0.0 + */ + public function registerMailProvider(string $class): void; + } diff --git a/lib/public/Mail/Provider/Address.php b/lib/public/Mail/Provider/Address.php new file mode 100644 index 00000000000..9cd2859a8a5 --- /dev/null +++ b/lib/public/Mail/Provider/Address.php @@ -0,0 +1,85 @@ +address = $value; + return $this; + } + + /** + * gets the mail address + * + * @since 30.0.0 + * + * @return string|null returns the mail address or null if one is not set + */ + public function getAddress(): string | null { + return $this->address; + } + + /** + * sets the mail address label/name + * + * @since 30.0.0 + * + * @param string $value mail address label/name + * + * @return self return this object for command chaining + */ + public function setLabel(string $value): self { + $this->label = $value; + return $this; + } + + /** + * gets the mail address label/name + * + * @since 30.0.0 + * + * @return string|null returns the mail address label/name or null if one is not set + */ + public function getLabel(): string | null { + return $this->label; + } + +} diff --git a/lib/public/Mail/Provider/Attachment.php b/lib/public/Mail/Provider/Attachment.php new file mode 100644 index 00000000000..d7790a3bbc6 --- /dev/null +++ b/lib/public/Mail/Provider/Attachment.php @@ -0,0 +1,139 @@ +name = $value; + return $this; + } + + /** + * gets the attachment file name + * + * @since 30.0.0 + * + * @return string | null returns the attachment file name or null if not set + */ + public function getName(): string | null { + return $this->name; + } + + /** + * sets the attachment mime type + * + * @since 30.0.0 + * + * @param string $value mime type (e.g. text/plain) + * + * @return self return this object for command chaining + */ + public function setType(string $value): self { + $this->type = $value; + return $this; + } + + /** + * gets the attachment mime type + * + * @since 30.0.0 + * + * @return string | null returns the attachment mime type or null if not set + */ + public function getType(): string | null { + return $this->type; + } + + /** + * sets the attachment contents (actual data) + * + * @since 30.0.0 + * + * @param string $value binary contents of file + * + * @return self return this object for command chaining + */ + public function setContents(string $value): self { + $this->contents = $value; + return $this; + } + + /** + * gets the attachment contents (actual data) + * + * @since 30.0.0 + * + * @return string | null returns the attachment contents or null if not set + */ + public function getContents(): string | null { + return $this->contents; + } + + /** + * sets the embedded status of the attachment + * + * @since 30.0.0 + * + * @param bool $value true - embedded / false - not embedded + * + * @return self return this object for command chaining + */ + public function setEmbedded(bool $value): self { + $this->embedded = $value; + return $this; + } + + /** + * gets the embedded status of the attachment + * + * @since 30.0.0 + * + * @return bool embedded status of the attachment + */ + public function getEmbedded(): bool { + return $this->embedded; + } + +} diff --git a/lib/public/Mail/Provider/Exception/Exception.php b/lib/public/Mail/Provider/Exception/Exception.php new file mode 100644 index 00000000000..7514c72a869 --- /dev/null +++ b/lib/public/Mail/Provider/Exception/Exception.php @@ -0,0 +1,18 @@ + collection of provider id and label ['jmap' => 'JMap Connector'] + */ + public function types(): array; + + /** + * retrieve all registered mail providers + * + * @since 30.0.0 + * + * @return array collection of provider id and object ['jmap' => IProviderObject] + */ + public function providers(): array; + + /** + * retrieve a provider with a specific id + * + * @since 30.0.0 + * + * @param string $providerId provider id + * + * @return IProvider|null + */ + public function findProviderById(string $providerId): IProvider | null; + + /** + * retrieve all services for all registered mail providers + * + * @since 30.0.0 + * + * @param string $userId user id + * + * @return array> collection of provider id, service id and object ['jmap' => ['Service1' => IServiceObject]] + */ + public function services(string $userId): array; + + /** + * retrieve a service with a specific id + * + * @since 30.0.0 + * + * @param string $userId user id + * @param string $serviceId service id + * @param string $providerId provider id + * + * @return IService|null returns service object or null if none found + */ + public function findServiceById(string $userId, string $serviceId, ?string $providerId = null): IService | null; + + /** + * retrieve a service for a specific mail address + * returns first service with specific primary address + * + * @since 30.0.0 + * + * @param string $userId user id + * @param string $address mail address (e.g. test@example.com) + * @param string $providerId provider id + * + * @return IService|null returns service object or null if none found + */ + public function findServiceByAddress(string $userId, string $address, ?string $providerId = null): IService | null; + +} diff --git a/lib/public/Mail/Provider/IMessage.php b/lib/public/Mail/Provider/IMessage.php new file mode 100644 index 00000000000..71021981362 --- /dev/null +++ b/lib/public/Mail/Provider/IMessage.php @@ -0,0 +1,232 @@ + collection of all recipient mail address objects + */ + public function getTo(): array; + + /** + * sets the copy to recipient(s) of this message + * + * @since 30.0.0 + * + * @param IAddress ...$value collection of or one or more mail address objects + * + * @return self return this object for command chaining + */ + public function setCc(IAddress ...$value): self; + + /** + * gets the copy to recipient(s) of this message + * + * @since 30.0.0 + * + * @param array collection of all copied recipient mail address objects + */ + public function getCc(): array; + + /** + * sets the blind copy to recipient(s) of this message + * + * @since 30.0.0 + * + * @param IAddress ...$value collection of or one or more mail address objects + * + * @return self return this object for command chaining + */ + public function setBcc(IAddress ...$value): self; + + /** + * gets the blind copy to recipient(s) of this message + * + * @since 30.0.0 + * + * @param array collection of all blind copied recipient mail address objects + */ + public function getBcc(): array; + + /** + * sets the subject of this message + * + * @since 30.0.0 + * + * @param string $value subject of mail message + * + * @return self return this object for command chaining + */ + public function setSubject(string $value): self; + + /** + * gets the subject of this message + * + * @since 30.0.0 + * + * @param string|null subject of message or null if one is not set + */ + public function getSubject(): string | null; + + /** + * sets the plain text or html body of this message + * + * @since 30.0.0 + * + * @param string $value text or html body of message + * @param bool $html html flag - true for html + * + * @return self return this object for command chaining + */ + public function setBody(string $value, bool $html): self; + + /** + * gets either the html or plain text body of this message + * + * html body will be returned over plain text if html body exists + * + * @since 30.0.0 + * + * @param string|null html/plain body of this message or null if one is not set + */ + public function getBody(): string | null; + + /** + * sets the html body of this message + * + * @since 30.0.0 + * + * @param string $value html body of message + * + * @return self return this object for command chaining + */ + public function setBodyHtml(string $value): self; + + /** + * gets the html body of this message + * + * @since 30.0.0 + * + * @param string|null html body of this message or null if one is not set + */ + public function getBodyHtml(): string | null; + + /** + * sets the plain text body of this message + * + * @since 30.0.0 + * + * @param string $value plain text body of message + * + * @return self return this object for command chaining + */ + public function setBodyPlain(string $value): self; + + /** + * gets the plain text body of this message + * + * @since 30.0.0 + * + * @param string|null plain text body of this message or null if one is not set + */ + public function getBodyPlain(): string | null; + + /** + * sets the attachments of this message + * + * @since 30.0.0 + * + * @param IAttachment ...$value collection of or one or more mail attachment objects + * + * @return self return this object for command chaining + */ + public function setAttachments(IAttachment ...$value): self; + + /** + * gets the attachments of this message + * + * @since 30.0.0 + * + * @return array collection of all mail attachment objects + */ + public function getAttachments(): array; +} diff --git a/lib/public/Mail/Provider/IMessageSend.php b/lib/public/Mail/Provider/IMessageSend.php new file mode 100644 index 00000000000..e68f18cf95c --- /dev/null +++ b/lib/public/Mail/Provider/IMessageSend.php @@ -0,0 +1,31 @@ + collection of service id and object ['1' => IServiceObject] + */ + public function listServices(string $userId): array; + + /** + * retrieve a service with a specific id + * + * @since 30.0.0 + * + * @param string $userId user id + * @param string $serviceId service id + * + * @return IService|null returns service object or null if none found + */ + public function findServiceById(string $userId, string $serviceId): IService | null; + + /** + * retrieve a service for a specific mail address + * + * @since 30.0.0 + * + * @param string $userId user id + * @param string $address mail address (e.g. test@example.com) + * + * @return IService|null returns service object or null if none found + */ + public function findServiceByAddress(string $userId, string $address): IService | null; + + /** + * construct a new empty service object + * + * @since 30.0.0 + * + * @return IService blank service object + */ + public function initiateService(): IService; + +} diff --git a/lib/public/Mail/Provider/IService.php b/lib/public/Mail/Provider/IService.php new file mode 100644 index 00000000000..a8fd73749bb --- /dev/null +++ b/lib/public/Mail/Provider/IService.php @@ -0,0 +1,119 @@ + collection of mail address objects + */ + public function getSecondaryAddresses(): array; + + /** + * sets the secondary mailing addresses (aliases) for this service + * + * @since 30.0.0 + * + * @param IAddress ...$value collection of one or more mail address objects + * + * @return self return this object for command chaining + */ + public function setSecondaryAddresses(IAddress ...$value): self; + + /** + * construct a new empty message object + * + * @since 30.0.0 + * + * @return IMessage blank message object + */ + public function initiateMessage(): IMessage; + +} diff --git a/lib/public/Mail/Provider/Message.php b/lib/public/Mail/Provider/Message.php new file mode 100644 index 00000000000..58638963640 --- /dev/null +++ b/lib/public/Mail/Provider/Message.php @@ -0,0 +1,338 @@ +data['id'])) ? $this->data['id'] : ''; + } + + /** + * sets the sender of this message + * + * @since 30.0.0 + * + * @param IAddress $value sender's mail address object + * + * @return self return this object for command chaining + */ + public function setFrom(IAddress $value): self { + // create or update field in data store with value + $this->data['from'] = $value; + // return this object for command chaining + return $this; + } + + /** + * gets the sender of this message + * + * @since 30.0.0 + * + * @param IAddress|null sender's mail address object + */ + public function getFrom(): IAddress | null { + // evaluate if data store field exists and return value(s) or null otherwise + return (isset($this->data['from'])) ? $this->data['from'] : null; + } + + /** + * sets the sender's reply to address of this message + * + * @since 30.0.0 + * + * @param IAddress $value senders's reply to mail address object + * + * @return self return this object for command chaining + */ + public function setReplyTo(IAddress $value): self { + // create or update field in data store with value + $this->data['replyTo'] = $value; + // return this object for command chaining + return $this; + } + + /** + * gets the sender's reply to address of this message + * + * @since 30.0.0 + * + * @param IAddress|null sender's reply to mail address object + */ + public function getReplyTo(): IAddress | null { + // evaluate if data store field exists and return value(s) or null otherwise + return (isset($this->data['replyTo'])) ? $this->data['replyTo'] : null; + } + + /** + * sets the recipient(s) of this message + * + * @since 30.0.0 + * + * @param IAddress ...$value collection of or one or more mail address objects + * + * @return self return this object for command chaining + */ + public function setTo(IAddress ...$value): self { + // create or update field in data store with value + $this->data['to'] = $value; + // return this object for command chaining + return $this; + } + + /** + * gets the recipient(s) of this message + * + * @since 30.0.0 + * + * @param array collection of all recipient mail address objects + */ + public function getTo(): array { + // evaluate if data store field exists and return value(s) or empty collection + return (isset($this->data['to'])) ? $this->data['to'] : []; + } + + /** + * sets the copy to recipient(s) of this message + * + * @since 30.0.0 + * + * @param IAddress ...$value collection of or one or more mail address objects + * + * @return self return this object for command chaining + */ + public function setCc(IAddress ...$value): self { + // create or update field in data store with value + $this->data['cc'] = $value; + // return this object for command chaining + return $this; + } + + /** + * gets the copy to recipient(s) of this message + * + * @since 30.0.0 + * + * @param array collection of all copied recipient mail address objects + */ + public function getCc(): array { + // evaluate if data store field exists and return value(s) or empty collection + return (isset($this->data['cc'])) ? $this->data['cc'] : []; + } + + /** + * sets the blind copy to recipient(s) of this message + * + * @since 30.0.0 + * + * @param IAddress ...$value collection of or one or more mail address objects + * + * @return self return this object for command chaining + */ + public function setBcc(IAddress ...$value): self { + // create or update field in data store with value + $this->data['bcc'] = $value; + // return this object for command chaining + return $this; + } + + /** + * gets the blind copy to recipient(s) of this message + * + * @since 30.0.0 + * + * @param array collection of all blind copied recipient mail address objects + */ + public function getBcc(): array { + // evaluate if data store field exists and return value(s) or empty collection + return (isset($this->data['bcc'])) ? $this->data['bcc'] : []; + } + + /** + * sets the subject of this message + * + * @since 30.0.0 + * + * @param string $value subject of mail message + * + * @return self return this object for command chaining + */ + public function setSubject(string $value): self { + // create or update field in data store with value + $this->data['subject'] = $value; + // return this object for command chaining + return $this; + } + + /** + * gets the subject of this message + * + * @since 30.0.0 + * + * @param string|null subject of message or null if one is not set + */ + public function getSubject(): string | null { + // evaluate if data store field exists and return value(s) or null otherwise + return (isset($this->data['subject'])) ? $this->data['subject'] : null; + } + + /** + * sets the plain text or html body of this message + * + * @since 30.0.0 + * + * @param string $value text or html body of message + * @param bool $html html flag - true for html + * + * @return self return this object for command chaining + */ + public function setBody(string $value, bool $html = false): self { + // evaluate html flag and create or update appropriate field in data store with value + if ($html) { + $this->data['bodyHtml'] = $value; + } else { + $this->data['bodyPlain'] = $value; + } + // return this object for command chaining + return $this; + } + + /** + * gets either the html or plain text body of this message + * + * html body will be returned over plain text if html body exists + * + * @since 30.0.0 + * + * @param string|null html/plain body of this message or null if one is not set + */ + public function getBody(): string | null { + // evaluate if data store field(s) exists and return value + if (isset($this->data['bodyHtml'])) { + return $this->data['bodyHtml']; + } elseif (isset($this->data['bodyPlain'])) { + return $this->data['bodyPlain']; + } + // return null if data fields did not exist in data store + return null; + } + + /** + * sets the html body of this message + * + * @since 30.0.0 + * + * @param string $value html body of message + * + * @return self return this object for command chaining + */ + public function setBodyHtml(string $value): self { + // create or update field in data store with value + $this->data['bodyHtml'] = $value; + // return this object for command chaining + return $this; + } + + /** + * gets the html body of this message + * + * @since 30.0.0 + * + * @param string|null html body of this message or null if one is not set + */ + public function getBodyHtml(): string | null { + // evaluate if data store field exists and return value(s) or null otherwise + return (isset($this->data['bodyHtml'])) ? $this->data['bodyHtml'] : null; + } + + /** + * sets the plain text body of this message + * + * @since 30.0.0 + * + * @param string $value plain text body of message + * + * @return self return this object for command chaining + */ + public function setBodyPlain(string $value): self { + // create or update field in data store with value + $this->data['bodyPlain'] = $value; + // return this object for command chaining + return $this; + } + + /** + * gets the plain text body of this message + * + * @since 30.0.0 + * + * @param string|null plain text body of this message or null if one is not set + */ + public function getBodyPlain(): string | null { + // evaluate if data store field exists and return value(s) or null otherwise + return (isset($this->data['bodyPlain'])) ? $this->data['bodyPlain'] : null; + } + + /** + * sets the attachments of this message + * + * @since 30.0.0 + * + * @param IAttachment ...$value collection of or one or more mail attachment objects + * + * @return self return this object for command chaining + */ + public function setAttachments(IAttachment ...$value): self { + // create or update field in data store with value + $this->data['attachments'] = $value; + // return this object for command chaining + return $this; + } + + /** + * gets the attachments of this message + * + * @since 30.0.0 + * + * @return array collection of all mail attachment objects + */ + public function getAttachments(): array { + // evaluate if data store field exists and return value(s) or null otherwise + return (isset($this->data['attachments'])) ? $this->data['attachments'] : []; + } + +} diff --git a/tests/lib/Mail/Provider/AddressTest.php b/tests/lib/Mail/Provider/AddressTest.php new file mode 100644 index 00000000000..ee03f6f1e83 --- /dev/null +++ b/tests/lib/Mail/Provider/AddressTest.php @@ -0,0 +1,46 @@ +address = new Address('user1@testing.com', 'User One'); + + } + + public function testAddress(): void { + + // test set by constructor + $this->assertEquals('user1@testing.com', $this->address->getAddress()); + // test set by setter + $this->address->setAddress('user2@testing.com'); + $this->assertEquals('user2@testing.com', $this->address->getAddress()); + + } + + public function testLabel(): void { + + // test set by constructor + $this->assertEquals('User One', $this->address->getLabel()); + // test set by setter + $this->address->setLabel('User Two'); + $this->assertEquals('User Two', $this->address->getLabel()); + + } + +} diff --git a/tests/lib/Mail/Provider/AttachmentTest.php b/tests/lib/Mail/Provider/AttachmentTest.php new file mode 100644 index 00000000000..283391650b5 --- /dev/null +++ b/tests/lib/Mail/Provider/AttachmentTest.php @@ -0,0 +1,71 @@ +attachment = new Attachment( + 'This is the contents of a file', + 'example1.txt', + 'text/plain', + false + ); + + } + + public function testName(): void { + + // test set by constructor + $this->assertEquals('example1.txt', $this->attachment->getName()); + // test set by setter + $this->attachment->setName('example2.txt'); + $this->assertEquals('example2.txt', $this->attachment->getName()); + + } + + public function testType(): void { + + // test set by constructor + $this->assertEquals('text/plain', $this->attachment->getType()); + // test set by setter + $this->attachment->setType('text/html'); + $this->assertEquals('text/html', $this->attachment->getType()); + + } + + public function testContents(): void { + + // test set by constructor + $this->assertEquals('This is the contents of a file', $this->attachment->getContents()); + // test set by setter + $this->attachment->setContents('This is the modified contents of a file'); + $this->assertEquals('This is the modified contents of a file', $this->attachment->getContents()); + + } + + public function testEmbedded(): void { + + // test set by constructor + $this->assertEquals(false, $this->attachment->getEmbedded()); + // test set by setter + $this->attachment->setEmbedded(true); + $this->assertEquals(true, $this->attachment->getEmbedded()); + + } + +} diff --git a/tests/lib/Mail/Provider/ManagerTest.php b/tests/lib/Mail/Provider/ManagerTest.php new file mode 100644 index 00000000000..2658c324521 --- /dev/null +++ b/tests/lib/Mail/Provider/ManagerTest.php @@ -0,0 +1,189 @@ +logger = $this->createMock(LoggerInterface::class); + + // construct service registration + $registration = $this->createMock(ServiceRegistration::class); + $registration + ->method('getService') + ->willReturn('Mock\Provider\MailProvider'); + // construct registration context + $context = $this->createMock(RegistrationContext::class); + $context + ->method('getMailProviders') + ->willReturn([$registration]); + // construct coordinator + $this->coordinator = $this->createMock(Coordinator::class); + $this->coordinator + ->method('getRegistrationContext') + ->willReturn($context); + + // construct mail service + $this->service = $this->createMock(IService::class); + $this->service + ->method('id') + ->willReturn('100'); + $this->service + ->method('getLabel') + ->willReturn('Mock Mail Service'); + $this->service + ->method('getPrimaryAddress') + ->willReturn((new Address('user1@testing.com', 'User One'))); + // construct mail provider + $this->provider = $this->createMock(IProvider::class); + $this->provider + ->method('id') + ->willReturn('mock-provider'); + $this->provider + ->method('label') + ->willReturn('Mock Provider'); + $this->provider + ->method('listServices') + ->willReturnMap([ + ['user0', []], + ['user1', [$this->service->id() => $this->service]] + ]); + $this->provider + ->method('findServiceById') + ->willReturnMap([ + ['user0', '100', null], + ['user1', '100', $this->service] + ]); + $this->provider + ->method('findServiceByAddress') + ->willReturnMap([ + ['user0', 'user0@testing.com', null], + ['user1', 'user1@testing.com', $this->service] + ]); + // construct container interface + $this->container = $this->createMock(ContainerInterface::class); + $this->container + ->method('get') + ->willReturnMap([ + ['Mock\Provider\MailProvider', $this->provider] + ]); + + } + + public function testHas(): void { + + // construct mail manager + $manager = new Manager($this->coordinator, $this->container, $this->logger); + // test result with providers found + $this->assertTrue($manager->has()); + + } + + public function testCount(): void { + + // construct mail manager + $manager = new Manager($this->coordinator, $this->container, $this->logger); + // test result with providers found + $this->assertGreaterThan(0, $manager->count()); + + } + + public function testTypes(): void { + + // construct mail manager + $manager = new Manager($this->coordinator, $this->container, $this->logger); + // test result with providers found + $this->assertEquals(['mock-provider' => 'Mock Provider'], $manager->types()); + + } + + public function testProviders(): void { + + // construct mail manager + $manager = new Manager($this->coordinator, $this->container, $this->logger); + // test result with providers found + $this->assertEquals([$this->provider->id() => $this->provider], $manager->providers()); + + } + + public function testFindProviderById(): void { + + // construct mail manager + $manager = new Manager($this->coordinator, $this->container, $this->logger); + // test result with providers found + $this->assertEquals($this->provider, $manager->findProviderById($this->provider->id())); + + } + + public function testServices(): void { + + // construct mail manager + $manager = new Manager($this->coordinator, $this->container, $this->logger); + // test result with no services found + $this->assertEquals([], $manager->services('user0')); + // test result with services found + $this->assertEquals([$this->provider->id() => [$this->service->id() => $this->service]], $manager->services('user1')); + + } + + public function testFindServiceById(): void { + + // construct mail manager + $manager = new Manager($this->coordinator, $this->container, $this->logger); + // test result with no services found and not provider specified + $this->assertEquals(null, $manager->findServiceById('user0', '100')); + // test result with no services found and provider specified + $this->assertEquals(null, $manager->findServiceById('user0', '100', $this->provider->id())); + // test result with services found and not provider specified + $this->assertEquals($this->service, $manager->findServiceById('user1', '100')); + // test result with services found and provider specified + $this->assertEquals($this->service, $manager->findServiceById('user1', '100', $this->provider->id())); + + } + + public function testFindServiceByAddress(): void { + + // construct mail manager + $manager = new Manager($this->coordinator, $this->container, $this->logger); + // test result with no services found and not provider specified + $this->assertEquals(null, $manager->findServiceByAddress('user0', 'user0@testing.com')); + // test result with no services found and provider specified + $this->assertEquals(null, $manager->findServiceByAddress('user0', 'user0@testing.com', $this->provider->id())); + // test result with services found and not provider specified + $this->assertEquals($this->service, $manager->findServiceByAddress('user1', 'user1@testing.com')); + // test result with services found and provider specified + $this->assertEquals($this->service, $manager->findServiceByAddress('user1', 'user1@testing.com', $this->provider->id())); + + } + +} diff --git a/tests/lib/Mail/Provider/MessageTest.php b/tests/lib/Mail/Provider/MessageTest.php new file mode 100644 index 00000000000..1791a03421c --- /dev/null +++ b/tests/lib/Mail/Provider/MessageTest.php @@ -0,0 +1,163 @@ +message = new Message( + ['id' => 'cd02ea42-feac-4863-b9d8-484d16a587ea'] + ); + $this->address1 = new Address( + 'user1@testing.com', + 'User One' + ); + $this->address2 = new Address( + 'user2@testing.com', + 'User Two' + ); + $this->attachment1 = new Attachment( + 'This is the contents of the first attachment', + 'example1.txt', + 'text/plain', + false + ); + $this->attachment2 = new Attachment( + 'This is the contents of the second attachment', + 'example1.txt', + 'text/plain', + false + ); + + } + + public function testId(): void { + + // test set by constructor + $this->assertEquals('cd02ea42-feac-4863-b9d8-484d16a587ea', $this->message->id()); + + } + + public function testFrom(): void { + + // test not set + $this->assertNull($this->message->getFrom()); + // test set by setter + $this->message->setFrom($this->address1); + $this->assertEquals($this->address1, $this->message->getFrom()); + + } + + public function testReplyTo(): void { + + // test not set + $this->assertNull($this->message->getReplyTo()); + // test set by setter + $this->message->setReplyTo($this->address1); + $this->assertEquals($this->address1, $this->message->getReplyTo()); + + } + + public function testTo(): void { + + // test not set + $this->assertEquals([], $this->message->getTo()); + // test set by setter single + $this->message->setTo($this->address1); + $this->assertEquals([$this->address1], $this->message->getTo()); + // test set by setter multiple + $this->message->setTo($this->address1, $this->address2); + $this->assertEquals([$this->address1, $this->address2], $this->message->getTo()); + + } + + public function testCc(): void { + + // test not set + $this->assertEquals([], $this->message->getCc()); + // test set by setter single + $this->message->setCc($this->address1); + $this->assertEquals([$this->address1], $this->message->getCc()); + // test set by setter multiple + $this->message->setCc($this->address1, $this->address2); + $this->assertEquals([$this->address1, $this->address2], $this->message->getCc()); + + } + + public function testBcc(): void { + + // test not set + $this->assertEquals([], $this->message->getBcc()); + // test set by setter single + $this->message->setBcc($this->address1); + $this->assertEquals([$this->address1], $this->message->getBcc()); + // test set by setter multiple + $this->message->setBcc($this->address1, $this->address2); + $this->assertEquals([$this->address1, $this->address2], $this->message->getBcc()); + + } + + public function testSubject(): void { + + // test not set + $this->assertNull($this->message->getSubject()); + // test set by setter + $this->message->setSubject('Testing Mail Subject'); + $this->assertEquals('Testing Mail Subject', $this->message->getSubject()); + + } + + public function testBody(): void { + + // test not set + $this->assertNull($this->message->getBody()); + // test set by setter - text body + $this->message->setBody('Testing Text Body', false); + $this->assertEquals('Testing Text Body', $this->message->getBody()); + $this->message->setBodyPlain('Testing Text Body Again', false); + $this->assertEquals('Testing Text Body Again', $this->message->getBodyPlain()); + // test set by setter - html body + $this->message->setBody('Testing HTML Body', true); + $this->assertEquals('Testing HTML Body', $this->message->getBody()); + $this->message->setBodyHtml('Testing HTML Body Again', false); + $this->assertEquals('Testing HTML Body Again', $this->message->getBodyHtml()); + + } + + public function testAttachments(): void { + + // test not set + $this->assertEquals([], $this->message->getAttachments()); + // test set by setter single + $this->message->setAttachments($this->attachment1); + $this->assertEquals([$this->attachment1], $this->message->getAttachments()); + // test set by setter multiple + $this->message->setAttachments($this->attachment1, $this->attachment2); + $this->assertEquals([$this->attachment1, $this->attachment2], $this->message->getAttachments()); + + } +} -- 2.39.5