diff --git a/lib/AppInfo/BootstrapSingleton.php b/lib/AppInfo/BootstrapSingleton.php index 3d838e2c876e0753ce268e040a36abb6f19cba62..cf0afa67d3a5d7c57e035f10c144a4bb68172973 100644 --- a/lib/AppInfo/BootstrapSingleton.php +++ b/lib/AppInfo/BootstrapSingleton.php @@ -40,6 +40,7 @@ use OCA\Mail\Listener\AddressCollectionListener; use OCA\Mail\Listener\DeleteDraftListener; use OCA\Mail\Listener\DraftMailboxCreatorListener; use OCA\Mail\Listener\FlagRepliedMessageListener; +use OCA\Mail\Listener\InteractionListener; use OCA\Mail\Listener\MessageDeletedCacheUpdaterListener; use OCA\Mail\Listener\SaveSentMessageListener; use OCA\Mail\Listener\TrashMailboxCreatorListener; @@ -128,6 +129,7 @@ class BootstrapSingleton { $dispatcher->addServiceListener(MessageSentEvent::class, AddressCollectionListener::class); $dispatcher->addServiceListener(MessageSentEvent::class, DeleteDraftListener::class); $dispatcher->addServiceListener(MessageSentEvent::class, FlagRepliedMessageListener::class); + $dispatcher->addServiceListener(MessageSentEvent::class, InteractionListener::class); $dispatcher->addServiceListener(MessageSentEvent::class, SaveSentMessageListener::class); $dispatcher->addServiceListener(SaveDraftEvent::class, DraftMailboxCreatorListener::class); } diff --git a/lib/Events/MessageSentEvent.php b/lib/Events/MessageSentEvent.php index 9d132bdbdce49d744977ecaa0b5f9b2045d6b83f..c0642e68a8151334940af18a09e570ba83f04ed4 100644 --- a/lib/Events/MessageSentEvent.php +++ b/lib/Events/MessageSentEvent.php @@ -53,7 +53,7 @@ class MessageSentEvent extends Event { public function __construct(Account $account, NewMessageData $newMessageData, ?RepliedMessageData $repliedMessageData, - ?int $draftUid = null, + ?int $draftUid, IMessage $message, Horde_Mime_Mail $mail) { parent::__construct(); diff --git a/lib/Listener/InteractionListener.php b/lib/Listener/InteractionListener.php new file mode 100644 index 0000000000000000000000000000000000000000..ce48b5635a46780ad5bd7c92afeb9cf52e1436a6 --- /dev/null +++ b/lib/Listener/InteractionListener.php @@ -0,0 +1,88 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCA\Mail\Listener; + +use OCA\Mail\Address; +use OCA\Mail\Events\MessageSentEvent; +use OCP\Contacts\Events\ContactInteractedWithEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\EventDispatcher\IEventListener; +use OCP\ILogger; +use OCP\IUserSession; +use function class_exists; + +class InteractionListener implements IEventListener { + + /** @var IEventDispatcher */ + private $dispatcher; + + /** @var IUserSession */ + private $userSession; + + /** @var ILogger */ + private $logger; + + public function __construct(IEventDispatcher $dispatcher, + IUserSession $userSession, + ILogger $logger) { + $this->dispatcher = $dispatcher; + $this->userSession = $userSession; + $this->logger = $logger; + } + + /** + * @inheritDoc + */ + public function handle(Event $event): void { + if (!($event instanceof MessageSentEvent)) { + return; + } + if (!class_exists(ContactInteractedWithEvent::class)) { + $this->logger->debug(ContactInteractedWithEvent::class . ' does not exist, ignoring the event'); + return; + } + if (($user = $this->userSession->getUser()) === null) { + $this->logger->debug('no user object found'); + return; + } + $recipients = $event->getMessage()->getTo() + ->merge($event->getMessage()->getCC()) + ->merge($event->getMessage()->getBCC()); + foreach ($recipients->iterate() as $recipient) { + /** @var Address $recipient */ + $interactionEvent = new ContactInteractedWithEvent($user); + $email = $recipient->getEmail(); + if ($email === null) { + // Weird, bot ok + continue; + } + $interactionEvent->setEmail($email); + $this->dispatcher->dispatch(ContactInteractedWithEvent::class, $interactionEvent); + } + } + +} diff --git a/tests/Unit/Listener/InteractionListenerTest.php b/tests/Unit/Listener/InteractionListenerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..fa8ccb88111718db94701300911898bc504a0c55 --- /dev/null +++ b/tests/Unit/Listener/InteractionListenerTest.php @@ -0,0 +1,106 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OCA\Mail\Tests\Unit\Listener; + +use ChristophWurst\Nextcloud\Testing\ServiceMockObject; +use ChristophWurst\Nextcloud\Testing\TestCase; +use OCA\Mail\Address; +use OCA\Mail\AddressList; +use OCA\Mail\Events\MessageSentEvent; +use OCA\Mail\Listener\InteractionListener; +use OCA\Mail\Model\IMessage; +use OCP\Contacts\Events\ContactInteractedWithEvent; +use OCP\EventDispatcher\Event; +use OCP\IUser; +use function class_exists; + +class InteractionListenerTest extends TestCase { + + /** @var ServiceMockObject */ + private $serviceMock; + + /** @var InteractionListener */ + private $listener; + + protected function setUp(): void { + parent::setUp(); + + $this->serviceMock = $this->createServiceMock(InteractionListener::class); + + $this->listener = $this->serviceMock->getService(); + } + + public function testHandleUnrelated(): void { + $event = new Event(); + + $this->listener->handle($event); + + $this->addToAssertionCount(1); + } + + public function testHandle(): void { + if (!class_exists(ContactInteractedWithEvent::class)) { + $this->markTestSkipped(ContactInteractedWithEvent::class . ' does not exist'); + return; + } + + $to = new AddressList([ + new Address('rec 1', 'u1@domain.tld'), + new Address('rec 1', 'u2@domain.tld'), + ]); + $cc = new AddressList([ + new Address('rec 1', 'u3@domain.tld'), + ]); + $bcc = new AddressList([ + new Address('rec 1', 'u4@domain.tld'), + new Address('rec 1', 'u2@domain.tld'), // intentional duplicate + ]); + $event = $this->createMock(MessageSentEvent::class); + $message = $this->createMock(IMessage::class); + $event + ->method('getMessage') + ->willReturn($message); + $message + ->method('getTo') + ->willReturn($to); + $message + ->method('getCC') + ->willReturn($cc); + $message + ->method('getBCC') + ->willReturn($bcc); + $user = $this->createMock(IUser::class); + $this->serviceMock->getParameter('userSession') + ->method('getUser') + ->willReturn($user); + $this->serviceMock->getParameter('dispatcher') + ->expects($this->exactly(4)) + ->method('dispatch'); + + $this->listener->handle($event); + } + +}