• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LibreSign / libresign / 20439054098

22 Dec 2025 05:20PM UTC coverage: 43.411%. First build
20439054098

Pull #6243

github

web-flow
Merge 0dd4d80b6 into 6d6fcf9aa
Pull Request #6243: feat: unified search

0 of 99 new or added lines in 1 file covered. (0.0%)

5936 of 13674 relevant lines covered (43.41%)

5.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lib/Search/FileSearchProvider.php
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 OC\Files\FileInfo;
13
use OCA\Libresign\AppInfo\Application;
14
use OCA\Libresign\Db\File;
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
        ) {
NEW
36
        }
×
37

38
        #[\Override]
39
        public function getId(): string {
NEW
40
                return 'libresign_files';
×
41
        }
42

43
        #[\Override]
44
        public function getName(): string {
NEW
45
                return $this->l10n->t('LibreSign documents');
×
46
        }
47

48
        #[\Override]
49
        public function getOrder(string $route, array $routeParameters): int {
NEW
50
                if (strpos($route, Application::APP_ID . '.') === 0) {
×
NEW
51
                        return 0;
×
52
                }
NEW
53
                return 10;
×
54
        }
55

56
        #[\Override]
57
        public function search(IUser $user, ISearchQuery $query): SearchResult {
NEW
58
                if (!$this->appManager->isEnabledForUser(Application::APP_ID, $user)) {
×
NEW
59
                        return SearchResult::complete($this->l10n->t('LibreSign documents'), []);
×
60
                }
61

NEW
62
                $term = $query->getTerm();
×
NEW
63
                $limit = $query->getLimit();
×
NEW
64
                $offset = $query->getCursor();
×
65

66
                try {
NEW
67
                        $files = $this->searchFiles($user, $term, $limit, (int)$offset);
×
NEW
68
                } catch (\Exception $e) {
×
NEW
69
                        return SearchResult::complete($this->l10n->t('LibreSign documents'), []);
×
70
                }
71

NEW
72
                $results = array_map(function (File $file) use ($user) {
×
NEW
73
                        return $this->formatResult($file, $user);
×
NEW
74
                }, $files);
×
75

NEW
76
                return SearchResult::paginated(
×
NEW
77
                        $this->l10n->t('LibreSign documents'),
×
NEW
78
                        $results,
×
NEW
79
                        $offset + $limit
×
NEW
80
                );
×
81
        }
82

83
        /**
84
         * Search for LibreSign files matching the search term
85
         *
86
         * @param IUser $user Current user
87
         * @param string $term Search term
88
         * @param int $limit Maximum number of results
89
         * @param int $offset Offset for pagination
90
         * @return File[] Array of File entities
91
         */
92
        private function searchFiles(IUser $user, string $term, int $limit, int $offset): array {
NEW
93
                $qb = $this->db->getQueryBuilder();
×
94

NEW
95
                $qb->select('*')
×
NEW
96
                        ->from('libresign_file')
×
NEW
97
                        ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID())))
×
NEW
98
                        ->setMaxResults($limit)
×
NEW
99
                        ->setFirstResult($offset);
×
100

NEW
101
                if (!empty($term)) {
×
NEW
102
                        $qb->andWhere(
×
NEW
103
                                $qb->expr()->like('name', $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($term) . '%'))
×
NEW
104
                        );
×
105
                }
106

NEW
107
                $qb->orderBy('created_at', 'DESC');
×
108

NEW
109
                $result = $qb->executeQuery();
×
NEW
110
                $files = [];
×
111

NEW
112
                while ($row = $result->fetch()) {
×
113
                        try {
NEW
114
                                $file = new File();
×
NEW
115
                                $file->setId((int)$row['id']);
×
NEW
116
                                $file->setUserId($row['user_id']);
×
NEW
117
                                $file->setNodeId((int)($row['node_id'] ?? 0));
×
NEW
118
                                $file->setName($row['name'] ?? '');
×
NEW
119
                                $file->setStatus((int)($row['status'] ?? 0));
×
NEW
120
                                $file->setCreatedAt($row['created_at'] ?? '');
×
NEW
121
                                $files[] = $file;
×
NEW
122
                        } catch (\Exception $e) {
×
NEW
123
                                continue;
×
124
                        }
125
                }
126

NEW
127
                $result->closeCursor();
×
128

NEW
129
                return $files;
×
130
        }
131

132
        /**
133
         * Format a File entity as a SearchResultEntry
134
         *
135
         * @param File $file The file entity to format
136
         * @param IUser $user Current user
137
         * @return SearchResultEntry Formatted search result entry
138
         */
139
        private function formatResult(File $file, IUser $user): SearchResultEntry {
NEW
140
                $userFolder = $this->rootFolder->getUserFolder($user->getUID());
×
NEW
141
                $thumbnailUrl = '';
×
NEW
142
                $subline = '';
×
NEW
143
                $icon = '';
×
NEW
144
                $path = '';
×
145

146
                try {
NEW
147
                        $nodes = $userFolder->getById($file->getNodeId());
×
NEW
148
                        if (!empty($nodes)) {
×
NEW
149
                                $node = array_shift($nodes);
×
150

NEW
151
                                $icon = $node->getMimetype() === FileInfo::MIMETYPE_FOLDER
×
NEW
152
                                        ? 'icon-folder'
×
NEW
153
                                        : $this->mimeTypeDetector->mimeTypeIcon($node->getMimetype());
×
154

NEW
155
                                $thumbnailUrl = $this->urlGenerator->linkToRouteAbsolute(
×
NEW
156
                                        'core.Preview.getPreviewByFileId',
×
NEW
157
                                        [
×
NEW
158
                                                'x' => 32,
×
NEW
159
                                                'y' => 32,
×
NEW
160
                                                'fileId' => $node->getId()
×
NEW
161
                                        ]
×
NEW
162
                                );
×
163

NEW
164
                                $path = $userFolder->getRelativePath($node->getPath());
×
NEW
165
                                $subline = $this->formatSubline($path);
×
166
                        }
NEW
167
                } catch (\Exception $e) {
×
168
                }
169

NEW
170
                $status = $this->getStatusLabel($file->getStatus());
×
NEW
171
                if ($status) {
×
NEW
172
                        $subline .= $subline ? ' • ' . $status : $status;
×
173
                }
174

NEW
175
                $link = $this->urlGenerator->linkToRoute(
×
NEW
176
                        'files.View.showFile',
×
NEW
177
                        ['fileid' => $file->getNodeId()]
×
NEW
178
                );
×
179

NEW
180
                $searchResultEntry = new SearchResultEntry(
×
NEW
181
                        $thumbnailUrl,
×
NEW
182
                        $file->getName() ?? $this->l10n->t('Unnamed document'),
×
NEW
183
                        $subline,
×
NEW
184
                        $this->urlGenerator->getAbsoluteURL($link),
×
NEW
185
                        $icon,
×
NEW
186
                );
×
187

NEW
188
                $searchResultEntry->addAttribute('fileId', (string)$file->getNodeId());
×
NEW
189
                $searchResultEntry->addAttribute('path', $path);
×
190

NEW
191
                return $searchResultEntry;
×
192
        }
193

194
        /**
195
         * Format the subline showing file location
196
         *
197
         * @param string $path File path
198
         * @return string Formatted subline text
199
         */
200
        private function formatSubline(string $path): string {
NEW
201
                if (strrpos($path, '/') > 0) {
×
NEW
202
                        $path = ltrim(dirname($path), '/');
×
NEW
203
                        return $this->l10n->t('in %s', [$path]);
×
204
                } else {
NEW
205
                        return '';
×
206
                }
207
        }
208

209
        /**
210
         * Get the translated label for a file status
211
         *
212
         * @param int|null $status File status code
213
         * @return string Translated status label
214
         */
215
        private function getStatusLabel(?int $status): string {
NEW
216
                return match ($status) {
×
NEW
217
                        File::STATUS_DRAFT => $this->l10n->t('Draft'),
×
NEW
218
                        File::STATUS_ABLE_TO_SIGN => $this->l10n->t('Able to sign'),
×
NEW
219
                        File::STATUS_PARTIAL_SIGNED => $this->l10n->t('Partially signed'),
×
NEW
220
                        File::STATUS_SIGNED => $this->l10n->t('Signed'),
×
NEW
221
                        File::STATUS_DELETED => $this->l10n->t('Deleted'),
×
NEW
222
                        default => '',
×
NEW
223
                };
×
224
        }
225
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc