diff --git a/lib/Controller/FileController.php b/lib/Controller/FileController.php index 3973b2be65..4b1d30f5f7 100644 --- a/lib/Controller/FileController.php +++ b/lib/Controller/FileController.php @@ -414,7 +414,7 @@ private function fetchPreview( * * @param LibresignNewFile $file File to save * @param string $name The name of file to sign - * @param LibresignFolderSettings $settings Settings to define the pattern to store the file. See more informations at FolderService::getFolderName method. + * @param LibresignFolderSettings $settings Settings to define how and where the file should be stored * @param list $files Multiple files to create an envelope (optional, use either file or files) * @return DataResponse|DataResponse * diff --git a/lib/Service/EnvelopeService.php b/lib/Service/EnvelopeService.php index 2d8a599c9f..c9cbd1340a 100644 --- a/lib/Service/EnvelopeService.php +++ b/lib/Service/EnvelopeService.php @@ -53,14 +53,15 @@ public function createEnvelope(string $name, string $userId, int $filesCount = 0 $parentFolder = $this->folderService->getFolder(); - $folderName = $name . '_' . substr(UUIDUtil::getUUID(), 0, 8); + $uuid = UUIDUtil::getUUID(); + $folderName = $name . '_' . $uuid; $envelopeFolder = $parentFolder->newFolder($folderName); $envelope = new FileEntity(); $envelope->setNodeId($envelopeFolder->getId()); $envelope->setNodeTypeEnum(NodeType::ENVELOPE); $envelope->setName($name); - $envelope->setUuid(UUIDUtil::getUUID()); + $envelope->setUuid($uuid); $envelope->setCreatedAt(new DateTime()); $envelope->setStatus(FileEntity::STATUS_DRAFT); @@ -106,6 +107,23 @@ public function getEnvelopeByFileId(int $fileId): ?FileEntity { } } + public function getEnvelopeFolder(FileEntity $envelope): \OCP\Files\Folder { + $userId = $envelope->getUserId(); + if (!$userId) { + throw new LibresignException('Envelope does not have a user'); + } + + $this->folderService->setUserId($userId); + $userFolder = $this->folderService->getFolder(); + + $envelopeFolderNode = $userFolder->getFirstNodeById($envelope->getNodeId()); + if (!$envelopeFolderNode instanceof \OCP\Files\Folder) { + throw new LibresignException('Envelope folder not found'); + } + + return $envelopeFolderNode; + } + private function getMaxFilesPerEnvelope(): int { return $this->appConfig->getValueInt(Application::APP_ID, 'envelope_max_files', 50); } diff --git a/lib/Service/File/UploadProcessor.php b/lib/Service/File/UploadProcessor.php index dca0421cbd..4ea6f4605d 100644 --- a/lib/Service/File/UploadProcessor.php +++ b/lib/Service/File/UploadProcessor.php @@ -48,9 +48,10 @@ public function getNodeFromUploadedFile(array $data): Node { $extension = $this->mimeService->getExtension($content); $this->validateFileContent($content, $extension); - $userFolder = $this->folderService->getFolder(); - $folderName = $this->folderService->getFolderName($data, $data['userManager']); - $folderToFile = $userFolder->newFolder($folderName); + $folderToFile = $this->folderService->getFolderForFile($data, $data['userManager']); + if (!$folderToFile instanceof \OCP\Files\Folder) { + throw new LibresignException('Envelope folder not found'); + } @unlink($uploadedFile['tmp_name']); diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php index e72fcc99a3..8d96ed8640 100644 --- a/lib/Service/FileService.php +++ b/lib/Service/FileService.php @@ -120,9 +120,7 @@ public function getNodeFromData(array $data): Node { $this->validateFileContent($content, $extension); - $userFolder = $this->folderService->getFolder(); - $folderName = $this->folderService->getFolderName($data, $data['userManager']); - $folderToFile = $userFolder->newFolder($folderName); + $folderToFile = $this->folderService->getFolderForFile($data, $data['userManager']); $filename = $this->resolveFileName($data, $extension); return $folderToFile->newFile($filename, $content); } diff --git a/lib/Service/FolderService.php b/lib/Service/FolderService.php index 6d55b522f9..b46ea02ec9 100644 --- a/lib/Service/FolderService.php +++ b/lib/Service/FolderService.php @@ -124,6 +124,29 @@ private function getLibreSignDefaultPath(): string { return $path; } + /** + * Get or create the folder where a file should be stored + * + * @param array $data Must contain 'settings' and optionally 'name', 'userManager' + * @param mixed $identifier User or string identifier + * @return Folder The folder where files should be created + * @throws LibresignException + */ + public function getFolderForFile(array $data, $identifier): Folder { + $userFolder = $this->getFolder(); + + if (isset($data['settings']['envelopeFolderId'])) { + $envelopeFolder = $userFolder->getFirstNodeById($data['settings']['envelopeFolderId']); + if ($envelopeFolder === null || !$envelopeFolder instanceof Folder) { + throw new LibresignException($this->l10n->t('Envelope folder not found')); + } + return $envelopeFolder; + } + + $folderName = $this->getFolderName($data, $identifier); + return $userFolder->newFolder($folderName); + } + /** * @param array{settings: array, name: string} $data * @param IUser $owner diff --git a/lib/Service/RequestSignatureService.php b/lib/Service/RequestSignatureService.php index f6c8aa256b..78f9e49653 100644 --- a/lib/Service/RequestSignatureService.php +++ b/lib/Service/RequestSignatureService.php @@ -164,9 +164,9 @@ public function saveEnvelope(array $data): array { try { $envelope = $this->envelopeService->createEnvelope($envelopeName, $userId, $filesCount); - $envelopeFolderName = 'envelope-' . $envelope->getUuid(); + $envelopeFolder = $this->envelopeService->getEnvelopeFolder($envelope); $envelopeSettings = array_merge($data['settings'] ?? [], [ - 'folderName' => $envelopeFolderName, + 'envelopeFolderId' => $envelopeFolder->getId(), ]); foreach ($data['files'] as $fileData) { diff --git a/openapi-full.json b/openapi-full.json index 3950dd9f6c..9b2e144fd5 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -4847,7 +4847,7 @@ "settings": { "$ref": "#/components/schemas/FolderSettings", "default": [], - "description": "Settings to define the pattern to store the file. See more informations at FolderService::getFolderName method." + "description": "Settings to define how and where the file should be stored" }, "files": { "type": "array", diff --git a/openapi.json b/openapi.json index e29adc7356..18dc76a8b8 100644 --- a/openapi.json +++ b/openapi.json @@ -4697,7 +4697,7 @@ "settings": { "$ref": "#/components/schemas/FolderSettings", "default": [], - "description": "Settings to define the pattern to store the file. See more informations at FolderService::getFolderName method." + "description": "Settings to define how and where the file should be stored" }, "files": { "type": "array", diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index 6ebf1dcb15..8c4c613c94 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -3343,7 +3343,7 @@ export interface operations { */ name?: string; /** - * @description Settings to define the pattern to store the file. See more informations at FolderService::getFolderName method. + * @description Settings to define how and where the file should be stored * @default [] */ settings?: components["schemas"]["FolderSettings"]; diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts index 85ec6810e0..e204c4c534 100644 --- a/src/types/openapi/openapi.ts +++ b/src/types/openapi/openapi.ts @@ -2865,7 +2865,7 @@ export interface operations { */ name?: string; /** - * @description Settings to define the pattern to store the file. See more informations at FolderService::getFolderName method. + * @description Settings to define how and where the file should be stored * @default [] */ settings?: components["schemas"]["FolderSettings"]; diff --git a/tests/php/Unit/Service/EnvelopeServiceTest.php b/tests/php/Unit/Service/EnvelopeServiceTest.php index ecc31554d3..9c3cf89490 100644 --- a/tests/php/Unit/Service/EnvelopeServiceTest.php +++ b/tests/php/Unit/Service/EnvelopeServiceTest.php @@ -163,4 +163,27 @@ public function testReturnsEnvelopeWhenFileHasParent(): void { $this->assertNotNull($result); $this->assertSame(5, $result->getId()); } + + public function testEnvelopeUuidMatchesFolderName(): void { + $this->fileMapper->method('insert')->willReturnArgument(0); + + $mockFolder = $this->createMock(Folder::class); + $mockEnvelopeFolder = $this->createMock(Folder::class); + $mockEnvelopeFolder->method('getId')->willReturn(999); + + $capturedFolderName = ''; + $mockFolder->method('newFolder')->willReturnCallback( + function ($folderName) use ($mockEnvelopeFolder, &$capturedFolderName) { + $capturedFolderName = $folderName; + return $mockEnvelopeFolder; + } + ); + + $this->folderService->method('getFolder')->willReturn($mockFolder); + + $envelope = $this->service->createEnvelope('Contract', 'user1'); + + $this->assertStringStartsWith('Contract_', $capturedFolderName); + $this->assertStringContainsString($envelope->getUuid(), $capturedFolderName); + } } diff --git a/tests/php/Unit/Service/File/UploadProcessorTest.php b/tests/php/Unit/Service/File/UploadProcessorTest.php index 21bca95bab..eb7ecbd567 100644 --- a/tests/php/Unit/Service/File/UploadProcessorTest.php +++ b/tests/php/Unit/Service/File/UploadProcessorTest.php @@ -69,12 +69,8 @@ public function testGetNodeFromUploadedFileSuccess(): void { $this->mimeService->method('getExtension')->with($content)->willReturn($extension); $this->pdfValidator->method('validate'); - $folder = $this->createMock(Folder::class); - $this->folderService->method('getFolder')->willReturn($folder); - $this->folderService->method('getFolderName')->willReturn('LibreSign'); - $targetFolder = $this->createMock(Folder::class); - $folder->method('newFolder')->willReturn($targetFolder); + $this->folderService->method('getFolderForFile')->willReturn($targetFolder); $node = $this->createMock(Node::class); $targetFolder->method('newFile')->willReturn($node); @@ -110,12 +106,8 @@ public function testGetNodeFromUploadedFileValidatesUpload(): void { $this->mimeService->method('getExtension')->willReturn('pdf'); $this->pdfValidator->method('validate'); - $folder = $this->createMock(Folder::class); - $this->folderService->method('getFolder')->willReturn($folder); - $this->folderService->method('getFolderName')->willReturn('LibreSign'); - $targetFolder = $this->createMock(Folder::class); - $folder->method('newFolder')->willReturn($targetFolder); + $this->folderService->method('getFolderForFile')->willReturn($targetFolder); $node = $this->createMock(Node::class); $targetFolder->method('newFile')->willReturn($node); @@ -151,12 +143,8 @@ public function testGetNodeFromUploadedFileValidatesPdf(): void { ->method('validate') ->with($content); - $folder = $this->createMock(Folder::class); - $this->folderService->method('getFolder')->willReturn($folder); - $this->folderService->method('getFolderName')->willReturn('LibreSign'); - $targetFolder = $this->createMock(Folder::class); - $folder->method('newFolder')->willReturn($targetFolder); + $this->folderService->method('getFolderForFile')->willReturn($targetFolder); $node = $this->createMock(Node::class); $targetFolder->method('newFile')->willReturn($node); @@ -187,12 +175,8 @@ public function testProcessUploadedFilesWithRollbackSuccess(): void { $this->pdfValidator->method('validate'); $this->validateHelper->method('validateNewFile'); - $folder = $this->createMock(Folder::class); - $this->folderService->method('getFolder')->willReturn($folder); - $this->folderService->method('getFolderName')->willReturn('LibreSign'); - $targetFolder = $this->createMock(Folder::class); - $folder->method('newFolder')->willReturn($targetFolder); + $this->folderService->method('getFolderForFile')->willReturn($targetFolder); $node = $this->createMock(Node::class); $node->method('getId')->willReturn(123); @@ -229,12 +213,8 @@ public function testProcessUploadedFilesWithRollbackValidatesNewFiles(): void { 'userManager' => $user, ]); - $folder = $this->createMock(Folder::class); - $this->folderService->method('getFolder')->willReturn($folder); - $this->folderService->method('getFolderName')->willReturn('LibreSign'); - $targetFolder = $this->createMock(Folder::class); - $folder->method('newFolder')->willReturn($targetFolder); + $this->folderService->method('getFolderForFile')->willReturn($targetFolder); $node = $this->createMock(Node::class); $node->method('getId')->willReturn(123); @@ -261,12 +241,8 @@ public function testProcessUploadedFilesWithRollbackOnValidationError(): void { ->method('validateNewFile') ->willThrowException(new LibresignException('Invalid file')); - $folder = $this->createMock(Folder::class); - $this->folderService->method('getFolder')->willReturn($folder); - $this->folderService->method('getFolderName')->willReturn('LibreSign'); - $targetFolder = $this->createMock(Folder::class); - $folder->method('newFolder')->willReturn($targetFolder); + $this->folderService->method('getFolderForFile')->willReturn($targetFolder); $node = $this->createMock(Node::class); $node->method('getId')->willReturn(123); @@ -299,12 +275,8 @@ public function testProcessUploadedFilesWithRollbackLogsDeleteError(): void { ->method('validateNewFile') ->willThrowException(new LibresignException('Invalid file')); - $folder = $this->createMock(Folder::class); - $this->folderService->method('getFolder')->willReturn($folder); - $this->folderService->method('getFolderName')->willReturn('LibreSign'); - $targetFolder = $this->createMock(Folder::class); - $folder->method('newFolder')->willReturn($targetFolder); + $this->folderService->method('getFolderForFile')->willReturn($targetFolder); $node = $this->createMock(Node::class); $node->method('getId')->willReturn(123); @@ -344,12 +316,8 @@ public function testProcessUploadedFilesReturnsCorrectStructure(): void { $this->pdfValidator->method('validate'); $this->validateHelper->method('validateNewFile'); - $folder = $this->createMock(Folder::class); - $this->folderService->method('getFolder')->willReturn($folder); - $this->folderService->method('getFolderName')->willReturn('LibreSign'); - $targetFolder = $this->createMock(Folder::class); - $folder->method('newFolder')->willReturn($targetFolder); + $this->folderService->method('getFolderForFile')->willReturn($targetFolder); $node = $this->createMock(Node::class); $node->method('getId')->willReturn(123); diff --git a/tests/php/Unit/Service/FolderServiceTest.php b/tests/php/Unit/Service/FolderServiceTest.php index b830a4f703..af4bc8f3a6 100644 --- a/tests/php/Unit/Service/FolderServiceTest.php +++ b/tests/php/Unit/Service/FolderServiceTest.php @@ -274,4 +274,64 @@ public static function providerGetFolderName(): array { ], ]; } + + public function testGetFolderForFileUsesEnvelopeFolderWhenProvided(): void { + $envelopeFolderId = 456; + $data = [ + 'settings' => [ + 'envelopeFolderId' => $envelopeFolderId, + ], + ]; + + $mockUserFolder = $this->createMock(Folder::class); + $mockEnvelopeFolder = $this->createMock(Folder::class); + + $mockUserFolder->expects($this->once()) + ->method('getFirstNodeById') + ->with($envelopeFolderId) + ->willReturn($mockEnvelopeFolder); + + $this->appConfig->method('getUserValue')->willReturn('/LibreSign'); + $this->groupManager->method('isInGroup')->willReturn(false); + + $userFolder = $this->createMock(Folder::class); + $userFolder->method('isUpdateable')->willReturn(true); + $userFolder->method('get')->willReturn($mockUserFolder); + $this->root->method('getUserFolder')->willReturn($userFolder); + + $service = $this->getInstance('testuser'); + $result = $service->getFolderForFile($data, 'testuser'); + + $this->assertInstanceOf(Folder::class, $result); + } + + public function testGetFolderForFileCreatesNewFolderWhenNoEnvelopeId(): void { + $data = [ + 'name' => 'Document', + 'settings' => [], + ]; + + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('user1'); + + $mockUserFolder = $this->createMock(Folder::class); + $mockNewFolder = $this->createMock(Folder::class); + + $mockUserFolder->expects($this->once()) + ->method('newFolder') + ->willReturn($mockNewFolder); + + $this->appConfig->method('getUserValue')->willReturn('/LibreSign'); + $this->groupManager->method('isInGroup')->willReturn(false); + + $userFolder = $this->createMock(Folder::class); + $userFolder->method('isUpdateable')->willReturn(true); + $userFolder->method('get')->willReturn($mockUserFolder); + $this->root->method('getUserFolder')->willReturn($userFolder); + + $service = $this->getInstance('user1'); + $result = $service->getFolderForFile($data, $user); + + $this->assertInstanceOf(Folder::class, $result); + } }