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