diff --git a/lib/Db/FileMapper.php b/lib/Db/FileMapper.php index e941ece8bb..dd41b3b532 100644 --- a/lib/Db/FileMapper.php +++ b/lib/Db/FileMapper.php @@ -281,7 +281,7 @@ public function neutralizeDeletedUser(string $userId, string $displayName): void */ public function getChildrenFiles(int $parentId): array { $cached = array_filter($this->file, fn ($f) => $f->getParentFileId() === $parentId); - if (!empty($cached)) { + if (!empty($cached) && count($cached) > 1) { return array_values($cached); } diff --git a/lib/Service/IdentifyMethod/IdentifyService.php b/lib/Service/IdentifyMethod/IdentifyService.php index 1111dee882..ecc0986a1e 100644 --- a/lib/Service/IdentifyMethod/IdentifyService.php +++ b/lib/Service/IdentifyMethod/IdentifyService.php @@ -67,16 +67,25 @@ private function propagateIdentifiedDateToEnvelopeChildren(IdentifyMethod $ident $signRequest = $this->signRequestMapper->getById($identifyMethod->getSignRequestId()); $fileEntity = $this->fileMapper->getById($signRequest->getFileId()); - if (method_exists($fileEntity, 'getNodeType') && $fileEntity->getNodeType() !== 'envelope') { + if ($fileEntity->getNodeType() === 'envelope') { + $envelopeId = $fileEntity->getId(); + } elseif ($fileEntity->hasParent()) { + $envelopeId = $fileEntity->getParentFileId(); + } else { return; } $children = $this->signRequestMapper->getByEnvelopeChildrenAndIdentifyMethod( - $fileEntity->getId(), + $envelopeId, $signRequest->getId(), ); foreach ($children as $childSignRequest) { + // Skip the current sign request to avoid updating it twice + if ($childSignRequest->getId() === $signRequest->getId()) { + continue; + } + $childMethods = $this->identifyMethodMapper->getIdentifyMethodsFromSignRequestId($childSignRequest->getId()); foreach ($childMethods as $childEntity) { diff --git a/lib/Service/SignFileService.php b/lib/Service/SignFileService.php index d2342d17fc..94b767337e 100644 --- a/lib/Service/SignFileService.php +++ b/lib/Service/SignFileService.php @@ -388,21 +388,26 @@ public function sign(): void { * @return array Array of ['file' => FileEntity, 'signRequest' => SignRequestEntity] */ private function getSignRequestsToSign(): array { - if (!$this->libreSignFile->isEnvelope()) { + if (!$this->libreSignFile->isEnvelope() + && !$this->libreSignFile->hasParent() + ) { return [[ 'file' => $this->libreSignFile, 'signRequest' => $this->signRequest, ]]; } - $childFiles = $this->fileMapper->getChildrenFiles($this->libreSignFile->getId()); + $envelopeId = $this->libreSignFile->isEnvelope() + ? $this->libreSignFile->getId() + : $this->libreSignFile->getParentFileId(); + $childFiles = $this->fileMapper->getChildrenFiles($envelopeId); if (empty($childFiles)) { throw new LibresignException('No files found in envelope'); } $childSignRequests = $this->signRequestMapper->getByEnvelopeChildrenAndIdentifyMethod( - $this->libreSignFile->getId(), + $envelopeId, $this->signRequest->getId() ); diff --git a/tests/php/Unit/Service/IdentifyMethod/IdentifyServiceTest.php b/tests/php/Unit/Service/IdentifyMethod/IdentifyServiceTest.php new file mode 100644 index 0000000000..b7b8b11f05 --- /dev/null +++ b/tests/php/Unit/Service/IdentifyMethod/IdentifyServiceTest.php @@ -0,0 +1,152 @@ +identifyMethodMapper = $this->createMock(IdentifyMethodMapper::class); + $this->sessionService = $this->createMock(SessionService::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->signRequestMapper = $this->createMock(SignRequestMapper::class); + $this->l10n = $this->createMock(IL10N::class); + $this->fileMapper = $this->createMock(FileMapper::class); + $this->hasher = $this->createMock(IHasher::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->service = new IdentifyService( + $this->identifyMethodMapper, + $this->sessionService, + $this->timeFactory, + $this->eventDispatcher, + $this->rootFolder, + $this->appConfig, + $this->signRequestMapper, + $this->l10n, + $this->fileMapper, + $this->hasher, + $this->userManager, + $this->urlGenerator, + $this->logger, + ); + } + + public function testPropagateIdentifiedDateSkipsCurrentRequestAndUpdatesSiblings(): void { + $identifyMethod = new IdentifyMethod(); + $identifyMethod->setSignRequestId(1); + $identifyMethod->setIdentifierKey('email'); + $identifyMethod->setIdentifierValue('user@example.com'); + $identifyMethod->setIdentifiedAtDate('2024-01-01T00:00:00Z'); + + $parentEnvelopeId = 99; + + $currentFile = new File(); + $currentFile->setId(10); + $currentFile->setParentFileId($parentEnvelopeId); + + $currentSignRequest = new SignRequest(); + $currentSignRequest->setId(1); + $currentSignRequest->setFileId($currentFile->getId()); + + $siblingFile = new File(); + $siblingFile->setId(11); + $siblingFile->setParentFileId($parentEnvelopeId); + + $siblingSignRequest = new SignRequest(); + $siblingSignRequest->setId(2); + $siblingSignRequest->setFileId($siblingFile->getId()); + + $this->signRequestMapper + ->expects($this->once()) + ->method('getById') + ->with($identifyMethod->getSignRequestId()) + ->willReturn($currentSignRequest); + + $this->fileMapper + ->expects($this->once()) + ->method('getById') + ->with($currentFile->getId()) + ->willReturn($currentFile); + + $this->signRequestMapper + ->expects($this->once()) + ->method('getByEnvelopeChildrenAndIdentifyMethod') + ->with($parentEnvelopeId, $currentSignRequest->getId()) + ->willReturn([$currentSignRequest, $siblingSignRequest]); + + $siblingIdentifyMethod = new IdentifyMethod(); + $siblingIdentifyMethod->setSignRequestId($siblingSignRequest->getId()); + $siblingIdentifyMethod->setIdentifierKey($identifyMethod->getIdentifierKey()); + $siblingIdentifyMethod->setIdentifierValue($identifyMethod->getIdentifierValue()); + + $this->identifyMethodMapper + ->expects($this->exactly(2)) + ->method('getIdentifyMethodsFromSignRequestId') + ->willReturnMap([ + [$identifyMethod->getSignRequestId(), []], + [$siblingSignRequest->getId(), [$siblingIdentifyMethod]], + ]); + + $this->identifyMethodMapper + ->expects($this->once()) + ->method('insertOrUpdate') + ->with($identifyMethod); + + $this->identifyMethodMapper + ->expects($this->once()) + ->method('update') + ->with($this->callback(function (IdentifyMethod $updated) use ($siblingIdentifyMethod, $identifyMethod) { + return $updated->getSignRequestId() === $siblingIdentifyMethod->getSignRequestId() + && $updated->getIdentifiedAtDate() == $identifyMethod->getIdentifiedAtDate(); + })); + + $this->service->save($identifyMethod); + } +} diff --git a/tests/php/Unit/Service/SignFileServiceTest.php b/tests/php/Unit/Service/SignFileServiceTest.php index a914a8f309..3b9b542f24 100644 --- a/tests/php/Unit/Service/SignFileServiceTest.php +++ b/tests/php/Unit/Service/SignFileServiceTest.php @@ -625,6 +625,52 @@ public static function providerGetOrGeneratePfxContent(): array { ]; } + public function testGetSignRequestsToSignWhenFileHasParentEnvelope(): void { + $service = $this->getService(); + + $envelopeId = 99; + $childFile = new File(); + $childFile->setId(10); + $childFile->setParentFileId($envelopeId); + + $siblingFile = new File(); + $siblingFile->setId(11); + $siblingFile->setParentFileId($envelopeId); + + $signRequest = new SignRequest(); + $signRequest->setId(200); + $signRequest->setFileId($childFile->getId()); + + $siblingSignRequest = new SignRequest(); + $siblingSignRequest->setId(201); + $siblingSignRequest->setFileId($siblingFile->getId()); + + $this->fileMapper + ->expects($this->once()) + ->method('getChildrenFiles') + ->with($envelopeId) + ->willReturn([$childFile, $siblingFile]); + + $this->signRequestMapper + ->expects($this->once()) + ->method('getByEnvelopeChildrenAndIdentifyMethod') + ->with($envelopeId, $signRequest->getId()) + ->willReturn([$signRequest, $siblingSignRequest]); + + $result = self::invokePrivate( + $service + ->setLibreSignFile($childFile) + ->setSignRequest($signRequest), + 'getSignRequestsToSign' + ); + + $this->assertCount(2, $result); + $this->assertSame($childFile, $result[0]['file']); + $this->assertSame($signRequest, $result[0]['signRequest']); + $this->assertSame($siblingFile, $result[1]['file']); + $this->assertSame($siblingSignRequest, $result[1]['signRequest']); + } + #[DataProvider('providerStoreUserMetadata')] public function testStoreUserMetadata(bool $collectMetadata, ?array $previous, array $new, ?array $expected): void { $signRequest = new \OCA\Libresign\Db\SignRequest();