diff --git a/lib/Command/ResetDocument.php b/lib/Command/ResetDocument.php index 4702db69abb..c45b67a89cc 100644 --- a/lib/Command/ResetDocument.php +++ b/lib/Command/ResetDocument.php @@ -77,10 +77,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 b1858f454e4..867e3e177bd 100644 --- a/lib/Cron/Cleanup.php +++ b/lib/Cron/Cleanup.php @@ -53,10 +53,8 @@ public function __construct(ITimeFactory $time, protected function run($argument) { $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; @@ -71,5 +69,8 @@ protected function run($argument) { $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 33d11d31709..d238138ba48 100644 --- a/lib/Db/DocumentMapper.php +++ b/lib/Db/DocumentMapper.php @@ -23,6 +23,7 @@ namespace OCA\Text\Db; +use Generator; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -55,12 +56,33 @@ public function find($documentId): Document { return Document::fromRow($data); } - public function findAll(): array { + public function findAll(): Generator { $qb = $this->db->getQueryBuilder(); $result = $qb->select('*') ->from($this->getTableName()) - ->execute(); + ->executeQuery(); + try { + while ($row = $result->fetch()) { + yield $this->mapRowToEntity($row); + } + } finally { + $result->closeCursor(); + } + } + + 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; + } - return $this->findEntities($qb); + 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 4ca6a39d99c..7ddbba007dc 100644 --- a/lib/Db/SessionMapper.php +++ b/lib/Db/SessionMapper.php @@ -80,6 +80,17 @@ public function findAll($documentId) { 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; + } + public function findAllActive($documentId) { $qb = $this->db->getQueryBuilder(); $qb->select('id', 'color', 'document_id', 'last_awareness_message', 'last_contact', 'user_id', 'guest_name') @@ -139,6 +150,12 @@ public function deleteByDocumentId($documentId) { return $qb->execute(); } + public function clearAll(): void { + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->getTableName()) + ->executeStatement(); + } + public function isUserInDocument($documentId, $userId): bool { $qb = $this->db->getQueryBuilder(); $result = $qb->select('*') diff --git a/lib/Db/StepMapper.php b/lib/Db/StepMapper.php index 39cb1baea37..956ec87cb4e 100644 --- a/lib/Db/StepMapper.php +++ b/lib/Db/StepMapper.php @@ -73,6 +73,12 @@ public function deleteAll($documentId): void { ->executeStatement(); } + public function clearAll(): void { + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->getTableName()) + ->executeStatement(); + } + // not in use right now public function deleteBeforeVersion($documentId, $version): void { $qb = $this->db->getQueryBuilder(); diff --git a/lib/Migration/ResetSessionsBeforeYjs.php b/lib/Migration/ResetSessionsBeforeYjs.php index ded1c8bdf3e..f04229a1959 100644 --- a/lib/Migration/ResetSessionsBeforeYjs.php +++ b/lib/Migration/ResetSessionsBeforeYjs.php @@ -2,7 +2,6 @@ namespace OCA\Text\Migration; -use OCA\Text\Db\Document; use OCA\Text\Service\DocumentService; use OCP\IConfig; 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 0c2c87d0a38..53c52bb63c2 100644 --- a/lib/Service/DocumentService.php +++ b/lib/Service/DocumentService.php @@ -58,6 +58,7 @@ use OCP\Files\SimpleFS\ISimpleFile; use OCP\ICache; use OCP\ICacheFactory; +use OCP\IConfig; use OCP\IRequest; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; @@ -88,8 +89,9 @@ class DocumentService { private ILockingProvider $lockingProvider; private ILockManager $lockManager; private IUserMountCache $userMountCache; + private IConfig $config; - public function __construct(DocumentMapper $documentMapper, StepMapper $stepMapper, SessionMapper $sessionMapper, IAppData $appData, $userId, IRootFolder $rootFolder, ICacheFactory $cacheFactory, LoggerInterface $logger, ShareManager $shareManager, IRequest $request, IManager $directManager, ILockingProvider $lockingProvider, ILockManager $lockManager, IUserMountCache $userMountCache) { + public function __construct(DocumentMapper $documentMapper, StepMapper $stepMapper, SessionMapper $sessionMapper, IAppData $appData, $userId, IRootFolder $rootFolder, ICacheFactory $cacheFactory, LoggerInterface $logger, ShareManager $shareManager, IRequest $request, IManager $directManager, ILockingProvider $lockingProvider, ILockManager $lockManager, IUserMountCache $userMountCache, IConfig $config) { $this->documentMapper = $documentMapper; $this->stepMapper = $stepMapper; $this->sessionMapper = $sessionMapper; @@ -102,6 +104,7 @@ public function __construct(DocumentMapper $documentMapper, StepMapper $stepMapp $this->lockingProvider = $lockingProvider; $this->lockManager = $lockManager; $this->userMountCache = $userMountCache; + $this->config = $config; $token = $request->getParam('token'); if ($this->userId === null && $token !== null) { try { @@ -638,4 +641,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 98c18721033..69f22d6aaad 100644 --- a/lib/Service/SessionService.php +++ b/lib/Service/SessionService.php @@ -123,6 +123,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 00d740216e3..945a773db5b 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\Lock\ILockingProvider; use OCP\Share\IManager; @@ -52,6 +53,7 @@ public function setUp(): void { $this->lockingProvider = $this->createMock(ILockingProvider::class); $this->lockManager = $this->createMock(ILockManager::class); $this->userMountCache = $this->createMock(IUserMountCache::class); + $config = $this->createMock(IConfig::class); $this->documentService = new DocumentService( $this->documentMapper, @@ -68,6 +70,7 @@ public function setUp(): void { $this->lockingProvider, $this->lockManager, $this->userMountCache, + $config, ); }