Skip to content

Commit 85ed9f6

Browse files
committed
feat: unified search
Signed-off-by: Crisciany Souza <crisciany.souza@librecode.coop>
1 parent 6d6fcf9 commit 85ed9f6

3 files changed

Lines changed: 194 additions & 0 deletions

File tree

lib/AppInfo/Application.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use OCA\Libresign\Middleware\GlobalInjectionMiddleware;
2828
use OCA\Libresign\Middleware\InjectionMiddleware;
2929
use OCA\Libresign\Notification\Notifier;
30+
use OCA\Libresign\Search\FileSearchProvider;
3031
use OCP\AppFramework\App;
3132
use OCP\AppFramework\Bootstrap\IBootContext;
3233
use OCP\AppFramework\Bootstrap\IBootstrap;
@@ -64,6 +65,8 @@ public function register(IRegistrationContext $context): void {
6465

6566
$context->registerNotifierService(Notifier::class);
6667

68+
$context->registerSearchProvider(FileSearchProvider::class);
69+
6770
$context->registerEventListener(LoadSidebar::class, LoadSidebarListener::class);
6871
$context->registerEventListener(BeforeNodeDeletedEvent::class, BeforeNodeDeletedListener::class);
6972
$context->registerEventListener(CacheEntryRemovedEvent::class, BeforeNodeDeletedListener::class);

lib/Db/SignRequestMapper.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,49 @@ public function getFilesAssociatedFilesWithMeFormatted(
416416
return $return;
417417
}
418418

419+
public function getFilesToSearchProvider(IUser $user, string $term, int $limit, int $offset): array {
420+
$filter = [
421+
'page' => ($offset / $limit) + 1,
422+
'length' => $limit,
423+
];
424+
425+
$qb = $this->getFilesAssociatedFilesWithMeQueryBuilder($user->getUID(), $filter);
426+
427+
if (!empty($term)) {
428+
$qb->andWhere(
429+
$qb->expr()->like('f.name', $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($term) . '%'))
430+
);
431+
}
432+
433+
$qb->orderBy('f.created_at', 'DESC');
434+
435+
$result = $qb->executeQuery();
436+
$files = [];
437+
438+
while ($row = $result->fetch()) {
439+
try {
440+
$file = new File();
441+
$file->setId((int)$row['id']);
442+
$file->setUserId($row['user_id']);
443+
$file->setNodeId((int)($row['node_id'] ?? 0));
444+
$file->setSignedNodeId($row['signed_node_id'] ? (int)$row['signed_node_id'] : null);
445+
$file->setName($row['name'] ?? '');
446+
$file->setStatus((int)($row['status'] ?? 0));
447+
$file->setUuid($row['uuid'] ?? '');
448+
$file->setCreatedAt($row['created_at'] ?? '');
449+
450+
451+
$files[] = $file;
452+
} catch (\Exception $e) {
453+
continue;
454+
}
455+
}
456+
457+
$result->closeCursor();
458+
459+
return $files;
460+
}
461+
419462
/**
420463
* @param array<SignRequest> $signRequests
421464
* @return FileElement[][]

lib/Search/FileSearchProvider.php

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Libresign\Search;
11+
12+
use OCA\Libresign\AppInfo\Application;
13+
use OCA\Libresign\Db\File;
14+
use OCA\Libresign\Db\SignRequestMapper;
15+
use OCP\App\IAppManager;
16+
use OCP\Files\IMimeTypeDetector;
17+
use OCP\Files\IRootFolder;
18+
use OCP\IDBConnection;
19+
use OCP\IL10N;
20+
use OCP\IURLGenerator;
21+
use OCP\IUser;
22+
use OCP\Search\IProvider;
23+
use OCP\Search\ISearchQuery;
24+
use OCP\Search\SearchResult;
25+
use OCP\Search\SearchResultEntry;
26+
27+
class FileSearchProvider implements IProvider {
28+
public function __construct(
29+
private IL10N $l10n,
30+
private IURLGenerator $urlGenerator,
31+
private IRootFolder $rootFolder,
32+
private IAppManager $appManager,
33+
private IDBConnection $db,
34+
private IMimeTypeDetector $mimeTypeDetector,
35+
private SignRequestMapper $fileMapper,
36+
) {
37+
}
38+
39+
#[\Override]
40+
public function getId(): string {
41+
return 'libresign_files';
42+
}
43+
44+
#[\Override]
45+
public function getName(): string {
46+
return $this->l10n->t('LibreSign documents');
47+
}
48+
49+
#[\Override]
50+
public function getOrder(string $route, array $routeParameters): int {
51+
if (strpos($route, Application::APP_ID . '.') === 0) {
52+
return 0;
53+
}
54+
return 10;
55+
}
56+
57+
#[\Override]
58+
public function search(IUser $user, ISearchQuery $query): SearchResult {
59+
if (!$this->appManager->isEnabledForUser(Application::APP_ID, $user)) {
60+
return SearchResult::complete($this->l10n->t('LibreSign documents'), []);
61+
}
62+
63+
$term = $query->getTerm();
64+
$limit = $query->getLimit();
65+
$offset = $query->getCursor();
66+
67+
try {
68+
$files = $this->fileMapper->getFilesToSearchProvider($user, $term, $limit, (int)$offset);
69+
} catch (\Exception $e) {
70+
return SearchResult::complete($this->l10n->t('LibreSign documents'), []);
71+
}
72+
73+
$results = array_map(function (File $file) use ($user) {
74+
return $this->formatResult($file, $user);
75+
}, $files);
76+
77+
return SearchResult::paginated(
78+
$this->l10n->t('LibreSign documents'),
79+
$results,
80+
$offset + $limit
81+
);
82+
}
83+
84+
/**
85+
* Format a File entity as a SearchResultEntry
86+
*
87+
* @param File $file The file entity to format
88+
* @param IUser $user Current user
89+
* @return SearchResultEntry Formatted search result entry
90+
*/
91+
private function formatResult(File $file, IUser $user): SearchResultEntry {
92+
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
93+
$thumbnailUrl = '';
94+
$subline = '';
95+
$icon = '';
96+
$path = '';
97+
98+
try {
99+
$nodes = $userFolder->getById($file->getNodeId());
100+
if (!empty($nodes)) {
101+
$node = array_shift($nodes);
102+
103+
$icon = $this->mimeTypeDetector->mimeTypeIcon($node->getMimetype());
104+
105+
$thumbnailUrl = $this->urlGenerator->linkToRouteAbsolute(
106+
'core.Preview.getPreviewByFileId',
107+
[
108+
'x' => 32,
109+
'y' => 32,
110+
'fileId' => $node->getId()
111+
]
112+
);
113+
114+
$path = $userFolder->getRelativePath($node->getPath());
115+
$subline = $this->formatSubline($path);
116+
}
117+
} catch (\Exception $e) {
118+
}
119+
120+
$link = $this->urlGenerator->linkToRoute(
121+
'files.View.showFile',
122+
['fileid' => $file->getNodeId()]
123+
);
124+
125+
$searchResultEntry = new SearchResultEntry(
126+
$thumbnailUrl,
127+
$file->getName() ?? $this->l10n->t('Unnamed document'),
128+
$subline,
129+
$this->urlGenerator->getAbsoluteURL($link),
130+
$icon,
131+
);
132+
133+
$searchResultEntry->addAttribute('fileId', (string)$file->getNodeId());
134+
$searchResultEntry->addAttribute('path', $path);
135+
136+
return $searchResultEntry;
137+
}
138+
139+
private function formatSubline(string $path): string {
140+
if (strrpos($path, '/') > 0) {
141+
$path = ltrim(dirname($path), '/');
142+
return $this->l10n->t('in %s', [$path]);
143+
} else {
144+
return '';
145+
}
146+
}
147+
148+
}

0 commit comments

Comments
 (0)