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

LibreSign / libresign / 19892877141

03 Dec 2025 11:52AM UTC coverage: 41.414%. First build
19892877141

Pull #5965

github

web-flow
Merge 33d171749 into 6ee4c16d1
Pull Request #5965: feat: id docs sorting

9 of 29 new or added lines in 4 files covered. (31.03%)

5137 of 12404 relevant lines covered (41.41%)

4.15 hits per line

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

44.0
/lib/Db/IdDocsMapper.php
1
<?php
2

3
declare(strict_types=1);
4
/**
5
 * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8

9
namespace OCA\Libresign\Db;
10

11
use OCA\Libresign\Helper\Pagination;
12
use OCP\AppFramework\Db\QBMapper;
13
use OCP\DB\QueryBuilder\IQueryBuilder;
14
use OCP\DB\Types;
15
use OCP\IDBConnection;
16
use OCP\IL10N;
17
use OCP\IURLGenerator;
18

19
/**
20
 * Class FileMapper
21
 *
22
 * @package OCA\Libresign\DB
23
 * @template-extends QBMapper<IdDocs>
24
 */
25
class IdDocsMapper extends QBMapper {
26
        public function __construct(
27
                IDBConnection $db,
28
                private IURLGenerator $urlGenerator,
29
                private FileMapper $fileMapper,
30
                private SignRequestMapper $signRequestMapper,
31
                private FileTypeMapper $fileTypeMapper,
32
                private IL10N $l10n,
33
        ) {
34
                parent::__construct($db, 'libresign_id_docs');
47✔
35
        }
36

37
        public function save(int $fileId, ?int $signRequestId, ?string $userId, string $fileType): IdDocs {
38
                $idDocs = new IdDocs();
1✔
39
                $idDocs->setFileId($fileId);
1✔
40
                $idDocs->setSignRequestId($signRequestId);
1✔
41
                $idDocs->setUserId($userId);
1✔
42
                $idDocs->setFileType($fileType);
1✔
43
                return $this->insert($idDocs);
1✔
44
        }
45

46
        public function getByUserIdAndFileId(string $userId, int $fileId): IdDocs {
47
                $qb = $this->db->getQueryBuilder();
×
48
                $qb->select('*')
×
49
                        ->from($this->getTableName())
×
50
                        ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
×
51
                        ->andWhere($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
×
52
                return $this->findEntity($qb);
×
53
        }
54

55
        /**
56
         * @return IdDocs[]
57
         */
58
        public function getByUserId(string $userId): array {
59
                $qb = $this->db->getQueryBuilder();
×
60
                $qb->select('*')
×
61
                        ->from($this->getTableName())
×
62
                        ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)));
×
63
                return $this->findEntities($qb);
×
64
        }
65

66
        public function getByUserIdAndNodeId(string $userId, int $nodeId): IdDocs {
67
                $qb = $this->db->getQueryBuilder();
×
68
                $qb->select('id.*')
×
69
                        ->from($this->getTableName(), 'id')
×
70
                        ->join('id', 'libresign_file', 'f', 'f.id = id.file_id')
×
71
                        ->where($qb->expr()->eq('id.user_id', $qb->createNamedParameter($userId)))
×
72
                        ->andWhere($qb->expr()->eq('f.node_id', $qb->createNamedParameter($nodeId, IQueryBuilder::PARAM_INT)));
×
73
                return $this->findEntity($qb);
×
74
        }
75

76
        public function getByFileId(int $fileId): IdDocs {
77
                $qb = $this->db->getQueryBuilder();
×
78
                $qb->select('*')
×
79
                        ->from($this->getTableName())
×
80
                        ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
×
81
                return $this->findEntity($qb);
×
82
        }
83

84
        public function deleteByFileId(int $fileId): void {
85
                $qb = $this->db->getQueryBuilder();
×
86
                $qb->delete($this->getTableName())
×
87
                        ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
×
88
                $qb->executeStatement();
×
89
        }
90

91
        public function getByUserAndType(string $userId, string $fileType): ?IdDocs {
92
                $qb = $this->db->getQueryBuilder();
1✔
93
                $qb->select('*')
1✔
94
                        ->from($this->getTableName())
1✔
95
                        ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
1✔
96
                        ->andWhere($qb->expr()->eq('file_type', $qb->createNamedParameter($fileType)));
1✔
97
                try {
98
                        return $this->findEntity($qb);
1✔
99
                } catch (\Throwable) {
1✔
100
                        return null;
1✔
101
                }
102
        }
103

104
        public function list(array $filter, ?int $page = null, ?int $length = null, array $sort = []): array {
105
                $filter['length'] = $length;
1✔
106
                $filter['page'] = $page;
1✔
107
                $filter['sort'] = $sort;
1✔
108
                $pagination = $this->getDocs($filter);
1✔
109
                $pagination->setMaxPerPage($length);
1✔
110
                $pagination->setCurrentPage($page);
1✔
111
                $currentPageResults = $pagination->getCurrentPageResults();
1✔
112

113
                $url = $this->urlGenerator->linkToRoute('libresign.page.getPdf', ['uuid' => '_replace_']);
1✔
114
                $url = str_replace('_replace_', '', $url);
1✔
115

116
                $data = [];
1✔
117
                $fileIds = [];
1✔
118

119
                foreach ($currentPageResults as $row) {
1✔
120
                        $fileIds[] = $row['id'];
×
121
                        $data[] = $this->formatListRow($row, $url);
×
122
                }
123
                $signers = $this->signRequestMapper->getByMultipleFileId($fileIds);
1✔
124
                $return['data'] = $this->assocFileToSignRequestAndFormat($data, $signers);
1✔
125
                $return['pagination'] = $pagination;
1✔
126
                return $return;
1✔
127
        }
128

129
        private function getQueryBuilder(array $filter = [], bool $count = false): IQueryBuilder {
130
                $qb = $this->db->getQueryBuilder();
1✔
131
                if ($count) {
1✔
132
                        $qb->select($qb->func()->count())
1✔
133
                                ->setFirstResult(0)
1✔
134
                                ->setMaxResults(null);
1✔
135
                } else {
136
                        $qb
1✔
137
                                ->select(
1✔
138
                                        'f.id',
1✔
139
                                        'f.uuid',
1✔
140
                                        'f.name',
1✔
141
                                        'f.callback',
1✔
142
                                        'f.status',
1✔
143
                                        'f.node_id',
1✔
144
                                        'id.file_type',
1✔
145
                                        'f.created_at',
1✔
146
                                )
1✔
147
                                ->groupBy(
1✔
148
                                        'f.id',
1✔
149
                                        'f.uuid',
1✔
150
                                        'f.name',
1✔
151
                                        'f.callback',
1✔
152
                                        'f.status',
1✔
153
                                        'f.node_id',
1✔
154
                                        'f.created_at',
1✔
155
                                        'id.file_type',
1✔
156
                                );
1✔
157
                        if (isset($filter['length']) && isset($filter['page'])) {
1✔
158
                                $qb->setFirstResult($filter['length'] * ($filter['page'] - 1));
1✔
159
                                $qb->setMaxResults($filter['length']);
1✔
160
                        }
161

162
                        if (!empty($filter['sort'])) {
1✔
NEW
163
                                $allowedSortFields = [
×
NEW
164
                                        'owner' => 'u.displayname',
×
NEW
165
                                        'file_type' => 'id.file_type',
×
NEW
166
                                        'status' => 'f.status',
×
NEW
167
                                        'created_at' => 'f.created_at',
×
NEW
168
                                ];
×
169

NEW
170
                                foreach ($filter['sort'] as $field => $direction) {
×
NEW
171
                                        if (!isset($allowedSortFields[$field])) {
×
NEW
172
                                                continue;
×
173
                                        }
NEW
174
                                        $direction = strtoupper($direction) === 'ASC' ? 'ASC' : 'DESC';
×
NEW
175
                                        $qb->addOrderBy($allowedSortFields[$field], $direction);
×
176
                                }
177
                        } else {
178
                                $qb->orderBy('f.created_at', 'DESC');
1✔
179
                        }
180
                }
181
                $qb
1✔
182
                        ->from($this->getTableName(), 'id')
1✔
183
                        ->join('id', 'libresign_file', 'f', 'f.id = id.file_id');
1✔
184

185
                $needsUserJoin = !$count || !empty($filter['userId']) || (!empty($filter['sort']) && isset($filter['sort']['owner']));
1✔
186

187
                if ($needsUserJoin) {
1✔
188
                        if (!$count) {
1✔
189
                                $qb->selectAlias('u.uid_lower', 'account_uid')
1✔
190
                                        ->selectAlias('u.displayname', 'account_displayname')
1✔
191
                                        ->addGroupBy('u.uid_lower')
1✔
192
                                        ->addGroupBy('u.displayname');
1✔
193
                        }
194
                        $joinType = !empty($filter['userId']) ? 'join' : 'leftJoin';
1✔
195
                        $qb->$joinType('id', 'users', 'u', 'id.user_id = u.uid');
1✔
196
                }
197

198
                if (!empty($filter['userId'])) {
1✔
199
                        $qb->where(
×
200
                                $qb->expr()->eq('id.user_id', $qb->createNamedParameter($filter['userId'])),
×
201
                        );
×
202
                }
203
                if (!empty($filter['approved'])) {
1✔
204
                        if ($filter['approved'] === 'yes') {
×
205
                                $qb->andWhere(
×
206
                                        $qb->expr()->eq('f.status', $qb->createNamedParameter(File::STATUS_SIGNED, Types::INTEGER)),
×
207
                                );
×
208
                        }
209
                }
210
                return $qb;
1✔
211
        }
212

213
        private function getDocs(array $filter = []): Pagination {
214
                $qb = $this->getQueryBuilder(
1✔
215
                        filter: $filter,
1✔
216
                );
1✔
217
                $countQb = $this->getQueryBuilder(
1✔
218
                        filter: $filter,
1✔
219
                        count: true,
1✔
220
                );
1✔
221

222
                $pagination = new Pagination($qb, $this->urlGenerator, $countQb);
1✔
223
                return $pagination;
1✔
224
        }
225

226
        private function formatListRow(array $row, string $url): array {
227
                $row['nodeId'] = (int)$row['node_id'];
×
228
                $row['status'] = (int)$row['status'];
×
229
                $row['statusText'] = $this->getIdDocStatusText((int)$row['status']);
×
230
                $row['account'] = [
×
231
                        'uid' => $row['account_uid'],
×
232
                        'displayName' => $row['account_displayname']
×
233
                ];
×
234
                $row['file_type'] = [
×
235
                        'type' => $row['file_type'],
×
236
                        'name' => $this->fileTypeMapper->getNameOfType($row['file_type']),
×
237
                        'description' => $this->fileTypeMapper->getDescriptionOfType($row['file_type']),
×
238
                        'key' => $row['file_type'],
×
239
                ];
×
240
                $row['created_at'] = (new \DateTime())
×
241
                        ->setTimestamp((int)$row['created_at'])
×
242
                        ->format('Y-m-d H:i:s');
×
243
                $row['file'] = [
×
244
                        'name' => $row['name'],
×
245
                        'status' => $row['status'],
×
246
                        'statusText' => $this->getIdDocStatusText((int)$row['status']),
×
247
                        'created_at' => $row['created_at'],
×
248
                        'file' => [
×
249
                                'type' => 'pdf',
×
250
                                'nodeId' => (int)$row['node_id'],
×
251
                                'url' => $url . $row['uuid'],
×
252
                        ],
×
253
                        'callback' => $row['callback'],
×
254
                        'uuid' => $row['uuid'],
×
255
                ];
×
256
                unset(
×
257
                        $row['node_id'],
×
258
                        $row['name'],
×
259
                        $row['account_displayname'],
×
260
                        $row['account_uid'],
×
261
                        $row['callback'],
×
262
                        $row['uuid'],
×
263
                        $row['account_uid'],
×
264
                );
×
265
                return $row;
×
266
        }
267

268
        /**
269
         * @param array $files
270
         * @param SignRequest[] $signers
271
         */
272
        private function assocFileToSignRequestAndFormat(array $files, array $signers): array {
273
                foreach ($files as $key => $file) {
1✔
274
                        $totalSigned = 0;
×
275
                        $files[$key]['file']['signers'] = [];
×
276
                        foreach ($signers as $signerKey => $signer) {
×
277
                                if ($signer->getFileId() === $file['id']) {
×
278
                                        $data = [
×
279
                                                'description' => $signer->getDescription(),
×
280
                                                'displayName' => $signer->getDisplayName(),
×
281
                                                'request_sign_date' => (new \DateTime())
×
282
                                                        ->setTimestamp($signer->getCreatedAt()->getTimestamp())
×
283
                                                        ->format('Y-m-d H:i:s'),
×
284
                                                'sign_date' => null,
×
285
                                                'signRequestId' => $signer->getId(),
×
286
                                        ];
×
287
                                        if ($signer->getSigned()) {
×
288
                                                $data['sign_date'] = (new \DateTime())
×
289
                                                        ->setTimestamp($signer->getSigned()->getTimestamp())
×
290
                                                        ->format('Y-m-d H:i:s');
×
291
                                                $totalSigned++;
×
292
                                        }
293
                                        $files[$key]['file']['signers'][] = $data;
×
294
                                        unset($signers[$signerKey]);
×
295
                                }
296
                        }
297
                        unset($files[$key]['id']);
×
298
                }
299
                return $files;
1✔
300
        }
301

302
        private function getIdDocStatusText(int $status): string {
303
                return match ($status) {
×
304
                        File::STATUS_ABLE_TO_SIGN => $this->l10n->t('waiting for approval'),
×
305
                        File::STATUS_SIGNED => $this->l10n->t('approved'),
×
306
                        default => $this->fileMapper->getTextOfStatus($status) ?? '',
×
307
                };
×
308
        }
309
}
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

© 2026 Coveralls, Inc