5aa152a241575f268c47a850c6603f8518e0438b
[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 // If the file is already indexed, then the file information will
97 // be updated on the existing record
98 if ($fileObject->isIndexed()) {
99 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_file', sprintf('uid = %d', $fileObject->getUid()), $fileInfo);
100 } else {
101 // Check if a file has been moved outside of FAL -- we have some
102 // orphaned index record in this case we could update
103 $otherFiles = $this->getRepository()->findBySha1Hash($fileInfo['sha1']);
104 $movedFile = FALSE;
105 /** @var $otherFile t3lib_file_File */
106 foreach ($otherFiles as $otherFile) {
107 if (!$otherFile->exists()) {
108 // @todo: create a log entry
109 $movedFile = TRUE;
110 $otherFile->updateProperties($fileInfo);
111 $this->getRepository()->update($otherFile);
112 $fileInfo['uid'] = $otherFile->getUid();
113 $fileObject = $otherFile;
114 // Skip the rest of the files here as we might have more files that are missing, but we can only
115 // have one entry. The optimal solution would be to merge these records then, but this requires
116 // some more advanced logic that we currently have not implemented.
117 break;
118 }
119 }
120
121 // File was not moved, so it is a new index record
122 if ($movedFile === FALSE) {
123 // Crdate and tstamp should not be present when updating
124 // the file object, as they only relate to the index record
125 $indexRecord = array_merge($fileInfo,
126 array(
127 'crdate' => $GLOBALS['EXEC_TIME'],
128 'tstamp' => $GLOBALS['EXEC_TIME']
129 )
130 );
131 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_file', $indexRecord);
132 $fileInfo['uid'] = $GLOBALS['TYPO3_DB']->sql_insert_id();
133 }
134 }
135
136 // Check for an error during the execution and throw an exception
137 $error = $GLOBALS['TYPO3_DB']->sql_error();
138 if ($error) {
139 throw new RuntimeException('Error during file indexing: "' . $error . '"', 1314455642);
140 }
141
142 // Signal slot AFTER the file was indexed
143 $this->emitPostFileIndexSignal($fileObject, $fileInfo);
144
145 if ($updateObject) {
146 $fileObject->updateProperties($fileInfo);
147 return $fileObject;
148 } else {
149 return $fileInfo;
150 }
151 }
152
153 /**
154 * Indexes an array of file objects
155 * currently this is done in a simple way, however could be changed to be more performant
156 *
157 * @param t3lib_file_File[] $fileObjects
158 * @return void
159 */
160 public function indexFiles(array $fileObjects) {
161 foreach ($fileObjects as $fileObject) {
162 $this->indexFile($fileObject);
163 }
164 }
165
166 /**
167 * Indexes all files in a given storage folder.
168 * currently this is done in a simple way, however could be changed to be more performant
169 *
170 * @param t3lib_file_Folder $folder
171 * @return int The number of indexed files.
172 */
173 public function indexFilesInFolder(t3lib_file_Folder $folder) {
174 $numberOfIndexedFiles = 0;
175
176 // Index all files in this folder
177 $fileObjects = $folder->getFiles();
178 foreach ($fileObjects as $fileObject) {
179 $this->indexFile($fileObject);
180 $numberOfIndexedFiles++;
181 }
182
183 // Call this function recursively for each subfolder
184 $subFolders = $folder->getSubfolders();
185 foreach ($subFolders as $subFolder) {
186 $numberOfIndexedFiles += $this->indexFilesInFolder($subFolder);
187 }
188
189 return $numberOfIndexedFiles;
190 }
191
192
193 /**
194 * Fetches the information for a sys_file record
195 * based on a single file
196 * this function shouldn't be used, if someone needs to fetch the file information
197 * from a file object, should be done by getProperties etc
198 *
199 * @param t3lib_file_File $file the file to fetch the information from
200 * @return array the file information as an array
201 */
202 protected function gatherFileInformation(t3lib_file_File $file) {
203 $storage = $file->getStorage();
204
205 // TODO: See if we can't just return info, as it contains most of the
206 // stuff we put together in array form again later here.
207 $info = $storage->getFileInfo($file);
208
209 $fileInfo = array(
210 'creation_date' => $info['ctime'],
211 'modification_date' => $info['mtime'],
212 'size' => $info['size'],
213 'identifier' => $file->getIdentifier(),
214 'storage' => $storage->getUid(),
215 'name' => $file->getName(),
216 'sha1' => $storage->hashFile($file, 'sha1'),
217 'type' => $file->getType(),
218 'mime_type' => $file->getMimeType(),
219 'extension' => $file->getExtension(),
220 );
221
222 return $fileInfo;
223 }
224
225
226 /**
227 * Signal that is called after a file object was indexed
228 *
229 * @param t3lib_file_File $fileObject
230 * @param array $fileInfo
231 * @signal
232 */
233 protected function emitPreFileIndexSignal(t3lib_file_File $fileObject, $fileInfo) {
234 /** @var t3lib_SignalSlot_Dispatcher $dispatcher */
235 $dispatcher = t3lib_div::makeInstance('t3lib_SignalSlot_Dispatcher');
236 $dispatcher->dispatch('t3lib_file_Storage', 'preFileIndex', array($fileObject, $fileInfo));
237 }
238
239 /**
240 * Signal that is called after a file object was indexed
241 *
242 * @param t3lib_file_File $fileObject
243 * @param array $fileInfo
244 * @signal
245 */
246 protected function emitPostFileIndexSignal(t3lib_file_File $fileObject, $fileInfo) {
247 /** @var t3lib_SignalSlot_Dispatcher $dispatcher */
248 $dispatcher = t3lib_div::makeInstance('t3lib_SignalSlot_Dispatcher');
249 $dispatcher->dispatch('t3lib_file_Storage', 'postFileIndex', array($fileObject, $fileInfo));
250 }
251
252 }
253
254
255 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/file/Service/IndexerService.php'])) {
256 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/file/Service/IndexerService.php']);
257 }
258
259 ?>