[TASK] Delete sys_file(_metadata) entries when a file is deleted 71/23671/21
authorFrans Saris <franssaris@gmail.com>
Sun, 8 Sep 2013 12:39:27 +0000 (14:39 +0200)
committerSteffen Ritter <info@rs-websystems.de>
Fri, 18 Oct 2013 12:16:20 +0000 (14:16 +0200)
In order to keep sys_file and sys_file_processedfile
as much in sync with the real file system as possible
we delete the relevant sys_file and sys_file_metadata
entries as soon as a file is deleted.

Resolves: #51698
Releases: 6.2, 6.1, 6.0
Change-Id: Ifa6a565702c26dc02c01634076726ef9f2272f84
Reviewed-on: https://review.typo3.org/23671
Reviewed-by: Steffen Ritter
Tested-by: Steffen Ritter
typo3/sysext/core/Classes/Resource/Index/FileIndexRepository.php
typo3/sysext/core/Classes/Resource/Index/MetaDataRepository.php
typo3/sysext/core/Classes/Resource/ProcessedFile.php
typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php
typo3/sysext/core/Classes/Resource/Processing/FileDeletionAspect.php [new file with mode: 0644]
typo3/sysext/core/ext_localconf.php

index 7963407..f4b5e0f 100644 (file)
@@ -216,4 +216,14 @@ class FileIndexRepository implements SingletonInterface {
                }
                return $where;
        }
+
+       /**
+        * Remove a sys_file record from the database
+        *
+        * @param integer $fileUid
+        * @return void
+        */
+       public function remove($fileUid) {
+               $this->getDatabase()->exec_DELETEquery($this->table, 'uid=' . intval($fileUid));
+       }
 }
\ No newline at end of file
index e4ce8ea..347eda1 100644 (file)
@@ -135,6 +135,15 @@ class MetaDataRepository implements SingletonInterface {
                }
        }
 
+       /**
+        * Remove all metadata records for a certain file from the database
+        *
+        * @param integer $fileUid
+        * @return void
+        */
+       public function removeByFileUid($fileUid) {
+               $this->getDatabase()->exec_DELETEquery($this->tableName, 'file=' . intval($fileUid));
+       }
 
        /**
         * Get the SignalSlot dispatcher
@@ -154,8 +163,6 @@ class MetaDataRepository implements SingletonInterface {
                return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
        }
 
-
-
        /**
         * Signal that is called after a record has been loaded from database
         * Allows other places to do extension of metadata at runtime or
@@ -167,5 +174,4 @@ class MetaDataRepository implements SingletonInterface {
        protected function emitRecordPostRetrievalSignal(\ArrayObject $data) {
                $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Index\\MetaDataRepository', 'recordPostRetrieval', array($data));
        }
-
 }
\ No newline at end of file
index 27c622a..52c1f17 100644 (file)
@@ -403,10 +403,13 @@ class ProcessedFile extends AbstractFile {
        }
 
        /**
+        * Delete processed file
+        *
+        * @param boolean $force
         * @return boolean
         */
-       public function delete() {
-               if ($this->isUnchanged()) {
+       public function delete($force = FALSE) {
+               if (!$force && $this->isUnchanged()) {
                        return FALSE;
                }
                return parent::delete();
index 820a3c7..85f9104 100644 (file)
@@ -166,6 +166,29 @@ class ProcessedFileRepository extends AbstractRepository {
        }
 
        /**
+        * @param FileInterface $file
+        * @return array<ProcessedFile>
+        * @throws \InvalidArgumentException
+        */
+       public function findAllByOriginalFile(FileInterface $file) {
+               if (!$file instanceof File) {
+                       throw new \InvalidArgumentException('Parameter is no File object but got type "'
+                               . (is_object($file) ? get_class($file) : gettype($file)) . '"', 1382006142);
+               }
+               $whereClause = 'original=' . intval($file->getUid()) . $this->getWhereClauseForEnabledFields();
+               $rows = $this->databaseConnection->exec_SELECTgetRows('*', $this->table, $whereClause);
+
+               $itemList = array();
+               if ($rows !== NULL) {
+                       foreach ($rows as $row) {
+                               $itemList[] = $this->createDomainObject($row);
+                       }
+               }
+               return $itemList;
+       }
+
+
+       /**
         * Removes all array keys which cannot be persisted
         *
         * @param array $data
diff --git a/typo3/sysext/core/Classes/Resource/Processing/FileDeletionAspect.php b/typo3/sysext/core/Classes/Resource/Processing/FileDeletionAspect.php
new file mode 100644 (file)
index 0000000..436d3e7
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Processing;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Frans Saris <franssaris@gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Resource\ProcessedFile;
+use TYPO3\CMS\Core\Resource\FileInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Class FileDeletionAspect
+ *
+ * We do not have AOP in TYPO3 for now, thus the aspect which
+ * deals with deleted files is a slot which reacts on a signal
+ * on file deletion.
+ *
+ * The aspect cleans up database records, processed files and filereferences
+ */
+class FileDeletionAspect {
+
+       /**
+        * Return a file index repository
+        *
+        * @return \TYPO3\CMS\Core\Resource\Index\FileIndexRepository
+        */
+       protected function getFileIndexRepository() {
+               return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\FileIndexRepository');
+       }
+
+       /**
+        * Return a metadata repository
+        *
+        * @return \TYPO3\CMS\Core\Resource\Index\MetaDataRepository
+        */
+       protected function getMetaDataRepository() {
+               return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\MetaDataRepository');
+       }
+
+       /**
+        * Return a processed file repository
+        *
+        * @return \TYPO3\CMS\Core\Resource\ProcessedFileRepository
+        */
+       protected function getProcessedFileRepository() {
+               return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\ProcessedFileRepository');
+       }
+
+       /**
+        * Wrapper method for getting DatabaseConnection
+        *
+        * @return \TYPO3\CMS\Core\Database\DatabaseConnection
+        */
+       protected function getDatabase() {
+               return $GLOBALS['TYPO3_DB'];
+       }
+
+       /**
+        * Cleanup database record for a deleted file
+        *
+        * @param FileInterface $fileObject
+        * @return void
+        */
+       public function removeFromRepository(FileInterface $fileObject) {
+               // remove file from repository
+               if ($fileObject instanceof File) {
+                       $this->getFileIndexRepository()->remove($fileObject->getUid());
+                       $this->getMetaDataRepository()->removeByFileUid($fileObject->getUid());
+                       // remove all references
+                       $this->getDatabase()->exec_DELETEquery(
+                               'sys_file_reference',
+                               'uid_local=' . intval($fileObject->getUid()) . ' AND table_local = \'sys_file\''
+                       );
+                       $this->cleanupProcessedFiles($fileObject);
+               } elseif ($fileObject instanceof ProcessedFile) {
+                       $this->getDatabase()->exec_DELETEquery('sys_file_processedfile', 'uid=' . intval($fileObject->getUid()));
+               }
+       }
+
+       /**
+        * Remove all processed files that belong to the given File object
+        *
+        * @param FileInterface $fileObject
+        * @return void
+        */
+       public function cleanupProcessedFiles(FileInterface $fileObject) {
+
+               // only delete processed files of File objects
+               if (!$fileObject instanceof File) {
+                       return;
+               }
+
+               /** @var $processedFile \TYPO3\CMS\Core\Resource\ProcessedFile */
+               foreach ($this->getProcessedFileRepository()->findAllByOriginalFile($fileObject) as $processedFile) {
+                       $processedFile->delete(TRUE);
+               }
+       }
+}
\ No newline at end of file
index f9f60cd..1484a69 100644 (file)
@@ -11,3 +11,10 @@ if (TYPO3_MODE === 'BE' && !(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_INSTALL)) {
                'addUserPermissionsToStorage'
        );
 }
+
+\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher')->connect(
+       'TYPO3\\CMS\\Core\\Resource\\ResourceStorage',
+       \TYPO3\CMS\Core\Resource\ResourceStorage::SIGNAL_PostFileDelete,
+       'TYPO3\\CMS\\Core\\Resource\\Processing\\FileDeletionAspect',
+       'removeFromRepository'
+);
\ No newline at end of file