diff --git a/appinfo/info.xml b/appinfo/info.xml index 93c5366146..3319d846bf 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -82,6 +82,7 @@ Developed with ❤️ by [LibreCode](https://librecode.coop). Help us transform OCA\Libresign\Activity\Settings\FileToSign OCA\Libresign\Activity\Settings\FileSigned + OCA\Libresign\Activity\Settings\SignRequestCanceled OCA\Libresign\Activity\Filter @@ -89,6 +90,7 @@ Developed with ❤️ by [LibreCode](https://librecode.coop). Help us transform OCA\Libresign\Activity\Provider\SignRequest OCA\Libresign\Activity\Provider\Signed + OCA\Libresign\Activity\Provider\SignRequestCanceled diff --git a/lib/Activity/Listener.php b/lib/Activity/Listener.php index 5006088eda..faceeb55f6 100644 --- a/lib/Activity/Listener.php +++ b/lib/Activity/Listener.php @@ -14,6 +14,7 @@ use OCA\Libresign\Db\SignRequestMapper; use OCA\Libresign\Events\SendSignNotificationEvent; use OCA\Libresign\Events\SignedEvent; +use OCA\Libresign\Events\SignRequestCanceledEvent; use OCA\Libresign\Service\AccountService; use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod; use OCP\Activity\Exceptions\UnknownActivityException; @@ -42,7 +43,7 @@ public function __construct( } public function handle(Event $event): void { - /** @var SendSignNotificationEvent|SignedEvent $event */ + /** @var SendSignNotificationEvent|SignedEvent|SignRequestCanceledEvent $event */ match ($event::class) { SendSignNotificationEvent::class => $this->generateNewSignNotificationActivity( $event->getSignRequest(), @@ -54,6 +55,11 @@ public function handle(Event $event): void { $event->getLibreSignFile(), $event->getIdentifyMethod(), ), + SignRequestCanceledEvent::class => $this->generateCanceledActivity( + $event->getSignRequest(), + $event->getLibreSignFile(), + $event->getIdentifyMethod(), + ), }; } @@ -166,6 +172,51 @@ protected function generateSignedEventActivity( } } + protected function generateCanceledActivity( + SignRequest $signRequest, + FileEntity $libreSignFile, + IIdentifyMethod $identifyMethod, + ): void { + $actor = $this->userSession->getUser(); + if (!$actor instanceof IUser) { + return; + } + $actorId = $actor->getUID(); + + $event = $this->activityManager->generateEvent(); + try { + $event + ->setApp(Application::APP_ID) + ->setType(SignRequestCanceledEvent::SIGN_REQUEST_CANCELED) + ->setAuthor($actorId) + ->setObject('signRequest', $signRequest->getId()) + ->setTimestamp($this->timeFactory->getTime()) + ->setAffectedUser($identifyMethod->getEntity()->getIdentifierValue()) + ->setGenerateNotification(false); + + $event->setSubject('sign_request_canceled', [ + 'from' => $this->getUserParameter( + $actor->getUID(), + $actor->getDisplayName(), + ), + 'file' => $this->getFileParameter($signRequest, $libreSignFile), + 'signer' => $this->getUserParameter( + $identifyMethod->getEntity()->getIdentifierValue(), + $signRequest->getDisplayName(), + ), + 'signRequest' => [ + 'type' => 'sign-request', + 'id' => (string)$signRequest->getId(), + 'name' => $actor->getDisplayName(), + ], + ]); + $this->activityManager->publish($event); + } catch (UnknownActivityException $e) { + $this->logger->error($e->getMessage(), ['exception' => $e]); + return; + } + } + /** * @return array{type: 'file', id: string, name: string, path: string, link: string} */ diff --git a/lib/Activity/Provider/SignRequestCanceled.php b/lib/Activity/Provider/SignRequestCanceled.php new file mode 100644 index 0000000000..b4a2cdda33 --- /dev/null +++ b/lib/Activity/Provider/SignRequestCanceled.php @@ -0,0 +1,83 @@ +getApp() !== Application::APP_ID) { + throw new UnknownActivityException('app'); + } + + if ($event->getSubject() !== 'sign_request_canceled') { + throw new UnknownActivityException('subject'); + } + + $this->definitions->definitions['sign-request'] = [ + 'author' => 'LibreSign', + 'since' => '28.0.0', + 'parameters' => [ + 'id' => [ + 'since' => '28.0.0', + 'required' => true, + 'description' => 'The id of SignRequest object', + 'example' => '12345', + ], + 'name' => [ + 'since' => '28.0.0', + 'required' => true, + 'description' => 'The display name of signer', + 'example' => 'John Doe', + ], + ], + ]; + + if ($this->activityManager->getRequirePNG()) { + $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.png'))); + } else { + $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg'))); + } + + $l = $this->languageFactory->get(Application::APP_ID, $language); + $parameters = $event->getSubjectParameters(); + + $subject = $l->t('{from} canceled the signature request for {file}'); + $event->setParsedSubject( + str_replace( + ['{from}', '{file}'], + [ + $parameters['from']['name'], + $parameters['file']['name'], + ], + $subject + )) + ->setRichSubject($subject, $parameters); + + return $event; + } +} diff --git a/lib/Activity/Settings/SignRequestCanceled.php b/lib/Activity/Settings/SignRequestCanceled.php new file mode 100644 index 0000000000..333c7b1f4d --- /dev/null +++ b/lib/Activity/Settings/SignRequestCanceled.php @@ -0,0 +1,63 @@ +l->t('A signature request has been canceled'); + } + + /** + * {@inheritdoc} + */ + #[\Override] + public function getPriority(): int { + return 51; + } + + /** + * {@inheritdoc} + */ + #[\Override] + public function canChangeNotification(): bool { + return true; + } + + /** + * {@inheritdoc} + */ + #[\Override] + public function canChangeMail() { + return true; + } +} diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 8757513f57..5ce1732db8 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -14,6 +14,7 @@ use OCA\Libresign\Capabilities; use OCA\Libresign\Events\SendSignNotificationEvent; use OCA\Libresign\Events\SignedEvent; +use OCA\Libresign\Events\SignRequestCanceledEvent; use OCA\Libresign\Files\TemplateLoader as FilesTemplateLoader; use OCA\Libresign\Listener\BeforeNodeDeletedListener; use OCA\Libresign\Listener\LoadAdditionalListener; @@ -72,14 +73,17 @@ public function register(IRegistrationContext $context): void { // Activity listeners $context->registerEventListener(SendSignNotificationEvent::class, ActivityListener::class); $context->registerEventListener(SignedEvent::class, ActivityListener::class); + $context->registerEventListener(SignRequestCanceledEvent::class, ActivityListener::class); // Notification listeners $context->registerEventListener(SendSignNotificationEvent::class, NotificationListener::class); $context->registerEventListener(SignedEvent::class, NotificationListener::class); + $context->registerEventListener(SignRequestCanceledEvent::class, NotificationListener::class); // MailNotify listener $context->registerEventListener(SendSignNotificationEvent::class, MailNotifyListener::class); $context->registerEventListener(SignedEvent::class, MailNotifyListener::class); + $context->registerEventListener(SignRequestCanceledEvent::class, MailNotifyListener::class); // TwofactorGateway listener $context->registerEventListener(SendSignNotificationEvent::class, TwofactorGatewayListener::class); diff --git a/lib/Events/SignRequestCanceledEvent.php b/lib/Events/SignRequestCanceledEvent.php new file mode 100644 index 0000000000..4a06ed4b0b --- /dev/null +++ b/lib/Events/SignRequestCanceledEvent.php @@ -0,0 +1,37 @@ +libreSignFile; + } + + public function getSignRequest(): SignRequest { + return $this->signRequest; + } + + public function getIdentifyMethod(): IIdentifyMethod { + return $this->identifyMethod; + } +} diff --git a/lib/Listener/MailNotifyListener.php b/lib/Listener/MailNotifyListener.php index 36e405f8d5..8ba2db6afc 100644 --- a/lib/Listener/MailNotifyListener.php +++ b/lib/Listener/MailNotifyListener.php @@ -13,6 +13,7 @@ use OCA\Libresign\Db\SignRequestMapper; use OCA\Libresign\Events\SendSignNotificationEvent; use OCA\Libresign\Events\SignedEvent; +use OCA\Libresign\Events\SignRequestCanceledEvent; use OCA\Libresign\Service\IdentifyMethod\IdentifyService; use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod; use OCA\Libresign\Service\MailService; @@ -36,7 +37,7 @@ public function __construct( } public function handle(Event $event): void { - /** @var SendSignNotificationEvent|SignedEvent $event */ + /** @var SendSignNotificationEvent|SignedEvent|SignRequestCanceledEvent $event */ match ($event::class) { SendSignNotificationEvent::class => $this->sendSignMailNotification( $event->getSignRequest(), @@ -48,6 +49,11 @@ public function handle(Event $event): void { $event->getLibreSignFile(), $event->getUser(), ), + SignRequestCanceledEvent::class => $this->sendCanceledMailNotification( + $event->getSignRequest(), + $event->getIdentifyMethod(), + $event->getLibreSignFile(), + ), }; } @@ -120,6 +126,47 @@ protected function sendSignedMailNotification( } } + protected function sendCanceledMailNotification( + SignRequest $signRequest, + IIdentifyMethod $identifyMethod, + FileEntity $libreSignFile, + ): void { + try { + if ($identifyMethod->getEntity()->isDeletedAccount()) { + return; + } + + $email = ''; + if ($identifyMethod->getName() === 'account') { + $userId = $identifyMethod->getEntity()->getIdentifierValue(); + $user = $this->userManager->get($userId); + if ($user) { + $email = $user->getEMailAddress(); + } + } elseif ($identifyMethod->getName() === 'email') { + $email = $identifyMethod->getEntity()->getIdentifierValue(); + } + + if (empty($email)) { + return; + } + + $users = $this->userManager->getByEmail($email); + if (count($users) === 1) { + $userId = $users[0]->getUID(); + if ($this->isNotificationDisabledAtActivity($userId, SignRequestCanceledEvent::SIGN_REQUEST_CANCELED)) { + return; + } + } + + $this->mail->notifyCanceledRequest($signRequest, $email, $libreSignFile); + + } catch (\InvalidArgumentException $e) { + $this->logger->error($e->getMessage(), ['exception' => $e]); + return; + } + } + private function isNotificationDisabledAtActivity(string $userId, string $type): bool { if (!class_exists(\OCA\Activity\UserSettings::class)) { return false; diff --git a/lib/Listener/NotificationListener.php b/lib/Listener/NotificationListener.php index 539b15e9a2..05cd3e1e10 100644 --- a/lib/Listener/NotificationListener.php +++ b/lib/Listener/NotificationListener.php @@ -14,6 +14,7 @@ use OCA\Libresign\Db\SignRequestMapper; use OCA\Libresign\Events\SendSignNotificationEvent; use OCA\Libresign\Events\SignedEvent; +use OCA\Libresign\Events\SignRequestCanceledEvent; use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod; use OCP\AppFramework\Utility\ITimeFactory; use OCP\EventDispatcher\Event; @@ -49,6 +50,12 @@ public function handle(Event $event): void { $event->getLibreSignFile(), $event->getIdentifyMethod(), ); + } elseif ($event instanceof SignRequestCanceledEvent) { + $this->sendCanceledNotification( + $event->getSignRequest(), + $event->getLibreSignFile(), + $event->getIdentifyMethod(), + ); } } @@ -149,6 +156,53 @@ private function sendSignedNotification( $this->notificationManager->notify($notification); } + private function sendCanceledNotification( + SignRequest $signRequest, + FileEntity $libreSignFile, + IIdentifyMethod $identifyMethod, + ): void { + $actor = $this->userSession->getUser(); + if (!$actor instanceof IUser) { + return; + } + if ($identifyMethod->getEntity()->isDeletedAccount()) { + return; + } + $notificationDisabled = $this->isNotificationDisabledAtActivity( + $identifyMethod->getEntity()->getIdentifierValue(), + SignRequestCanceledEvent::SIGN_REQUEST_CANCELED, + ); + if ($notificationDisabled) { + return; + } + + $notification = $this->notificationManager->createNotification(); + $notification + ->setApp(AppInfoApplication::APP_ID) + ->setObject('signRequest', (string)$signRequest->getId()) + ->setDateTime((new \DateTime())->setTimestamp($this->timeFactory->now()->getTimestamp())) + ->setUser($identifyMethod->getEntity()->getIdentifierValue()) + ->setSubject('sign_request_canceled', [ + 'from' => $this->getUserParameter( + $actor->getUID(), + $actor->getDisplayName(), + ), + 'file' => [ + 'type' => 'file', + 'id' => (string)$libreSignFile->getNodeId(), + 'name' => $libreSignFile->getName(), + 'path' => $libreSignFile->getName(), + ], + 'signRequest' => [ + 'type' => 'sign-request', + 'id' => (string)$signRequest->getId(), + 'name' => $actor->getDisplayName(), + ], + ]); + + $this->notificationManager->notify($notification); + } + private function isNotificationDisabledAtActivity(string $userId, string $type): bool { if (!class_exists(\OCA\Activity\UserSettings::class)) { return false; @@ -160,7 +214,9 @@ private function isNotificationDisabledAtActivity(string $userId, string $type): 'notification', $type ); - if (!$notificationSetting) { + // If setting is explicitly false, notifications are disabled + // If setting is null/not configured, notifications are enabled by default + if ($notificationSetting === false) { return true; } } diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php index 9d9f1a6b1a..0e9ce8e2a8 100644 --- a/lib/Notification/Notifier.php +++ b/lib/Notification/Notifier.php @@ -49,6 +49,7 @@ public function prepare(INotification $notification, string $languageCode): INot 'new_sign_request' => $this->parseSignRequest($notification, $l, false), 'update_sign_request' => $this->parseSignRequest($notification, $l, true), 'file_signed' => $this->parseSigned($notification, $l), + 'sign_request_canceled' => $this->parseCanceled($notification, $l), default => throw new UnknownNotificationException(), }; } @@ -203,4 +204,67 @@ private function parseSigned( return $notification; } + + private function parseCanceled( + INotification $notification, + IL10N $l, + ): INotification { + + $this->definitions->definitions['sign-request'] = [ + 'author' => 'LibreSign', + 'since' => '28.0.0', + 'parameters' => [ + 'id' => [ + 'since' => '28.0.0', + 'required' => true, + 'description' => 'The id of SignRequest object', + 'example' => '12345', + ], + 'name' => [ + 'since' => '28.0.0', + 'required' => true, + 'description' => 'The display name of signer', + 'example' => 'John Doe', + ], + ], + ]; + + $parameters = $notification->getSubjectParameters(); + $notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg'))); + + if (isset($parameters['from']) && isset($parameters['file'])) { + $subject = $l->t('{from} canceled the signature request for {file}'); + $notification->setParsedSubject( + str_replace( + ['{from}', '{file}'], + [ + $parameters['from']['name'], + $parameters['file']['name'], + ], + $subject + )) + ->setRichSubject($subject, $parameters); + } + + if (isset($parameters['signRequest']) && isset($parameters['signRequest']['id'])) { + $dismissAction = $notification->createAction() + ->setParsedLabel($l->t('Dismiss notification')) + ->setLink( + $this->url->linkToOCSRouteAbsolute( + 'libresign.notify.notificationDismiss', + [ + 'apiVersion' => 'v1', + 'timestamp' => $notification->getDateTime()->getTimestamp(), + 'objectType' => 'signRequest', + 'objectId' => $parameters['signRequest']['id'], + 'subject' => 'sign_request_canceled', + ], + ), + IAction::TYPE_DELETE + ); + $notification->addParsedAction($dismissAction); + } + + return $notification; + } } diff --git a/lib/Service/MailService.php b/lib/Service/MailService.php index cb209482d8..bb100e5f4e 100644 --- a/lib/Service/MailService.php +++ b/lib/Service/MailService.php @@ -132,6 +132,29 @@ public function notifySignedUser(SignRequest $signRequest, string $email, File $ } } + public function notifyCanceledRequest(SignRequest $signRequest, string $email, File $libreSignFile): void { + $emailTemplate = $this->mailer->createEMailTemplate('settings.TestEmail'); + // TRANSLATORS The subject of the email that is sent when a signature request has been canceled. + $emailTemplate->setSubject($this->l10n->t('LibreSign: A signature request has been canceled')); + $emailTemplate->addHeader(); + $emailTemplate->addHeading($this->l10n->t('Signature request canceled'), false); + // TRANSLATORS The text in the email that is sent when a signature request has been canceled. %s will be replaced with the name of the file. + $emailTemplate->addBodyText($this->l10n->t('The request for you to sign "%s" has been canceled.', [$libreSignFile->getName()])); + $message = $this->mailer->createMessage(); + if ($signRequest->getDisplayName()) { + $message->setTo([$email => $signRequest->getDisplayName()]); + } else { + $message->setTo([$email]); + } + $message->useTemplate($emailTemplate); + try { + $this->mailer->send($message); + } catch (\Exception $e) { + $this->logger->error('Notify canceled request mail could not be sent: ' . $e->getMessage()); + // Don't throw exception to avoid breaking the flow when mail fails + } + } + public function sendCodeToSign(string $email, string $name, string $code): void { $emailTemplate = $this->mailer->createEMailTemplate('settings.TestEmail'); $emailTemplate->setSubject($this->l10n->t('LibreSign: Code to sign file')); diff --git a/lib/Service/RequestSignatureService.php b/lib/Service/RequestSignatureService.php index 16ec243584..21497a265d 100644 --- a/lib/Service/RequestSignatureService.php +++ b/lib/Service/RequestSignatureService.php @@ -16,10 +16,12 @@ use OCA\Libresign\Db\SignRequest as SignRequestEntity; use OCA\Libresign\Db\SignRequestMapper; use OCA\Libresign\Enum\SignatureFlow; +use OCA\Libresign\Events\SignRequestCanceledEvent; use OCA\Libresign\Handler\DocMdpHandler; use OCA\Libresign\Helper\ValidateHelper; use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\IMimeTypeDetector; use OCP\Files\Node; use OCP\Http\Client\IClientService; @@ -51,6 +53,7 @@ public function __construct( protected LoggerInterface $logger, protected SequentialSigningService $sequentialSigningService, protected IAppConfig $appConfig, + protected IEventDispatcher $eventDispatcher, ) { } @@ -479,10 +482,12 @@ private function getSignRequestByIdentifyMethod(IIdentifyMethod $identifyMethod, public function unassociateToUser(int $fileId, int $signRequestId): void { $signRequest = $this->signRequestMapper->getByFileIdAndSignRequestId($fileId, $signRequestId); $deletedOrder = $signRequest->getSigningOrder(); + $groupedIdentifyMethods = $this->identifyMethod->getIdentifyMethodsFromSignRequestId($signRequestId); + + $this->dispatchCancellationEventIfNeeded($signRequest, $fileId, $groupedIdentifyMethods); try { $this->signRequestMapper->delete($signRequest); - $groupedIdentifyMethods = $this->identifyMethod->getIdentifyMethodsFromSignRequestId($signRequestId); foreach ($groupedIdentifyMethods as $identifyMethods) { foreach ($identifyMethods as $identifyMethod) { $identifyMethod->delete(); @@ -498,6 +503,32 @@ public function unassociateToUser(int $fileId, int $signRequestId): void { } } + private function dispatchCancellationEventIfNeeded( + SignRequestEntity $signRequest, + int $fileId, + array $groupedIdentifyMethods, + ): void { + if ($signRequest->getStatus() !== \OCA\Libresign\Enum\SignRequestStatus::ABLE_TO_SIGN->value) { + return; + } + + try { + $libreSignFile = $this->fileMapper->getByFileId($fileId); + foreach ($groupedIdentifyMethods as $identifyMethods) { + foreach ($identifyMethods as $identifyMethod) { + $event = new SignRequestCanceledEvent( + $signRequest, + $libreSignFile, + $identifyMethod, + ); + $this->eventDispatcher->dispatchTyped($event); + } + } + } catch (\Throwable $e) { + $this->logger->error('Error dispatching SignRequestCanceledEvent: ' . $e->getMessage(), ['exception' => $e]); + } + } + public function deleteRequestSignature(array $data): void { if (!empty($data['uuid'])) { $signatures = $this->signRequestMapper->getByFileUuid($data['uuid']); diff --git a/tests/integration/features/sign/cancel.feature b/tests/integration/features/sign/cancel.feature new file mode 100644 index 0000000000..22eab141a8 --- /dev/null +++ b/tests/integration/features/sign/cancel.feature @@ -0,0 +1,60 @@ +Feature: sign-request-cancel + Scenario: Delete pending signature request sends cancellation notification + Given as user "admin" + And user "signer1" exists + And reset notifications of user "signer1" + And run the command "libresign:configure:openssl --cn test" with result code 0 + And sending "post" to ocs "/apps/libresign/api/v1/request-signature" + | file | {"url":"/apps/libresign/develop/pdf"} | + | users | [{"identify":{"account":"signer1"}}] | + | name | document | + And the response should have a status code 200 + And sending "get" to ocs "/apps/libresign/api/v1/file/list" + And fetch field "(NODE_ID)ocs.data.data.0.nodeId" from previous JSON response + And fetch field "(SIGN_REQUEST_ID)ocs.data.data.0.signers.0.signRequestId" from previous JSON response + When sending "delete" to ocs "/apps/libresign/api/v1/sign/file_id//" + Then the response should have a status code 200 + When as user "signer1" + And sending "get" to ocs "/apps/notifications/api/v2/notifications" + Then the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data[0].subject | admin canceled the signature request for document | + + Scenario: Delete draft request does not send cancellation notification + Given as user "admin" + And user "signer1" exists + And reset notifications of user "signer1" + And run the command "libresign:configure:openssl --cn test" with result code 0 + And sending "post" to ocs "/apps/libresign/api/v1/request-signature" + | file | {"url":"/apps/libresign/develop/pdf"} | + | users | [{"identify":{"account":"signer1"},"notify":false}] | + | name | document | + | status | 0 | + And the response should have a status code 200 + When sending "get" to ocs "/apps/libresign/api/v1/file/list" + And fetch field "(NODE_ID)ocs.data.data.0.nodeId" from previous JSON response + And fetch field "(SIGN_REQUEST_ID)ocs.data.data.0.signers.0.signRequestId" from previous JSON response + And sending "delete" to ocs "/apps/libresign/api/v1/sign/file_id//" + Then the response should have a status code 200 + When as user "signer1" + And sending "get" to ocs "/apps/notifications/api/v2/notifications" + Then the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data | [] | + + Scenario: Delete signer removes them from file list + Given as user "admin" + And user "signer1" exists + And run the command "libresign:configure:openssl --cn test" with result code 0 + And sending "post" to ocs "/apps/libresign/api/v1/request-signature" + | file | {"url":"/apps/libresign/develop/pdf"} | + | users | [{"identify":{"account":"signer1"}}] | + | name | document | + When sending "get" to ocs "/apps/libresign/api/v1/file/list" + And fetch field "(NODE_ID)ocs.data.data.0.nodeId" from previous JSON response + And fetch field "(SIGN_REQUEST_ID)ocs.data.data.0.signers.0.signRequestId" from previous JSON response + And sending "delete" to ocs "/apps/libresign/api/v1/sign/file_id//" + And sending "get" to ocs "/apps/libresign/api/v1/file/list" + Then the response should be a JSON array with the following mandatory values + | key | value | + | (jq).ocs.data.data[0].signers | [] | diff --git a/tests/php/Unit/Service/RequestSignatureServiceTest.php b/tests/php/Unit/Service/RequestSignatureServiceTest.php index 0e8f0f5d01..2631680b1f 100644 --- a/tests/php/Unit/Service/RequestSignatureServiceTest.php +++ b/tests/php/Unit/Service/RequestSignatureServiceTest.php @@ -19,6 +19,7 @@ use OCA\Libresign\Service\PdfParserService; use OCA\Libresign\Service\RequestSignatureService; use OCA\Libresign\Service\SequentialSigningService; +use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\IMimeTypeDetector; use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; @@ -50,6 +51,7 @@ final class RequestSignatureServiceTest extends \OCA\Libresign\Tests\Unit\TestCa private LoggerInterface&MockObject $loggerInterface; private SequentialSigningService&MockObject $sequentialSigningService; private IAppConfig&MockObject $appConfig; + private IEventDispatcher&MockObject $eventDispatcher; public function setUp(): void { parent::setUp(); @@ -76,6 +78,7 @@ public function setUp(): void { $this->loggerInterface = $this->createMock(LoggerInterface::class); $this->sequentialSigningService = $this->createMock(SequentialSigningService::class); $this->appConfig = $this->createMock(IAppConfig::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); } private function getService(?SequentialSigningService $sequentialSigningService = null): RequestSignatureService { @@ -97,6 +100,7 @@ private function getService(?SequentialSigningService $sequentialSigningService $this->loggerInterface, $sequentialSigningService ?? $this->sequentialSigningService, $this->appConfig, + $this->eventDispatcher, ); }