Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
SettingsLoader
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
7 / 7
18
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 loadSettings
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
4
 loadApproverSignatureMethods
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIdentificationDocumentsStatus
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 getIdDocFiles
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 calculateStatusFromFiles
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 getUserIdentificationSettings
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/**
6 * SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
7 * SPDX-License-Identifier: AGPL-3.0-or-later
8 */
9
10namespace OCA\Libresign\Service\File;
11
12use OCA\Libresign\Db\File;
13use OCA\Libresign\Db\IdDocsMapper;
14use OCA\Libresign\Db\SignRequest;
15use OCA\Libresign\Enum\FileStatus;
16use OCA\Libresign\Service\IdDocsPolicyService;
17use OCA\Libresign\Service\IdentifyMethodService;
18use OCP\IUser;
19
20/** @psalm-import-type LibresignSettings from \OCA\Libresign\ResponseDefinitions */
21class SettingsLoader {
22    public const IDENTIFICATION_DOCUMENTS_DISABLED = 0;
23    public const IDENTIFICATION_DOCUMENTS_NEED_SEND = 1;
24    public const IDENTIFICATION_DOCUMENTS_NEED_APPROVAL = 2;
25    public const IDENTIFICATION_DOCUMENTS_APPROVED = 3;
26
27    public function __construct(
28        private AccountSettingsProvider $accountSettingsProvider,
29        private IdDocsPolicyService $idDocsPolicyService,
30        private IdDocsMapper $idDocsMapper,
31        private IdentifyMethodService $identifyMethodService,
32    ) {
33    }
34
35    public function loadSettings(
36        \stdClass $fileData,
37        FileResponseOptions $options,
38    ): void {
39        if (!$options->isShowSettings()) {
40            return;
41        }
42
43        $fileData->settings = $this->getUserIdentificationSettings($options->getMe(), $options->getSignRequest());
44
45        if ($options->getMe()) {
46            $fileData->settings = array_merge(
47                $fileData->settings,
48                $this->accountSettingsProvider->getSettings($options->getMe()),
49            );
50            $fileData->settings['phoneNumber'] = $this->accountSettingsProvider->getPhoneNumber($options->getMe());
51            if ($this->idDocsPolicyService->canApproverSignIdDoc(
52                $options->getMe(),
53                $fileData->id,
54                $fileData->status,
55            )) {
56                $fileData->settings['canSign'] = true;
57                $fileData->settings['isApprover'] = true;
58                $this->loadApproverSignatureMethods($fileData, $options->getMe());
59            }
60        }
61    }
62
63    /**
64     * Loading the settings must not create or persist the approver's sign
65     * request; that only happens when they sign.
66     */
67    private function loadApproverSignatureMethods(\stdClass $fileData, IUser $approver): void {
68        $fileData->settings['signatureMethods'] = $this->identifyMethodService->getSignMethodsOfAccount($approver->getUID());
69    }
70
71    public function getIdentificationDocumentsStatus(?IUser $user = null, ?SignRequest $signRequest = null): int {
72        if (!$this->idDocsPolicyService->isIdentificationDocumentsEnabled($user, $signRequest)) {
73            return self::IDENTIFICATION_DOCUMENTS_DISABLED;
74        }
75
76        if ($user && $this->idDocsPolicyService->userCanApproveValidationDocuments($user, false)) {
77            return self::IDENTIFICATION_DOCUMENTS_APPROVED;
78        }
79
80        $files = $this->getIdDocFiles($user, $signRequest);
81
82        return $this->calculateStatusFromFiles($files);
83    }
84
85    /** @return array<int, File>|null */
86    private function getIdDocFiles(?IUser $user, ?SignRequest $signRequest): ?array {
87        if ($user) {
88            return $this->idDocsMapper->getFilesOfAccount($user->getUID());
89        }
90
91        if ($signRequest) {
92            return $this->idDocsMapper->getFilesOfSignRequest($signRequest->getId());
93        }
94
95        return null;
96    }
97
98    /** @param array<int, File>|null $files */
99    private function calculateStatusFromFiles(?array $files): int {
100        if (empty($files)) {
101            return self::IDENTIFICATION_DOCUMENTS_NEED_SEND;
102        }
103
104        $deleted = array_filter($files, fn (File $file) => $file->getStatus() === FileStatus::DELETED->value);
105        if (count($deleted) === count($files)) {
106            return self::IDENTIFICATION_DOCUMENTS_NEED_SEND;
107        }
108
109        $signed = array_filter($files, fn (File $file) => $file->getStatus() === FileStatus::SIGNED->value);
110        if (count($signed) !== count($files)) {
111            return self::IDENTIFICATION_DOCUMENTS_NEED_APPROVAL;
112        }
113
114        return self::IDENTIFICATION_DOCUMENTS_APPROVED;
115    }
116
117    /**
118     * Get user identification documents settings
119     * These are user-specific settings, not file-specific
120     * Always returns complete settings payload with defaults.
121     * Canonical API shape is documented as LibresignSettings in ResponseDefinitions.
122     *
123     * @psalm-return LibresignSettings
124     */
125    public function getUserIdentificationSettings(?IUser $user, ?SignRequest $signRequest = null): array {
126        $status = $this->getIdentificationDocumentsStatus($user, $signRequest);
127
128        return [
129            'canSign' => false,
130            'canRequestSign' => false,
131            'phoneNumber' => '',
132            'hasSignatureFile' => false,
133            'needIdentificationDocuments' => in_array($status, [
134                self::IDENTIFICATION_DOCUMENTS_NEED_SEND,
135                self::IDENTIFICATION_DOCUMENTS_NEED_APPROVAL,
136            ], true),
137            'identificationDocumentsWaitingApproval' => $status === self::IDENTIFICATION_DOCUMENTS_NEED_APPROVAL,
138        ];
139    }
140}