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

LibreSign / libresign / 21362389162

26 Jan 2026 03:01PM UTC coverage: 46.283%. First build
21362389162

Pull #6587

github

web-flow
Merge 8c8ae564c into 507d36d8b
Pull Request #6587: feat: improve async signing error handling

260 of 308 new or added lines in 10 files covered. (84.42%)

7757 of 16760 relevant lines covered (46.28%)

4.99 hits per line

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

49.33
/lib/Db/SignRequestMapper.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\Enum\FileStatus;
12
use OCA\Libresign\Enum\SignRequestStatus;
13
use OCA\Libresign\Helper\Pagination;
14
use OCA\Libresign\Service\IdentifyMethod\IIdentifyMethod;
15
use OCA\Libresign\Service\IdentifyMethodService;
16
use OCP\AppFramework\Db\DoesNotExistException;
17
use OCP\AppFramework\Db\Entity;
18
use OCP\DB\QueryBuilder\IQueryBuilder;
19
use OCP\ICacheFactory;
20
use OCP\IDBConnection;
21
use OCP\IL10N;
22
use OCP\IURLGenerator;
23
use OCP\IUser;
24

25
/**
26
 * Class SignRequestMapper
27
 *
28
 * @package OCA\Libresign\DB
29
 * @template-extends CachedQBMapper<SignRequest>
30
 */
31
class SignRequestMapper extends CachedQBMapper {
32
        private bool $firstNotification = false;
33

34
        public function __construct(
35
                IDBConnection $db,
36
                protected IL10N $l10n,
37
                protected FileMapper $fileMapper,
38
                private IURLGenerator $urlGenerator,
39
                ICacheFactory $cacheFactory,
40
        ) {
41
                parent::__construct($db, $cacheFactory, 'libresign_sign_request');
47✔
42
        }
43

44
        #[\Override]
45
        public function update(Entity $entity): SignRequest {
46
                $entityId = $entity->getId();
13✔
47
                if ($entityId !== null) {
13✔
48
                        $cached = $this->cacheGet('id:' . $entityId);
13✔
49
                        if ($cached instanceof SignRequest) {
13✔
50
                                $uuid = $cached->getUuid();
13✔
51
                                if ($uuid !== '') {
13✔
52
                                        $this->cacheRemove('uuid:' . $uuid);
13✔
53
                                }
54
                        }
55
                }
56
                /** @var SignRequest */
57
                return parent::update($entity);
13✔
58
        }
59

60
        #[\Override]
61
        protected function cacheEntity(Entity $entity): void {
62
                parent::cacheEntity($entity);
16✔
63
                if ($entity instanceof SignRequest) {
16✔
64
                        $uuid = $entity->getUuid();
16✔
65
                        if ($uuid !== '') {
16✔
66
                                $this->cacheSet('uuid:' . $uuid, $entity->getId());
16✔
67
                        }
68
                }
69
        }
70

71
        /**
72
         * @return boolean true when is the first notification
73
         */
74
        public function incrementNotificationCounter(SignRequest $signRequest, string $method): bool {
75
                $this->db->beginTransaction();
13✔
76
                try {
77
                        $fromDatabase = $this->getById($signRequest->getId());
13✔
78
                        $metadata = $fromDatabase->getMetadata();
13✔
79
                        if (!isset($metadata['notify'])) {
13✔
80
                                $this->firstNotification = true;
13✔
81
                        }
82

83
                        $notificationEntry = [
13✔
84
                                'method' => $method,
13✔
85
                                'date' => time(),
13✔
86
                        ];
13✔
87

88
                        if (!empty($fromDatabase->getDescription())) {
13✔
89
                                $notificationEntry['description'] = $fromDatabase->getDescription();
×
90
                        }
91

92
                        $metadata['notify'][] = $notificationEntry;
13✔
93
                        $fromDatabase->setMetadata($metadata);
13✔
94
                        $this->update($fromDatabase);
13✔
95
                        $this->db->commit();
13✔
96
                } catch (\Throwable) {
×
97
                        $this->db->rollBack();
×
98
                }
99
                return $this->firstNotification;
13✔
100
        }
101

102
        /**
103
         * Get sign request by UUID
104
         *
105
         * @throws DoesNotExistException
106
         */
107
        public function getByUuid(string $uuid): SignRequest {
108
                if ($uuid !== '') {
5✔
109
                        $cachedId = $this->cacheGet('uuid:' . $uuid);
5✔
110
                        if (is_int($cachedId) || (is_string($cachedId) && ctype_digit($cachedId))) {
5✔
111
                                return $this->getById((int)$cachedId);
4✔
112
                        }
113
                }
114
                $qb = $this->db->getQueryBuilder();
1✔
115

116
                $qb->select('*')
1✔
117
                        ->from($this->getTableName())
1✔
118
                        ->where(
1✔
119
                                $qb->expr()->eq('uuid', $qb->createNamedParameter($uuid))
1✔
120
                        );
1✔
121
                /** @var SignRequest */
122
                $signRequest = $this->findEntity($qb);
1✔
123
                $this->cacheEntity($signRequest);
×
124
                return $signRequest;
×
125
        }
126

127
        /**
128
         * @throws DoesNotExistException
129
         */
130
        public function getByUuidUncached(string $uuid): SignRequest {
NEW
131
                $qb = $this->db->getQueryBuilder();
×
132

NEW
133
                $qb->select('*')
×
NEW
134
                        ->from($this->getTableName())
×
NEW
135
                        ->where(
×
NEW
136
                                $qb->expr()->eq('uuid', $qb->createNamedParameter($uuid))
×
NEW
137
                        );
×
138

139
                /** @var SignRequest */
NEW
140
                return $this->findEntity($qb);
×
141
        }
142

143
        public function getByEmailAndFileId(string $email, int $fileId): SignRequest {
144
                $qb = $this->db->getQueryBuilder();
×
145

146
                $qb->select('*')
×
147
                        ->from($this->getTableName())
×
148
                        ->where(
×
149
                                $qb->expr()->eq('email', $qb->createNamedParameter($email))
×
150
                        )
×
151
                        ->andWhere(
×
152
                                $qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))
×
153
                        );
×
154
                /** @var SignRequest */
155
                $signRequest = $this->findEntity($qb);
×
156
                $this->cacheEntity($signRequest);
×
157
                return $signRequest;
×
158
        }
159

160
        public function getByIdentifyMethodAndFileId(IIdentifyMethod $identifyMethod, int $fileId): SignRequest {
161
                $qb = $this->db->getQueryBuilder();
13✔
162
                $qb->select('sr.*')
13✔
163
                        ->from($this->getTableName(), 'sr')
13✔
164
                        ->join('sr', 'libresign_identify_method', 'im', 'sr.id = im.sign_request_id')
13✔
165
                        ->where($qb->expr()->eq('im.identifier_key', $qb->createNamedParameter($identifyMethod->getEntity()->getIdentifierKey())))
13✔
166
                        ->andWhere($qb->expr()->eq('im.identifier_value', $qb->createNamedParameter($identifyMethod->getEntity()->getIdentifierValue())))
13✔
167
                        ->andWhere($qb->expr()->eq('sr.file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
13✔
168
                /** @var SignRequest */
169
                $signRequest = $this->findEntity($qb);
13✔
170
                $this->cacheEntity($signRequest);
×
171
                return $signRequest;
×
172
        }
173

174
        /**
175
         * Get all signers by fileId
176
         *
177
         * @return SignRequest[]
178
         */
179
        public function getByFileId(int $fileId): array {
180
                $qb = $this->db->getQueryBuilder();
15✔
181

182
                $qb->select('*')
15✔
183
                        ->from($this->getTableName())
15✔
184
                        ->where(
15✔
185
                                $qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))
15✔
186
                        );
15✔
187
                /** @var SignRequest[] */
188
                $signers = $this->findEntities($qb);
15✔
189
                foreach ($signers as $signRequest) {
15✔
190
                        $this->cacheEntity($signRequest);
13✔
191
                }
192
                return $signers;
15✔
193
        }
194

195
        /**
196
         * @throws DoesNotExistException
197
         */
198
        public function getById(int $signRequestId): SignRequest {
199
                $cached = $this->cacheGet('id:' . $signRequestId);
15✔
200
                if ($cached instanceof SignRequest) {
15✔
201
                        return $cached;
14✔
202
                }
203
                $qb = $this->db->getQueryBuilder();
1✔
204

205
                $qb->select('*')
1✔
206
                        ->from($this->getTableName())
1✔
207
                        ->where(
1✔
208
                                $qb->expr()->eq('id', $qb->createNamedParameter($signRequestId, IQueryBuilder::PARAM_INT))
1✔
209
                        );
1✔
210

211
                /** @var SignRequest */
212
                $signRequest = $this->findEntity($qb);
1✔
213
                $this->cacheEntity($signRequest);
1✔
214
                return $signRequest;
1✔
215
        }
216

217
        /**
218
         * Get sign requests of child files from an envelope for the same signer
219
         *
220
         * @return SignRequest[]
221
         */
222
        public function getByEnvelopeChildrenAndIdentifyMethod(int $parentFileId, int $signRequestId): array {
223
                $qb = $this->db->getQueryBuilder();
×
224

225
                $qb->select('sr.*')
×
226
                        ->from('libresign_file', 'f')
×
227
                        ->innerJoin('f', $this->getTableName(), 'sr', $qb->expr()->eq('sr.file_id', 'f.id'))
×
228
                        ->innerJoin('sr', 'libresign_identify_method', 'im', $qb->expr()->eq('im.sign_request_id', 'sr.id'))
×
229
                        ->innerJoin('im', 'libresign_identify_method', 'im2',
×
230
                                $qb->expr()->andX(
×
231
                                        $qb->expr()->eq('im2.sign_request_id', $qb->createNamedParameter($signRequestId, IQueryBuilder::PARAM_INT)),
×
232
                                        $qb->expr()->eq('im2.identifier_key', 'im.identifier_key'),
×
233
                                        $qb->expr()->eq('im2.identifier_value', 'im.identifier_value')
×
234
                                )
×
235
                        )
×
236
                        ->where(
×
237
                                $qb->expr()->eq('f.parent_file_id', $qb->createNamedParameter($parentFileId, IQueryBuilder::PARAM_INT))
×
238
                        );
×
239

240
                /** @var SignRequest[] */
241
                $signRequests = $this->findEntities($qb);
×
242
                foreach ($signRequests as $signRequest) {
×
243
                        $this->cacheEntity($signRequest);
×
244
                }
245
                return $signRequests;
×
246
        }
247

248
        /**
249
         * @return \Generator<IdentifyMethod>
250
         */
251
        public function findRemindersCandidates(): \Generator {
252
                $qb = $this->db->getQueryBuilder();
×
253
                $qb->select(
×
254
                        'sr.id AS sr_id',
×
255
                        'sr.file_id AS sr_file_id',
×
256
                        'sr.uuid AS sr_uuid',
×
257
                        'sr.display_name AS sr_display_name',
×
258
                        'sr.description AS sr_description',
×
259
                        'sr.metadata AS sr_metadata',
×
260
                        'sr.signed_hash AS sr_signed_hash',
×
261
                        'sr.created_at AS sr_created_at',
×
262
                        'sr.signed AS sr_signed',
×
263

264
                        'im.id AS im_id',
×
265
                        'im.mandatory AS im_mandatory',
×
266
                        'im.code AS im_code',
×
267
                        'im.identifier_key AS im_identifier_key',
×
268
                        'im.identifier_value AS im_identifier_value',
×
269
                        'im.attempts AS im_attempts',
×
270
                        'im.identified_at_date AS im_identified_at_date',
×
271
                        'im.last_attempt_date AS im_last_attempt_date',
×
272
                        'im.sign_request_id AS im_sign_request_id',
×
273
                        'im.metadata AS im_metadata',
×
274
                )
×
275
                        ->from('libresign_sign_request', 'sr')
×
276
                        ->join('sr', 'libresign_identify_method', 'im', 'sr.id = im.sign_request_id')
×
277
                        ->join('sr', 'libresign_file', 'f', 'sr.file_id = f.id')
×
278
                        ->where($qb->expr()->isNull('sr.signed'))
×
279
                        ->andWhere($qb->expr()->neq('im.identifier_value', $qb->createNamedParameter('deleted_users')))
×
280
                        ->andWhere($qb->expr()->in('f.status', $qb->createNamedParameter([
×
281
                                FileStatus::ABLE_TO_SIGN->value,
×
282
                                FileStatus::PARTIAL_SIGNED->value
×
283
                        ], IQueryBuilder::PARAM_INT_ARRAY)))
×
284
                        ->setParameter('st', [1,2], IQueryBuilder::PARAM_INT_ARRAY)
×
285
                        ->orderBy('sr.id', 'ASC');
×
286

287
                $result = $qb->executeQuery();
×
288
                try {
289
                        /** @var array<string, mixed> $row */
290
                        while ($row = $result->fetch()) {
×
291
                                $signRequest = new SignRequest();
×
292
                                $identifyMethod = new IdentifyMethod();
×
293
                                foreach ($row as $key => $value) {
×
294
                                        $prop = $identifyMethod->columnToProperty(substr($key, 3));
×
295
                                        if (str_starts_with($key, 'sr_')) {
×
296
                                                $signRequest->{'set' . lcfirst($prop)}($value);
×
297
                                        } else {
298
                                                $identifyMethod->{'set' . lcfirst($prop)}($value);
×
299
                                        }
300
                                }
301
                                $signRequest->resetUpdatedFields();
×
302
                                $identifyMethod->resetUpdatedFields();
×
303
                                $this->cacheEntity($signRequest);
×
304
                                yield $identifyMethod;
×
305
                        }
306
                } finally {
307
                        $result->closeCursor();
×
308
                }
309
        }
310

311
        /**
312
         * Get all signers by multiple fileId
313
         * Includes signers from both the files themselves and their children files (for envelopes)
314
         *
315
         * @return SignRequest[]
316
         */
317
        public function getByMultipleFileId(array $fileId) {
318
                $qb = $this->db->getQueryBuilder();
4✔
319

320
                $qb->select('sr.*')
4✔
321
                        ->from($this->getTableName(), 'sr')
4✔
322
                        ->join('sr', 'libresign_file', 'f', $qb->expr()->eq('sr.file_id', 'f.id'))
4✔
323
                        ->where(
4✔
324
                                $qb->expr()->orX(
4✔
325
                                        $qb->expr()->in('f.id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT_ARRAY)),
4✔
326
                                        $qb->expr()->in('f.parent_file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT_ARRAY))
4✔
327
                                )
4✔
328
                        )
4✔
329
                        ->orderBy('sr.id', 'ASC');
4✔
330

331
                /** @var SignRequest[] */
332
                $signers = $this->findEntities($qb);
4✔
333
                foreach ($signers as $signRequest) {
4✔
334
                        $this->cacheEntity($signRequest);
2✔
335
                }
336
                return $signers;
4✔
337
        }
338

339
        /**
340
         * Get all signers by fileId
341
         *
342
         * @return SignRequest[]
343
         */
344
        public function getByNodeId(int $nodeId) {
345
                $qb = $this->db->getQueryBuilder();
×
346

347
                $qb->select('sr.*')
×
348
                        ->from($this->getTableName(), 'sr')
×
349
                        ->join('sr', 'libresign_file', 'f', 'sr.file_id = f.id')
×
350
                        ->where(
×
351
                                $qb->expr()->eq('f.node_id', $qb->createNamedParameter($nodeId, IQueryBuilder::PARAM_INT))
×
352
                        );
×
353

354
                /** @var SignRequest[] */
355
                $signers = $this->findEntities($qb);
×
356
                foreach ($signers as $signRequest) {
×
357
                        $this->cacheEntity($signRequest);
×
358
                }
359
                return $signers;
×
360
        }
361

362
        /**
363
         * Get all signers by File Uuid
364
         *
365
         * @param string $nodeId
366
         * @return SignRequest[]
367
         */
368
        public function getByFileUuid(string $uuid) {
369
                $qb = $this->db->getQueryBuilder();
×
370

371
                $qb->select('sr.*')
×
372
                        ->from($this->getTableName(), 'sr')
×
373
                        ->join('sr', 'libresign_file', 'f', 'sr.file_id = f.id')
×
374
                        ->where(
×
375
                                $qb->expr()->eq('f.uuid', $qb->createNamedParameter($uuid))
×
376
                        );
×
377

378
                /** @var SignRequest[] */
379
                $signers = $this->findEntities($qb);
×
380
                foreach ($signers as $signRequest) {
×
381
                        $this->cacheEntity($signRequest);
×
382
                }
383
                return $signers;
×
384
        }
385

386
        public function getBySignerUuidAndUserId(string $uuid): SignRequest {
387
                $qb = $this->db->getQueryBuilder();
×
388

389
                $qb->select('sr.*')
×
390
                        ->from($this->getTableName(), 'sr')
×
391
                        ->where(
×
392
                                $qb->expr()->eq('sr.uuid', $qb->createNamedParameter($uuid))
×
393
                        );
×
394

395
                /** @var SignRequest */
396
                $signRequest = $this->findEntity($qb);
×
397
                $this->cacheEntity($signRequest);
×
398
                return $signRequest;
×
399
        }
400

401
        public function getByFileIdAndUserId(int $file_id): SignRequest {
402
                $qb = $this->db->getQueryBuilder();
×
403

404
                $qb->select('sr.*')
×
405
                        ->from($this->getTableName(), 'sr')
×
406
                        ->join('sr', 'libresign_file', 'f', 'sr.file_id = f.id')
×
407
                        ->where(
×
408
                                $qb->expr()->eq('f.node_id', $qb->createNamedParameter($file_id, IQueryBuilder::PARAM_INT))
×
409
                        );
×
410

411
                /** @var SignRequest */
412
                $signRequest = $this->findEntity($qb);
×
413
                $this->cacheEntity($signRequest);
×
414
                return $signRequest;
×
415
        }
416

417
        public function getByFileIdAndEmail(int $file_id, string $email): SignRequest {
418
                $qb = $this->db->getQueryBuilder();
×
419

420
                $qb->select('sr.*')
×
421
                        ->from($this->getTableName(), 'sr')
×
422
                        ->join('sr', 'libresign_file', 'f', 'sr.file_id = f.id')
×
423
                        ->where(
×
424
                                $qb->expr()->eq('f.node_id', $qb->createNamedParameter($file_id, IQueryBuilder::PARAM_INT))
×
425
                        )
×
426
                        ->andWhere(
×
427
                                $qb->expr()->eq('sr.email', $qb->createNamedParameter($email))
×
428
                        );
×
429

430
                /** @var SignRequest */
431
                $signRequest = $this->findEntity($qb);
×
432
                $this->cacheEntity($signRequest);
×
433
                return $signRequest;
×
434
        }
435

436
        public function getByFileIdAndSignRequestId(int $fileId, int $signRequestId): SignRequest {
437
                $qb = $this->db->getQueryBuilder();
2✔
438

439
                $qb->select('sr.*')
2✔
440
                        ->from($this->getTableName(), 'sr')
2✔
441
                        ->where(
2✔
442
                                $qb->expr()->eq('sr.file_id', $qb->createNamedParameter($fileId))
2✔
443
                        )
2✔
444
                        ->andWhere(
2✔
445
                                $qb->expr()->eq('sr.id', $qb->createNamedParameter($signRequestId))
2✔
446
                        );
2✔
447

448
                /** @var SignRequest */
449
                $signRequest = $this->findEntity($qb);
2✔
450
                $this->cacheEntity($signRequest);
2✔
451
                return $signRequest;
2✔
452
        }
453

454
        /**
455
         * @return array{data: list<File>, pagination: Pagination}
456
         */
457
        public function getFilesAssociatedFilesWithMe(
458
                IUser $user,
459
                array $filter,
460
                ?int $page = null,
461
                ?int $length = null,
462
                ?array $sort = [],
463
        ): array {
464
                $filter['email'] = $user->getEMailAddress();
1✔
465
                $filter['length'] = $length;
1✔
466
                $filter['page'] = $page;
1✔
467
                $pagination = $this->getFilesAssociatedFilesWithMeStmt($user->getUID(), $filter, $sort);
1✔
468
                $pagination->setMaxPerPage($length);
1✔
469
                $pagination->setCurrentPage($page);
1✔
470
                $currentPageResults = $pagination->getCurrentPageResults();
1✔
471

472
                $data = [];
1✔
473
                foreach ($currentPageResults as $row) {
1✔
474
                        $file = new File();
×
475
                        $data[] = $file->fromRow($row);
×
476
                }
477
                /** @var array{data: list<File>, pagination: Pagination} */
478
                return [
1✔
479
                        'data' => $data,
1✔
480
                        'pagination' => $pagination,
1✔
481
                ];
1✔
482
        }
483

484
        public function getFilesToSearchProvider(IUser $user, string $fileName, int $limit, int $offset): array {
485
                $filter = [
×
486
                        'page' => ($offset / $limit) + 1,
×
487
                        'length' => $limit,
×
488
                        'fileName' => $fileName,
×
489
                ];
×
490

491
                $sort = [
×
492
                        'sortBy' => 'created_at',
×
493
                        'sortDirection' => 'desc',
×
494
                ];
×
495

496
                $qb = $this->getFilesAssociatedFilesWithMeQueryBuilder($user->getUID(), $filter, false, $sort);
×
497

498
                $result = $qb->executeQuery();
×
499
                $files = [];
×
500

501
                while ($row = $result->fetch()) {
×
502
                        try {
503
                                $file = File::fromRow($row);
×
504

505
                                $files[] = $file;
×
506
                        } catch (\Exception $e) {
×
507
                                continue;
×
508
                        }
509
                }
510

511
                $result->closeCursor();
×
512

513
                return $files;
×
514
        }
515

516
        /**
517
         * @param array<SignRequest> $signRequests
518
         * @return FileElement[][]
519
         */
520
        public function getVisibleElementsFromSigners(array $signRequests): array {
521
                $signRequestIds = array_map(fn (SignRequest $signRequest): int => $signRequest->getId(), $signRequests);
6✔
522
                if (!$signRequestIds) {
6✔
523
                        return [];
2✔
524
                }
525
                $qb = $this->db->getQueryBuilder();
4✔
526

527
                $qb->select('fe.*')
4✔
528
                        ->from('libresign_file_element', 'fe')
4✔
529
                        ->where(
4✔
530
                                $qb->expr()->in('fe.sign_request_id', $qb->createParameter('signRequestIds'))
4✔
531
                        );
4✔
532

533
                $return = [];
4✔
534
                foreach (array_chunk($signRequestIds, 1000) as $signRequestIdsChunk) {
4✔
535
                        $qb->setParameter('signRequestIds', $signRequestIdsChunk, IQueryBuilder::PARAM_INT_ARRAY);
4✔
536
                        $cursor = $qb->executeQuery();
4✔
537
                        while ($row = $cursor->fetch()) {
4✔
538
                                $return[$row['sign_request_id']][] = (new FileElement())->fromRow($row);
×
539
                        }
540
                }
541
                return $return;
4✔
542
        }
543

544
        /**
545
         * @param array<SignRequest> $signRequests
546
         * @return array<array-key, array<array-key, \OCP\AppFramework\Db\Entity&\OCA\Libresign\Db\IdentifyMethod>>
547
         */
548
        public function getIdentifyMethodsFromSigners(array $signRequests): array {
549
                $signRequestIds = array_map(fn (SignRequest $signRequest): int => $signRequest->getId(), $signRequests);
4✔
550
                if (!$signRequestIds) {
4✔
551
                        return [];
2✔
552
                }
553
                $qb = $this->db->getQueryBuilder();
2✔
554
                $qb->select('im.*')
2✔
555
                        ->from('libresign_identify_method', 'im')
2✔
556
                        ->where(
2✔
557
                                $qb->expr()->in('im.sign_request_id', $qb->createParameter('signRequestIds'))
2✔
558
                        )
2✔
559
                        ->orderBy('im.mandatory', 'DESC')
2✔
560
                        ->addOrderBy('im.identified_at_date', 'ASC');
2✔
561

562
                $return = [];
2✔
563
                foreach (array_chunk($signRequestIds, 1000) as $signRequestIdsChunk) {
2✔
564
                        $qb->setParameter('signRequestIds', $signRequestIdsChunk, IQueryBuilder::PARAM_INT_ARRAY);
2✔
565
                        $cursor = $qb->executeQuery();
2✔
566
                        while ($row = $cursor->fetch()) {
2✔
567
                                $identifyMethod = new IdentifyMethod();
2✔
568
                                $return[$row['sign_request_id']][$row['identifier_key']] = $identifyMethod->fromRow($row);
2✔
569
                        }
570
                }
571
                return $return;
2✔
572
        }
573

574
        public function getMyLibresignFile(string $userId, ?array $filter = []): File {
575
                $qb = $this->getFilesAssociatedFilesWithMeQueryBuilder(
×
576
                        userId: $userId,
×
577
                        filter: $filter,
×
578
                );
×
579
                $cursor = $qb->executeQuery();
×
580
                $row = $cursor->fetch();
×
581
                if (!$row) {
×
582
                        throw new DoesNotExistException('LibreSign file not found');
×
583
                }
584

585
                $file = new File();
×
586
                return $file->fromRow($row);
×
587
        }
588

589
        private function getFilesAssociatedFilesWithMeQueryBuilder(
590
                string $userId,
591
                array $filter = [],
592
                bool $count = false,
593
                array $sort = [],
594
        ): IQueryBuilder {
595
                $qb = $this->db->getQueryBuilder();
1✔
596
                $qb->from('libresign_file', 'f')
1✔
597
                        ->leftJoin('f', 'libresign_sign_request', 'sr', 'sr.file_id = f.id')
1✔
598
                        ->leftJoin('f', 'libresign_identify_method', 'im', $qb->expr()->eq('sr.id', 'im.sign_request_id'))
1✔
599
                        ->leftJoin('f', 'libresign_id_docs', 'id', 'id.file_id = f.id');
1✔
600
                if ($count) {
1✔
601
                        $qb->select($qb->func()->count())
1✔
602
                                ->setFirstResult(0)
1✔
603
                                ->setMaxResults(null);
1✔
604
                } else {
605
                        $qb->select(
1✔
606
                                'f.id',
1✔
607
                                'f.node_id',
1✔
608
                                'f.signed_node_id',
1✔
609
                                'f.user_id',
1✔
610
                                'f.uuid',
1✔
611
                                'f.name',
1✔
612
                                'f.status',
1✔
613
                                'f.metadata',
1✔
614
                                'f.created_at',
1✔
615
                                'f.signature_flow',
1✔
616
                                'f.docmdp_level',
1✔
617
                                'f.node_type',
1✔
618
                                'f.parent_file_id'
1✔
619
                        )
1✔
620
                                ->groupBy(
1✔
621
                                        'f.id',
1✔
622
                                        'f.node_id',
1✔
623
                                        'f.signed_node_id',
1✔
624
                                        'f.user_id',
1✔
625
                                        'f.uuid',
1✔
626
                                        'f.name',
1✔
627
                                        'f.status',
1✔
628
                                        'f.created_at',
1✔
629
                                        'f.signature_flow',
1✔
630
                                        'f.docmdp_level',
1✔
631
                                        'f.node_type',
1✔
632
                                        'f.parent_file_id'
1✔
633
                                );
1✔
634
                        // metadata is a json column, the right way is to use f.metadata::text
635
                        // when the database is PostgreSQL. The problem is that the command
636
                        // addGroupBy add quotes over all text send as argument. With
637
                        // PostgreSQL json columns don't have problem if not added to group by.
638
                        if ($qb->getConnection()->getDatabaseProvider() !== IDBConnection::PLATFORM_POSTGRES) {
1✔
639
                                $qb->addGroupBy('f.metadata');
1✔
640
                        }
641
                        if (isset($filter['length']) && isset($filter['page'])) {
1✔
642
                                $qb->setFirstResult($filter['length'] * ($filter['page'] - 1));
1✔
643
                                $qb->setMaxResults($filter['length']);
1✔
644
                        }
645
                }
646

647
                $or = [
1✔
648
                        $qb->expr()->eq('f.user_id', $qb->createNamedParameter($userId)),
1✔
649
                        $qb->expr()->andX(
1✔
650
                                $qb->expr()->eq('im.identifier_key', $qb->createNamedParameter(IdentifyMethodService::IDENTIFY_ACCOUNT)),
1✔
651
                                $qb->expr()->eq('im.identifier_value', $qb->createNamedParameter($userId)),
1✔
652
                                $qb->expr()->neq('f.status', $qb->createNamedParameter(FileStatus::DRAFT->value)),
1✔
653
                                $qb->expr()->neq('sr.status', $qb->createNamedParameter(SignRequestStatus::DRAFT->value)),
1✔
654
                        )
1✔
655
                ];
1✔
656
                $qb->where($qb->expr()->orX(...$or))
1✔
657
                        ->andWhere($qb->expr()->isNull('id.id'));
1✔
658

659
                if ($filter) {
1✔
660
                        if (isset($filter['email']) && filter_var($filter['email'], FILTER_VALIDATE_EMAIL)) {
1✔
661
                                $or[] = $qb->expr()->andX(
×
662
                                        $qb->expr()->eq('im.identifier_key', $qb->createNamedParameter(IdentifyMethodService::IDENTIFY_EMAIL)),
×
663
                                        $qb->expr()->eq('im.identifier_value', $qb->createNamedParameter($filter['email']))
×
664
                                );
×
665
                        }
666
                        if (!empty($filter['signer_uuid']) && !empty($filter['parentFileId'])) {
1✔
667
                                $qb->leftJoin('f', 'libresign_file', 'parent', $qb->expr()->eq('f.parent_file_id', 'parent.id'));
×
668
                                $qb->innerJoin('parent', 'libresign_sign_request', 'psr', $qb->expr()->eq('psr.file_id', 'parent.id'))
×
669
                                        ->andWhere($qb->expr()->eq('psr.uuid', $qb->createNamedParameter($filter['signer_uuid'])));
×
670
                        } elseif (!empty($filter['signer_uuid'])) {
1✔
671
                                $qb->andWhere(
×
672
                                        $qb->expr()->eq('sr.uuid', $qb->createNamedParameter($filter['signer_uuid']))
×
673
                                );
×
674
                        }
675
                        if (!empty($filter['nodeIds'])) {
1✔
676
                                $qb->andWhere(
×
677
                                        $qb->expr()->orX(
×
678
                                                $qb->expr()->in('f.node_id', $qb->createNamedParameter($filter['nodeIds'], IQueryBuilder::PARAM_INT_ARRAY)),
×
679
                                                $qb->expr()->in('f.signed_node_id', $qb->createNamedParameter($filter['nodeIds'], IQueryBuilder::PARAM_INT_ARRAY))
×
680
                                        )
×
681
                                );
×
682
                        }
683
                        if (!empty($filter['fileIds'])) {
1✔
684
                                $qb->andWhere(
×
685
                                        $qb->expr()->in('f.id', $qb->createNamedParameter($filter['fileIds'], IQueryBuilder::PARAM_INT_ARRAY))
×
686
                                );
×
687
                        }
688
                        if (!empty($filter['status'])) {
1✔
689
                                $qb->andWhere(
×
690
                                        $qb->expr()->in('f.status', $qb->createNamedParameter($filter['status'], IQueryBuilder::PARAM_INT_ARRAY))
×
691
                                );
×
692
                        }
693
                        if (!empty($filter['start'])) {
1✔
694
                                $start = (new \DateTime('@' . $filter['start'], new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');
×
695
                                $qb->andWhere(
×
696
                                        $qb->expr()->gte('f.created_at', $qb->createNamedParameter($start, IQueryBuilder::PARAM_STR))
×
697
                                );
×
698
                        }
699
                        if (!empty($filter['end'])) {
1✔
700
                                $end = (new \DateTime('@' . $filter['end'], new \DateTimeZone('UTC')))->format('Y-m-d H:i:s');
×
701
                                $qb->andWhere(
×
702
                                        $qb->expr()->lte('f.created_at', $qb->createNamedParameter($end, IQueryBuilder::PARAM_STR))
×
703
                                );
×
704
                        }
705
                        if (!empty($filter['fileName'])) {
1✔
706
                                $qb->andWhere(
×
707
                                        $qb->expr()->like('f.name', $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($filter['fileName']) . '%'))
×
708
                                );
×
709
                        }
710
                        if (!empty($filter['parentFileId'])) {
1✔
711
                                $qb->andWhere(
×
712
                                        $qb->expr()->eq('f.parent_file_id', $qb->createNamedParameter($filter['parentFileId'], IQueryBuilder::PARAM_INT))
×
713
                                );
×
714
                        } else {
715
                                $qb->andWhere($qb->expr()->isNull('f.parent_file_id'));
1✔
716
                        }
717
                }
718

719
                if (!empty($sort['sortBy']) && !empty($sort['sortDirection'])) {
1✔
720
                        switch ($sort['sortBy']) {
×
721
                                case 'name':
×
722
                                case 'status':
×
723
                                        $qb->orderBy(
×
724
                                                $qb->func()->lower('f.' . $sort['sortBy']),
×
725
                                                $sort['sortDirection'] == 'asc' ? 'asc' : 'desc'
×
726
                                        );
×
727
                                        break;
×
728
                                case 'created_at':
×
729
                                        $qb->orderBy(
×
730
                                                'f.' . $sort['sortBy'],
×
731
                                                $sort['sortDirection'] == 'asc' ? 'asc' : 'desc'
×
732
                                        );
×
733
                        }
734
                }
735

736
                return $qb;
1✔
737
        }
738

739
        private function getFilesAssociatedFilesWithMeStmt(
740
                string $userId,
741
                ?array $filter = [],
742
                ?array $sort = [],
743
        ): Pagination {
744
                $qb = $this->getFilesAssociatedFilesWithMeQueryBuilder($userId, $filter, false, $sort);
1✔
745

746
                $countQb = $this->getFilesAssociatedFilesWithMeQueryBuilder(
1✔
747
                        userId: $userId,
1✔
748
                        filter: $filter,
1✔
749
                        count: true,
1✔
750
                );
1✔
751

752
                $pagination = new Pagination($qb, $this->urlGenerator, $countQb);
1✔
753
                return $pagination;
1✔
754
        }
755

756
        public function getTextOfSignerStatus(int $status): string {
757
                return SignRequestStatus::from($status)->getLabel($this->l10n);
4✔
758
        }
759
}
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