8e4afdb533dc60e4b6ebf2e4d67a345fc4508ab0
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / ProcessedFileRepository.php
1 <?php
2 namespace TYPO3\CMS\Core\Resource;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Log\LoggerAwareInterface;
18 use Psr\Log\LoggerAwareTrait;
19 use TYPO3\CMS\Core\Database\ConnectionPool;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21
22 /**
23 * Repository for accessing files
24 * it also serves as the public API for the indexing part of files in general
25 */
26 class ProcessedFileRepository extends AbstractRepository implements LoggerAwareInterface
27 {
28 use LoggerAwareTrait;
29
30 /**
31 * The main object type of this class. In some cases (fileReference) this
32 * repository can also return FileReference objects, implementing the
33 * common FileInterface.
34 *
35 * @var string
36 */
37 protected $objectType = ProcessedFile::class;
38
39 /**
40 * Main File object storage table. Note that this repository also works on
41 * the sys_file_reference table when returning FileReference objects.
42 *
43 * @var string
44 */
45 protected $table = 'sys_file_processedfile';
46
47 /**
48 * As determining the table columns is a costly operation this is done only once during runtime and cached then
49 *
50 * @var array
51 * @see cleanUnavailableColumns()
52 */
53 protected $tableColumns = [];
54
55 /**
56 * Creates this object.
57 */
58 public function __construct()
59 {
60 parent::__construct();
61 }
62
63 /**
64 * Creates a ProcessedFile object from a file object and a processing configuration
65 *
66 * @param FileInterface $originalFile
67 * @param string $taskType
68 * @param array $configuration
69 * @return ProcessedFile
70 */
71 public function createNewProcessedFileObject(FileInterface $originalFile, $taskType, array $configuration)
72 {
73 return GeneralUtility::makeInstance(
74 $this->objectType,
75 $originalFile,
76 $taskType,
77 $configuration
78 );
79 }
80
81 /**
82 * @param array $databaseRow
83 * @return ProcessedFile
84 */
85 protected function createDomainObject(array $databaseRow)
86 {
87 $originalFile = $this->factory->getFileObject((int)$databaseRow['original']);
88 $originalFile->setStorage($this->factory->getStorageObject($originalFile->getProperty('storage')));
89 $taskType = $databaseRow['task_type'];
90 $configuration = unserialize($databaseRow['configuration']);
91
92 return GeneralUtility::makeInstance(
93 $this->objectType,
94 $originalFile,
95 $taskType,
96 $configuration,
97 $databaseRow
98 );
99 }
100
101 /**
102 * @param ResourceStorage $storage
103 * @param string $identifier
104 *
105 * @return null|ProcessedFile
106 */
107 public function findByStorageAndIdentifier(ResourceStorage $storage, $identifier)
108 {
109 $processedFileObject = null;
110 if ($storage->hasFile($identifier)) {
111 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
112 $databaseRow = $queryBuilder
113 ->select('*')
114 ->from($this->table)
115 ->where(
116 $queryBuilder->expr()->eq(
117 'storage',
118 $queryBuilder->createNamedParameter($storage->getUid(), \PDO::PARAM_INT)
119 ),
120 $queryBuilder->expr()->eq(
121 'identifier',
122 $queryBuilder->createNamedParameter($identifier, \PDO::PARAM_STR)
123 )
124 )
125 ->execute()
126 ->fetch();
127
128 if ($databaseRow) {
129 $processedFileObject = $this->createDomainObject($databaseRow);
130 }
131 }
132 return $processedFileObject;
133 }
134 /**
135 * Adds a processedfile object in the database
136 *
137 * @param ProcessedFile $processedFile
138 */
139 public function add($processedFile)
140 {
141 if ($processedFile->isPersisted()) {
142 $this->update($processedFile);
143 } else {
144 $insertFields = $processedFile->toArray();
145 $insertFields['crdate'] = $insertFields['tstamp'] = time();
146 $insertFields = $this->cleanUnavailableColumns($insertFields);
147
148 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
149
150 $connection->insert(
151 $this->table,
152 $insertFields
153 );
154
155 $uid = $connection->lastInsertId($this->table);
156 $processedFile->updateProperties(['uid' => $uid]);
157 }
158 }
159
160 /**
161 * Updates an existing file object in the database
162 *
163 * @param ProcessedFile $processedFile
164 */
165 public function update($processedFile)
166 {
167 if ($processedFile->isPersisted()) {
168 $uid = (int)$processedFile->getUid();
169 $updateFields = $this->cleanUnavailableColumns($processedFile->toArray());
170 $updateFields['tstamp'] = time();
171
172 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
173 $connection->update(
174 $this->table,
175 $updateFields,
176 [
177 'uid' => (int)$uid
178 ]
179 );
180 }
181 }
182
183 /**
184 * @param \TYPO3\CMS\Core\Resource\File|\TYPO3\CMS\Core\Resource\FileInterface $file
185 * @param string $taskType The task that should be executed on the file
186 * @param array $configuration
187 *
188 * @return ProcessedFile
189 */
190 public function findOneByOriginalFileAndTaskTypeAndConfiguration(FileInterface $file, $taskType, array $configuration)
191 {
192 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
193
194 $databaseRow = $queryBuilder
195 ->select('*')
196 ->from($this->table)
197 ->where(
198 $queryBuilder->expr()->eq(
199 'original',
200 $queryBuilder->createNamedParameter($file->getUid(), \PDO::PARAM_INT)
201 ),
202 $queryBuilder->expr()->eq('task_type', $queryBuilder->createNamedParameter($taskType, \PDO::PARAM_STR)),
203 $queryBuilder->expr()->eq(
204 'configurationsha1',
205 $queryBuilder->createNamedParameter(sha1(serialize($configuration)), \PDO::PARAM_STR)
206 )
207 )
208 ->execute()
209 ->fetch();
210
211 if (is_array($databaseRow)) {
212 $processedFile = $this->createDomainObject($databaseRow);
213 } else {
214 $processedFile = $this->createNewProcessedFileObject($file, $taskType, $configuration);
215 }
216 return $processedFile;
217 }
218
219 /**
220 * @param FileInterface $file
221 * @return ProcessedFile[]
222 * @throws \InvalidArgumentException
223 */
224 public function findAllByOriginalFile(FileInterface $file)
225 {
226 if (!$file instanceof File) {
227 throw new \InvalidArgumentException('Parameter is no File object but got type "'
228 . (is_object($file) ? get_class($file) : gettype($file)) . '"', 1382006142);
229 }
230
231 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
232 $result = $queryBuilder
233 ->select('*')
234 ->from($this->table)
235 ->where(
236 $queryBuilder->expr()->eq(
237 'original',
238 $queryBuilder->createNamedParameter($file->getUid(), \PDO::PARAM_INT)
239 )
240 )
241 ->execute();
242
243 $itemList = [];
244 while ($row = $result->fetch()) {
245 $itemList[] = $this->createDomainObject($row);
246 }
247 return $itemList;
248 }
249
250 /**
251 * Removes all processed files and also deletes the associated physical files
252 *
253 * @param int|NULL $storageUid If not NULL, only the processed files of the given storage are removed
254 * @return int Number of failed deletions
255 */
256 public function removeAll($storageUid = null)
257 {
258 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
259 $result = $queryBuilder
260 ->select('*')
261 ->from($this->table)
262 ->where(
263 $queryBuilder->expr()->neq('identifier', $queryBuilder->createNamedParameter('', \PDO::PARAM_STR))
264 )
265 ->execute();
266
267 $errorCount = 0;
268
269 while ($row = $result->fetch()) {
270 if ($storageUid && (int)$storageUid !== (int)$row['storage']) {
271 continue;
272 }
273 try {
274 $file = $this->createDomainObject($row);
275 $file->getStorage()->setEvaluatePermissions(false);
276 $file->delete(true);
277 } catch (\Exception $e) {
278 $this->logger->error(
279 'Failed to delete file "' . $row['identifier'] . '" in storage uid ' . $row['storage'] . '.',
280 [
281 'exception' => $e
282 ]
283 );
284 ++$errorCount;
285 }
286 }
287
288 GeneralUtility::makeInstance(ConnectionPool::class)
289 ->getConnectionForTable($this->table)
290 ->truncate($this->table);
291
292 return $errorCount;
293 }
294
295 /**
296 * Removes all array keys which cannot be persisted
297 *
298 * @param array $data
299 *
300 * @return array
301 */
302 protected function cleanUnavailableColumns(array $data)
303 {
304 // As determining the table columns is a costly operation this is done only once during runtime and cached then
305 if (empty($this->tableColumns[$this->table])) {
306 $this->tableColumns[$this->table] = GeneralUtility::makeInstance(ConnectionPool::class)
307 ->getConnectionForTable($this->table)
308 ->getSchemaManager()
309 ->listTableColumns($this->table);
310 }
311
312 return array_intersect_key($data, $this->tableColumns[$this->table]);
313 }
314 }