Skip to content

Commit 823a85b

Browse files
authored
Merge pull request #6182 from LibreSign/backport/6181/stable31
[stable31] feat: cancel signature request notification
2 parents abbaaa6 + 040b625 commit 823a85b

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;
@@ -42,7 +43,7 @@ public function __construct(
4243
}
4344

4445
public function handle(Event $event): void {
45-
/** @var SendSignNotificationEvent|SignedEvent $event */
46+
/** @var SendSignNotificationEvent|SignedEvent|SignRequestCanceledEvent $event */
4647
match ($event::class) {
4748
SendSignNotificationEvent::class => $this->generateNewSignNotificationActivity(
4849
$event->getSignRequest(),
@@ -54,6 +55,11 @@ public function handle(Event $event): void {
5455
$event->getLibreSignFile(),
5556
$event->getIdentifyMethod(),
5657
),
58+
SignRequestCanceledEvent::class => $this->generateCanceledActivity(
59+
$event->getSignRequest(),
60+
$event->getLibreSignFile(),
61+
$event->getIdentifyMethod(),
62+
),
5763
};
5864
}
5965

@@ -166,6 +172,51 @@ protected function generateSignedEventActivity(
166172
}
167173
}
168174

175+
protected function generateCanceledActivity(
176+
SignRequest $signRequest,
177+
FileEntity $libreSignFile,
178+
IIdentifyMethod $identifyMethod,
179+
): void {
180+
$actor = $this->userSession->getUser();
181+
if (!$actor instanceof IUser) {
182+
return;
183+
}
184+
$actorId = $actor->getUID();
185+
186+
$event = $this->activityManager->generateEvent();
187+
try {
188+
$event
189+
->setApp(Application::APP_ID)
190+
->setType(SignRequestCanceledEvent::SIGN_REQUEST_CANCELED)
191+
->setAuthor($actorId)
192+
->setObject('signRequest', $signRequest->getId())
193+
->setTimestamp($this->timeFactory->getTime())
194+
->setAffectedUser($identifyMethod->getEntity()->getIdentifierValue())
195+
->setGenerateNotification(false);
196+
197+
$event->setSubject('sign_request_canceled', [
198+
'from' => $this->getUserParameter(
199+
$actor->getUID(),
200+
$actor->getDisplayName(),
201+
),
202+
'file' => $this->getFileParameter($signRequest, $libreSignFile),
203+
'signer' => $this->getUserParameter(
204+
$identifyMethod->getEntity()->getIdentifierValue(),
205+
$signRequest->getDisplayName(),
206+
),
207+
'signRequest' => [
208+
'type' => 'sign-request',
209+
'id' => (string)$signRequest->getId(),
210+
'name' => $actor->getDisplayName(),
211+
],
212+
]);
213+
$this->activityManager->publish($event);
214+
} catch (UnknownActivityException $e) {
215+
$this->logger->error($e->getMessage(), ['exception' => $e]);
216+
return;
217+
}
218+
}
219+
169220
/**
170221
* @return array{type: 'file', id: string, name: string, path: string, link: string}
171222
*/
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;
@@ -72,14 +73,17 @@ public function register(IRegistrationContext $context): void {
7273
// Activity listeners
7374
$context->registerEventListener(SendSignNotificationEvent::class, ActivityListener::class);
7475
$context->registerEventListener(SignedEvent::class, ActivityListener::class);
76+
$context->registerEventListener(SignRequestCanceledEvent::class, ActivityListener::class);
7577

7678
// Notification listeners
7779
$context->registerEventListener(SendSignNotificationEvent::class, NotificationListener::class);
7880
$context->registerEventListener(SignedEvent::class, NotificationListener::class);
81+
$context->registerEventListener(SignRequestCanceledEvent::class, NotificationListener::class);
7982

8083
// MailNotify listener
8184
$context->registerEventListener(SendSignNotificationEvent::class, MailNotifyListener::class);
8285
$context->registerEventListener(SignedEvent::class, MailNotifyListener::class);
86+
$context->registerEventListener(SignRequestCanceledEvent::class, MailNotifyListener::class);
8387

8488
// TwofactorGateway listener
8589
$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;
@@ -36,7 +37,7 @@ public function __construct(
3637
}
3738

3839
public function handle(Event $event): void {
39-
/** @var SendSignNotificationEvent|SignedEvent $event */
40+
/** @var SendSignNotificationEvent|SignedEvent|SignRequestCanceledEvent $event */
4041
match ($event::class) {
4142
SendSignNotificationEvent::class => $this->sendSignMailNotification(
4243
$event->getSignRequest(),
@@ -48,6 +49,11 @@ public function handle(Event $event): void {
4849
$event->getLibreSignFile(),
4950
$event->getUser(),
5051
),
52+
SignRequestCanceledEvent::class => $this->sendCanceledMailNotification(
53+
$event->getSignRequest(),
54+
$event->getIdentifyMethod(),
55+
$event->getLibreSignFile(),
56+
),
5157
};
5258
}
5359

@@ -120,6 +126,47 @@ protected function sendSignedMailNotification(
120126
}
121127
}
122128

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

0 commit comments

Comments
 (0)