Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
EnvelopeService
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 8
342
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isEnabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 validateEnvelopeConstraints
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 createEnvelope
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 addFileToEnvelope
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 getEnvelopeByFileId
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getEnvelopeFolder
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 getMaxFilesPerEnvelope
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service;
10
11use DateTime;
12use OCA\Libresign\AppInfo\Application;
13use OCA\Libresign\Db\File as FileEntity;
14use OCA\Libresign\Db\FileMapper;
15use OCA\Libresign\Enum\FileStatus;
16use OCA\Libresign\Enum\NodeType;
17use OCA\Libresign\Exception\LibresignException;
18use OCA\Libresign\Service\Policy\PolicyService;
19use OCA\Libresign\Service\Policy\Provider\Envelope\EnvelopePolicy;
20use OCP\AppFramework\Db\DoesNotExistException;
21use OCP\IAppConfig;
22use OCP\IL10N;
23use Sabre\DAV\UUIDUtil;
24
25class EnvelopeService {
26    public function __construct(
27        protected FileMapper $fileMapper,
28        protected IL10N $l10n,
29        protected PolicyService $policyService,
30        protected IAppConfig $appConfig,
31        protected FolderService $folderService,
32    ) {
33    }
34
35    public function isEnabled(): bool {
36        return $this->policyService->resolve(EnvelopePolicy::KEY)->getEffectiveValueAsBool(true);
37    }
38
39    /**
40     * @throws LibresignException
41     */
42    public function validateEnvelopeConstraints(int $fileCount): void {
43        if (!$this->isEnabled()) {
44            // TRANSLATORS Error shown when the multi-document envelope feature is disabled by the administrator.
45            throw new LibresignException($this->l10n->t('Envelope feature is disabled'));
46        }
47
48        $maxFiles = $this->getMaxFilesPerEnvelope();
49        if ($fileCount > $maxFiles) {
50            throw new LibresignException(
51                // TRANSLATORS Error shown when adding documents would exceed the configured envelope size limit. %s is the maximum number of files allowed.
52                $this->l10n->t('Maximum number of files per envelope (%s) exceeded', [$maxFiles])
53            );
54        }
55    }
56
57    public function createEnvelope(
58        string $name,
59        string $userId,
60        int $filesCount = 0,
61        ?string $path = null,
62    ): FileEntity {
63        $this->folderService->setUserId($userId);
64
65        $uuid = UUIDUtil::getUUID();
66        if ($path) {
67            $envelopeFolder = $this->folderService->getOrCreateFolderByAbsolutePath($path);
68        } else {
69            $parentFolder = $this->folderService->getFolder();
70            $folderName = $name . '_' . $uuid;
71            $envelopeFolder = $parentFolder->newFolder($folderName);
72        }
73
74        $envelope = new FileEntity();
75        $envelope->setNodeId($envelopeFolder->getId());
76        $envelope->setNodeTypeEnum(NodeType::ENVELOPE);
77        $envelope->setName($name);
78        $envelope->setUuid($uuid);
79        $envelope->setCreatedAt(new DateTime());
80        $envelope->setStatusEnum(FileStatus::DRAFT);
81
82        $envelope->setMetadata(['filesCount' => $filesCount]);
83
84        if ($userId) {
85            $envelope->setUserId($userId);
86        }
87
88        return $this->fileMapper->insert($envelope);
89    }
90
91    public function addFileToEnvelope(int $envelopeId, FileEntity $file): FileEntity {
92        $envelope = $this->fileMapper->getById($envelopeId);
93
94        if (!$envelope->isEnvelope()) {
95            // TRANSLATORS Error shown when the given identifier does not refer to a signature envelope.
96            throw new LibresignException($this->l10n->t('The specified ID is not an envelope'));
97        }
98
99        if ($envelope->getStatus() > FileStatus::DRAFT->value) {
100            // TRANSLATORS Error shown when trying to add documents to an envelope that already started the signing process.
101            throw new LibresignException($this->l10n->t('Cannot add files to an envelope that is already in signing process'));
102        }
103
104        $maxFiles = $this->getMaxFilesPerEnvelope();
105        $currentCount = $this->fileMapper->countChildrenFiles($envelopeId);
106        if ($currentCount >= $maxFiles) {
107            throw new LibresignException(
108                // TRANSLATORS Error shown when adding documents would exceed the configured envelope size limit. %s is the maximum number of files allowed.
109                $this->l10n->t('Maximum number of files per envelope (%s) exceeded', [$maxFiles])
110            );
111        }
112
113        $file->setParentFileId($envelopeId);
114        $file->setNodeTypeEnum(NodeType::FILE);
115
116        return $this->fileMapper->update($file);
117    }
118
119    public function getEnvelopeByFileId(int $fileId): ?FileEntity {
120        try {
121            return $this->fileMapper->getParentEnvelope($fileId);
122        } catch (DoesNotExistException) {
123            return null;
124        }
125    }
126
127    public function getEnvelopeFolder(FileEntity $envelope): \OCP\Files\Folder {
128        $userId = $envelope->getUserId();
129        if (!$userId) {
130            throw new LibresignException('Envelope does not have a user');
131        }
132
133        $this->folderService->setUserId($userId);
134        $userRootFolder = $this->folderService->getUserRootFolder();
135
136        $envelopeFolderNode = $userRootFolder->getFirstNodeById($envelope->getNodeId());
137        if (!$envelopeFolderNode instanceof \OCP\Files\Folder) {
138            throw new LibresignException('Envelope folder not found');
139        }
140
141        return $envelopeFolderNode;
142    }
143
144    private function getMaxFilesPerEnvelope(): int {
145        return $this->appConfig->getValueInt(Application::APP_ID, 'envelope_max_files', 50);
146    }
147}