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

LibreSign / libresign / 21268273482

22 Jan 2026 11:13PM UTC coverage: 45.185%. First build
21268273482

Pull #6526

github

web-flow
Merge 16c32ce29 into 068f2ad0f
Pull Request #6526: fix: progress cache factory

84 of 183 new or added lines in 6 files covered. (45.9%)

7348 of 16262 relevant lines covered (45.19%)

4.95 hits per line

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

0.0
/lib/Controller/PageController.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\Controller;
10

11
use OCA\Libresign\AppInfo\Application;
12
use OCA\Libresign\Db\FileMapper;
13
use OCA\Libresign\Db\SignRequestMapper;
14
use OCA\Libresign\Exception\LibresignException;
15
use OCA\Libresign\Helper\JSActions;
16
use OCA\Libresign\Helper\ValidateHelper;
17
use OCA\Libresign\Middleware\Attribute\PrivateValidation;
18
use OCA\Libresign\Middleware\Attribute\RequireSetupOk;
19
use OCA\Libresign\Middleware\Attribute\RequireSignRequestUuid;
20
use OCA\Libresign\Service\AccountService;
21
use OCA\Libresign\Service\FileService;
22
use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\TokenService;
23
use OCA\Libresign\Service\IdentifyMethodService;
24
use OCA\Libresign\Service\RequestSignatureService;
25
use OCA\Libresign\Service\SessionService;
26
use OCA\Libresign\Service\SignerElementsService;
27
use OCA\Libresign\Service\SignFileService;
28
use OCA\Viewer\Event\LoadViewer;
29
use OCP\AppFramework\Db\DoesNotExistException;
30
use OCP\AppFramework\Http;
31
use OCP\AppFramework\Http\Attribute\AnonRateLimit;
32
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
33
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
34
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
35
use OCP\AppFramework\Http\Attribute\PublicPage;
36
use OCP\AppFramework\Http\ContentSecurityPolicy;
37
use OCP\AppFramework\Http\DataResponse;
38
use OCP\AppFramework\Http\FileDisplayResponse;
39
use OCP\AppFramework\Http\TemplateResponse;
40
use OCP\AppFramework\Services\IInitialState;
41
use OCP\EventDispatcher\IEventDispatcher;
42
use OCP\IAppConfig;
43
use OCP\IL10N;
44
use OCP\IRequest;
45
use OCP\IURLGenerator;
46
use OCP\IUserSession;
47
use OCP\Util;
48
use Psr\Log\LoggerInterface;
49

50
class PageController extends AEnvironmentPageAwareController {
51
        public function __construct(
52
                IRequest $request,
53
                protected IUserSession $userSession,
54
                private SessionService $sessionService,
55
                private IInitialState $initialState,
56
                private AccountService $accountService,
57
                protected SignFileService $signFileService,
58
                protected RequestSignatureService $requestSignatureService,
59
                private SignerElementsService $signerElementsService,
60
                protected IL10N $l10n,
61
                private IdentifyMethodService $identifyMethodService,
62
                private IAppConfig $appConfig,
63
                private FileService $fileService,
64
                private FileMapper $fileMapper,
65
                private SignRequestMapper $signRequestMapper,
66
                private LoggerInterface $logger,
67
                private ValidateHelper $validateHelper,
68
                private IEventDispatcher $eventDispatcher,
69
                private IURLGenerator $urlGenerator,
70
        ) {
71
                parent::__construct(
×
72
                        request: $request,
×
73
                        signFileService: $signFileService,
×
74
                        l10n: $l10n,
×
75
                        userSession: $userSession,
×
76
                );
×
77
        }
78

79
        /**
80
         * Index page
81
         *
82
         * @return TemplateResponse<Http::STATUS_OK, array{}>
83
         *
84
         * 200: OK
85
         */
86
        #[NoAdminRequired]
87
        #[NoCSRFRequired]
88
        #[RequireSetupOk(template: 'main')]
89
        #[FrontpageRoute(verb: 'GET', url: '/')]
90
        public function index(): TemplateResponse {
91
                $this->initialState->provideInitialState('config', $this->accountService->getConfig($this->userSession->getUser()));
×
92
                $this->initialState->provideInitialState('filters', $this->accountService->getConfigFilters($this->userSession->getUser()));
×
93
                $this->initialState->provideInitialState('certificate_engine', $this->accountService->getCertificateEngineName());
×
94

95
                try {
96
                        $this->validateHelper->canRequestSign($this->userSession->getUser());
×
97
                        $this->initialState->provideInitialState('can_request_sign', true);
×
98
                } catch (LibresignException) {
×
99
                        $this->initialState->provideInitialState('can_request_sign', false);
×
100
                }
101

102
                $this->provideSignerSignatues();
×
103
                $this->initialState->provideInitialState('identify_methods', $this->identifyMethodService->getIdentifyMethodsSettings());
×
104
                $this->initialState->provideInitialState('signature_flow', $this->appConfig->getValueString(Application::APP_ID, 'signature_flow', \OCA\Libresign\Enum\SignatureFlow::NONE->value));
×
105
                $this->initialState->provideInitialState('legal_information', $this->appConfig->getValueString(Application::APP_ID, 'legal_information'));
×
106

107
                Util::addScript(Application::APP_ID, 'libresign-main');
×
108

109
                if (class_exists(LoadViewer::class)) {
×
110
                        $this->eventDispatcher->dispatchTyped(new LoadViewer());
×
111
                }
112

113
                $response = new TemplateResponse(Application::APP_ID, 'main');
×
114

115
                $policy = new ContentSecurityPolicy();
×
116
                $policy->allowEvalScript(true);
×
117
                $policy->addAllowedFrameDomain('\'self\'');
×
118
                $response->setContentSecurityPolicy($policy);
×
119

120
                return $response;
×
121
        }
122

123
        /**
124
         * Index page to authenticated users
125
         *
126
         * This router is used to be possible render pages with /f/, is a
127
         * workaround at frontend side to identify pages with authenticated accounts
128
         *
129
         * @return TemplateResponse<Http::STATUS_OK, array{}>
130
         *
131
         * 200: OK
132
         */
133
        #[NoAdminRequired]
134
        #[NoCSRFRequired]
135
        #[RequireSetupOk(template: 'main')]
136
        #[FrontpageRoute(verb: 'GET', url: '/f/')]
137
        public function indexF(): TemplateResponse {
138
                return $this->index();
×
139
        }
140

141
        /**
142
         * Incomplete page
143
         *
144
         * @return TemplateResponse<Http::STATUS_OK, array{}>
145
         *
146
         * 200: OK
147
         */
148
        #[NoAdminRequired]
149
        #[NoCSRFRequired]
150
        #[FrontpageRoute(verb: 'GET', url: '/f/incomplete')]
151
        public function incomplete(): TemplateResponse {
152
                Util::addScript(Application::APP_ID, 'libresign-main');
×
153
                $response = new TemplateResponse(Application::APP_ID, 'main');
×
154
                return $response;
×
155
        }
156

157
        /**
158
         * Incomplete page in full screen
159
         *
160
         * @return TemplateResponse<Http::STATUS_OK, array{}>
161
         *
162
         * 200: OK
163
         */
164
        #[PublicPage]
165
        #[NoCSRFRequired]
166
        #[FrontpageRoute(verb: 'GET', url: '/p/incomplete')]
167
        public function incompleteP(): TemplateResponse {
168
                Util::addScript(Application::APP_ID, 'libresign-main');
×
169
                $response = new TemplateResponse(Application::APP_ID, 'main', [], TemplateResponse::RENDER_AS_BASE);
×
170
                return $response;
×
171
        }
172

173
        /**
174
         * Main page to authenticated signer with a path
175
         *
176
         * The path is used only by frontend
177
         *
178
         * @param string $path The path that was sent from frontend
179
         * @return TemplateResponse<Http::STATUS_OK, array{}>
180
         *
181
         * 200: OK
182
         */
183
        #[NoAdminRequired]
184
        #[NoCSRFRequired]
185
        #[RequireSetupOk(template: 'main')]
186
        #[FrontpageRoute(verb: 'GET', url: '/f/{path}', requirements: ['path' => '.+'])]
187
        public function indexFPath(string $path): TemplateResponse {
188
                if (preg_match('/validation\/(?<uuid>[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/', $path, $matches)) {
×
189

190
                        try {
NEW
191
                                $this->fileService->setFileByUuid($matches['uuid']);
×
192
                        } catch (LibresignException) {
×
193
                                try {
NEW
194
                                        $this->fileService->setFileBySignerUuid($matches['uuid']);
×
NEW
195
                                } catch (LibresignException) {
×
NEW
196
                                        throw new LibresignException(json_encode([
×
NEW
197
                                                'action' => JSActions::ACTION_DO_NOTHING,
×
NEW
198
                                                'errors' => [['message' => $this->l10n->t('Invalid UUID')]],
×
NEW
199
                                        ]), Http::STATUS_NOT_FOUND);
×
200
                                }
201
                        }
202

NEW
203
                        $this->initialState->provideInitialState('file_info',
×
NEW
204
                                $this->fileService
×
NEW
205
                                        ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
NEW
206
                                        ->setHost($this->request->getServerHost())
×
NEW
207
                                        ->setMe($this->userSession->getUser())
×
NEW
208
                                        ->showVisibleElements()
×
NEW
209
                                        ->showSigners()
×
NEW
210
                                        ->showSettings()
×
NEW
211
                                        ->showMessages()
×
NEW
212
                                        ->showValidateFile()
×
NEW
213
                                        ->toArray()
×
NEW
214
                        );
×
215
                } elseif (preg_match('/sign\/(?<uuid>[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/', $path, $matches)) {
×
216
                        try {
217
                                $signRequest = $this->signFileService->getSignRequestByUuid($matches['uuid']);
×
NEW
218
                                if ($signRequest->getStatusEnum() === \OCA\Libresign\Enum\SignRequestStatus::SIGNED) {
×
NEW
219
                                        $file = $this->signFileService->getFile($signRequest->getFileId());
×
NEW
220
                                        $redirectUrl = $this->urlGenerator->linkToRouteAbsolute('libresign.page.indexFPath', [
×
NEW
221
                                                'path' => 'validation/' . $file->getUuid(),
×
NEW
222
                                        ]);
×
NEW
223
                                        throw new LibresignException(json_encode([
×
NEW
224
                                                'action' => JSActions::ACTION_REDIRECT,
×
NEW
225
                                                'redirect' => $redirectUrl,
×
NEW
226
                                        ]), Http::STATUS_SEE_OTHER);
×
227
                                }
228
                                $file = $this->fileService
×
229
                                        ->setFile($this->signFileService->getFile($signRequest->getFileId()))
×
230
                                        ->setMe($this->userSession->getUser())
×
231
                                        ->setSignRequest($signRequest)
×
232
                                        ->showSettings()
×
233
                                        ->toArray();
×
234
                                $this->initialState->provideInitialState('needIdentificationDocuments', $file['settings']['needIdentificationDocuments'] ?? false);
×
235
                                $this->initialState->provideInitialState('identificationDocumentsWaitingApproval', $file['settings']['identificationDocumentsWaitingApproval'] ?? false);
×
NEW
236
                        } catch (LibresignException $e) {
×
NEW
237
                                throw $e;
×
238
                        } catch (\Throwable $e) {
×
239
                                throw new LibresignException(json_encode([
×
240
                                        'action' => JSActions::ACTION_DO_NOTHING,
×
241
                                        'errors' => [['message' => $this->l10n->t('Invalid UUID')]],
×
242
                                ]), Http::STATUS_NOT_FOUND);
×
243
                        }
244
                }
245
                return $this->index();
×
246
        }
247

248

249
        /**
250
         * Sign page to authenticated signer
251
         *
252
         * @param string $uuid Sign request uuid
253
         * @return TemplateResponse<Http::STATUS_OK, array{}>
254
         *
255
         * 200: OK
256
         */
257
        #[NoAdminRequired]
258
        #[NoCSRFRequired]
259
        #[RequireSetupOk]
260
        #[PublicPage]
261
        #[RequireSignRequestUuid(redirectIfSignedToValidation: true)]
262
        #[FrontpageRoute(verb: 'GET', url: '/f/sign/{uuid}')]
263
        public function signF(string $uuid): TemplateResponse {
264
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN_INTERNAL);
×
265
                return $this->index();
×
266
        }
267

268
        /**
269
         * Sign page to authenticated signer with the path of file
270
         *
271
         * The path is used only by frontend
272
         *
273
         * @param string $uuid Sign request uuid
274
         * @return TemplateResponse<Http::STATUS_OK, array{}>
275
         *
276
         * 200: OK
277
         */
278
        #[NoAdminRequired]
279
        #[NoCSRFRequired]
280
        #[RequireSetupOk]
281
        #[PublicPage]
282
        #[RequireSignRequestUuid(redirectIfSignedToValidation: true)]
283
        #[FrontpageRoute(verb: 'GET', url: '/f/sign/{uuid}/{path}', requirements: ['path' => '.+'])]
284
        public function signFPath(string $uuid): TemplateResponse {
285
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN_INTERNAL);
×
286
                return $this->index();
×
287
        }
288

289
        /**
290
         * Sign page to unauthenticated signer
291
         *
292
         * The path is used only by frontend
293
         *
294
         * @param string $uuid Sign request uuid
295
         * @return TemplateResponse<Http::STATUS_OK, array{}>
296
         *
297
         * 200: OK
298
         */
299
        #[NoAdminRequired]
300
        #[NoCSRFRequired]
301
        #[RequireSetupOk]
302
        #[PublicPage]
303
        #[RequireSignRequestUuid(redirectIfSignedToValidation: true)]
304
        #[FrontpageRoute(verb: 'GET', url: '/p/sign/{uuid}/{path}', requirements: ['path' => '.+'])]
305
        public function signPPath(string $uuid): TemplateResponse {
306
                return $this->sign($uuid);
×
307
        }
308

309
        /**
310
         * Sign page to unauthenticated signer
311
         *
312
         * The path is used only by frontend
313
         *
314
         * @param string $uuid Sign request uuid
315
         * @return TemplateResponse<Http::STATUS_OK, array{}>
316
         *
317
         * 200: OK
318
         */
319
        #[PrivateValidation]
320
        #[NoAdminRequired]
321
        #[NoCSRFRequired]
322
        #[RequireSetupOk]
323
        #[PublicPage]
324
        #[RequireSignRequestUuid(redirectIfSignedToValidation: true)]
325
        #[FrontpageRoute(verb: 'GET', url: '/p/sign/{uuid}')]
326
        public function sign(string $uuid): TemplateResponse {
327
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN);
×
328
                $this->initialState->provideInitialState('config',
×
329
                        $this->accountService->getConfig($this->userSession->getUser())
×
330
                );
×
331
                $this->initialState->provideInitialState('filename', $this->getFileEntity()->getName());
×
332
                $file = $this->fileService
×
333
                        ->setFile($this->getFileEntity())
×
334
                        ->setHost($this->request->getServerHost())
×
335
                        ->setMe($this->userSession->getUser())
×
336
                        ->setSignerIdentified()
×
337
                        ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
338
                        ->setSignRequest($this->getSignRequestEntity())
×
339
                        ->showVisibleElements()
×
340
                        ->showSigners()
×
341
                        ->showSettings()
×
342
                        ->toArray();
×
343
                $this->initialState->provideInitialState('config', [
×
344
                        'identificationDocumentsFlow' => $file['settings']['needIdentificationDocuments'] ?? false,
×
345
                ]);
×
346
                $this->initialState->provideInitialState('id', $file['id']);
×
347
                $this->initialState->provideInitialState('nodeId', $file['nodeId']);
×
348
                $this->initialState->provideInitialState('needIdentificationDocuments', $file['settings']['needIdentificationDocuments'] ?? false);
×
349
                $this->initialState->provideInitialState('identificationDocumentsWaitingApproval', $file['settings']['identificationDocumentsWaitingApproval'] ?? false);
×
350
                $this->initialState->provideInitialState('status', $file['status']);
×
351
                $this->initialState->provideInitialState('statusText', $file['statusText']);
×
352
                $this->initialState->provideInitialState('signers', $file['signers']);
×
353
                $this->initialState->provideInitialState('sign_request_uuid', $uuid);
×
354
                $this->provideSignerSignatues();
×
355
                $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH);
×
356
                $this->initialState->provideInitialState('description', $this->getSignRequestEntity()->getDescription() ?? '');
×
357
                if ($this->getFileEntity()->getNodeType() === 'envelope') {
×
358
                        $this->initialState->provideInitialState('pdfs', []);
×
359
                        $this->initialState->provideInitialState('envelopeFiles', $this->getEnvelopeChildFiles());
×
360
                } else {
361
                        $this->initialState->provideInitialState('pdfs', $this->getPdfUrls());
×
362
                        $this->initialState->provideInitialState('envelopeFiles', []);
×
363
                }
364
                $this->initialState->provideInitialState('nodeId', $this->getFileEntity()->getNodeId());
×
365
                $this->initialState->provideInitialState('nodeType', $this->getFileEntity()->getNodeType());
×
366

367
                Util::addScript(Application::APP_ID, 'libresign-external');
×
368
                $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE);
×
369

370
                $policy = new ContentSecurityPolicy();
×
371
                $policy->allowEvalScript(true);
×
372
                $response->setContentSecurityPolicy($policy);
×
373

374
                return $response;
×
375
        }
376

377
        private function provideSignerSignatues(): void {
378
                $signatures = [];
×
379
                if ($this->userSession->getUser()) {
×
380
                        $signatures = $this->signerElementsService->getUserElements($this->userSession->getUser()->getUID());
×
381
                } else {
382
                        $signatures = $this->signerElementsService->getElementsFromSessionAsArray();
×
383
                }
384
                $this->initialState->provideInitialState('user_signatures', $signatures);
×
385
        }
386

387
        /**
388
         * @return string[] Array of PDF URLs
389
         */
390
        private function getPdfUrls(): array {
391
                return $this->signFileService->getPdfUrlsForSigning(
×
392
                        $this->getFileEntity(),
×
393
                        $this->getSignRequestEntity()
×
394
                );
×
395
        }
396

397
        private function getEnvelopeChildFiles(): array {
398
                $childFiles = $this->fileMapper->getChildrenFiles($this->getFileEntity()->getId());
×
399
                $result = [];
×
400

401
                foreach ($childFiles as $childFile) {
×
402

403
                        $childSignRequest = $this->signRequestMapper->getByFileIdAndSignRequestId(
×
404
                                $childFile->getId(),
×
405
                                $this->getSignRequestEntity()->getId()
×
406
                        );
×
407

408
                        $fileData = $this->fileService
×
409
                                ->setFile($childFile)
×
410
                                ->setHost($this->request->getServerHost())
×
411
                                ->setSignerIdentified()
×
412
                                ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
413
                                ->setSignRequest($childSignRequest)
×
414
                                ->showSigners()
×
415
                                ->toArray();
×
416

417
                        $result[] = $fileData;
×
418
                }
419

420
                return $result;
×
421
        }
422

423
        /**
424
         * Show signature page for identification document approval
425
         *
426
         * @param string $uuid File UUID for the identification document approval
427
         * @return TemplateResponse<Http::STATUS_OK, array{}>
428
         *
429
         * 200: OK
430
         * 404: Invalid UUID
431
         */
432
        #[NoAdminRequired]
433
        #[NoCSRFRequired]
434
        #[RequireSetupOk]
435
        #[FrontpageRoute(verb: 'GET', url: '/p/id-docs/approve/{uuid}')]
436
        public function signIdDoc($uuid): TemplateResponse {
437
                try {
438
                        $fileEntity = $this->signFileService->getFileByUuid($uuid);
×
439
                        $this->signFileService->getIdDocById($fileEntity->getId());
×
440
                } catch (DoesNotExistException) {
×
441
                        throw new LibresignException(json_encode([
×
442
                                'action' => JSActions::ACTION_DO_NOTHING,
×
443
                                'errors' => [['message' => $this->l10n->t('Invalid UUID')]],
×
444
                        ]), Http::STATUS_NOT_FOUND);
×
445
                }
446
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN_ID_DOC);
×
447
                $this->initialState->provideInitialState('config',
×
448
                        $this->accountService->getConfig($this->userSession->getUser())
×
449
                );
×
450
                $this->initialState->provideInitialState('signer',
×
451
                        $this->signFileService->getSignerData(
×
452
                                $this->userSession->getUser(),
×
453
                        )
×
454
                );
×
455
                $this->initialState->provideInitialState('identifyMethods',
×
456
                        $this->signFileService->getAvailableIdentifyMethodsFromSettings()
×
457
                );
×
458
                $this->initialState->provideInitialState('filename', $fileEntity->getName());
×
459
                $file = $this->fileService
×
460
                        ->setFile($fileEntity)
×
461
                        ->setHost($this->request->getServerHost())
×
462
                        ->setMe($this->userSession->getUser())
×
463
                        ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
464
                        ->showVisibleElements()
×
465
                        ->showSigners()
×
466
                        ->toArray();
×
467
                $this->initialState->provideInitialState('id', $file['id']);
×
468
                $this->initialState->provideInitialState('nodeId', $file['nodeId']);
×
469
                $this->initialState->provideInitialState('status', $file['status']);
×
470
                $this->initialState->provideInitialState('statusText', $file['statusText']);
×
471
                $this->initialState->provideInitialState('visibleElements', []);
×
472
                $this->initialState->provideInitialState('signers', []);
×
473
                $this->provideSignerSignatues();
×
474
                $signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId());
×
475
                $this->initialState->provideInitialState('signature_methods', $signatureMethods);
×
476
                $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH);
×
477
                $this->initialState->provideInitialState('description', '');
×
478
                $this->initialState->provideInitialState('pdf', [
×
479
                        'url' => $this->signFileService->getFileUrl($fileEntity->getId(), $uuid)
×
480
                ]);
×
481

482
                Util::addScript(Application::APP_ID, 'libresign-external');
×
483
                $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE);
×
484

485
                $policy = new ContentSecurityPolicy();
×
486
                $policy->allowEvalScript(true);
×
487
                $response->setContentSecurityPolicy($policy);
×
488

489
                return $response;
×
490
        }
491

492
        /**
493
         * Use UUID of file to get PDF
494
         *
495
         * @param string $uuid File uuid
496
         * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
497
         *
498
         * 200: OK
499
         * 401: Validation page not accessible if unauthenticated
500
         * 404: File not found
501
         */
502
        #[PrivateValidation]
503
        #[NoAdminRequired]
504
        #[NoCSRFRequired]
505
        #[RequireSetupOk]
506
        #[PublicPage]
507
        #[AnonRateLimit(limit: 300, period: 60)]
508
        #[FrontpageRoute(verb: 'GET', url: '/p/pdf/{uuid}')]
509
        public function getPdf($uuid) {
510
                try {
511
                        $file = $this->accountService->getPdfByUuid($uuid);
×
512
                } catch (DoesNotExistException) {
×
513
                        return new DataResponse([], Http::STATUS_NOT_FOUND);
×
514
                }
515

516
                return new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
×
517
        }
518

519
        /**
520
         * Use UUID of user to get PDF
521
         *
522
         * @param string $uuid Sign request uuid
523
         * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>
524
         *
525
         * 200: OK
526
         * 401: Validation page not accessible if unauthenticated
527
         */
528
        #[PrivateValidation]
529
        #[NoAdminRequired]
530
        #[NoCSRFRequired]
531
        #[RequireSignRequestUuid]
532
        #[PublicPage]
533
        #[RequireSetupOk]
534
        #[AnonRateLimit(limit: 300, period: 60)]
535
        #[FrontpageRoute(verb: 'GET', url: '/pdf/{uuid}')]
536
        public function getPdfFile($uuid): FileDisplayResponse {
537
                $files = $this->getNextcloudFiles();
×
538
                if (empty($files)) {
×
539
                        throw new LibresignException(json_encode([
×
540
                                'action' => JSActions::ACTION_DO_NOTHING,
×
541
                                'errors' => [['message' => $this->l10n->t('File not found')]],
×
542
                        ]), Http::STATUS_NOT_FOUND);
×
543
                }
544
                $file = current($files);
×
545
                return new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
×
546
        }
547

548
        /**
549
         * Show validation page
550
         *
551
         * @return TemplateResponse<Http::STATUS_OK, array{}>
552
         *
553
         * 200: OK
554
         * 401: Validation page not accessible if unauthenticated
555
         */
556
        #[PrivateValidation]
557
        #[NoAdminRequired]
558
        #[NoCSRFRequired]
559
        #[RequireSetupOk(template: 'validation')]
560
        #[PublicPage]
561
        #[AnonRateLimit(limit: 30, period: 60)]
562
        #[FrontpageRoute(verb: 'GET', url: '/p/validation')]
563
        public function validation(): TemplateResponse {
564
                if ($this->getFileEntity()) {
×
565
                        $this->initialState->provideInitialState('config',
×
566
                                $this->accountService->getConfig($this->userSession->getUser())
×
567
                        );
×
568
                        $this->initialState->provideInitialState('file', [
×
569
                                'uuid' => $this->getFileEntity()?->getUuid(),
×
570
                                'description' => $this->getSignRequestEntity()?->getDescription(),
×
571
                        ]);
×
572
                        $this->initialState->provideInitialState('filename', $this->getFileEntity()?->getName());
×
573
                        $this->initialState->provideInitialState('pdfs', $this->getPdfUrls());
×
574
                        $this->initialState->provideInitialState('signer',
×
575
                                $this->signFileService->getSignerData(
×
576
                                        $this->userSession->getUser(),
×
577
                                        $this->getSignRequestEntity(),
×
578
                                )
×
579
                        );
×
580
                }
581

582
                Util::addScript(Application::APP_ID, 'libresign-validation');
×
583
                $response = new TemplateResponse(Application::APP_ID, 'validation', [], TemplateResponse::RENDER_AS_BASE);
×
584

585
                return $response;
×
586
        }
587

588
        /**
589
         * Show validation page
590
         *
591
         * The path is used only by frontend
592
         *
593
         * @param string $uuid Sign request uuid
594
         * @return TemplateResponse<Http::STATUS_OK, array{}>
595
         *
596
         * 200: OK
597
         * 303: Redirected to validation page
598
         * 401: Validation page not accessible if unauthenticated
599
         */
600
        #[PrivateValidation]
601
        #[NoAdminRequired]
602
        #[NoCSRFRequired]
603
        #[RequireSetupOk]
604
        #[PublicPage]
605
        #[AnonRateLimit(limit: 30, period: 60)]
606
        #[FrontpageRoute(verb: 'GET', url: '/validation/{uuid}')]
607
        public function validationFileWithShortUrl(): TemplateResponse {
608
                return $this->validationFilePublic($this->request->getParam('uuid'));
×
609
        }
610

611
        /**
612
         * Show validation page
613
         *
614
         * @param string $uuid Sign request uuid
615
         * @return TemplateResponse<Http::STATUS_OK, array{}>
616
         *
617
         * 200: OK
618
         */
619
        #[NoAdminRequired]
620
        #[NoCSRFRequired]
621
        #[RequireSetupOk(template: 'main')]
622
        #[PublicPage]
623
        #[RequireSignRequestUuid]
624
        #[FrontpageRoute(verb: 'GET', url: '/reset-password')]
625
        public function resetPassword(): TemplateResponse {
626
                $this->initialState->provideInitialState('config',
×
627
                        $this->accountService->getConfig($this->userSession->getUser())
×
628
                );
×
629

630
                Util::addScript(Application::APP_ID, 'libresign-main');
×
631
                $response = new TemplateResponse(Application::APP_ID, 'reset_password');
×
632

633
                return $response;
×
634
        }
635

636
        /**
637
         * Public page to show validation for a specific file UUID
638
         *
639
         * @param string $uuid File uuid
640
         * @return TemplateResponse<Http::STATUS_OK, array{}>
641
         *
642
         * 200: OK
643
         * 401: Validation page not accessible if unauthenticated
644
         */
645
        #[PrivateValidation]
646
        #[NoAdminRequired]
647
        #[NoCSRFRequired]
648
        #[RequireSetupOk(template: 'validation')]
649
        #[PublicPage]
650
        #[AnonRateLimit(limit: 30, period: 60)]
651
        #[FrontpageRoute(verb: 'GET', url: '/p/validation/{uuid}')]
652
        public function validationFilePublic(string $uuid): TemplateResponse {
653
                try {
654
                        $this->signFileService->getFileByUuid($uuid);
×
655
                        $this->fileService->setFileByUuid($uuid);
×
656
                } catch (DoesNotExistException) {
×
657
                        try {
658
                                $signRequest = $this->signFileService->getSignRequestByUuid($uuid);
×
659
                                $libresignFile = $this->signFileService->getFile($signRequest->getFileId());
×
660
                                $this->fileService->setFile($libresignFile);
×
661
                        } catch (DoesNotExistException) {
×
662
                                $this->initialState->provideInitialState('action', JSActions::ACTION_DO_NOTHING);
×
663
                                $this->initialState->provideInitialState('errors', [['message' => $this->l10n->t('Invalid UUID')]]);
×
664
                        }
665
                }
666
                if ($this->userSession->isLoggedIn()) {
×
667
                        $this->initialState->provideInitialState('config',
×
668
                                $this->accountService->getConfig($this->userSession->getUser())
×
669
                        );
×
670
                        $this->fileService->setMe($this->userSession->getUser());
×
671
                } else {
672
                        $this->initialState->provideInitialState('config',
×
673
                                $this->accountService->getConfig()
×
674
                        );
×
675
                }
676

677
                $this->initialState->provideInitialState('legal_information', $this->appConfig->getValueString(Application::APP_ID, 'legal_information'));
×
678

679
                $this->initialState->provideInitialState('file_info',
×
680
                        $this->fileService
×
681
                                ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
682
                                ->setHost($this->request->getServerHost())
×
683
                                ->showVisibleElements()
×
684
                                ->showSigners()
×
685
                                ->showSettings()
×
686
                                ->showMessages()
×
687
                                ->showValidateFile()
×
688
                                ->toArray()
×
689
                );
×
690

691
                Util::addScript(Application::APP_ID, 'libresign-validation');
×
692
                if (class_exists(LoadViewer::class)) {
×
693
                        $this->eventDispatcher->dispatchTyped(new LoadViewer());
×
694
                }
695
                $response = new TemplateResponse(Application::APP_ID, 'validation', [], TemplateResponse::RENDER_AS_BASE);
×
696

697
                return $response;
×
698
        }
699
}
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