Skip to content

Commit e784c39

Browse files
authored
Merge pull request #6181 from LibreSign/feat/cancel-signature-request-notification
feat: cancel signature request notification
2 parents 95a26cb + 21aee75 commit e784c39

13 files changed

Lines changed: 529 additions & 4 deletions

File tree

appinfo/info.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,15 @@ Developed with ❤️ by [LibreCode](https://librecode.coop). Help us transform
8282
<settings>
8383
<setting>OCA\Libresign\Activity\Settings\FileToSign</setting>
8484
<setting>OCA\Libresign\Activity\Settings\FileSigned</setting>
85+
<setting>OCA\Libresign\Activity\Settings\SignRequestCanceled</setting>
8586
</settings>
8687
<filters>
8788
<filter>OCA\Libresign\Activity\Filter</filter>
8889
</filters>
8990
<providers>
9091
<provider>OCA\Libresign\Activity\Provider\SignRequest</provider>
9192
<provider>OCA\Libresign\Activity\Provider\Signed</provider>
93+
<provider>OCA\Libresign\Activity\Provider\SignRequestCanceled</provider>
9294
</providers>
9395
</activity>
9496
<navigations>

lib/Activity/Listener.php

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use OCA\Libresign\Db\SignRequestMapper;
1515
use OCA\Libresign\Events\SendSignNotificationEvent;
1616
use OCA\Libresign\Events\SignedEvent;
17+
use OCA\Libresign\Events\SignRequestCanceledEvent;
1718
use OCA\Libresign\Service\AccountService;
1819
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
1920
use OCP\Activity\Exceptions\UnknownActivityException;
@@ -43,7 +44,7 @@ public function __construct(
4344

4445
#[\Override]
4546
public function handle(Event $event): void {
46-
/** @var SendSignNotificationEvent|SignedEvent $event */
47+
/** @var SendSignNotificationEvent|SignedEvent|SignRequestCanceledEvent $event */
4748
match ($event::class) {
4849
SendSignNotificationEvent::class => $this->generateNewSignNotificationActivity(
4950
$event->getSignRequest(),
@@ -55,6 +56,11 @@ public function handle(Event $event): void {
5556
$event->getLibreSignFile(),
5657
$event->getIdentifyMethod(),
5758
),
59+
SignRequestCanceledEvent::class => $this->generateCanceledActivity(
60+
$event->getSignRequest(),
61+
$event->getLibreSignFile(),
62+
$event->getIdentifyMethod(),
63+
),
5864
};
5965
}
6066

@@ -167,6 +173,51 @@ protected function generateSignedEventActivity(
167173
}
168174
}
169175

176+
protected function generateCanceledActivity(
177+
SignRequest $signRequest,
178+
FileEntity $libreSignFile,
179+
IIdentifyMethod $identifyMethod,
180+
): void {
181+
$actor = $this->userSession->getUser();
182+
if (!$actor instanceof IUser) {
183+
return;
184+
}
185+
$actorId = $actor->getUID();
186+
187+
$event = $this->activityManager->generateEvent();
188+
try {
189+
$event
190+
->setApp(Application::APP_ID)
191+
->setType(SignRequestCanceledEvent::SIGN_REQUEST_CANCELED)
192+
->setAuthor($actorId)
193+
->setObject('signRequest', $signRequest->getId())
194+
->setTimestamp($this->timeFactory->getTime())
195+
->setAffectedUser($identifyMethod->getEntity()->getIdentifierValue())
196+
->setGenerateNotification(false);
197+
198+
$event->setSubject('sign_request_canceled', [
199+
'from' => $this->getUserParameter(
200+
$actor->getUID(),
201+
$actor->getDisplayName(),
202+
),
203+
'file' => $this->getFileParameter($signRequest, $libreSignFile),
204+
'signer' => $this->getUserParameter(
205+
$identifyMethod->getEntity()->getIdentifierValue(),
206+
$signRequest->getDisplayName(),
207+
),
208+
'signRequest' => [
209+
'type' => 'sign-request',
210+
'id' => (string)$signRequest->getId(),
211+
'name' => $actor->getDisplayName(),
212+
],
213+
]);
214+
$this->activityManager->publish($event);
215+
} catch (UnknownActivityException $e) {
216+
$this->logger->error($e->getMessage(), ['exception' => $e]);
217+
return;
218+
}
219+
}
220+
170221
/**
171222
* @return array{type: 'file', id: string, name: string, path: string, link: string}
172223
*/
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Activity\Provider;
10+
11+
use OCA\Libresign\AppInfo\Application;
12+
use OCP\Activity\Exceptions\UnknownActivityException;
13+
use OCP\Activity\IEvent;
14+
use OCP\Activity\IManager;
15+
use OCP\Activity\IProvider;
16+
use OCP\IURLGenerator;
17+
use OCP\IUserManager;
18+
use OCP\L10N\IFactory;
19+
use OCP\RichObjectStrings\Definitions;
20+
21+
class SignRequestCanceled implements IProvider {
22+
public function __construct(
23+
protected IFactory $languageFactory,
24+
protected IURLGenerator $url,
25+
protected Definitions $definitions,
26+
protected IManager $activityManager,
27+
protected IUserManager $userManager,
28+
) {
29+
}
30+
31+
#[\Override]
32+
public function parse($language, IEvent $event, ?IEvent $previousEvent = null): IEvent {
33+
if ($event->getApp() !== Application::APP_ID) {
34+
throw new UnknownActivityException('app');
35+
}
36+
37+
if ($event->getSubject() !== 'sign_request_canceled') {
38+
throw new UnknownActivityException('subject');
39+
}
40+
41+
$this->definitions->definitions['sign-request'] = [
42+
'author' => 'LibreSign',
43+
'since' => '28.0.0',
44+
'parameters' => [
45+
'id' => [
46+
'since' => '28.0.0',
47+
'required' => true,
48+
'description' => 'The id of SignRequest object',
49+
'example' => '12345',
50+
],
51+
'name' => [
52+
'since' => '28.0.0',
53+
'required' => true,
54+
'description' => 'The display name of signer',
55+
'example' => 'John Doe',
56+
],
57+
],
58+
];
59+
60+
if ($this->activityManager->getRequirePNG()) {
61+
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.png')));
62+
} else {
63+
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_ID, 'app-dark.svg')));
64+
}
65+
66+
$l = $this->languageFactory->get(Application::APP_ID, $language);
67+
$parameters = $event->getSubjectParameters();
68+
69+
$subject = $l->t('{from} canceled the signature request for {file}');
70+
$event->setParsedSubject(
71+
str_replace(
72+
['{from}', '{file}'],
73+
[
74+
$parameters['from']['name'],
75+
$parameters['file']['name'],
76+
],
77+
$subject
78+
))
79+
->setRichSubject($subject, $parameters);
80+
81+
return $event;
82+
}
83+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Activity\Settings;
10+
11+
use OCA\Libresign\Events\SignRequestCanceledEvent;
12+
use OCA\Libresign\Helper\ValidateHelper;
13+
use OCP\IL10N;
14+
use OCP\IUserSession;
15+
16+
class SignRequestCanceled extends LibresignActivitySettings {
17+
public function __construct(
18+
protected IL10N $l,
19+
protected ValidateHelper $validateHelper,
20+
protected IUserSession $userSession,
21+
) {
22+
}
23+
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
#[\Override]
28+
public function getIdentifier(): string {
29+
return SignRequestCanceledEvent::SIGN_REQUEST_CANCELED;
30+
}
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
#[\Override]
36+
public function getName(): string {
37+
return $this->l->t('A signature request has been <strong>canceled</strong>');
38+
}
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
#[\Override]
44+
public function getPriority(): int {
45+
return 51;
46+
}
47+
48+
/**
49+
* {@inheritdoc}
50+
*/
51+
#[\Override]
52+
public function canChangeNotification(): bool {
53+
return true;
54+
}
55+
56+
/**
57+
* {@inheritdoc}
58+
*/
59+
#[\Override]
60+
public function canChangeMail() {
61+
return true;
62+
}
63+
}

lib/AppInfo/Application.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use OCA\Libresign\Capabilities;
1515
use OCA\Libresign\Events\SendSignNotificationEvent;
1616
use OCA\Libresign\Events\SignedEvent;
17+
use OCA\Libresign\Events\SignRequestCanceledEvent;
1718
use OCA\Libresign\Files\TemplateLoader as FilesTemplateLoader;
1819
use OCA\Libresign\Listener\BeforeNodeDeletedListener;
1920
use OCA\Libresign\Listener\LoadAdditionalListener;
@@ -74,14 +75,17 @@ public function register(IRegistrationContext $context): void {
7475
// Activity listeners
7576
$context->registerEventListener(SendSignNotificationEvent::class, ActivityListener::class);
7677
$context->registerEventListener(SignedEvent::class, ActivityListener::class);
78+
$context->registerEventListener(SignRequestCanceledEvent::class, ActivityListener::class);
7779

7880
// Notification listeners
7981
$context->registerEventListener(SendSignNotificationEvent::class, NotificationListener::class);
8082
$context->registerEventListener(SignedEvent::class, NotificationListener::class);
83+
$context->registerEventListener(SignRequestCanceledEvent::class, NotificationListener::class);
8184

8285
// MailNotify listener
8386
$context->registerEventListener(SendSignNotificationEvent::class, MailNotifyListener::class);
8487
$context->registerEventListener(SignedEvent::class, MailNotifyListener::class);
88+
$context->registerEventListener(SignRequestCanceledEvent::class, MailNotifyListener::class);
8589

8690
// TwofactorGateway listener
8791
$context->registerEventListener(SendSignNotificationEvent::class, TwofactorGatewayListener::class);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Events;
10+
11+
use OCA\Libresign\Db\File as FileEntity;
12+
use OCA\Libresign\Db\SignRequest;
13+
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
14+
use OCP\EventDispatcher\Event;
15+
16+
class SignRequestCanceledEvent extends Event {
17+
public const SIGN_REQUEST_CANCELED = 'libresign_sign_request_canceled';
18+
19+
public function __construct(
20+
private SignRequest $signRequest,
21+
private FileEntity $libreSignFile,
22+
private IIdentifyMethod $identifyMethod,
23+
) {
24+
}
25+
26+
public function getLibreSignFile(): FileEntity {
27+
return $this->libreSignFile;
28+
}
29+
30+
public function getSignRequest(): SignRequest {
31+
return $this->signRequest;
32+
}
33+
34+
public function getIdentifyMethod(): IIdentifyMethod {
35+
return $this->identifyMethod;
36+
}
37+
}

lib/Listener/MailNotifyListener.php

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCA\Libresign\Db\SignRequestMapper;
1414
use OCA\Libresign\Events\SendSignNotificationEvent;
1515
use OCA\Libresign\Events\SignedEvent;
16+
use OCA\Libresign\Events\SignRequestCanceledEvent;
1617
use OCA\Libresign\Service\IdentifyMethod\IdentifyService;
1718
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
1819
use OCA\Libresign\Service\MailService;
@@ -37,7 +38,7 @@ public function __construct(
3738

3839
#[\Override]
3940
public function handle(Event $event): void {
40-
/** @var SendSignNotificationEvent|SignedEvent $event */
41+
/** @var SendSignNotificationEvent|SignedEvent|SignRequestCanceledEvent $event */
4142
match ($event::class) {
4243
SendSignNotificationEvent::class => $this->sendSignMailNotification(
4344
$event->getSignRequest(),
@@ -49,6 +50,11 @@ public function handle(Event $event): void {
4950
$event->getLibreSignFile(),
5051
$event->getUser(),
5152
),
53+
SignRequestCanceledEvent::class => $this->sendCanceledMailNotification(
54+
$event->getSignRequest(),
55+
$event->getIdentifyMethod(),
56+
$event->getLibreSignFile(),
57+
),
5258
};
5359
}
5460

@@ -121,6 +127,47 @@ protected function sendSignedMailNotification(
121127
}
122128
}
123129

130+
protected function sendCanceledMailNotification(
131+
SignRequest $signRequest,
132+
IIdentifyMethod $identifyMethod,
133+
FileEntity $libreSignFile,
134+
): void {
135+
try {
136+
if ($identifyMethod->getEntity()->isDeletedAccount()) {
137+
return;
138+
}
139+
140+
$email = '';
141+
if ($identifyMethod->getName() === 'account') {
142+
$userId = $identifyMethod->getEntity()->getIdentifierValue();
143+
$user = $this->userManager->get($userId);
144+
if ($user) {
145+
$email = $user->getEMailAddress();
146+
}
147+
} elseif ($identifyMethod->getName() === 'email') {
148+
$email = $identifyMethod->getEntity()->getIdentifierValue();
149+
}
150+
151+
if (empty($email)) {
152+
return;
153+
}
154+
155+
$users = $this->userManager->getByEmail($email);
156+
if (count($users) === 1) {
157+
$userId = $users[0]->getUID();
158+
if ($this->isNotificationDisabledAtActivity($userId, SignRequestCanceledEvent::SIGN_REQUEST_CANCELED)) {
159+
return;
160+
}
161+
}
162+
163+
$this->mail->notifyCanceledRequest($signRequest, $email, $libreSignFile);
164+
165+
} catch (\InvalidArgumentException $e) {
166+
$this->logger->error($e->getMessage(), ['exception' => $e]);
167+
return;
168+
}
169+
}
170+
124171
private function isNotificationDisabledAtActivity(string $userId, string $type): bool {
125172
if (!class_exists(\OCA\Activity\UserSettings::class)) {
126173
return false;

0 commit comments

Comments
 (0)