Skip to content

Commit 85b1b90

Browse files
authored
Merge pull request #6256 from LibreSign/feat/dashboard-pending-signatures-widget
feat: add dashboard pending signatures widget
2 parents bb3e1f3 + e39d1e6 commit 85b1b90

2 files changed

Lines changed: 167 additions & 0 deletions

File tree

lib/AppInfo/Application.php

Lines changed: 3 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;
@@ -91,5 +92,7 @@ public function register(IRegistrationContext $context): void {
9192
$context->registerEventListener(SignedEvent::class, TwofactorGatewayListener::class);
9293

9394
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
95+
96+
$context->registerDashboardWidget(PendingSignaturesWidget::class);
9497
}
9598
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
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\AppInfo\Application;
13+
use OCA\Libresign\Db\File;
14+
use OCA\Libresign\Db\SignRequest;
15+
use OCA\Libresign\Db\SignRequestMapper;
16+
use OCA\Libresign\Enum\FileStatus;
17+
use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory;
18+
use OCP\Dashboard\IAPIWidget;
19+
use OCP\Dashboard\IAPIWidgetV2;
20+
use OCP\Dashboard\IButtonWidget;
21+
use OCP\Dashboard\IConditionalWidget;
22+
use OCP\Dashboard\IIconWidget;
23+
use OCP\Dashboard\Model\WidgetButton;
24+
use OCP\Dashboard\Model\WidgetItem;
25+
use OCP\Dashboard\Model\WidgetItems;
26+
use OCP\IL10N;
27+
use OCP\IURLGenerator;
28+
use OCP\IUserSession;
29+
use Override;
30+
31+
class PendingSignaturesWidget implements IAPIWidget, IAPIWidgetV2, IButtonWidget, IConditionalWidget, IIconWidget {
32+
public function __construct(
33+
private IL10N $l10n,
34+
private IURLGenerator $urlGenerator,
35+
private SignRequestMapper $signRequestMapper,
36+
private IUserSession $userSession,
37+
private CertificateEngineFactory $certificateEngineFactory,
38+
) {
39+
}
40+
41+
#[Override]
42+
public function getId(): string {
43+
return 'libresign_pending_signatures';
44+
}
45+
46+
#[Override]
47+
public function getTitle(): string {
48+
return $this->l10n->t('Pending signatures');
49+
}
50+
51+
#[Override]
52+
public function getOrder(): int {
53+
return 10;
54+
}
55+
56+
#[Override]
57+
public function getIconClass(): string {
58+
return 'icon-libresign';
59+
}
60+
61+
#[Override]
62+
public function getIconUrl(): string {
63+
return $this->urlGenerator->getAbsoluteURL(
64+
$this->urlGenerator->imagePath(Application::APP_ID, 'app-dark.svg')
65+
);
66+
}
67+
68+
#[Override]
69+
public function getUrl(): ?string {
70+
return $this->urlGenerator->linkToRouteAbsolute('libresign.page.index');
71+
}
72+
73+
#[Override]
74+
public function load(): void {
75+
// No special loading required
76+
}
77+
78+
#[Override]
79+
public function isEnabled(): bool {
80+
return $this->certificateEngineFactory->getEngine()->isSetupOk();
81+
}
82+
83+
#[Override]
84+
public function getItemsV2(string $userId, ?string $since = null, int $limit = 7): WidgetItems {
85+
$user = $this->userSession->getUser();
86+
if (!$user) {
87+
return new WidgetItems([], $this->l10n->t('User not found'));
88+
}
89+
90+
$result = $this->signRequestMapper->getFilesAssociatedFilesWithMe(
91+
$user,
92+
['status' => [FileStatus::ABLE_TO_SIGN->value, FileStatus::PARTIAL_SIGNED->value]],
93+
1,
94+
$limit,
95+
['sortBy' => 'created_at', 'sortDirection' => 'desc']
96+
);
97+
98+
$items = [];
99+
100+
foreach ($result['data'] as $fileEntity) {
101+
$signRequests = $this->signRequestMapper->getByFileId($fileEntity->getId());
102+
103+
foreach ($signRequests as $signRequest) {
104+
if ($signRequest->getSigned()) {
105+
continue;
106+
}
107+
108+
$item = new WidgetItem(
109+
$fileEntity->getName(),
110+
$this->getSubtitle($signRequest, $fileEntity),
111+
$this->urlGenerator->linkToRouteAbsolute('libresign.page.signFPath', ['uuid' => $signRequest->getUuid(), 'path' => 'pdf']),
112+
$this->urlGenerator->getAbsoluteURL(
113+
$this->urlGenerator->imagePath('core', 'filetypes/application-pdf.svg')
114+
),
115+
$this->getTimestamp($fileEntity)
116+
);
117+
118+
$items[] = $item;
119+
}
120+
}
121+
122+
return new WidgetItems(
123+
$items,
124+
empty($items) ? $this->l10n->t('No pending signatures') : '',
125+
);
126+
}
127+
128+
private function getSubtitle(SignRequest $signRequest, File $fileEntity): string {
129+
$displayName = $signRequest->getDisplayName();
130+
$createdAt = $fileEntity->getCreatedAt();
131+
if ($createdAt instanceof \DateTime) {
132+
$date = $createdAt->format('d/m/Y');
133+
// TRANSLATORS %s is the sender name, %s is the date
134+
return $this->l10n->t('From: %s • Date: %s', [$displayName, $date]);
135+
}
136+
// TRANSLATORS %s is the sender name
137+
return $this->l10n->t('From: %s', [$displayName]);
138+
}
139+
140+
private function getTimestamp(File $fileEntity): string {
141+
$createdAt = $fileEntity->getCreatedAt();
142+
if ($createdAt instanceof \DateTime) {
143+
return (string)$createdAt->getTimestamp();
144+
}
145+
return '';
146+
}
147+
148+
#[Override]
149+
public function getItems(string $userId, ?string $since = null, int $limit = 7): array {
150+
$widgetItems = $this->getItemsV2($userId, $since, $limit);
151+
return array_values($widgetItems->getItems());
152+
}
153+
154+
#[Override]
155+
public function getWidgetButtons(string $userId): array {
156+
return [
157+
new WidgetButton(
158+
WidgetButton::TYPE_MORE,
159+
$this->urlGenerator->linkToRouteAbsolute('libresign.page.index'),
160+
$this->l10n->t('View all documents')
161+
),
162+
];
163+
}
164+
}

0 commit comments

Comments
 (0)