diff --git a/lib/Command/ResetDocument.php b/lib/Command/ResetDocument.php index def66cfd48f..f7e293dd213 100644 --- a/lib/Command/ResetDocument.php +++ b/lib/Command/ResetDocument.php @@ -60,10 +60,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 1; } + if ($all && $fullReset) { + // Truncate tables and clear document directory + $this->documentService->clearAll(); + return 0; + } + if ($all) { - $fileIds = array_map(static function (Document $document) { - return $document->getId(); - }, $this->documentService->getAll()); + $fileIds = []; + foreach ($this->documentService->getAll() as $document) { + $fileIds[] = $document->getId(); + } } else { $fileIds = [$fileId]; } diff --git a/lib/Cron/Cleanup.php b/lib/Cron/Cleanup.php index 984d3ff8f39..e28b3ff4d8f 100644 --- a/lib/Cron/Cleanup.php +++ b/lib/Cron/Cleanup.php @@ -39,10 +39,8 @@ public function __construct(ITimeFactory $time, */ protected function run($argument): void { $this->logger->debug('Run cleanup job for text documents'); - $documents = $this->documentService->getAll(); - foreach ($documents as $document) { - $allSessions = $this->sessionService->getAllSessions($document->getId()); - if (count($allSessions) > 0) { + foreach ($this->documentService->getAll() as $document) { + if ($this->sessionService->countAllSessions($document->getId()) > 0) { // Do not reset if there are any sessions left // Inactive sessions will get removed further down and will trigger a reset next time continue; @@ -57,5 +55,8 @@ protected function run($argument): void { $this->logger->debug('Run cleanup job for text sessions'); $removedSessions = $this->sessionService->removeInactiveSessionsWithoutSteps(null); $this->logger->debug('Removed ' . $removedSessions . ' inactive sessions'); + + $this->logger->debug('Run cleanup job for obsolete documents folders'); + $this->documentService->cleanupOldDocumentsFolders(); } } diff --git a/lib/Db/DocumentMapper.php b/lib/Db/DocumentMapper.php index 54388ef07e4..c1d653604c3 100644 --- a/lib/Db/DocumentMapper.php +++ b/lib/Db/DocumentMapper.php @@ -6,6 +6,7 @@ namespace OCA\Text\Db; +use Generator; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -38,12 +39,33 @@ public function find(int $documentId): Document { return Document::fromRow($data); } - public function findAll(): array { + public function findAll(): Generator { $qb = $this->db->getQueryBuilder(); $result = $qb->select('*') ->from($this->getTableName()) ->executeQuery(); + try { + while ($row = $result->fetch()) { + yield $this->mapRowToEntity($row); + } + } finally { + $result->closeCursor(); + } + } - return $this->findEntities($qb); + public function countAll(): int { + $qb = $this->db->getQueryBuilder(); + $qb->select($qb->func()->count('id')) + ->from($this->getTableName()); + $result = $qb->executeQuery(); + $count = (int)$result->fetchOne(); + $result->closeCursor(); + return $count; + } + + public function clearAll(): void { + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->getTableName()) + ->executeStatement(); } } diff --git a/lib/Db/SessionMapper.php b/lib/Db/SessionMapper.php index 22fa1cfe60b..383704f40cd 100644 --- a/lib/Db/SessionMapper.php +++ b/lib/Db/SessionMapper.php @@ -68,6 +68,17 @@ public function findAll(int $documentId): array { return $this->findEntities($qb); } + public function countAll(int $documentId): int { + $qb = $this->db->getQueryBuilder(); + $qb->select('id', 'color', 'document_id', 'last_awareness_message', 'last_contact', 'user_id', 'guest_name') + ->from($this->getTableName()) + ->where($qb->expr()->eq('document_id', $qb->createNamedParameter($documentId))); + $result = $qb->executeQuery(); + $count = (int)$result->fetchOne(); + $result->closeCursor(); + return $count; + } + /** * @return Session[] * @@ -137,6 +148,12 @@ public function deleteByDocumentId(int $documentId): int { return $qb->executeStatement(); } + public function clearAll(): void { + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->getTableName()) + ->executeStatement(); + } + public function isUserInDocument(int $documentId, string $userId): bool { $qb = $this->db->getQueryBuilder(); $result = $qb->select('*') diff --git a/lib/Db/StepMapper.php b/lib/Db/StepMapper.php index 86a9e3c03a8..b92d335b670 100644 --- a/lib/Db/StepMapper.php +++ b/lib/Db/StepMapper.php @@ -62,6 +62,12 @@ public function deleteAll(int $documentId): void { ->executeStatement(); } + public function clearAll(): void { + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->getTableName()) + ->executeStatement(); + } + // not in use right now public function deleteBeforeVersion(int $documentId, int $version): int { $qb = $this->db->getQueryBuilder(); diff --git a/lib/Migration/ResetSessionsBeforeYjs.php b/lib/Migration/ResetSessionsBeforeYjs.php index cf4bc71c917..6fef416dc20 100644 --- a/lib/Migration/ResetSessionsBeforeYjs.php +++ b/lib/Migration/ResetSessionsBeforeYjs.php @@ -7,7 +7,6 @@ namespace OCA\Text\Migration; -use OCA\Text\Db\Document; use OCA\Text\Service\DocumentService; use OCP\IAppConfig; use OCP\Migration\IOutput; @@ -32,20 +31,6 @@ public function run(IOutput $output): void { return; } - $fileIds = array_map(static function (Document $document) { - return $document->getId(); - }, $this->documentService->getAll()); - - if (!$fileIds) { - return; - } - - $output->startProgress(count($fileIds)); - foreach ($fileIds as $fileId) { - $this->documentService->unlock($fileId); - $this->documentService->resetDocument($fileId, true); - $output->advance(); - } - $output->finishProgress(); + $this->documentService->clearAll(); } } diff --git a/lib/Service/DocumentService.php b/lib/Service/DocumentService.php index d16ffabcd18..8d3a8bccafb 100644 --- a/lib/Service/DocumentService.php +++ b/lib/Service/DocumentService.php @@ -42,6 +42,7 @@ use OCP\Files\SimpleFS\ISimpleFile; use OCP\ICache; use OCP\ICacheFactory; +use OCP\IConfig; use OCP\IRequest; use OCP\Lock\LockedException; use OCP\PreConditionNotMetException; @@ -70,8 +71,9 @@ class DocumentService { private IAppData $appData; private ILockManager $lockManager; private IUserMountCache $userMountCache; + private IConfig $config; - public function __construct(DocumentMapper $documentMapper, StepMapper $stepMapper, SessionMapper $sessionMapper, IAppData $appData, ?string $userId, IRootFolder $rootFolder, ICacheFactory $cacheFactory, LoggerInterface $logger, ShareManager $shareManager, IRequest $request, IManager $directManager, ILockManager $lockManager, IUserMountCache $userMountCache) { + public function __construct(DocumentMapper $documentMapper, StepMapper $stepMapper, SessionMapper $sessionMapper, IAppData $appData, ?string $userId, IRootFolder $rootFolder, ICacheFactory $cacheFactory, LoggerInterface $logger, ShareManager $shareManager, IRequest $request, IManager $directManager, ILockManager $lockManager, IUserMountCache $userMountCache, IConfig $config) { $this->documentMapper = $documentMapper; $this->stepMapper = $stepMapper; $this->sessionMapper = $sessionMapper; @@ -83,6 +85,7 @@ public function __construct(DocumentMapper $documentMapper, StepMapper $stepMapp $this->shareManager = $shareManager; $this->lockManager = $lockManager; $this->userMountCache = $userMountCache; + $this->config = $config; $token = $request->getParam('token'); if ($this->userId === null && $token !== null) { try { @@ -428,7 +431,7 @@ public function resetDocument(int $documentId, bool $force = false): void { } } - public function getAll(): array { + public function getAll(): \Generator { return $this->documentMapper->findAll(); } @@ -642,4 +645,40 @@ public function unlock(int $fileId): void { } catch (NoLockProviderException | PreConditionNotMetException | NotFoundException $e) { } } + + public function countAll(): int { + return $this->documentMapper->countAll(); + } + + private function getFullAppFolder(): Folder { + $appFolder = $this->rootFolder->get('appdata_' . $this->config->getSystemValueString('instanceid', '') . '/text'); + if (!$appFolder instanceof Folder) { + throw new NotFoundException('Folder not found'); + } + return $appFolder; + } + + public function clearAll(): void { + $this->stepMapper->clearAll(); + $this->sessionMapper->clearAll(); + $this->documentMapper->clearAll(); + try { + $appFolder = $this->getFullAppFolder(); + $appFolder->get('documents')->move($appFolder->getPath() . '/documents_old_' . time()); + } catch (NotFoundException) { + } + $this->ensureDocumentsFolder(); + } + + public function cleanupOldDocumentsFolders(): void { + try { + $appFolder = $this->getFullAppFolder(); + foreach ($appFolder->getDirectoryListing() as $node) { + if (str_starts_with($node->getName(), 'documents_old_')) { + $node->delete(); + } + } + } catch (NotFoundException) { + } + } } diff --git a/lib/Service/SessionService.php b/lib/Service/SessionService.php index b89817cea22..1f7a46e274d 100644 --- a/lib/Service/SessionService.php +++ b/lib/Service/SessionService.php @@ -106,6 +106,10 @@ public function getAllSessions(int $documentId): array { }, $sessions); } + public function countAllSessions(int $documentId): int { + return $this->sessionMapper->countAll($documentId); + } + public function getActiveSessions(int $documentId): array { $sessions = $this->sessionMapper->findAllActive($documentId); return array_map(function (Session $session) { diff --git a/tests/unit/Service/DocumentServiceTest.php b/tests/unit/Service/DocumentServiceTest.php index 7b886ebd46d..8d091f2564f 100644 --- a/tests/unit/Service/DocumentServiceTest.php +++ b/tests/unit/Service/DocumentServiceTest.php @@ -14,6 +14,7 @@ use OCP\Files\Lock\ILockManager; use OCP\Files\NotPermittedException; use OCP\ICacheFactory; +use OCP\IConfig; use OCP\IRequest; use OCP\Share\IManager; use Psr\Log\LoggerInterface; @@ -49,6 +50,7 @@ public function setUp(): void { $this->directManager = $this->createMock(\OCP\DirectEditing\IManager::class); $this->lockManager = $this->createMock(ILockManager::class); $this->userMountCache = $this->createMock(IUserMountCache::class); + $config = $this->createMock(IConfig::class); $this->documentService = new DocumentService( $this->documentMapper, @@ -64,6 +66,7 @@ public function setUp(): void { $this->directManager, $this->lockManager, $this->userMountCache, + $config, ); }