[TASK] Add more fixers for php-cs-fixer
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / Index / MetaDataRepository.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\Database\ConnectionPool;
19 use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction;
20 use TYPO3\CMS\Core\Resource\Exception\InvalidUidException;
21 use TYPO3\CMS\Core\Resource\File;
22 use TYPO3\CMS\Core\SingletonInterface;
23 use TYPO3\CMS\Core\Type\File as FileType;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\CMS\Extbase\Object\ObjectManager;
26 use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
27
28 /**
29 * Repository Class as an abstraction layer to sys_file_metadata
30 *
31 * Every access to table sys_file_metadata which is not handled by TCEmain
32 * has to use this Repository class
33 */
34 class MetaDataRepository implements SingletonInterface
35 {
36 /**
37 * @var string
38 */
39 protected $tableName = 'sys_file_metadata';
40
41 /**
42 * Internal storage for database table fields
43 *
44 * @var array
45 */
46 protected $tableFields = [];
47
48 /**
49 * Wrapper method for getting DatabaseConnection
50 *
51 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
52 */
53 protected function getDatabaseConnection()
54 {
55 return $GLOBALS['TYPO3_DB'];
56 }
57
58 /**
59 * Returns array of meta-data properties
60 *
61 * @param File $file
62 * @return array
63 */
64 public function findByFile(File $file)
65 {
66 $record = $this->findByFileUid($file->getUid());
67
68 // It could be possible that the meta information is freshly
69 // created and inserted into the database. If this is the case
70 // we have to take care about correct meta information for width and
71 // height in case of an image.
72 if (!empty($record['newlyCreated'])) {
73 if ($file->getType() === File::FILETYPE_IMAGE && $file->getStorage()->getDriverType() === 'Local') {
74 $fileNameAndPath = $file->getForLocalProcessing(false);
75
76 $imageInfo = GeneralUtility::makeInstance(FileType\ImageInfo::class, $fileNameAndPath);
77
78 $additionalMetaInformation = [
79 'width' => $imageInfo->getWidth(),
80 'height' => $imageInfo->getHeight(),
81 ];
82
83 $this->update($file->getUid(), $additionalMetaInformation);
84 }
85 $record = $this->findByFileUid($file->getUid());
86 }
87
88 return $record;
89 }
90
91 /**
92 * Retrieves metadata for file
93 *
94 * @param int $uid
95 * @return array
96 * @throws InvalidUidException
97 */
98 public function findByFileUid($uid)
99 {
100 $uid = (int)$uid;
101 if ($uid <= 0) {
102 throw new InvalidUidException('Metadata can only be retrieved for indexed files. UID: "' . $uid . '"', 1381590731);
103 }
104
105 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->tableName);
106
107 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(RootLevelRestriction::class));
108
109 $record = $queryBuilder
110 ->select('*')
111 ->from($this->tableName)
112 ->where(
113 $queryBuilder->expr()->eq('file', $uid),
114 $queryBuilder->expr()->in('sys_language_uid', [0, -1])
115 )
116 ->execute()
117 ->fetch();
118
119 if (empty($record)) {
120 $record = $this->createMetaDataRecord($uid);
121 }
122
123 $passedData = new \ArrayObject($record);
124
125 $this->emitRecordPostRetrievalSignal($passedData);
126 return $passedData->getArrayCopy();
127 }
128
129 /**
130 * Create empty
131 *
132 * @param int $fileUid
133 * @param array $additionalFields
134 * @return array
135 */
136 public function createMetaDataRecord($fileUid, array $additionalFields = [])
137 {
138 $emptyRecord = [
139 'file' => (int)$fileUid,
140 'pid' => 0,
141 'crdate' => $GLOBALS['EXEC_TIME'],
142 'tstamp' => $GLOBALS['EXEC_TIME'],
143 'cruser_id' => isset($GLOBALS['BE_USER']->user['uid']) ? (int)$GLOBALS['BE_USER']->user['uid'] : 0,
144 'l10n_diffsource' => ''
145 ];
146 $emptyRecord = array_merge($emptyRecord, $additionalFields);
147
148 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->tableName);
149 $connection->insert(
150 $this->tableName,
151 $emptyRecord
152 );
153
154 $record = $emptyRecord;
155 $record['uid'] = $connection->lastInsertId();
156 $record['newlyCreated'] = true;
157
158 $this->emitRecordCreatedSignal($record);
159
160 return $record;
161 }
162
163 /**
164 * Updates the metadata record in the database
165 *
166 * @param int $fileUid the file uid to update
167 * @param array $data Data to update
168 * @return void
169 * @internal
170 */
171 public function update($fileUid, array $data)
172 {
173 if (empty($this->tableFields)) {
174 $this->tableFields = GeneralUtility::makeInstance(ConnectionPool::class)
175 ->getConnectionForTable($this->tableName)
176 ->getSchemaManager()
177 ->listTableColumns($this->tableName);
178 }
179 $updateRow = array_intersect_key($data, $this->tableFields);
180 if (array_key_exists('uid', $updateRow)) {
181 unset($updateRow['uid']);
182 }
183 $row = $this->findByFileUid($fileUid);
184 if (!empty($updateRow)) {
185 $updateRow['tstamp'] = time();
186 GeneralUtility::makeInstance(ConnectionPool::class)
187 ->getConnectionForTable($this->tableName)
188 ->update(
189 $this->tableName,
190 $updateRow,
191 [
192 'uid' => (int)$row['uid']
193 ]
194 );
195
196 $this->emitRecordUpdatedSignal(array_merge($row, $updateRow));
197 }
198 }
199
200 /**
201 * Remove all metadata records for a certain file from the database
202 *
203 * @param int $fileUid
204 * @return void
205 */
206 public function removeByFileUid($fileUid)
207 {
208 GeneralUtility::makeInstance(ConnectionPool::class)
209 ->getConnectionForTable($this->tableName)
210 ->delete(
211 $this->tableName,
212 [
213 'file' => (int)$fileUid
214 ]
215 );
216 $this->emitRecordDeletedSignal($fileUid);
217 }
218
219 /**
220 * Get the SignalSlot dispatcher
221 *
222 * @return Dispatcher
223 */
224 protected function getSignalSlotDispatcher()
225 {
226 return $this->getObjectManager()->get(Dispatcher::class);
227 }
228
229 /**
230 * Get the ObjectManager
231 *
232 * @return ObjectManager
233 */
234 protected function getObjectManager()
235 {
236 return GeneralUtility::makeInstance(ObjectManager::class);
237 }
238
239 /**
240 * Signal that is called after a record has been loaded from database
241 * Allows other places to do extension of metadata at runtime or
242 * for example translation and workspace overlay
243 *
244 * @param \ArrayObject $data
245 * @signal
246 */
247 protected function emitRecordPostRetrievalSignal(\ArrayObject $data)
248 {
249 $this->getSignalSlotDispatcher()->dispatch(self::class, 'recordPostRetrieval', [$data]);
250 }
251
252 /**
253 * Signal that is called after an IndexRecord is updated
254 *
255 * @param array $data
256 * @signal
257 */
258 protected function emitRecordUpdatedSignal(array $data)
259 {
260 $this->getSignalSlotDispatcher()->dispatch(self::class, 'recordUpdated', [$data]);
261 }
262
263 /**
264 * Signal that is called after an IndexRecord is created
265 *
266 * @param array $data
267 * @signal
268 */
269 protected function emitRecordCreatedSignal(array $data)
270 {
271 $this->getSignalSlotDispatcher()->dispatch(self::class, 'recordCreated', [$data]);
272 }
273
274 /**
275 * Signal that is called after an IndexRecord is deleted
276 *
277 * @param int $fileUid
278 * @signal
279 */
280 protected function emitRecordDeletedSignal($fileUid)
281 {
282 $this->getSignalSlotDispatcher()->dispatch(self::class, 'recordDeleted', [$fileUid]);
283 }
284
285 /**
286 * @return MetaDataRepository
287 */
288 public static function getInstance()
289 {
290 return GeneralUtility::makeInstance(self::class);
291 }
292 }