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