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

LibreSign / libresign / 21462840629

29 Jan 2026 02:05AM UTC coverage: 46.387%. First build
21462840629

Pull #6606

github

web-flow
Merge 035d18460 into 5e46f22a3
Pull Request #6606: fix: load viewer when open siging page

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

7820 of 16858 relevant lines covered (46.39%)

5.07 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\File\FileListService;
22
use OCA\Libresign\Service\FileService;
23
use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\TokenService;
24
use OCA\Libresign\Service\IdentifyMethodService;
25
use OCA\Libresign\Service\RequestSignatureService;
26
use OCA\Libresign\Service\SessionService;
27
use OCA\Libresign\Service\SignerElementsService;
28
use OCA\Libresign\Service\SignFileService;
29
use OCA\Viewer\Event\LoadViewer;
30
use OCP\AppFramework\Db\DoesNotExistException;
31
use OCP\AppFramework\Http;
32
use OCP\AppFramework\Http\Attribute\AnonRateLimit;
33
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
34
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
35
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
36
use OCP\AppFramework\Http\Attribute\PublicPage;
37
use OCP\AppFramework\Http\ContentSecurityPolicy;
38
use OCP\AppFramework\Http\DataResponse;
39
use OCP\AppFramework\Http\FileDisplayResponse;
40
use OCP\AppFramework\Http\TemplateResponse;
41
use OCP\AppFramework\Services\IInitialState;
42
use OCP\EventDispatcher\IEventDispatcher;
43
use OCP\IAppConfig;
44
use OCP\IL10N;
45
use OCP\IRequest;
46
use OCP\IURLGenerator;
47
use OCP\IUserSession;
48
use OCP\Util;
49
use Psr\Log\LoggerInterface;
50

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

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

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

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

110
                Util::addScript(Application::APP_ID, 'libresign-main');
×
111

112
                if (class_exists(LoadViewer::class)) {
×
113
                        $this->eventDispatcher->dispatchTyped(new LoadViewer());
×
114
                }
115

116
                $response = new TemplateResponse(Application::APP_ID, 'main');
×
117

118
                $policy = new ContentSecurityPolicy();
×
119
                $policy->allowEvalScript(true);
×
120
                $policy->addAllowedFrameDomain('\'self\'');
×
121
                $response->setContentSecurityPolicy($policy);
×
122

123
                return $response;
×
124
        }
125

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

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

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

176
        /**
177
         * Main page to authenticated signer with a path
178
         *
179
         * The path is used only by frontend
180
         *
181
         * @param string $path The path that was sent from frontend
182
         * @return TemplateResponse<Http::STATUS_OK, array{}>
183
         *
184
         * 200: OK
185
         */
186
        #[NoAdminRequired]
187
        #[NoCSRFRequired]
188
        #[RequireSetupOk(template: 'main')]
189
        #[FrontpageRoute(verb: 'GET', url: '/f/{path}', requirements: ['path' => '.+'])]
190
        public function indexFPath(string $path): TemplateResponse {
191
                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)) {
×
192

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

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

251

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

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

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

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

371
                Util::addScript(Application::APP_ID, 'libresign-external');
×
NEW
372
                if (class_exists(LoadViewer::class)) {
×
NEW
373
                        $this->eventDispatcher->dispatchTyped(new LoadViewer());
×
374
                }
375
                $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE);
×
376

377
                $policy = new ContentSecurityPolicy();
×
378
                $policy->allowEvalScript(true);
×
379
                $response->setContentSecurityPolicy($policy);
×
380

381
                return $response;
×
382
        }
383

384
        private function provideSignerSignatues(): void {
385
                $signatures = [];
×
386
                if ($this->userSession->getUser()) {
×
387
                        $signatures = $this->signerElementsService->getUserElements($this->userSession->getUser()->getUID());
×
388
                } else {
389
                        $signatures = $this->signerElementsService->getElementsFromSessionAsArray();
×
390
                }
391
                $this->initialState->provideInitialState('user_signatures', $signatures);
×
392
        }
393

394
        /**
395
         * @return string[] Array of PDF URLs
396
         */
397
        private function getPdfUrls(): array {
398
                return $this->signFileService->getPdfUrlsForSigning(
×
399
                        $this->getFileEntity(),
×
400
                        $this->getSignRequestEntity()
×
401
                );
×
402
        }
403

404
        private function getEnvelopeChildFiles(): array {
405
                $parentFileId = $this->getFileEntity()->getId();
×
406
                $currentSignRequest = $this->getSignRequestEntity();
×
407
                $childFiles = $this->fileMapper->getChildrenFiles($parentFileId);
×
408
                $childSignRequests = $this->signRequestMapper->getByEnvelopeChildrenAndIdentifyMethod(
×
409
                        $parentFileId,
×
410
                        $currentSignRequest->getId()
×
411
                );
×
412

413
                $signRequestsByFileId = [];
×
414
                foreach ($childSignRequests as $childSignRequest) {
×
415
                        $signRequestsByFileId[$childSignRequest->getFileId()] = true;
×
416
                }
417

418
                foreach ($childFiles as $childFile) {
×
419
                        if (!isset($signRequestsByFileId[$childFile->getId()])) {
×
420
                                $this->logger->warning('Missing sign request for envelope child file', [
×
421
                                        'parentFileId' => $parentFileId,
×
422
                                        'childFileId' => $childFile->getId(),
×
423
                                        'signRequestId' => $currentSignRequest->getId(),
×
424
                                ]);
×
425
                        }
426
                }
427

428
                return $this->fileListService->formatEnvelopeChildFilesForSignRequest(
×
429
                        $childFiles,
×
430
                        $childSignRequests,
×
431
                        $currentSignRequest,
×
432
                );
×
433
        }
434

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

494
                Util::addScript(Application::APP_ID, 'libresign-external');
×
495
                $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE);
×
496

497
                $policy = new ContentSecurityPolicy();
×
498
                $policy->allowEvalScript(true);
×
499
                $response->setContentSecurityPolicy($policy);
×
500

501
                return $response;
×
502
        }
503

504
        /**
505
         * Use UUID of file to get PDF
506
         *
507
         * @param string $uuid File uuid
508
         * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
509
         *
510
         * 200: OK
511
         * 401: Validation page not accessible if unauthenticated
512
         * 404: File not found
513
         */
514
        #[PrivateValidation]
515
        #[NoAdminRequired]
516
        #[NoCSRFRequired]
517
        #[RequireSetupOk]
518
        #[PublicPage]
519
        #[AnonRateLimit(limit: 300, period: 60)]
520
        #[FrontpageRoute(verb: 'GET', url: '/p/pdf/{uuid}')]
521
        public function getPdf($uuid) {
522
                try {
523
                        $file = $this->accountService->getPdfByUuid($uuid);
×
524
                } catch (DoesNotExistException) {
×
525
                        return new DataResponse([], Http::STATUS_NOT_FOUND);
×
526
                }
527

528
                return new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
×
529
        }
530

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

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

594
                Util::addScript(Application::APP_ID, 'libresign-validation');
×
595
                $response = new TemplateResponse(Application::APP_ID, 'validation', [], TemplateResponse::RENDER_AS_BASE);
×
596

597
                return $response;
×
598
        }
599

600
        /**
601
         * Show validation page
602
         *
603
         * The path is used only by frontend
604
         *
605
         * @param string $uuid Sign request uuid
606
         * @return TemplateResponse<Http::STATUS_OK, array{}>
607
         *
608
         * 200: OK
609
         * 303: Redirected to validation page
610
         * 401: Validation page not accessible if unauthenticated
611
         */
612
        #[PrivateValidation]
613
        #[NoAdminRequired]
614
        #[NoCSRFRequired]
615
        #[RequireSetupOk]
616
        #[PublicPage]
617
        #[AnonRateLimit(limit: 30, period: 60)]
618
        #[FrontpageRoute(verb: 'GET', url: '/validation/{uuid}')]
619
        public function validationFileWithShortUrl(): TemplateResponse {
620
                return $this->validationFilePublic($this->request->getParam('uuid'));
×
621
        }
622

623
        /**
624
         * Show validation page
625
         *
626
         * @param string $uuid Sign request uuid
627
         * @return TemplateResponse<Http::STATUS_OK, array{}>
628
         *
629
         * 200: OK
630
         */
631
        #[NoAdminRequired]
632
        #[NoCSRFRequired]
633
        #[RequireSetupOk(template: 'main')]
634
        #[PublicPage]
635
        #[RequireSignRequestUuid]
636
        #[FrontpageRoute(verb: 'GET', url: '/reset-password')]
637
        public function resetPassword(): TemplateResponse {
638
                $this->initialState->provideInitialState('config',
×
639
                        $this->accountService->getConfig($this->userSession->getUser())
×
640
                );
×
641

642
                Util::addScript(Application::APP_ID, 'libresign-main');
×
643
                $response = new TemplateResponse(Application::APP_ID, 'reset_password');
×
644

645
                return $response;
×
646
        }
647

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

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

691
                $this->initialState->provideInitialState('file_info',
×
692
                        $this->fileService
×
693
                                ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
694
                                ->setHost($this->request->getServerHost())
×
695
                                ->showVisibleElements()
×
696
                                ->showSigners()
×
697
                                ->showSettings()
×
698
                                ->showMessages()
×
699
                                ->showValidateFile()
×
700
                                ->toArray()
×
701
                );
×
702

703
                Util::addScript(Application::APP_ID, 'libresign-validation');
×
704
                if (class_exists(LoadViewer::class)) {
×
705
                        $this->eventDispatcher->dispatchTyped(new LoadViewer());
×
706
                }
707
                $response = new TemplateResponse(Application::APP_ID, 'validation', [], TemplateResponse::RENDER_AS_BASE);
×
708

709
                return $response;
×
710
        }
711
}
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