e468ebc9f84a584640fef7e81d96282131c9b4c0
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / Index / FileIndexRepository.php
1 <?php
2
3 namespace TYPO3\CMS\Core\Resource\Index;
4
5 /***************************************************************
6 * Copyright notice
7 *
8 * (c) 2013 Steffen Ritter <steffen.ritter@typo3.org>
9 * All rights reserved
10 *
11 * This script is part of the TYPO3 project. The TYPO3 project is
12 * free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
19 * A copy is found in the textfile GPL.txt and important notices to the license
20 * from the author is found in LICENSE.txt distributed with these scripts.
21 *
22 *
23 * This script is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * This copyright notice MUST APPEAR in all copies of the script!
29 ***************************************************************/
30
31 use TYPO3\CMS\Core\SingletonInterface;
32 use TYPO3\CMS\Core\Utility\GeneralUtility;
33 use TYPO3\CMS\Core\Resource\File;
34
35 /**
36 * Repository Class as an abstraction layer to sys_file
37 *
38 * Every access to table sys_file_metadata which is not handled by TCEmain
39 * has to use this Repository class.
40 *
41 * This is meant for FAL internal use only!.
42 */
43 class FileIndexRepository implements SingletonInterface {
44
45 /**
46 * @var string
47 */
48 protected $table = 'sys_file';
49
50 /**
51 * A list of properties which are to be persisted
52 *
53 * @var array
54 */
55 protected $fields = array(
56 'uid', 'pid', 'missing', 'type', 'storage', 'identifier', 'identifier_hash', 'extension',
57 'mime_type', 'name', 'sha1', 'size', 'creation_date', 'modification_date', 'folder_hash'
58 );
59
60 /**
61 * Gets database instance
62 *
63 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
64 */
65 protected function getDatabase() {
66 return $GLOBALS['TYPO3_DB'];
67 }
68
69 /**
70 * Gets the Resource Factory
71 *
72 * @return \TYPO3\CMS\Core\Resource\ResourceFactory
73 */
74 protected function getResourceFactory() {
75 return \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance();
76 }
77
78
79 /**
80 * Returns an Instance of the Repository
81 *
82 * @return FileIndexRepository
83 */
84 public static function getInstance() {
85 return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\FileIndexRepository');
86 }
87
88 /**
89 * Retrieves Index record for a given $combinedIdentifier
90 *
91 * @param string $combinedIdentifier
92 * @return array|boolean
93 */
94 public function findOneByCombinedIdentifier($combinedIdentifier) {
95 list($storageUid, $identifier) = GeneralUtility::trimExplode(':', $combinedIdentifier, FALSE, 2);
96 return $this->findOneByStorageUidAndIdentifier($storageUid, $identifier);
97 }
98
99 /**
100 * Retrieves Index record for a given $fileUid
101 *
102 * @param int $fileUid
103 * @return array|boolean
104 */
105 public function findOneByUid($fileUid) {
106 $row = $this->getDatabase()->exec_SELECTgetSingleRow(
107 implode(',', $this->fields),
108 $this->table,
109 'uid=' . intval($fileUid)
110 );
111 return is_array($row) ? $row : FALSE;
112 }
113
114 /**
115 * Retrieves Index record for a given $storageUid and $identifier
116 *
117 * @param int $storageUid
118 * @param string $identifier
119 * @return array|boolean
120 *
121 * @internal only for use from FileRepository
122 */
123 public function findOneByStorageUidAndIdentifier($storageUid, $identifier) {
124 $identifierHash = $this->getResourceFactory()->getStorageObject($storageUid)->hashFileIdentifier($identifier);
125 return $this->findOneByStorageUidAndIdentifierHash($storageUid, $identifierHash);
126 }
127
128 /**
129 * Retrieves Index record for a given $storageUid and $identifier
130 *
131 * @param integer $storageUid
132 * @param string $identifierHash
133 * @return array|boolean
134 *
135 * @internal only for use from FileRepository
136 */
137 public function findOneByStorageUidAndIdentifierHash($storageUid, $identifierHash) {
138 $row = $this->getDatabase()->exec_SELECTgetSingleRow(
139 implode(',', $this->fields),
140 $this->table,
141 sprintf('storage=%u AND identifier_hash=%s', intval($storageUid), $this->getDatabase()->fullQuoteStr($identifierHash, $this->table))
142 );
143 return is_array($row) ? $row : FALSE;
144 }
145
146 /**
147 * Retrieves Index record for a given $fileObject
148 *
149 * @param \TYPO3\CMS\Core\Resource\FileInterface $fileObject
150 * @return array|boolean
151 *
152 * @internal only for use from FileRepository
153 */
154 public function findOneByFileObject(\TYPO3\CMS\Core\Resource\FileInterface $fileObject) {
155 $storageUid = $fileObject->getStorage()->getUid();
156 $identifierHash = $fileObject->getHashedIdentifier();
157 return $this->findOneByStorageUidAndIdentifierHash($storageUid, $identifierHash);
158 }
159
160 /**
161 * Returns all indexed files which match the content hash
162 * Used by the indexer to detect already present files
163 *
164 * @param string $hash
165 * @return mixed
166 */
167 public function findByContentHash($hash) {
168 if (!preg_match('/^[0-9a-f]{40}$/i', $hash)) {
169 return array();
170 }
171 $resultRows = $this->getDatabase()->exec_SELECTgetRows(
172 implode(',', $this->fields),
173 $this->table,
174 'sha1=' . $this->getDatabase()->fullQuoteStr($hash, $this->table)
175 );
176 return $resultRows;
177 }
178
179 /**
180 * Adds a file to the index
181 *
182 * @param File $file
183 * @return void
184 */
185 public function add(File $file) {
186 if ($this->hasIndexRecord($file)) {
187 $this->update($file);
188 if ($file->_getPropertyRaw('uid') === NULL) {
189 $file->updateProperties($this->findOneByFileObject($file));
190 }
191 } else {
192 $file->updateProperties(array('uid' => $this->insertRecord($file->getProperties())));
193 }
194 }
195
196 /**
197 * Add data from record (at indexing time)
198 *
199 * @param array $data
200 * @return array
201 */
202 public function addRaw(array $data) {
203 $data['uid'] = $this->insertRecord($data);
204 return $data;
205 }
206
207 /**
208 * Helper to reduce code duplication
209 *
210 * @param array $data
211 *
212 * @return integer
213 */
214 protected function insertRecord(array $data) {
215 $data = array_intersect_key($data, array_flip($this->fields));
216 $data['tstamp'] = time();
217 $this->getDatabase()->exec_INSERTquery($this->table, $data);
218 $data['uid'] = $this->getDatabase()->sql_insert_id();
219 $this->emitRecordCreated($data);
220 return $data['uid'];
221 }
222 /**
223 * Checks if a file is indexed
224 *
225 * @param File $file
226 * @return boolean
227 */
228 public function hasIndexRecord(File $file) {
229 return $this->getDatabase()->exec_SELECTcountRows('uid', $this->table, $this->getWhereClauseForFile($file)) >= 1;
230 }
231
232 /**
233 * Updates the index record in the database
234 *
235 * @param File $file
236 * @return void
237 */
238 public function update(File $file) {
239 $updatedProperties = array_intersect($this->fields, $file->getUpdatedProperties());
240 $updateRow = array();
241 foreach ($updatedProperties as $key) {
242 $updateRow[$key] = $file->getProperty($key);
243 }
244 if (count($updateRow) > 0) {
245 $updateRow['tstamp'] = time();
246 $this->getDatabase()->exec_UPDATEquery($this->table, $this->getWhereClauseForFile($file), $updateRow);
247 $this->emitRecordUpdated(array_intersect_key($file->getProperties(), array_flip($this->fields)));
248 }
249 }
250
251 /**
252 * Finds the files needed for second indexer step
253 *
254 * @param \TYPO3\CMS\Core\Resource\ResourceStorage $storage
255 * @param integer $limit
256 * @return array
257 */
258 public function findInStorageWithIndexOutstanding(\TYPO3\CMS\Core\Resource\ResourceStorage $storage, $limit = -1) {
259 return $this->getDatabase()->exec_SELECTgetRows(
260 implode(',', $this->fields),
261 $this->table,
262 'tstamp > last_indexed AND storage = ' . intval($storage->getUid()),
263 '',
264 'tstamp ASC',
265 intval($limit) > 0 ? intval($limit) : ''
266 );
267 }
268
269
270 /**
271 * Helper function for the Indexer to detect missing files
272 *
273 * @param \TYPO3\CMS\Core\Resource\ResourceStorage $storage
274 * @param array $uidList
275 * @return array
276 */
277 public function findInStorageAndNotInUidList(\TYPO3\CMS\Core\Resource\ResourceStorage $storage, array $uidList) {
278 array_walk($uidList, 'intval');
279 $uidList = array_unique($uidList);
280
281 return $this->getDatabase()->exec_SELECTgetRows(
282 implode(',', $this->fields),
283 $this->table,
284 'storage = ' . intval($storage->getUid()) . ' AND uid NOT IN (' . implode(',', $uidList) . ')'
285 );
286 }
287
288 /**
289 * Updates the timestamp when the file indexer extracted metadata
290 *
291 * @param integer $fileUid
292 * @return void
293 */
294 public function updateIndexingTime($fileUid) {
295 $this->getDatabase()->exec_UPDATEquery($this->table, 'uid = ' . intval($fileUid), array('last_indexed' => time()));
296 }
297
298 /**
299 * Marks given file as missing in sys_file
300 *
301 * @param integer $fileUid
302 * @return void
303 */
304 public function markFileAsMissing($fileUid) {
305 $this->getDatabase()->exec_UPDATEquery($this->table, 'uid = ' . intval($fileUid), array('missing' => 1));
306 }
307
308 /**
309 * Returns a where clause to find a file in database
310 *
311 * @param File $file
312 *
313 * @return string
314 */
315 protected function getWhereClauseForFile(File $file) {
316 if (intval($file->_getPropertyRaw('uid')) > 0) {
317 $where = 'uid=' . intval($file->getUid());
318 } else {
319 $where = sprintf(
320 'storage=%u AND identifier=%s',
321 intval($file->getStorage()->getUid()),
322 $this->getDatabase()->fullQuoteStr($file->_getPropertyRaw('identifier'), $this->table)
323 );
324 }
325 return $where;
326 }
327
328 /**
329 * Remove a sys_file record from the database
330 *
331 * @param integer $fileUid
332 * @return void
333 */
334 public function remove($fileUid) {
335 $this->getDatabase()->exec_DELETEquery($this->table, 'uid=' . intval($fileUid));
336 $this->emitRecordDeleted($fileUid);
337 }
338
339 /*
340 * Get the SignalSlot dispatcher
341 *
342 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
343 */
344 protected function getSignalSlotDispatcher() {
345 return $this->getObjectManager()->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
346 }
347
348 /**
349 * Get the ObjectManager
350 *
351 * @return \TYPO3\CMS\Extbase\Object\ObjectManager
352 */
353 protected function getObjectManager() {
354 return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
355 }
356
357
358
359 /**
360 * Signal that is called after an IndexRecord is updated
361 *
362 * @param array $data
363 * @signal
364 */
365 protected function emitRecordUpdated(array $data) {
366 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Index\\FileIndexRepository', 'recordUpdated', array($data));
367 }
368
369 /**
370 * Signal that is called after an IndexRecord is created
371 *
372 * @param array $data
373 * @signal
374 */
375 protected function emitRecordCreated(array $data) {
376 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Index\\FileIndexRepository', 'recordCreated', array($data));
377 }
378
379 /**
380 * Signal that is called after an IndexRecord is deleted
381 *
382 * @param integer $fileUid
383 * @signal
384 */
385 protected function emitRecordDeleted($fileUid) {
386 $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Resource\\Index\\FileIndexRepository', 'recordDeleted', array($fileUid));
387 }
388 }