From 7c6f038dece54d334118b82d6e45d05f390f5d5b Mon Sep 17 00:00:00 2001 From: Simon Ouellet Date: Tue, 10 Feb 2026 10:02:18 -0500 Subject: [PATCH 1/3] WIP - bug metadata invalidate / --- Classes/Cache/CloudFrontCacheManager.php | 7 ++- Classes/Hooks/ClearCachePostProc.php | 10 +-- Tests/Functional/Hooks/FileMetadataTest.php | 67 +++++++++++++++++++++ 3 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 Tests/Functional/Hooks/FileMetadataTest.php diff --git a/Classes/Cache/CloudFrontCacheManager.php b/Classes/Cache/CloudFrontCacheManager.php index d577c79..4bed19c 100644 --- a/Classes/Cache/CloudFrontCacheManager.php +++ b/Classes/Cache/CloudFrontCacheManager.php @@ -55,9 +55,10 @@ public function __construct() */ public function fileMod(Folder|File|ProcessedFile $resource): void { + echo "fileMod called with resource identifier: " . $resource->getIdentifier() . "\n"; // Skip processed files that are already processed - if($resource instanceof ProcessedFile) { - if($resource->isProcessed()) return; + if ($resource instanceof ProcessedFile) { + if ($resource->isProcessed()) return; } $storage = $resource->getStorage(); @@ -72,7 +73,7 @@ public function fileMod(Folder|File|ProcessedFile $resource): void $this->enqueue($resource->getIdentifier() . $wildcard, $distributionIds); $this->clearCache(); - if(isset($GLOBALS['BE_USER'])) { + if (isset($GLOBALS['BE_USER'])) { $errorMessage = 'fileMod distributionsIds : ' . $distributionIds . ' resource identifier : ' . $resource->getIdentifier() . ' wildcard : ' . $wildcard; $GLOBALS['BE_USER']->writelog(4, 0, 0, 0, $errorMessage, ["ext" => "tm_cloudfront"]); } diff --git a/Classes/Hooks/ClearCachePostProc.php b/Classes/Hooks/ClearCachePostProc.php index ac34163..a428d7f 100644 --- a/Classes/Hooks/ClearCachePostProc.php +++ b/Classes/Hooks/ClearCachePostProc.php @@ -36,8 +36,7 @@ class ClearCachePostProc public function __construct( protected SiteFinder $siteFinder - ) - { + ) { $this->cloudFrontConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class) ->get('tm_cloudfront')['cloudfront']; $this->cacheManager = GeneralUtility::makeInstance(CloudFrontCacheManager::class); @@ -55,6 +54,9 @@ public function __construct( */ public function clearCachePostProc(&$params, &$pObj): void { + echo 'Running hook: ' . __METHOD__ . "\n"; + print_r($params); + // Reset the queue after processing for testing purposes $this->cacheManager->resetQueue(); @@ -109,7 +111,7 @@ public function clearCachePostProc(&$params, &$pObj): void // Priority to TsConfig settings if (!empty($tsConfig['TCEMAIN.'])) { - if(!empty($tsConfig['TCEMAIN.']['distributionIds'])) { + if (!empty($tsConfig['TCEMAIN.']['distributionIds'])) { $distributionIds = $tsConfig['TCEMAIN.']['distributionIds']; } } @@ -221,7 +223,7 @@ private function isPageDeleted($uid): int $queryBuilder->getRestrictions()->removeAll(); - return (int)$queryBuilder + return (int)$queryBuilder ->select('deleted') ->from('pages') ->where( diff --git a/Tests/Functional/Hooks/FileMetadataTest.php b/Tests/Functional/Hooks/FileMetadataTest.php new file mode 100644 index 0000000..abb72d2 --- /dev/null +++ b/Tests/Functional/Hooks/FileMetadataTest.php @@ -0,0 +1,67 @@ +importCSVDataSet(__DIR__ . '/../DataSet/be_users.csv'); + $this->importCSVDataSet(__DIR__ . '/../DataSet/sys_file_storage.csv'); + + $this->setUpBackendUser(1); + + $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['tm_cloudfront']['cloudfront'] = [ + 'distributionIds' => '{"www.example.com":"WWWWWWWWW","cdn.example.com":"CDNCDNCDNCDN"}', + 'mode' => 'table', + 'region' => 'us', + 'apikey' => 'AAAAAAAAAAAAAAA', + 'apisecret' => 'AAAAAAAAAAAAAAA', + 'version' => 'AAAAAAAAAAAAAAA', + ]; + } + + /** + * @test + */ + public function editingFileMetadataDoesNotCreateRootInvalidation(): void + { + $storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject(1); + + // Create a test file outside of the storage path + $tempFileName = tempnam(sys_get_temp_dir(), 'test-file'); + file_put_contents($tempFileName, 'test content'); + $file = $storage->addFile($tempFileName, $storage->getRootLevelFolder(), 'test-file.txt'); + + // Clear any invalidations created during file upload + GeneralUtility::makeInstance(ConnectionPool::class) + ->getConnectionForTable('tx_tmcloudfront_domain_model_invalidation') + ->truncate('tx_tmcloudfront_domain_model_invalidation'); + + // Simulate editing metadata via DataHandler + $actionService = GeneralUtility::makeInstance(ActionService::class); + $metadata = $file->getMetaData()->get(); + $actionService->modifyRecord('sys_file_metadata', (int)$metadata['uid'], [ + 'title' => 'New Title' + ]); + + // Assert that no "/" invalidation was created + $rows = $this->getAllRecords('tx_tmcloudfront_domain_model_invalidation'); + + $pathSegments = array_column($rows, 'pathsegment'); + $this->assertNotContains('/', $pathSegments, 'Root invalidation "/" should not be created when editing file metadata.'); + $this->assertContains('/test-file.txt', $pathSegments, 'The file path should be invalidated when editing its metadata.'); + } +} From 002b657bf4513e52ad954e8e7dfacc937d0937e6 Mon Sep 17 00:00:00 2001 From: Simon Ouellet Date: Wed, 11 Feb 2026 11:51:49 -0500 Subject: [PATCH 2/3] =?UTF-8?q?ajout=20de=20l'invalidation=20lors=20de=20l?= =?UTF-8?q?a=20mise=20=C3=A0=20jour=20d'une=20meta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FileAndFolderEventListener.php | 26 ++++++++++++++++--- Classes/Hooks/ClearCachePostProc.php | 5 +++- Configuration/Services.yaml | 5 ++++ Tests/Functional/Hooks/FileMetadataTest.php | 13 +++++----- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/Classes/EventListener/FileAndFolderEventListener.php b/Classes/EventListener/FileAndFolderEventListener.php index 9aa08ca..3f24ca4 100644 --- a/Classes/EventListener/FileAndFolderEventListener.php +++ b/Classes/EventListener/FileAndFolderEventListener.php @@ -18,8 +18,19 @@ namespace Toumoro\TmCloudfront\EventListener; use Toumoro\TmCloudfront\Cache\CloudFrontCacheManager; -use TYPO3\CMS\Core\Resource\Event\{AfterFileMovedEvent, AfterFileRenamedEvent, AfterFileReplacedEvent, - AfterFileDeletedEvent, AfterFileContentsSetEvent, AfterFolderMovedEvent, AfterFolderRenamedEvent, AfterFolderDeletedEvent}; +use TYPO3\CMS\Core\Resource\ResourceFactory; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Resource\Event\{ + AfterFileMovedEvent, + AfterFileRenamedEvent, + AfterFileReplacedEvent, + AfterFileDeletedEvent, + AfterFileContentsSetEvent, + AfterFolderMovedEvent, + AfterFolderRenamedEvent, + AfterFileMetaDataUpdatedEvent, + AfterFolderDeletedEvent +}; class FileAndFolderEventListener { @@ -49,7 +60,8 @@ public function afterFileDeleted(AfterFileDeletedEvent $event): void { try { $this->cacheManager->fileMod($event->getFile()); - } catch (\Exception $e) {} + } catch (\Exception $e) { + } } public function afterFileContentsSet(AfterFileContentsSetEvent $event): void @@ -72,4 +84,12 @@ public function afterFolderDeleted(AfterFolderDeletedEvent $event): void { $this->cacheManager->fileMod($event->getFolder()); } + public function afterMetadataUpdated(AfterFileMetaDataUpdatedEvent $event): void + { + + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $file = $resourceFactory->getFileObject($event->getFileUid()); + + $this->cacheManager->fileMod($file); + } } diff --git a/Classes/Hooks/ClearCachePostProc.php b/Classes/Hooks/ClearCachePostProc.php index a428d7f..b295e10 100644 --- a/Classes/Hooks/ClearCachePostProc.php +++ b/Classes/Hooks/ClearCachePostProc.php @@ -55,7 +55,10 @@ public function __construct( public function clearCachePostProc(&$params, &$pObj): void { echo 'Running hook: ' . __METHOD__ . "\n"; - print_r($params); + // looks if the table array key is set and in [content,pages] if not returns. This to avoid running hook on unwanted table like sys_file_metadata + if (!empty($params['table']) && !in_array($params['table'], ['tt_content', 'pages'])) { + return; + } // Reset the queue after processing for testing purposes $this->cacheManager->resetQueue(); diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index a757521..f0f7c80 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -48,3 +48,8 @@ services: identifier: 'tm.afterFolderDeleted' method: 'afterFolderDeleted' event: TYPO3\CMS\Core\Resource\Event\AfterFolderDeletedEvent + + - name: event.listener + identifier: 'tm.afterMetadataUpdated' + method: 'afterMetadataUpdated' + event: TYPO3\CMS\Core\Resource\Event\AfterFileMetaDataUpdatedEvent diff --git a/Tests/Functional/Hooks/FileMetadataTest.php b/Tests/Functional/Hooks/FileMetadataTest.php index abb72d2..eb14d72 100644 --- a/Tests/Functional/Hooks/FileMetadataTest.php +++ b/Tests/Functional/Hooks/FileMetadataTest.php @@ -2,10 +2,10 @@ namespace Toumoro\TmCloudfront\Tests\Functional\Hooks; +use TYPO3\CMS\Core\Resource\Index\MetaDataRepository; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\ActionService; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; class FileMetadataTest extends FunctionalTestCase @@ -34,8 +34,9 @@ protected function setUp(): void } /** - * @test + * test metadadata invalidation */ + #[\PHPUnit\Framework\Attributes\Test] public function editingFileMetadataDoesNotCreateRootInvalidation(): void { $storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject(1); @@ -50,11 +51,9 @@ public function editingFileMetadataDoesNotCreateRootInvalidation(): void ->getConnectionForTable('tx_tmcloudfront_domain_model_invalidation') ->truncate('tx_tmcloudfront_domain_model_invalidation'); - // Simulate editing metadata via DataHandler - $actionService = GeneralUtility::makeInstance(ActionService::class); - $metadata = $file->getMetaData()->get(); - $actionService->modifyRecord('sys_file_metadata', (int)$metadata['uid'], [ - 'title' => 'New Title' + $subject = $this->get(MetaDataRepository::class); + $result = $subject->update(1, [ + 'title' => 'test2', ]); // Assert that no "/" invalidation was created From af33ee35e5349270fab5b16b1f498d148755b081 Mon Sep 17 00:00:00 2001 From: Simon Ouellet Date: Thu, 12 Feb 2026 10:04:40 -0500 Subject: [PATCH 3/3] Suppression des invalidations lors de la modification des meta --- Classes/Cache/CloudFrontCacheManager.php | 10 ++++++---- Classes/EventListener/FileAndFolderEventListener.php | 11 ----------- Classes/Hooks/ClearCachePostProc.php | 1 - Classes/Task/ClearTask.php | 2 ++ Configuration/Services.yaml | 5 ----- Tests/Functional/Hooks/FileMetadataTest.php | 2 +- 6 files changed, 9 insertions(+), 22 deletions(-) diff --git a/Classes/Cache/CloudFrontCacheManager.php b/Classes/Cache/CloudFrontCacheManager.php index 4bed19c..b7349d2 100644 --- a/Classes/Cache/CloudFrontCacheManager.php +++ b/Classes/Cache/CloudFrontCacheManager.php @@ -55,7 +55,6 @@ public function __construct() */ public function fileMod(Folder|File|ProcessedFile $resource): void { - echo "fileMod called with resource identifier: " . $resource->getIdentifier() . "\n"; // Skip processed files that are already processed if ($resource instanceof ProcessedFile) { if ($resource->isProcessed()) return; @@ -332,13 +331,16 @@ public function getLanguagesDomains(int $uid_page): array return $domains; } - public function getLanguageHost(SiteLanguage $language): string { + public function getLanguageHost(SiteLanguage $language): string + { if ($host = $language->getBase()->getHost()) { return $host; } $currentRequest = $GLOBALS['TYPO3_REQUEST'] ?? null; - if ($currentRequest - && $currentRequest instanceof \Psr\Http\Message\ServerRequestInterface) { + if ( + $currentRequest + && $currentRequest instanceof \Psr\Http\Message\ServerRequestInterface + ) { return $currentRequest->getUri()->getHost(); } return ''; diff --git a/Classes/EventListener/FileAndFolderEventListener.php b/Classes/EventListener/FileAndFolderEventListener.php index 3f24ca4..a37a300 100644 --- a/Classes/EventListener/FileAndFolderEventListener.php +++ b/Classes/EventListener/FileAndFolderEventListener.php @@ -18,8 +18,6 @@ namespace Toumoro\TmCloudfront\EventListener; use Toumoro\TmCloudfront\Cache\CloudFrontCacheManager; -use TYPO3\CMS\Core\Resource\ResourceFactory; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Resource\Event\{ AfterFileMovedEvent, AfterFileRenamedEvent, @@ -28,7 +26,6 @@ AfterFileContentsSetEvent, AfterFolderMovedEvent, AfterFolderRenamedEvent, - AfterFileMetaDataUpdatedEvent, AfterFolderDeletedEvent }; @@ -84,12 +81,4 @@ public function afterFolderDeleted(AfterFolderDeletedEvent $event): void { $this->cacheManager->fileMod($event->getFolder()); } - public function afterMetadataUpdated(AfterFileMetaDataUpdatedEvent $event): void - { - - $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); - $file = $resourceFactory->getFileObject($event->getFileUid()); - - $this->cacheManager->fileMod($file); - } } diff --git a/Classes/Hooks/ClearCachePostProc.php b/Classes/Hooks/ClearCachePostProc.php index b295e10..1019a10 100644 --- a/Classes/Hooks/ClearCachePostProc.php +++ b/Classes/Hooks/ClearCachePostProc.php @@ -54,7 +54,6 @@ public function __construct( */ public function clearCachePostProc(&$params, &$pObj): void { - echo 'Running hook: ' . __METHOD__ . "\n"; // looks if the table array key is set and in [content,pages] if not returns. This to avoid running hook on unwanted table like sys_file_metadata if (!empty($params['table']) && !in_array($params['table'], ['tt_content', 'pages'])) { return; diff --git a/Classes/Task/ClearTask.php b/Classes/Task/ClearTask.php index c834451..090d6f1 100644 --- a/Classes/Task/ClearTask.php +++ b/Classes/Task/ClearTask.php @@ -2,6 +2,8 @@ namespace Toumoro\TmCloudfront\Task; + + use Aws\CloudFront\CloudFrontClient; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Database\Connection; diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index f0f7c80..a757521 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -48,8 +48,3 @@ services: identifier: 'tm.afterFolderDeleted' method: 'afterFolderDeleted' event: TYPO3\CMS\Core\Resource\Event\AfterFolderDeletedEvent - - - name: event.listener - identifier: 'tm.afterMetadataUpdated' - method: 'afterMetadataUpdated' - event: TYPO3\CMS\Core\Resource\Event\AfterFileMetaDataUpdatedEvent diff --git a/Tests/Functional/Hooks/FileMetadataTest.php b/Tests/Functional/Hooks/FileMetadataTest.php index eb14d72..a4231aa 100644 --- a/Tests/Functional/Hooks/FileMetadataTest.php +++ b/Tests/Functional/Hooks/FileMetadataTest.php @@ -61,6 +61,6 @@ public function editingFileMetadataDoesNotCreateRootInvalidation(): void $pathSegments = array_column($rows, 'pathsegment'); $this->assertNotContains('/', $pathSegments, 'Root invalidation "/" should not be created when editing file metadata.'); - $this->assertContains('/test-file.txt', $pathSegments, 'The file path should be invalidated when editing its metadata.'); + $this->assertNotContains('/test-file.txt', $pathSegments, 'The file path should not be invalidated when editing its metadata.'); } }