Skip to content

Commit e9e0bd7

Browse files
feat: add dashboard pending signatures widget
Signed-off-by: samuelsonmesquita <[email protected]>
1 parent 27812ed commit e9e0bd7

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed

lib/AppInfo/Application.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use OCA\Files\Event\LoadSidebar;
1313
use OCA\Libresign\Activity\Listener as ActivityListener;
1414
use OCA\Libresign\Capabilities;
15+
use OCA\Libresign\Dashboard\PendingSignaturesWidget;
1516
use OCA\Libresign\Events\SendSignNotificationEvent;
1617
use OCA\Libresign\Events\SignedEvent;
1718
use OCA\Libresign\Events\SignRequestCanceledEvent;
@@ -92,5 +93,8 @@ public function register(IRegistrationContext $context): void {
9293
$context->registerEventListener(SignedEvent::class, TwofactorGatewayListener::class);
9394

9495
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
96+
97+
// Register Dashboard Widget
98+
$context->registerDashboardWidget(PendingSignaturesWidget::class);
9599
}
96100
}
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 LibreSign
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Libresign\Dashboard;
11+
12+
use OCA\Libresign\Db\SignRequestMapper;
13+
use OCA\Libresign\Service\SignFileService;
14+
use OCP\Dashboard\IAPIWidgetV2;
15+
use OCP\Dashboard\IButtonWidget;
16+
use OCP\Dashboard\Model\WidgetButton;
17+
use OCP\Dashboard\Model\WidgetItem;
18+
use OCP\Dashboard\Model\WidgetItems;
19+
use OCP\IL10N;
20+
use OCP\IURLGenerator;
21+
use OCP\IUserSession;
22+
use OCP\Util;
23+
24+
class PendingSignaturesWidget implements IAPIWidgetV2, IButtonWidget {
25+
public function __construct(
26+
private IL10N $l10n,
27+
private IURLGenerator $urlGenerator,
28+
private SignFileService $signFileService,
29+
private SignRequestMapper $signRequestMapper,
30+
private IUserSession $userSession,
31+
) {
32+
}
33+
34+
/**
35+
* @inheritDoc
36+
*/
37+
public function getId(): string {
38+
return 'libresign_pending_signatures';
39+
}
40+
41+
/**
42+
* @inheritDoc
43+
*/
44+
public function getTitle(): string {
45+
return $this->l10n->t('Pending signatures');
46+
}
47+
48+
/**
49+
* @inheritDoc
50+
*/
51+
public function getOrder(): int {
52+
return 10;
53+
}
54+
55+
/**
56+
* @inheritDoc
57+
*/
58+
public function getIconClass(): string {
59+
return 'icon-libresign-dark';
60+
}
61+
62+
/**
63+
* @inheritDoc
64+
*/
65+
public function getUrl(): ?string {
66+
return $this->urlGenerator->linkToRouteAbsolute('libresign.page.index');
67+
}
68+
69+
/**
70+
* @inheritDoc
71+
*/
72+
public function load(): void {
73+
//Util::addScript('libresign', 'libresign-dashboard');
74+
}
75+
76+
/**
77+
* @inheritDoc
78+
*/
79+
public function getItemsV2(string $userId, ?string $since = null, int $limit = 7): WidgetItems {
80+
try {
81+
$user = $this->userSession->getUser();
82+
if (!$user) {
83+
return new WidgetItems([], $this->l10n->t('User not found'));
84+
}
85+
86+
$result = $this->signRequestMapper->getFilesAssociatedFilesWithMe(
87+
$user,
88+
['status' => [\OCA\Libresign\Db\File::STATUS_ABLE_TO_SIGN, \OCA\Libresign\Db\File::STATUS_PARTIAL_SIGNED]],
89+
1,
90+
$limit,
91+
['sortBy' => 'created_at', 'sortDirection' => 'desc']
92+
);
93+
94+
$items = [];
95+
96+
foreach ($result['data'] as $fileEntity) {
97+
try {
98+
$signRequest = $this->getSignRequestForUser($fileEntity, $user);
99+
100+
if (!$signRequest || $signRequest->getSigned()) {
101+
continue;
102+
}
103+
104+
$item = new WidgetItem(
105+
$this->getDocumentTitle($fileEntity),
106+
$this->getSubtitle($signRequest, $fileEntity),
107+
$this->urlGenerator->linkToRouteAbsolute('libresign.page.sign', ['uuid' => $signRequest->getUuid()]),
108+
$this->urlGenerator->getAbsoluteURL(
109+
$this->urlGenerator->imagePath('libresign', 'app-dark.svg')
110+
),
111+
$this->getTimestamp($fileEntity)
112+
);
113+
114+
$items[] = $item;
115+
} catch (\Exception $e) {
116+
continue;
117+
}
118+
}
119+
120+
return new WidgetItems(
121+
$items,
122+
empty($items) ? $this->l10n->t('No pending signatures') : '',
123+
);
124+
} catch (\Exception $e) {
125+
return new WidgetItems(
126+
[],
127+
$this->l10n->t('Error loading pending signatures'),
128+
);
129+
}
130+
}
131+
132+
private function getSignRequestForUser(\OCA\Libresign\Db\File $fileEntity, \OCP\IUser $user): ?\OCA\Libresign\Db\SignRequest {
133+
try {
134+
$signRequests = $this->signRequestMapper->getByFileId($fileEntity->getId());
135+
136+
foreach ($signRequests as $signRequest) {
137+
if ($this->signRequestBelongsToUser($signRequest, $user)) {
138+
return $signRequest;
139+
}
140+
}
141+
} catch (\Exception $e) {
142+
return null;
143+
}
144+
145+
return null;
146+
}
147+
148+
private function signRequestBelongsToUser(\OCA\Libresign\Db\SignRequest $signRequest, \OCP\IUser $user): bool {
149+
try {
150+
$validSignRequest = $this->signFileService->getSignRequestToSign(
151+
$this->signFileService->getFile($signRequest->getFileId()),
152+
$signRequest->getUuid(),
153+
$user
154+
);
155+
156+
return $validSignRequest->getId() === $signRequest->getId();
157+
} catch (\Exception $e) {
158+
return false;
159+
}
160+
}
161+
162+
private function getDocumentTitle(\OCA\Libresign\Db\File $fileEntity): string {
163+
if ($fileEntity->getName()) {
164+
return $fileEntity->getName();
165+
}
166+
167+
try {
168+
$files = $this->signFileService->getNextcloudFiles($fileEntity);
169+
if (!empty($files)) {
170+
$file = current($files);
171+
return $file->getName();
172+
}
173+
} catch (\Exception $e) {
174+
}
175+
176+
return $this->l10n->t('Document');
177+
}
178+
179+
private function getSubtitle(\OCA\Libresign\Db\SignRequest $signRequest, \OCA\Libresign\Db\File $fileEntity): string {
180+
$parts = [];
181+
182+
$displayName = $signRequest->getDisplayName();
183+
if ($displayName) {
184+
$parts[] = $this->l10n->t('From: %s', [$displayName]);
185+
}
186+
187+
$createdAt = $fileEntity->getCreatedAt();
188+
if ($createdAt instanceof \DateTime) {
189+
$date = $createdAt->format('d/m/Y');
190+
$parts[] = $this->l10n->t('Date: %s', [$date]);
191+
}
192+
193+
return implode('', $parts);
194+
}
195+
196+
private function getTimestamp(\OCA\Libresign\Db\File $fileEntity): string {
197+
$createdAt = $fileEntity->getCreatedAt();
198+
if ($createdAt instanceof \DateTime) {
199+
return (string)$createdAt->getTimestamp();
200+
}
201+
return '';
202+
}
203+
204+
/**
205+
* @inheritDoc
206+
* @deprecated Use getItemsV2 instead
207+
*/
208+
public function getItems(string $userId, ?string $since = null, int $limit = 7): array {
209+
$widgetItems = $this->getItemsV2($userId, $since, $limit);
210+
return $widgetItems->getItems();
211+
}
212+
213+
/**
214+
* @inheritDoc
215+
*/
216+
public function getWidgetButtons(string $userId): array {
217+
return [
218+
new WidgetButton(
219+
WidgetButton::TYPE_MORE,
220+
$this->urlGenerator->linkToRouteAbsolute('libresign.page.index'),
221+
$this->l10n->t('View all documents')
222+
),
223+
];
224+
}
225+
}

0 commit comments

Comments
 (0)