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