[TASK] gitlab-ci nightly slack notification
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / Index / MetaDataRepository.php
1 <?php
2
3 /*
4 * This file is part of the TYPO3 CMS project.
5 *
6 * It is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License, either version 2
8 * of the License, or any later version.
9 *
10 * For the full copyright and license information, please read the
11 * LICENSE.txt file that was distributed with this source code.
12 *
13 * The TYPO3 project - inspiring people to share!
14 */
15
16 namespace TYPO3\CMS\Core\Resource\Index;
17
18 use Doctrine\DBAL\Platforms\SQLServer2012Platform as SQLServerPlatform;
19 use Psr\EventDispatcher\EventDispatcherInterface;
20 use TYPO3\CMS\Core\Database\Connection;
21 use TYPO3\CMS\Core\Database\ConnectionPool;
22 use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction;
23 use TYPO3\CMS\Core\Resource\Event\AfterFileMetaDataCreatedEvent;
24 use TYPO3\CMS\Core\Resource\Event\AfterFileMetaDataDeletedEvent;
25 use TYPO3\CMS\Core\Resource\Event\AfterFileMetaDataUpdatedEvent;
26 use TYPO3\CMS\Core\Resource\Event\EnrichFileMetaDataEvent;
27 use TYPO3\CMS\Core\Resource\Exception\InvalidUidException;
28 use TYPO3\CMS\Core\Resource\File;
29 use TYPO3\CMS\Core\SingletonInterface;
30 use TYPO3\CMS\Core\Type\File\ImageInfo;
31 use TYPO3\CMS\Core\Utility\GeneralUtility;
32
33 /**
34 * Repository Class as an abstraction layer to sys_file_metadata
35 *
36 * Every access to table sys_file_metadata which is not handled by DataHandler
37 * has to use this Repository class
38 */
39 class MetaDataRepository implements SingletonInterface
40 {
41 /**
42 * @var string
43 */
44 protected $tableName = 'sys_file_metadata';
45
46 /**
47 * Internal storage for database table fields
48 *
49 * @var array
50 */
51 protected $tableFields = [];
52
53 /**
54 * @var EventDispatcherInterface
55 */
56 protected $eventDispatcher;
57
58 public function __construct(EventDispatcherInterface $eventDispatcher)
59 {
60 $this->eventDispatcher = $eventDispatcher;
61 }
62
63 /**
64 * Returns array of meta-data properties
65 *
66 * @param File $file
67 * @return array
68 */
69 public function findByFile(File $file)
70 {
71 $record = $this->findByFileUid($file->getUid());
72
73 // It could be possible that the meta information is freshly
74 // created and inserted into the database. If this is the case
75 // we have to take care about correct meta information for width and
76 // height in case of an image.
77 // This logic can be transferred into a custom PSR-14 event listener in the future by just using
78 // the AfterMetaDataCreated event.
79 if (!empty($record['crdate']) && (int)$record['crdate'] === $GLOBALS['EXEC_TIME']) {
80 if ($file->getType() === File::FILETYPE_IMAGE && $file->getStorage()->getDriverType() === 'Local') {
81 $fileNameAndPath = $file->getForLocalProcessing(false);
82
83 $imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $fileNameAndPath);
84
85 $additionalMetaInformation = [
86 'width' => $imageInfo->getWidth(),
87 'height' => $imageInfo->getHeight(),
88 ];
89
90 $this->update($file->getUid(), $additionalMetaInformation);
91 }
92 $record = $this->findByFileUid($file->getUid());
93 }
94
95 return $record;
96 }
97
98 /**
99 * Retrieves metadata for file
100 *
101 * @param int $uid
102 * @return array
103 * @throws InvalidUidException
104 */
105 public function findByFileUid($uid)
106 {
107 $uid = (int)$uid;
108 if ($uid <= 0) {
109 throw new InvalidUidException('Metadata can only be retrieved for indexed files. UID: "' . $uid . '"', 1381590731);
110 }
111
112 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->tableName);
113
114 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(RootLevelRestriction::class));
115
116 $record = $queryBuilder
117 ->select('*')
118 ->from($this->tableName)
119 ->where(
120 $queryBuilder->expr()->eq('file', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)),
121 $queryBuilder->expr()->in('sys_language_uid', $queryBuilder->createNamedParameter([0, -1], Connection::PARAM_INT_ARRAY))
122 )
123 ->execute()
124 ->fetch();
125
126 if (empty($record)) {
127 return [];
128 }
129
130 return $this->eventDispatcher->dispatch(new EnrichFileMetaDataEvent($uid, (int)$record['uid'], $record))->getRecord();
131 }
132
133 /**
134 * Create empty
135 *
136 * @param int $fileUid
137 * @param array $additionalFields
138 * @return array
139 */
140 public function createMetaDataRecord($fileUid, array $additionalFields = [])
141 {
142 $emptyRecord = [
143 'file' => (int)$fileUid,
144 'pid' => 0,
145 'crdate' => $GLOBALS['EXEC_TIME'],
146 'tstamp' => $GLOBALS['EXEC_TIME'],
147 'cruser_id' => isset($GLOBALS['BE_USER']->user['uid']) ? (int)$GLOBALS['BE_USER']->user['uid'] : 0,
148 'l10n_diffsource' => ''
149 ];
150 $additionalFields = array_intersect_key($additionalFields, $this->getTableFields());
151 $emptyRecord = array_merge($emptyRecord, $additionalFields);
152
153 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->tableName);
154 $connection->insert(
155 $this->tableName,
156 $emptyRecord,
157 ['l10n_diffsource' => Connection::PARAM_LOB]
158 );
159
160 $record = $emptyRecord;
161 $record['uid'] = $connection->lastInsertId($this->tableName);
162
163 return $this->eventDispatcher->dispatch(new AfterFileMetaDataCreatedEvent($fileUid, (int)$record['uid'], $record))->getRecord();
164 }
165
166 /**
167 * Updates the metadata record in the database
168 *
169 * @param int $fileUid the file uid to update
170 * @param array $data Data to update
171 * @internal
172 */
173 public function update($fileUid, array $data)
174 {
175 $updateRow = array_intersect_key($data, $this->getTableFields());
176 if (array_key_exists('uid', $updateRow)) {
177 unset($updateRow['uid']);
178 }
179 $row = $this->findByFileUid($fileUid);
180 if (!empty($updateRow)) {
181 $updateRow['tstamp'] = time();
182 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->tableName);
183 $types = [];
184 if ($connection->getDatabasePlatform() instanceof SQLServerPlatform) {
185 // mssql needs to set proper PARAM_LOB and others to update fields
186 $tableDetails = $connection->getSchemaManager()->listTableDetails($this->tableName);
187 foreach ($updateRow as $columnName => $columnValue) {
188 $types[$columnName] = $tableDetails->getColumn($columnName)->getType()->getBindingType();
189 }
190 }
191 $connection->update(
192 $this->tableName,
193 $updateRow,
194 [
195 'uid' => (int)$row['uid']
196 ],
197 $types
198 );
199
200 $this->eventDispatcher->dispatch(new AfterFileMetaDataUpdatedEvent($fileUid, (int)$row['uid'], array_merge($row, $updateRow)));
201 }
202 }
203
204 /**
205 * Remove all metadata records for a certain file from the database
206 *
207 * @param int $fileUid
208 */
209 public function removeByFileUid($fileUid)
210 {
211 GeneralUtility::makeInstance(ConnectionPool::class)
212 ->getConnectionForTable($this->tableName)
213 ->delete(
214 $this->tableName,
215 [
216 'file' => (int)$fileUid
217 ]
218 );
219
220 $this->eventDispatcher->dispatch(new AfterFileMetaDataDeletedEvent((int)$fileUid));
221 }
222
223 /**
224 * Gets the fields that are available in the table
225 *
226 * @return array
227 */
228 protected function getTableFields(): array
229 {
230 if (empty($this->tableFields)) {
231 $this->tableFields = GeneralUtility::makeInstance(ConnectionPool::class)
232 ->getConnectionForTable($this->tableName)
233 ->getSchemaManager()
234 ->listTableColumns($this->tableName);
235 }
236
237 return $this->tableFields;
238 }
239
240 /**
241 * @return MetaDataRepository
242 */
243 public static function getInstance()
244 {
245 return GeneralUtility::makeInstance(self::class);
246 }
247 }