a6924442d81d5ae806b7d24135e7aa6965ee6848
[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 /**
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 */
35 class IndexerService implements \TYPO3\CMS\Core\SingletonInterface {
36
37 /**
38 * @var \TYPO3\CMS\Core\Resource\FileRepository
39 */
40 protected $repository;
41
42 /**
43 * @var \TYPO3\CMS\Core\Resource\ResourceFactory
44 */
45 protected $factory;
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 = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\FileRepository');
63 }
64 return $this->repository;
65 }
66
67 /**
68 * Setter function for the fileFactory
69 * returns the object itself for chaining purposes
70 *
71 * @param \TYPO3\CMS\Core\Resource\ResourceFactory $factory
72 * @return \TYPO3\CMS\Core\Resource\Service\IndexerService
73 */
74 public function setFactory(\TYPO3\CMS\Core\Resource\ResourceFactory $factory) {
75 $this->factory = $factory;
76 return $this;
77 }
78
79 /**
80 * Creates or updates a file index entry from a file object.
81 *
82 * @param \TYPO3\CMS\Core\Resource\File $fileObject
83 * @param bool $updateObject Set this to FALSE to get the indexed values. You have to take care of updating the object yourself then!
84 * @return \TYPO3\CMS\Core\Resource\File|array the indexed $fileObject or an array of indexed properties.
85 */
86 public function indexFile(\TYPO3\CMS\Core\Resource\File $fileObject, $updateObject = TRUE) {
87 // Get the file information of this object
88 $fileInfo = $this->gatherFileInformation($fileObject);
89 // Signal slot BEFORE the file was indexed
90 $this->emitPreFileIndexSignal($fileObject, $fileInfo);
91 // @todo: this should be done via services in the future
92 // @todo: this should take remote services into account
93 if ($fileInfo['type'] == $fileObject::FILETYPE_IMAGE && !$fileInfo['width']) {
94 $rawFileLocation = $fileObject->getForLocalProcessing(FALSE);
95 list($fileInfo['width'], $fileInfo['height']) = getimagesize($rawFileLocation);
96 }
97 // If the file is already indexed, then the file information will
98 // be updated on the existing record
99 if ($fileObject->isIndexed()) {
100 $fileInfo['deleted'] = 0;
101 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_file', sprintf('uid = %d', $fileObject->getUid()), $fileInfo);
102 } else {
103 // Check if a file has been moved outside of FAL -- we have some
104 // orphaned index record in this case we could update
105 $otherFiles = $this->getRepository()->findBySha1Hash($fileInfo['sha1']);
106 $movedFile = FALSE;
107 /** @var $otherFile \TYPO3\CMS\Core\Resource\File */
108 foreach ($otherFiles as $otherFile) {
109 if (!$otherFile->exists()) {
110 // @todo: create a log entry
111 $movedFile = TRUE;
112 $otherFile->updateProperties($fileInfo);
113 $this->getRepository()->update($otherFile);
114 $fileInfo['uid'] = $otherFile->getUid();
115 $fileObject = $otherFile;
116 // Skip the rest of the files here as we might have more files that are missing, but we can only
117 // have one entry. The optimal solution would be to merge these records then, but this requires
118 // some more advanced logic that we currently have not implemented.
119 break;
120 }
121 }
122 // File was not moved, so it is a new index record
123 if ($movedFile === FALSE) {
124 // Crdate and tstamp should not be present when updating
125 // the file object, as they only relate to the index record
126 $additionalInfo = array(
127 'crdate' => $GLOBALS['EXEC_TIME'],
128 'tstamp' => $GLOBALS['EXEC_TIME']
129 );
130 if (TYPO3_MODE === 'BE') {
131 $additionalInfo['cruser_id'] = intval($GLOBALS['BE_USER']->user['uid']);
132 }
133 $indexRecord = array_merge($fileInfo, $additionalInfo);
134
135 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_file', $indexRecord);
136 $fileInfo['uid'] = $GLOBALS['TYPO3_DB']->sql_insert_id();
137 }
138 }
139 // Check for an error during the execution and throw an exception
140 $error = $GLOBALS['TYPO3_DB']->sql_error();
141 if ($error) {
142 throw new \RuntimeException('Error during file indexing: "' . $error . '"', 1314455642);
143 }
144 // Signal slot AFTER the file was indexed
145 $this->emitPostFileIndexSignal($fileObject, $fileInfo);
146 if ($updateObject) {
147 $fileObject->updateProperties($fileInfo);
148 return $fileObject;
149 } else {
150 return $fileInfo;
151 }
152 }
153
154 /**
155 * Indexes an array of file objects
156 * currently this is done in a simple way, however could be changed to be more performant
157 *
158 * @param \TYPO3\CMS\Core\Resource\File[] $fileObjects
159 * @return void
160 */
161 public function indexFiles(array $fileObjects) {
162 // emit signal
163 $this->emitPreMultipleFilesIndexSignal($fileObjects);
164 foreach ($fileObjects as $fileObject) {
165 $this->indexFile($fileObject);
166 }
167 // emit signal
168 $this->emitPostMultipleFilesIndexSignal($fileObjects);
169 }
170
171 /**
172 * Indexes all files in a given storage folder.
173 * currently this is done in a simple way, however could be changed to be more performant
174 *
175 * @param \TYPO3\CMS\Core\Resource\Folder $folder
176 * @return int The number of indexed files.
177 */
178 public function indexFilesInFolder(\TYPO3\CMS\Core\Resource\Folder $folder) {
179 $numberOfIndexedFiles = 0;
180 // Index all files in this folder
181 $fileObjects = $folder->getFiles();
182 // emit signal
183 $this->emitPreMultipleFilesIndexSignal($fileObjects);
184 foreach ($fileObjects as $fileObject) {
185 $this->indexFile($fileObject);
186 $numberOfIndexedFiles++;
187 }
188 // emit signal
189 $this->emitPostMultipleFilesIndexSignal($fileObjects);
190 // Call this function recursively for each subfolder
191 $subFolders = $folder->getSubfolders();
192 foreach ($subFolders as $subFolder) {
193 $numberOfIndexedFiles += $this->indexFilesInFolder($subFolder);
194 }
195 return $numberOfIndexedFiles;
196 }
197
198 /**
199 * Fetches the information for a sys_file record
200 * based on a single file
201 * this function shouldn't be used, if someone needs to fetch the file information
202 * from a file object, should be done by getProperties etc
203 *
204 * @param \TYPO3\CMS\Core\Resource\File $file the file to fetch the information from
205 * @return array the file information as an array
206 */
207 protected function gatherFileInformation(\TYPO3\CMS\Core\Resource\File $file) {
208 $fileInfo = new \ArrayObject(array());
209 $gatherDefaultInformation = new \stdClass();
210 $gatherDefaultInformation->getDefaultFileInfo = 1;
211 // signal before the files are modified
212 $this->emitPreGatherFileInformationSignal($file, $fileInfo, $gatherDefaultInformation);
213 // the check helps you to disable the regular file fetching,
214 // so a signal could actually remotely access the service
215 if ($gatherDefaultInformation->getDefaultFileInfo) {
216 $storage = $file->getStorage();
217 // TODO: See if we can't just return info, as it contains most of the
218 // stuff we put together in array form again later here.
219 $info = $storage->getFileInfo($file);
220 $defaultFileInfo = array(
221 'creation_date' => $info['ctime'],
222 'modification_date' => $info['mtime'],
223 'size' => $info['size'],
224 'identifier' => $file->getIdentifier(),
225 'storage' => $storage->getUid(),
226 'name' => $file->getName(),
227 'sha1' => $storage->hashFile($file, 'sha1'),
228 'type' => $file->getType(),
229 'mime_type' => $file->getMimeType(),
230 'extension' => $file->getExtension()
231 );
232 $fileInfo = array_merge($defaultFileInfo, $fileInfo->getArrayCopy());
233 $fileInfo = new \ArrayObject($fileInfo);
234 }
235 // signal after the file information is fetched
236 $this->emitPostGatherFileInformationSignal($file, $fileInfo, $gatherDefaultInformation);
237 return $fileInfo->getArrayCopy();
238 }
239
240 /**
241 * Signal that is called before the file information is fetched
242 * helpful if somebody wants to preprocess the record information
243 *
244 * @param \TYPO3\CMS\Core\Resource\File $fileObject
245 * @param array $fileInfo
246 * @param boolean $gatherDefaultInformation
247 * @signal
248 */
249 protected function emitPreGatherFileInformationSignal(\TYPO3\CMS\Core\Resource\File $fileObject, $fileInfo, $gatherDefaultInformation) {
250 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'preGatherFileInformation', array($fileObject, $fileInfo, $gatherDefaultInformation));
251 }
252
253 /**
254 * Signal that is called after a file object was indexed
255 *
256 * @param \TYPO3\CMS\Core\Resource\File $fileObject
257 * @param array $fileInfo
258 * @param boolean $hasGatheredDefaultInformation
259 * @signal
260 */
261 protected function emitPostGatherFileInformationSignal(\TYPO3\CMS\Core\Resource\File $fileObject, $fileInfo, $hasGatheredDefaultInformation) {
262 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'postGatherFileInformation', array($fileObject, $fileInfo, $hasGatheredDefaultInformation));
263 }
264
265 /**
266 * Signal that is called before a bunch of file objects are indexed
267 *
268 * @param array $fileObject
269 * @signal
270 */
271 protected function emitPreMultipleFilesIndexSignal(array $fileObjectsToIndex) {
272 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'preMultipleFileIndex', array($fileObjectsToIndex));
273 }
274
275 /**
276 * Signal that is called after multiple file objects were indexed
277 *
278 * @param array $fileObjectsToIndex
279 * @signal
280 */
281 protected function emitPostMultipleFilesIndexSignal(array $fileObjectsToIndex) {
282 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'postMultipleFileIndex', array($fileObjectsToIndex));
283 }
284
285 /**
286 * Signal that is called before a file object was indexed
287 *
288 * @param \TYPO3\CMS\Core\Resource\File $fileObject
289 * @param array $fileInfo
290 * @signal
291 */
292 protected function emitPreFileIndexSignal(\TYPO3\CMS\Core\Resource\File $fileObject, $fileInfo) {
293 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'preFileIndex', array($fileObject, $fileInfo));
294 }
295
296 /**
297 * Signal that is called after a file object was indexed
298 *
299 * @param \TYPO3\CMS\Core\Resource\File $fileObject
300 * @param array $fileInfo
301 * @signal
302 */
303 protected function emitPostFileIndexSignal(\TYPO3\CMS\Core\Resource\File $fileObject, $fileInfo) {
304 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Service\\IndexerService', 'postFileIndex', array($fileObject, $fileInfo));
305 }
306
307 /**
308 * Get the SignalSlot dispatcher
309 *
310 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
311 */
312 protected function getSignalSlotDispatcher() {
313 return $this->getObjectManager()->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
314 }
315
316 /**
317 * Get the ObjectManager
318 *
319 * @return \TYPO3\CMS\Extbase\Object\ObjectManager
320 */
321 protected function getObjectManager() {
322 return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
323 }
324
325 }
326
327
328 ?>