[FEATURE] New FAL Indexer and ExtractorRegistry 70/25370/6
authorSteffen Ritter <info@rs-websystems.de>
Wed, 13 Nov 2013 12:09:18 +0000 (13:09 +0100)
committerErnesto Baschny <ernst@cron-it.de>
Sat, 16 Nov 2013 02:30:16 +0000 (03:30 +0100)
A new Indexer for FAL according to the concept of the BluePrint, also
adding a new ExtractorRegistry where extensions can register their
extraction classes (implementing ExtractorInterface).

See: http://wiki.typo3.org/Blueprints/FAL-IndexerRework

Releases: 6.2
Resolves: #52841, #53599
Change-Id: I06e8d011096c88221aaf62bd0a25e681692dbfa0
Reviewed-on: https://review.typo3.org/25370
Reviewed-by: Frans Saris
Tested-by: Frans Saris
Reviewed-by: Ernesto Baschny
Tested-by: Ernesto Baschny
typo3/sysext/core/Classes/Resource/Index/ExtractorInterface.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Index/ExtractorRegistry.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Index/FileIndexRepository.php
typo3/sysext/core/Classes/Resource/Index/Indexer.php [new file with mode: 0644]
typo3/sysext/core/Classes/Resource/Index/MetaDataRepository.php
typo3/sysext/core/ext_tables.sql

diff --git a/typo3/sysext/core/Classes/Resource/Index/ExtractorInterface.php b/typo3/sysext/core/Classes/Resource/Index/ExtractorInterface.php
new file mode 100644 (file)
index 0000000..77d5702
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Index;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Steffen Ritter <steffen.ritter@typo3.org>
+ *  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;
+
+/**
+ * An Interface for MetaData extractors the FAL Indexer uses
+ */
+interface ExtractorInterface {
+
+       /**
+        * Returns an array of supported file types;
+        * An empty array indicates all filetypes
+        *
+        * @return array
+        */
+       public function getFileTypeRestrictions();
+
+
+       /**
+        * Get all supported DriverClasses
+        *
+        * Since some extractors may only work for local files, and other extractors
+        * are especially made for grabbing data from remote.
+        *
+        * Returns array of string with driver names of Drivers which are supported,
+        * If the driver did not register a name, it's the classname.
+        * empty array indicates no restrictions
+        *
+        * @return array
+        */
+       public function getDriverRestrictions();
+
+       /**
+        * Returns the priority of the extraction Service
+        * Should be between 1 and 100
+        *
+        * @return integer
+        */
+       public function getPriority();
+
+       /**
+        * Checks if the given file can be processed by this Extractor
+        *
+        * @param Resource\File $file
+        * @return boolean
+        */
+       public function canProcess(Resource\File $file);
+
+       /**
+        * The actual processing TASK
+        *
+        * Should return an array with database properties for sys_file_metadata to write
+        *
+        * @param Resource\File $file
+        * @return array
+        */
+       public function extractMetaData(Resource\File $file);
+
+
+}
\ No newline at end of file
diff --git a/typo3/sysext/core/Classes/Resource/Index/ExtractorRegistry.php b/typo3/sysext/core/Classes/Resource/Index/ExtractorRegistry.php
new file mode 100644 (file)
index 0000000..e9105ef
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Index;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Steffen Ritter <steffen.ritter@typo3.org>
+ *  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!
+ ***************************************************************/
+
+/**
+ * Registry for MetaData extraction Services
+ *
+ */
+class ExtractorRegistry implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * Registered ClassNames
+        * @var array
+        */
+       protected $extractors = array();
+
+       /**
+        * Instance Cache for Extractors
+        *
+        * @var ExtractorInterface[]
+        */
+       protected $instances = NULL;
+
+       /**
+        * Returns an instance of this class
+        *
+        * @return ExtractorRegistry
+        */
+       public static function getInstance() {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\ExtractorRegistry');
+       }
+
+       /**
+        * Allows to register MetaData extraction to the FAL Indexer
+        *
+        * @param string $className
+        * @throws \RuntimeException
+        */
+       public function registerExtractionService($className) {
+               if (!class_exists($className)) {
+                       throw new \RuntimeException('The Class you are registering is not available');
+               } elseif (!in_array('TYPO3\\CMS\\Core\\Resource\\Index\\ExtractorInterface', class_implements($className))) {
+                       throw new \RuntimeException('The extractor needs to implement the ExtractorInterface');
+               } else {
+                       $this->extractors[] = $className;
+               }
+       }
+
+       /**
+        * Get all registered extractors
+        *
+        * @return ExtractorInterface[]
+        */
+       public function getExtractors() {
+               if ($this->instances === NULL) {
+                       $this->instances = array();
+                       foreach ($this->extractors as $className) {
+                               /** @var ExtractorInterface $object */
+                               $object = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($className);
+                               $this->instances[$object->getPriority()] = $object;
+                       }
+                       krsort($this->instances);
+               }
+               return $this->instances;
+       }
+
+       /**
+        * Get Extractors which work for a special driver
+        *
+        * @param string $driverType
+        * @return ExtractorInterface[]
+        */
+       public function getExtractorsWithDriverSupport($driverType) {
+               $allExtractors = $this->getExtractors();
+
+               $filteredExtractors = array();
+               foreach ($allExtractors as $priority => $extractorObject) {
+                       if (count($extractorObject->getDriverRestrictions()) == 0) {
+                               $filteredExtractors[$priority] = $extractorObject;
+                       } elseif (in_array($driverType, $extractorObject->getDriverRestrictions())) {
+                               $filteredExtractors[$priority] = $extractorObject;
+                       }
+               }
+               return $filteredExtractors;
+       }
+
+}
\ No newline at end of file
index 9571394..e468ebc 100644 (file)
@@ -180,6 +180,7 @@ class FileIndexRepository implements SingletonInterface {
         * Adds a file to the index
         *
         * @param File $file
+        * @return void
         */
        public function add(File $file) {
                if ($this->hasIndexRecord($file)) {
@@ -188,15 +189,37 @@ class FileIndexRepository implements SingletonInterface {
                                $file->updateProperties($this->findOneByFileObject($file));
                        }
                } else {
-                       $data = array_intersect_key($file->getProperties(), array_flip($this->fields));
-                       $this->getDatabase()->exec_INSERTquery($this->table, $data);
-                       $data['uid'] = $this->getDatabase()->sql_insert_id();
-                       $file->updateProperties(array('uid' => $data['uid']));
-                       $this->emitRecordCreated($data);
+                       $file->updateProperties(array('uid' => $this->insertRecord($file->getProperties())));
                }
        }
 
        /**
+        * Add data from record (at indexing time)
+        *
+        * @param array $data
+        * @return array
+        */
+       public function addRaw(array $data) {
+               $data['uid'] = $this->insertRecord($data);
+               return $data;
+       }
+
+       /**
+        * Helper to reduce code duplication
+        *
+        * @param array $data
+        *
+        * @return integer
+        */
+       protected function insertRecord(array $data) {
+               $data = array_intersect_key($data, array_flip($this->fields));
+               $data['tstamp'] = time();
+               $this->getDatabase()->exec_INSERTquery($this->table, $data);
+               $data['uid'] = $this->getDatabase()->sql_insert_id();
+               $this->emitRecordCreated($data);
+               return $data['uid'];
+       }
+       /**
         * Checks if a file is indexed
         *
         * @param File $file
@@ -210,6 +233,7 @@ class FileIndexRepository implements SingletonInterface {
         * Updates the index record in the database
         *
         * @param File $file
+        * @return void
         */
        public function update(File $file) {
                $updatedProperties = array_intersect($this->fields, $file->getUpdatedProperties());
@@ -225,6 +249,63 @@ class FileIndexRepository implements SingletonInterface {
        }
 
        /**
+        * Finds the files needed for second indexer step
+        *
+        * @param \TYPO3\CMS\Core\Resource\ResourceStorage $storage
+        * @param integer $limit
+        * @return array
+        */
+       public function findInStorageWithIndexOutstanding(\TYPO3\CMS\Core\Resource\ResourceStorage $storage, $limit = -1) {
+               return $this->getDatabase()->exec_SELECTgetRows(
+                       implode(',', $this->fields),
+                       $this->table,
+                       'tstamp > last_indexed AND storage = ' . intval($storage->getUid()),
+                       '',
+                       'tstamp ASC',
+                       intval($limit) > 0 ? intval($limit) : ''
+               );
+       }
+
+
+       /**
+        * Helper function for the Indexer to detect missing files
+        *
+        * @param \TYPO3\CMS\Core\Resource\ResourceStorage $storage
+        * @param array $uidList
+        * @return array
+        */
+       public function findInStorageAndNotInUidList(\TYPO3\CMS\Core\Resource\ResourceStorage $storage, array $uidList) {
+               array_walk($uidList, 'intval');
+               $uidList = array_unique($uidList);
+
+               return $this->getDatabase()->exec_SELECTgetRows(
+                       implode(',', $this->fields),
+                       $this->table,
+                       'storage = ' . intval($storage->getUid()) . ' AND uid NOT IN (' . implode(',', $uidList) . ')'
+               );
+       }
+
+       /**
+        * Updates the timestamp when the file indexer extracted metadata
+        *
+        * @param integer $fileUid
+        * @return void
+        */
+       public function updateIndexingTime($fileUid) {
+               $this->getDatabase()->exec_UPDATEquery($this->table, 'uid = ' . intval($fileUid), array('last_indexed' => time()));
+       }
+
+       /**
+        * Marks given file as missing in sys_file
+        *
+        * @param integer $fileUid
+        * @return void
+        */
+       public function markFileAsMissing($fileUid) {
+               $this->getDatabase()->exec_UPDATEquery($this->table, 'uid = ' . intval($fileUid), array('missing' => 1));
+       }
+
+       /**
         * Returns a where clause to find a file in database
         *
         * @param File $file
diff --git a/typo3/sysext/core/Classes/Resource/Index/Indexer.php b/typo3/sysext/core/Classes/Resource/Index/Indexer.php
new file mode 100644 (file)
index 0000000..66ad579
--- /dev/null
@@ -0,0 +1,344 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Index;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Steffen Ritter <steffen.ritter@typo3.org>
+ *  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\ResourceStorage;
+use TYPO3\CMS\Core\Resource\File;
+
+/**
+ * The New FAL Indexer
+ */
+class Indexer {
+
+       /**
+        * @var array
+        */
+       protected $filesToUpdate = array();
+
+       /**
+        * @var integer[]
+        */
+       protected $identifiedFileUids = array();
+
+       /**
+        * @var ResourceStorage
+        */
+       protected $storage = NULL;
+
+       /**
+        * @param ResourceStorage $storage
+        */
+       public function __construct(ResourceStorage $storage) {
+               $this->storage = $storage;
+       }
+
+       /**
+        * Create index entry
+        *
+        * @param string $identifier
+        * @return File
+        */
+       public function createIndexEntry($identifier) {
+               $fileProperties = $this->gatherFileInformationArray($identifier);
+               $record = $this->getFileIndexRepository()->addRaw($fileProperties);
+               $fileObject = $this->getResourceFactory()->getFileObject($record['uid'], $record);
+               $this->extractRequiredMetaData($fileObject);
+               return $fileObject;
+       }
+
+       /**
+        * Update index entry
+        *
+        * @param File $fileObject
+        * @return void
+        */
+       public function updateIndexEntry(File $fileObject) {
+               $updatedInformation = $this->gatherFileInformationArray($fileObject->getIdentifier());
+               $fileObject->updateProperties($updatedInformation);
+               $this->getFileIndexRepository()->update($fileObject);
+               $this->extractRequiredMetaData($fileObject);
+       }
+
+       /**
+        * @return void
+        */
+       public function processChangesInStorages() {
+               // get all file-identifiers from the storage
+               $availableFiles = $this->storage->getFileIdentifiersInFolder($this->storage->getRootLevelFolder()->getIdentifier(), TRUE, TRUE);
+               $this->detectChangedFilesInStorage($availableFiles);
+               $this->processChangedAndNewFiles();
+
+               $this->detectMissingFiles();
+       }
+
+       /**
+        * @param integer $maximumFileCount
+        * @return void
+        */
+       public function runMetaDataExtraction($maximumFileCount = -1) {
+               $fileIndexRecords = $this->getFileIndexRepository()->findInStorageWithIndexOutstanding($this->storage, $maximumFileCount);
+
+               $extractionServices = $this->getExtractorRegistry()->getExtractorsWithDriverSupport($this->storage->getDriverType());
+               foreach ($fileIndexRecords as $indexRecord) {
+                       $fileObject = $this->getResourceFactory()->getFileObject($indexRecord['uid'], $indexRecord);
+
+                       $metaData = $fileObject->_getMetaData();
+                       foreach ($extractionServices as $service) {
+                               if ($service->canProcess($fileObject)) {
+                                       $metaData = array_merge(
+                                               $metaData,
+                                               $service->extractMetaData($fileObject)
+                                       );
+                               }
+                       }
+                       $this->getMetaDataRepository()->update($fileObject->getUid(), $metaData);
+                       $this->getFileIndexRepository()->updateIndexingTime($fileObject->getUid());
+               }
+       }
+
+       /**
+        * Since by now all files in filesystem have been looked at it is save to assume,
+        * that files that are in indexed but not touched in this run are missing
+        */
+       protected function detectMissingFiles() {
+               if (count($this->identifiedFileUids) > 0) {
+                       $indexedNotExistentFiles = $this->getFileIndexRepository()->findInStorageAndNotInUidList($this->storage, $this->identifiedFileUids);
+
+                       foreach ($indexedNotExistentFiles as $record) {
+                               if (!$this->storage->hasFile($record['identifier'])) {
+                                       $this->getFileIndexRepository()->markFileAsMissing($record['uid']);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Adds updated files to the processing queue
+        *
+        * @param array $fileIdentifierArray
+        * @return void
+        */
+       protected function detectChangedFilesInStorage(array $fileIdentifierArray) {
+               foreach ($fileIdentifierArray as $fileIdentifier) {
+                       // skip processed files
+                       if (strpos($fileIdentifier, $this->storage->getProcessingFolder()->getIdentifier()) === 0) {
+                               continue;
+                       }
+                       // Get the modification time for file-identifier from the storage
+                       $modificationTime = $this->storage->getFileInfoByIdentifier($fileIdentifier, array('mtime'));
+                       // Look if the the modification time in FS is higher than the one in database (key needed on timestamps)
+                       $indexRecord = $this->getFileIndexRepository()->findOneByStorageUidAndIdentifier($this->storage->getUid(), $fileIdentifier);
+
+                       if ($indexRecord !== FALSE) {
+                               $this->identifiedFileUids[] = $indexRecord['uid'];
+
+                               if ($indexRecord['modification_date'] < $modificationTime['mtime'] || $indexRecord['missing']) {
+                                       $this->filesToUpdate[$fileIdentifier] = $indexRecord;
+                               }
+                       } else {
+                               $this->filesToUpdate[$fileIdentifier] = NULL;
+                       }
+               }
+       }
+
+       /**
+        * Processes the Files which have been detected as "changed or new"
+        * in the storage
+        *
+        * @return void
+        */
+       protected function processChangedAndNewFiles() {
+               foreach ($this->filesToUpdate AS $identifier => $data) {
+                       if ($data == NULL) {
+                               $fileHash = $this->storage->hashFileByIdentifier($identifier, 'sha1');
+                               $files = $this->getFileIndexRepository()->findByContentHash($fileHash);
+                               if (count($files) > 0) {
+                                       foreach ($files as $fileIndexEntry) {
+                                               if ($fileIndexEntry['missing']) {
+                                                       $fileObject = $this->getResourceFactory()->getFileObject($data['uid'], $data);
+                                                       $fileObject->updateProperties(array(
+                                                               'identifier'
+                                                       ));
+                                                       $this->updateIndexEntry($fileObject);
+                                                       $this->identifiedFileUids[] = $fileObject->getUid();
+                                                       break;
+                                               }
+                                       }
+                               } else {
+                                       // index new file
+                                       $fileObject = $this->createIndexEntry($identifier);
+                                       $this->identifiedFileUids[] = $fileObject->getUid();
+                               }
+                       } else {
+                               // update existing file
+                               $fileObject = $this->getResourceFactory()->getFileObject($data['uid'], $data);
+                               $this->updateIndexEntry($fileObject);
+                       }
+               }
+       }
+
+       /**
+        * Since the core desperately needs image sizes in metadata table put them there
+        * This should be called after every "content" update and "record" creation
+        *
+        * @param File $fileObject
+        */
+       protected function extractRequiredMetaData(File $fileObject) {
+               // since the core desperately needs image sizes in metadata table do this manually
+               // prevent doing this for remote storages, remote storages must provide the data with extractors
+               if ($fileObject->getType() == File::FILETYPE_IMAGE && $this->storage->getDriverType() === 'Local') {
+                       $rawFileLocation = $fileObject->getForLocalProcessing(FALSE);
+                       $metaData = array();
+                       list($metaData['width'], $metaData['height']) = getimagesize($rawFileLocation);
+                       $this->getMetaDataRepository()->update($fileObject->getUid(), $metaData);
+               }
+       }
+
+       /****************************
+        *
+        *         UTILITY
+        *
+        ****************************/
+
+       /**
+        * Collects the information to be cached in sys_file
+        *
+        * @param string $identifier
+        * @return array
+        */
+       protected function gatherFileInformationArray($identifier) {
+               $fileInfo = $this->storage->getFileInfoByIdentifier($identifier);
+               $fileInfo = $this->transformFromDriverFileInfoArrayToFileObjectFormat($fileInfo);
+               $fileInfo['type'] = $this->getFileType($fileInfo['mime_type']);
+               $fileInfo['sha1'] = $this->storage->hashFileByIdentifier($identifier, 'sha1');
+               $fileInfo['extension'] = \TYPO3\CMS\Core\Utility\PathUtility::pathinfo($fileInfo['name'], PATHINFO_EXTENSION);
+               $fileInfo['missing'] = 0;
+
+               return $fileInfo;
+       }
+
+       /**
+        * Maps the mimetype to a sys_file table type
+        *
+        * @param string $mimeType
+        * @return string
+        */
+       protected function getFileType($mimeType) {
+               list($fileType) = explode('/', $mimeType);
+               switch (strtolower($fileType)) {
+                       case 'text':
+                               $type = File::FILETYPE_TEXT;
+                               break;
+                       case 'image':
+                               $type = File::FILETYPE_IMAGE;
+                               break;
+                       case 'audio':
+                               $type = File::FILETYPE_AUDIO;
+                               break;
+                       case 'video':
+                               $type = File::FILETYPE_VIDEO;
+                               break;
+                       case 'application':
+                       case 'software':
+                               $type = File::FILETYPE_APPLICATION;
+                               break;
+                       default:
+                               $type = File::FILETYPE_UNKNOWN;
+               }
+               return $type;
+       }
+
+       /**
+        * However it happened, the properties of a file object which
+        * are persisted to the database are named different than the
+        * properties the driver returns in getFileInfo.
+        * Therefore a mapping must happen.
+        *
+        * @param array $fileInfo
+        *
+        * @return array
+        */
+       protected function transformFromDriverFileInfoArrayToFileObjectFormat(array $fileInfo) {
+               $mappingInfo = array(
+                       // 'driverKey' => 'fileProperty' Key is from the driver, value is for the property in the file
+                       'size' => 'size',
+                       'atime' => NULL,
+                       'mtime' => 'modification_date',
+                       'ctime' => 'creation_date',
+                       'mimetype' => 'mime_type'
+               );
+               $mappedFileInfo = array();
+               foreach ($fileInfo as $key => $value) {
+                       if (array_key_exists($key, $mappingInfo)) {
+                               if ($mappingInfo[$key] !== NULL) {
+                                       $mappedFileInfo[$mappingInfo[$key]] = $value;
+                               }
+                       } else {
+                               $mappedFileInfo[$key] = $value;
+                       }
+               }
+               return $mappedFileInfo;
+       }
+
+
+       /**
+        * Returns an instance of the FileIndexRepository
+        *
+        * @return FileIndexRepository
+        */
+       protected function getFileIndexRepository() {
+               return FileIndexRepository::getInstance();
+       }
+
+       /**
+        * Returns an instance of the FileIndexRepository
+        *
+        * @return MetaDataRepository
+        */
+       protected function getMetaDataRepository() {
+               return MetaDataRepository::getInstance();
+       }
+
+       /**
+        * Returns the ResourceFactory
+        *
+        * @return \TYPO3\CMS\Core\Resource\ResourceFactory
+        */
+       protected function getResourceFactory() {
+               return \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance();
+       }
+
+       /**
+        * Returns an instance of the FileIndexRepository
+        *
+        * @return ExtractorRegistry
+        */
+       protected function getExtractorRegistry() {
+               return ExtractorRegistry::getInstance();
+       }
+}
\ No newline at end of file
index 4688016..8a6a3c1 100644 (file)
@@ -99,9 +99,10 @@ class MetaDataRepository implements SingletonInterface {
         * Create empty
         *
         * @param int $fileUid
+        * @param array $additionalFields
         * @return array
         */
-       protected function createMetaDataRecord($fileUid) {
+       public function createMetaDataRecord($fileUid, array $additionalFields = array()) {
                $emptyRecord =  array(
                        'file' => intval($fileUid),
                        'pid' => 0,
@@ -109,6 +110,7 @@ class MetaDataRepository implements SingletonInterface {
                        'tstamp' => $GLOBALS['EXEC_TIME'],
                        'cruser_id' => TYPO3_MODE == 'BE' ? $GLOBALS['BE_USER']->user['uid'] : 0
                );
+               $emptyRecord = array_merge($emptyRecord, $additionalFields);
                $this->getDatabase()->exec_INSERTquery($this->tableName, $emptyRecord);
                $record = $emptyRecord;
                $record['uid'] = $this->getDatabase()->sql_insert_id();
@@ -209,4 +211,11 @@ class MetaDataRepository implements SingletonInterface {
        protected function emitRecordDeleted($fileUid) {
                $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Index\\MetaDataRepository', 'recordDeleted', array($fileUid));
        }
+
+       /**
+        * @return \TYPO3\CMS\Core\Resource\Index\MetaDataRepository
+        */
+       public static function getInstance() {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\MetaDataRepository');
+       }
 }
\ No newline at end of file
index 57e4f39..b2e8455 100644 (file)
@@ -284,6 +284,7 @@ CREATE TABLE sys_file (
        pid int(11) DEFAULT '0' NOT NULL,
        # update timestamp of the database record, not the file!
        tstamp int(11) DEFAULT '0' NOT NULL,
+       last_indexed int(11) DEFAULT '0' NOT NULL,
 
        # management information
        missing tinyint(4) DEFAULT '0' NOT NULL,
@@ -305,6 +306,8 @@ CREATE TABLE sys_file (
 
        PRIMARY KEY (uid),
        KEY sel01 (storage,identifier_hash),
+       KEY tstamp (tstamp),
+       KEY lastindex (last_indexed),
        KEY sha1 (sha1(40))
 );