From 316317cc96d5ee2f6d56cac2bc33634dede8e1dd Mon Sep 17 00:00:00 2001 From: Mathis Koblin Date: Fri, 19 Jul 2024 08:42:08 +0200 Subject: [PATCH] Processed files for deleted sys_file record should be deleted as well #5 --- Classes/Command/UnduplicateCommand.php | 52 +++++++++++++++++-- ...s_file_duplicates_with_processed_files.csv | 8 +++ ...duplicates_with_processed_files_RESULT.csv | 6 +++ .../Command/UnduplicateCommandTest.php | 19 +++++++ 4 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 Tests/Functional/Command/DataSet/sys_file_duplicates_with_processed_files.csv create mode 100644 Tests/Functional/Command/DataSet/sys_file_duplicates_with_processed_files_RESULT.csv diff --git a/Classes/Command/UnduplicateCommand.php b/Classes/Command/UnduplicateCommand.php index f0ef2ee..35f88c3 100644 --- a/Classes/Command/UnduplicateCommand.php +++ b/Classes/Command/UnduplicateCommand.php @@ -8,6 +8,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -122,7 +123,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->dryRun = $input->getOption('dry-run'); $onlyThisIdentifier = $input->getOption('identifier'); - $onlyThisStorage = (int) $input->getOption('storage') ; + $onlyThisStorage = (int)$input->getOption('storage'); $queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_file'); $queryBuilder->count('*') @@ -156,7 +157,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->output->warning('Found empty identifier'); continue; } - $storage = (int) $row['storage']; + $storage = (int)$row['storage']; $files = $this->findDuplicateFilesForIdentifier($identifier, $storage); $originalUid = null; @@ -185,7 +186,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int )); if (!$this->dryRun) { $this->findAndUpdateReferences($originalUid, $oldFileUid); - $this->deleteOldFileRecord($fileRow['uid']); + $this->deleteOldFileRecord($oldFileUid); + $this->findAndDeleteOldProcessedFile($oldFileUid); } } } @@ -336,7 +338,7 @@ private function deleteReferencedRecord(array $referenceRow) $recordDeleteQueryBuilder->createNamedParameter($referenceRow['recuid'], PDO::PARAM_INT) ) ) - ->executeStatement(); + ->executeStatement(); } private function deleteReference(array $referenceRow) @@ -393,4 +395,46 @@ private function deleteOldFileRecord(int $oldFileUid) ) ->executeStatement(); } + + private function findAndDeleteOldProcessedFile(int $oldFileUid): void + { + $recordQueryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_file_processedfile'); + $results = $recordQueryBuilder->select('identifier') + ->from('sys_file_processedfile') + ->where( + $recordQueryBuilder->expr()->eq( + 'original', + $recordQueryBuilder->createNamedParameter($oldFileUid) + ) + ) + ->executeQuery(); + while ($record = $results->fetchAssociative()) { + // delete each file from file system + $this->output->writeln('Deleting processed file: ' . $record['identifier']); + $this->deleteProcessedFile($record['identifier']); + } + // delete all records in sys_file_processedfile + $recordQueryBuilder->delete('sys_file_processedfile') + ->where( + $recordQueryBuilder->expr()->eq( + 'original', + $recordQueryBuilder->createNamedParameter($oldFileUid, PDO::PARAM_INT) + ) + ) + ->executeStatement(); + } + + private function deleteProcessedFile(mixed $identifier): void + { + $file = Environment::getPublicPath() . "/fileadmin" . $identifier; + if (file_exists($file)) { + unlink($file); + // delete all empty parent folders + $dir = dirname($file); + while ($dir !== Environment::getPublicPath() . "/fileadmin" && count(scandir($dir)) === 2) { + rmdir($dir); + $dir = dirname($dir); + } + } + } } diff --git a/Tests/Functional/Command/DataSet/sys_file_duplicates_with_processed_files.csv b/Tests/Functional/Command/DataSet/sys_file_duplicates_with_processed_files.csv new file mode 100644 index 0000000..a4fd2e9 --- /dev/null +++ b/Tests/Functional/Command/DataSet/sys_file_duplicates_with_processed_files.csv @@ -0,0 +1,8 @@ +"sys_file",,, +,"uid","identifier","storage" +,1,"/test/abc.jpg",1 +,2,"/test/abc.jpg",1 +"sys_file_processedfile",,,,,,, +,"uid","original","storage","identifier" +,"991","1","1","/_processed_/3/c/csm_myfile_975bcb8fba.jpg" +,"992","2","1","/_processed_/3/c/csm_myfile_23231sdi99.jpg" diff --git a/Tests/Functional/Command/DataSet/sys_file_duplicates_with_processed_files_RESULT.csv b/Tests/Functional/Command/DataSet/sys_file_duplicates_with_processed_files_RESULT.csv new file mode 100644 index 0000000..5c87df7 --- /dev/null +++ b/Tests/Functional/Command/DataSet/sys_file_duplicates_with_processed_files_RESULT.csv @@ -0,0 +1,6 @@ +"sys_file",,, +,"uid","identifier","storage" +,2,"/test/abc.jpg",1 +"sys_file_processedfile",,,,,,, +,"uid","original","storage","identifier" +,"992","2","1","/_processed_/3/c/csm_myfile_23231sdi99.jpg" diff --git a/Tests/Functional/Command/UnduplicateCommandTest.php b/Tests/Functional/Command/UnduplicateCommandTest.php index b62f12c..1b99486 100644 --- a/Tests/Functional/Command/UnduplicateCommandTest.php +++ b/Tests/Functional/Command/UnduplicateCommandTest.php @@ -60,6 +60,25 @@ class UnduplicateCommandTest extends FunctionalTestCase self::assertEquals(0, $result['status']); } + /** + * Provide a processed file for the test run, so that it can be deleted + * @var array + */ + protected array $pathsToProvideInTestInstance = [ + 'typo3/sysext/frontend/Resources/Public/Icons/Extension.svg' => 'fileadmin/_processed_/3/c/csm_myfile_975bcb8fba.jpg', + ]; + + #[Test] public function unduplicateCommandFixesDuplicatesWithProcessedFiles() + { + $this->importCSVDataSet(__DIR__ . '/DataSet/sys_file_duplicates_with_processed_files.csv'); + + $result = $this->executeConsoleCommand('unduplicate:sysfile'); + + // the processed files are updated, so that the newer sys_file entry (uid=2) is used + $this->assertCSVDataSet(__DIR__ . '/DataSet/sys_file_duplicates_with_processed_files_RESULT.csv'); + self::assertEquals(0, $result['status']); + } + /** * based on TYPO3\CMS\Core\Tests\Functional\Command\AbstractCommandTest::executeConsoleCommand