604edafbd63784b0feb6c073a58cb8cc60ed9e75
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / Service / IndexerService.php
1 <?php
2 namespace TYPO3\CMS\Core\Resource\Service;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2011-2013 Andreas Wolf <andreas.wolf@ikt-werk.de>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 use TYPO3\CMS\Core\Resource\File;
30 use TYPO3\CMS\Core\Resource\Index\FileIndexRepository;
31 use TYPO3\CMS\Core\Utility\GeneralUtility;
32
33 /**
34 * Indexer for the virtual file system
35 * should only be accessed through the FileRepository for now
36 *
37 * @author Andreas Wolf <andreas.wolf@ikt-werk.de>
38 * @deprecated since TYPO3 CMS 6.2 LTS - will be removed 2 versions later
39 */
40 class IndexerService implements \TYPO3\CMS\Core\SingletonInterface {
41
42 /**
43 * @var \TYPO3\CMS\Core\Resource\FileRepository
44 */
45 protected $repository;
46
47 /**
48 * empty constructor, nothing to do here yet
49 */
50 public function __construct() {
51
52 }
53
54 /**
55 * Internal function to retrieve the file repository,
56 * if it does not exist, an instance will be created
57 *
58 * @return \TYPO3\CMS\Core\Resource\FileRepository
59 */
60 protected function getRepository() {
61 if ($this->repository === NULL) {
62 $this->repository = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\FileRepository');
63 }
64 return $this->repository;
65 }
66
67 /**
68 * @return \TYPO3\CMS\Core\Resource\Index\FileIndexRepository
69 */
70 protected function getFileIndexRepository() {
71 return GeneralUtility::makeInstance('TYPO3\CMS\Core\Resource\Index\FileIndexRepository');
72 }
73
74
75 /**
76 * Getter function for the fileFactory
77 *
78 * @return \TYPO3\CMS\Core\Resource\ResourceFactory
79 */
80 public function getFactory() {
81 return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\ResourceFactory');
82 }
83
84 /**
85 * Creates or updates a file index entry from a file object.
86 *
87 * @param File $fileObject
88 * @param bool $updateObject Set this to FALSE to get the indexed values. You have to take care of updating the object yourself then!
89 * @return File|array the indexed $fileObject or an array of indexed properties.
90 * @throws \RuntimeException
91 */
92 public function indexFile(File $fileObject, $updateObject = TRUE) {
93 $fileObject->setIndexingInProgess(TRUE);
94 // Get the file information of this object
95 $fileInfo = $this->gatherFileInformation($fileObject);
96 // Signal slot BEFORE the file was indexed
97 $this->emitPreFileIndexSignal($fileObject, $fileInfo);
98
99 // If the file is already indexed, then the file information will
100 // be updated on the existing record
101 if ($fileObject->isIndexed()) {
102 $fileInfo['missing'] = 0;
103 $fileObject->updateProperties($fileInfo);
104 $this->getFileIndexRepository()->update($fileObject);
105 } else {
106 // Check if a file has been moved outside of FAL -- we have some
107 // orphaned index record in this case we could update
108 $resultRows = $this->getFileIndexRepository()->findByContentHash($fileInfo['sha1']);
109 $otherFiles = array();
110 foreach ($resultRows as $row) {
111 $otherFiles[] = $this->getFactory()->getFileObject($row['uid'], $row);
112 }
113
114 $movedFile = FALSE;
115 /** @var $otherFile File */
116 foreach ($otherFiles as $otherFile) {
117 if (!$otherFile->exists()) {
118 // @todo: create a log entry
119 $movedFile = TRUE;
120 $fileInfo['missing'] = 0;
121 $otherFile->updateProperties($fileInfo);
122 $this->getFileIndexRepository()->update($otherFile);
123 $fileInfo['uid'] = $otherFile->getUid();
124 $fileObject = $otherFile;
125 // Skip the rest of the files here as we might have more files that are missing, but we can only
126 // have one entry. The optimal solution would be to merge these records then, but this requires
127 // some more advanced logic that we currently have not implemented.
128 break;
129 }
130 }
131 // File was not moved, so it is a new index record
132 if ($movedFile === FALSE) {
133 // Crdate and tstamp should not be present when updating
134 // the file object, as they only relate to the index record
135 $additionalInfo = array(
136 'crdate' => $GLOBALS['EXEC_TIME'],
137 'tstamp' => $GLOBALS['EXEC_TIME']
138 );
139 if (TYPO3_MODE === 'BE') {
140 $additionalInfo['cruser_id'] = intval($GLOBALS['BE_USER']->user['uid']);
141 }
142 $indexRecord = array_merge($fileInfo, $additionalInfo);
143
144 $fileObject->updateProperties($indexRecord);
145 $this->getFileIndexRepository()->add($fileObject);
146 }
147 }
148 // Check for an error during the execution and throw an exception
149 $error = $GLOBALS['TYPO3_DB']->sql_error();
150 if ($error) {
151 throw new \RuntimeException('Error during file indexing: "' . $error . '"', 1314455642);
152 }
153 if ($fileInfo['type'] == $fileObject::FILETYPE_IMAGE) {
154 $rawFileLocation = $fileObject->getForLocalProcessing(FALSE);
155 $metaData = array();
156 list($metaData['width'], $metaData['height']) = getimagesize($rawFileLocation);
157 $this->getMetaDataRepository()->update($fileObject->getUid(), $metaData);
158 }
159 // Signal slot AFTER the file was indexed
160 $this->emitPostFileIndexSignal($fileObject, $fileInfo);
161 $fileObject->setIndexingInProgess(FALSE);
162 if ($updateObject) {
163 return $fileObject;
164 } else {
165 return $fileInfo;
166 }
167 }
168
169 /**
170 * Indexes an array of file objects
171 * currently this is done in a simple way, however could be changed to be more performant
172 *
173 * @param File[] $fileObjects
174 * @return void
175 */
176 public function indexFiles(array $fileObjects) {
177 // emit signal
178 $this->emitPreMultipleFilesIndexSignal($fileObjects);
179 foreach ($fileObjects as $fileObject) {
180 $this->indexFile($fileObject);
181 }
182 // emit signal
183 $this->emitPostMultipleFilesIndexSignal($fileObjects);
184 }
185
186 /**
187 * Indexes all files in a given storage folder.
188 * currently this is done in a simple way, however could be changed to be more performant
189 *
190 * @param \TYPO3\CMS\Core\Resource\Folder $folder
191 * @return integer The number of indexed files.
192 */
193 public function indexFilesInFolder(\TYPO3\CMS\Core\Resource\Folder $folder) {
194 $numberOfIndexedFiles = 0;
195 $fileIdentifiers = array();
196
197 // Index all files in this folder
198 $fileObjects = $folder->getFiles();
199
200 // emit signal
201 $this->emitPreMultipleFilesIndexSignal($fileObjects);
202 foreach ($fileObjects as $fileObject) {
203 $this->indexFile($fileObject);
204 $fileIdentifiers[] = $fileObject->getIdentifier();
205 $numberOfIndexedFiles++;
206 }
207
208 // check for deleted files (file not found during indexing are marked as missing)
209 foreach ($this->getRepository()->getFileIndexRecordsForFolder($folder) as $file) {
210 if (!in_array($file['identifier'], $fileIdentifiers)) {
211 /** @var $fileObject File */
212 $fileObject = $this->getRepository()->findByIdentifier($file['uid']);
213 $fileObject->setMissing(TRUE);
214 $this->getFileIndexRepository()->update($fileObject);
215 }
216 }
217
218 // emit signal
219 $this->emitPostMultipleFilesIndexSignal($fileObjects);
220
221 // cleanup to prevent to much memory use on big folders
222 unset($fileObjects);
223 unset($fileIdentifiers);
224
225 // Call this function recursively for each subfolder
226 $subFolders = $folder->getSubfolders();
227 foreach ($subFolders as $subFolder) {
228 $numberOfIndexedFiles += $this->indexFilesInFolder($subFolder);
229 }
230 return $numberOfIndexedFiles;
231 }
232
233 /**
234 * Fetches the information for a sys_file record
235 * based on a single file
236 * this function shouldn't be used, if someone needs to fetch the file information
237 * from a file object, should be done by getProperties etc
238 *
239 * @param File $file the file to fetch the information from
240 * @return array the file information as an array
241 */
242 protected function gatherFileInformation(File $file) {
243 $fileInfo = new \ArrayObject(array());
244 $gatherDefaultInformation = new \stdClass();
245 $gatherDefaultInformation->getDefaultFileInfo = 1;
246 // signal before the files are modified
247 $this->emitPreGatherFileInformationSignal($file, $fileInfo, $gatherDefaultInformation);
248 // the check helps you to disable the regular file fetching,
249 // so a signal could actually remotely access the service
250 if ($gatherDefaultInformation->getDefaultFileInfo) {
251 $storage = $file->getStorage();
252 // TODO: See if we can't just return info, as it contains most of the
253 // stuff we put together in array form again later here.
254 $info = $storage->getFileInfo($file);
255 $defaultFileInfo = array(
256 'creation_date' => $info['ctime'],
257 'modification_date' => $info['mtime'],
258 'size' => $info['size'],
259 'identifier' => $file->getIdentifier(),
260 'identifier_hash' => $storage->hashFileIdentifier($file->getIdentifier()),
261 'folder_hash' => $storage->hashFileIdentifier($storage->getFolderIdentifierFromFileIdentifier($file->getIdentifier())),
262 'storage' => $storage->getUid(),
263 'name' => $file->getName(),
264 'sha1' => $storage->hashFile($file, 'sha1'),
265 'type' => $file->getType(),
266 'mime_type' => $file->getMimeType(),
267 'extension' => $file->getExtension()
268 );
269 $fileInfo = array_merge($defaultFileInfo, $fileInfo->getArrayCopy());
270 $fileInfo = new \ArrayObject($fileInfo);
271 }
272 // signal after the file information is fetched
273 $this->emitPostGatherFileInformationSignal($file, $fileInfo, $gatherDefaultInformation);
274 return $fileInfo->getArrayCopy();
275 }
276
277 /**
278 * Signal that is called before the file information is fetched
279 * helpful if somebody wants to preprocess the record information
280 *
281 * @param File $fileObject
282 * @param array $fileInfo
283 * @param boolean $gatherDefaultInformation
284 * @signal
285 */
286 protected function emitPreGatherFileInformationSignal(File $fileObject, $fileInfo, $gatherDefaultInformation) {
287 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'preGatherFileInformation', array($fileObject, $fileInfo, $gatherDefaultInformation));
288 }
289
290 /**
291 * Signal that is called after a file object was indexed
292 *
293 * @param File $fileObject
294 * @param array $fileInfo
295 * @param boolean $hasGatheredDefaultInformation
296 * @signal
297 */
298 protected function emitPostGatherFileInformationSignal(File $fileObject, $fileInfo, $hasGatheredDefaultInformation) {
299 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'postGatherFileInformation', array($fileObject, $fileInfo, $hasGatheredDefaultInformation));
300 }
301
302 /**
303 * Signal that is called before a bunch of file objects are indexed
304 *
305 * @param array $fileObject
306 * @signal
307 */
308 protected function emitPreMultipleFilesIndexSignal(array $fileObjectsToIndex) {
309 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'preMultipleFileIndex', array($fileObjectsToIndex));
310 }
311
312 /**
313 * Signal that is called after multiple file objects were indexed
314 *
315 * @param array $fileObjectsToIndex
316 * @signal
317 */
318 protected function emitPostMultipleFilesIndexSignal(array $fileObjectsToIndex) {
319 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'postMultipleFileIndex', array($fileObjectsToIndex));
320 }
321
322 /**
323 * Signal that is called before a file object was indexed
324 *
325 * @param File $fileObject
326 * @param array $fileInfo
327 * @signal
328 */
329 protected function emitPreFileIndexSignal(File $fileObject, $fileInfo) {
330 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'preFileIndex', array($fileObject, $fileInfo));
331 }
332
333 /**
334 * Signal that is called after a file object was indexed
335 *
336 * @param File $fileObject
337 * @param array $fileInfo
338 * @signal
339 */
340 protected function emitPostFileIndexSignal(File $fileObject, $fileInfo) {
341 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'postFileIndex', array($fileObject, $fileInfo));
342 }
343
344 /**
345 * Get the SignalSlot dispatcher
346 *
347 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
348 */
349 protected function getSignalSlotDispatcher() {
350 return $this->getObjectManager()->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
351 }
352
353 /**
354 * Get the ObjectManager
355 *
356 * @return \TYPO3\CMS\Extbase\Object\ObjectManager
357 */
358 protected function getObjectManager() {
359 return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
360 }
361
362 /**
363 * @return \TYPO3\CMS\Core\Resource\Index\MetaDataRepository
364 */
365 protected function getMetaDataRepository() {
366 return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\MetaDataRepository');
367 }
368
369 }