[TASK] Deprecate methods in PageRepository
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Resource / FileCollector.php
1 <?php
2 namespace TYPO3\CMS\Frontend\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\LinkHandling\LinkService;
20 use TYPO3\CMS\Core\Resource\Exception;
21 use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
22 use TYPO3\CMS\Core\Resource\FileCollectionRepository;
23 use TYPO3\CMS\Core\Resource\FileInterface;
24 use TYPO3\CMS\Core\Resource\FileRepository;
25 use TYPO3\CMS\Core\Resource\Folder;
26 use TYPO3\CMS\Core\Resource\ResourceFactory;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28
29 /**
30 * Object to collect files from various sources during runtime
31 * Sources can be file references, file collections or folders
32 *
33 * Use in FILES Content Object or for a Fluid Data Processor
34 *
35 * Is not persisted, use only in FE.
36 */
37 class FileCollector implements \Countable, LoggerAwareInterface
38 {
39 use LoggerAwareTrait;
40
41 /**
42 * The files
43 *
44 * @var array
45 */
46 protected $files = [];
47
48 /**
49 * The file repository
50 *
51 * @var \TYPO3\CMS\Core\Resource\FileRepository
52 */
53 protected $fileRepository;
54
55 /**
56 * The file collection repository
57 *
58 * @var \TYPO3\CMS\Core\Resource\FileCollectionRepository
59 */
60 protected $fileCollectionRepository;
61
62 /**
63 * The resource factory
64 *
65 * @var \TYPO3\CMS\Core\Resource\ResourceFactory
66 */
67 protected $resourceFactory;
68
69 /**
70 * Add files
71 *
72 * @param array $fileUids
73 */
74 public function addFiles(array $fileUids = [])
75 {
76 if (!empty($fileUids)) {
77 foreach ($fileUids as $fileUid) {
78 try {
79 $this->addFileObject($this->getResourceFactory()->getFileObject($fileUid));
80 } catch (Exception $e) {
81 $this->logger->warning(
82 'The file with uid "' . $fileUid
83 . '" could not be found and won\'t be included in frontend output',
84 ['exception' => $e]
85 );
86 }
87 }
88 }
89 }
90
91 /**
92 * Add files to the collection from a relation
93 *
94 * @param string $relationTable The table of the relation (e.g. tt_content or pages)
95 * @param string $relationField The field which holds the files (e.g. media or images)
96 * @param array $referenceRecord the record which is referencing the files
97 */
98 public function addFilesFromRelation($relationTable, $relationField, array $referenceRecord)
99 {
100 if (is_object($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE']->sys_page)) {
101 $fileReferences = $this->getFileReferences($relationTable, $relationField, $referenceRecord);
102 } else {
103 $fileReferences = $this->getFileRepository()->findByRelation($relationTable, $relationField, $referenceRecord['uid']);
104 }
105
106 if (!empty($fileReferences)) {
107 $this->addFileObjects($fileReferences);
108 }
109 }
110
111 /**
112 * Add files from UIDs of a reference
113 *
114 * @param array $fileReferenceUids
115 */
116 public function addFileReferences(array $fileReferenceUids = [])
117 {
118 foreach ($fileReferenceUids as $fileReferenceUid) {
119 $fileObject = $this->getFileRepository()->findFileReferenceByUid($fileReferenceUid);
120 $this->addFileObject($fileObject);
121 }
122 }
123
124 /**
125 * Add files to the collection from multiple file collections
126 *
127 * @param array $fileCollectionUids The file collections uids
128 */
129 public function addFilesFromFileCollections(array $fileCollectionUids = [])
130 {
131 foreach ($fileCollectionUids as $fileCollectionUid) {
132 $this->addFilesFromFileCollection($fileCollectionUid);
133 }
134 }
135
136 /**
137 * Add files to the collection from one single file collection
138 *
139 * @param int $fileCollectionUid The file collections uid
140 */
141 public function addFilesFromFileCollection($fileCollectionUid = null)
142 {
143 if (!empty($fileCollectionUid)) {
144 try {
145 $fileCollection = $this->getFileCollectionRepository()->findByUid($fileCollectionUid);
146
147 if ($fileCollection instanceof \TYPO3\CMS\Core\Resource\Collection\AbstractFileCollection) {
148 $fileCollection->loadContents();
149 $files = $fileCollection->getItems();
150
151 $this->addFileObjects($files);
152 }
153 } catch (Exception $e) {
154 $this->logger->warning(
155 'The file-collection with uid "' . $fileCollectionUid
156 . '" could not be found or contents could not be loaded and won\'t be included in frontend output.',
157 ['exception' => $e]
158 );
159 }
160 }
161 }
162
163 /**
164 * Add files to the collection from multiple folders
165 *
166 * @param array $folderIdentifiers The folder identifiers
167 * @param bool $recursive Add files recursive from given folders
168 */
169 public function addFilesFromFolders(array $folderIdentifiers = [], $recursive = false)
170 {
171 foreach ($folderIdentifiers as $folderIdentifier) {
172 $this->addFilesFromFolder($folderIdentifier, $recursive);
173 }
174 }
175
176 /**
177 * Add files to the collection from one single folder
178 *
179 * @param string $folderIdentifier The folder identifier
180 * @param bool $recursive Add files recursive from given folders
181 */
182 public function addFilesFromFolder($folderIdentifier, $recursive = false)
183 {
184 if ($folderIdentifier) {
185 try {
186 if (strpos($folderIdentifier, 't3://folder') === 0) {
187 // a t3://folder link to a folder in FAL
188 $linkService = GeneralUtility::makeInstance(LinkService::class);
189 $data = $linkService->resolveByStringRepresentation($folderIdentifier);
190 $folder = $data['folder'];
191 } else {
192 $folder = $this->getResourceFactory()->getFolderObjectFromCombinedIdentifier($folderIdentifier);
193 }
194 if ($folder instanceof Folder) {
195 $files = $folder->getFiles(0, 0, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive);
196 $this->addFileObjects(array_values($files));
197 }
198 } catch (Exception $e) {
199 $this->logger->warning(
200 'The folder with identifier "' . $folderIdentifier
201 . '" could not be found and won\'t be included in frontend output',
202 ['exception' => $e]
203 );
204 }
205 }
206 }
207
208 /**
209 * Sort the file objects based on a property
210 *
211 * @param string $sortingProperty The sorting property
212 * @param string $sortingOrder can be ascending or descending or "random"
213 */
214 public function sort($sortingProperty = '', $sortingOrder = 'ascending')
215 {
216 if ($sortingProperty !== '' && count($this->files) > 1) {
217 @usort(
218 $this->files,
219 function (
220 FileInterface $a,
221 FileInterface $b
222 ) use ($sortingProperty) {
223 if ($a->hasProperty($sortingProperty) && $b->hasProperty($sortingProperty)) {
224 return strnatcasecmp($a->getProperty($sortingProperty), $b->getProperty($sortingProperty));
225 }
226 return 0;
227 }
228 );
229
230 switch (strtolower($sortingOrder)) {
231 case 'descending':
232 case 'desc':
233 $this->files = array_reverse($this->files);
234 break;
235 case 'random':
236 case 'rand':
237 shuffle($this->files);
238 break;
239 }
240 }
241 }
242
243 /**
244 * Add a file object to the collection
245 *
246 * @param FileInterface $file The file object
247 */
248 public function addFileObject(FileInterface $file)
249 {
250 $this->files[] = $file;
251 }
252
253 /**
254 * Add multiple file objects to the collection
255 *
256 * @param FileInterface[] $files The file objects
257 */
258 public function addFileObjects($files)
259 {
260 $this->files = array_merge($this->files, $files);
261 }
262
263 /**
264 * Final getter method to fetch the accumulated data
265 *
266 * @return array
267 */
268 public function getFiles()
269 {
270 return $this->files;
271 }
272
273 /**
274 * @return int
275 */
276 public function count()
277 {
278 return count($this->files);
279 }
280
281 /**
282 * Gets file references for a given record field.
283 *
284 * @param string $tableName Name of the table
285 * @param string $fieldName Name of the field
286 * @param array $element The parent element referencing to files
287 * @return array
288 */
289 protected function getFileReferences($tableName, $fieldName, array $element): array
290 {
291 /** @var $fileRepository FileRepository */
292 $fileRepository = GeneralUtility::makeInstance(FileRepository::class);
293 $currentId = !empty($element['uid']) ? $element['uid'] : 0;
294
295 // Fetch the references of the default element
296 try {
297 $references = $fileRepository->findByRelation($tableName, $fieldName, $currentId);
298 } catch (FileDoesNotExistException $e) {
299 /**
300 * We just catch the exception here
301 * Reasoning: There is nothing an editor or even admin could do
302 */
303 return [];
304 } catch (\InvalidArgumentException $e) {
305 /**
306 * The storage does not exist anymore
307 * Log the exception message for admins as they maybe can restore the storage
308 */
309 $logMessage = $e->getMessage() . ' (table: "' . $tableName . '", fieldName: "' . $fieldName . '", currentId: ' . $currentId . ')';
310 $this->logger->error($logMessage, ['exception' => $e]);
311 return [];
312 }
313
314 $localizedId = null;
315 if (isset($element['_LOCALIZED_UID'])) {
316 $localizedId = $element['_LOCALIZED_UID'];
317 } elseif (isset($element['_PAGES_OVERLAY_UID'])) {
318 $localizedId = $element['_PAGES_OVERLAY_UID'];
319 }
320
321 $isTableLocalizable = (
322 !empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
323 && !empty($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
324 );
325 if ($isTableLocalizable && $localizedId !== null) {
326 $localizedReferences = $fileRepository->findByRelation($tableName, $fieldName, $localizedId);
327 $references = $localizedReferences;
328 }
329
330 return $references;
331 }
332
333 /**
334 * @return ResourceFactory
335 */
336 protected function getResourceFactory()
337 {
338 if ($this->resourceFactory === null) {
339 $this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
340 }
341 return $this->resourceFactory;
342 }
343
344 /**
345 * @return FileCollectionRepository
346 */
347 protected function getFileCollectionRepository()
348 {
349 if ($this->fileCollectionRepository === null) {
350 $this->fileCollectionRepository = GeneralUtility::makeInstance(FileCollectionRepository::class);
351 }
352 return $this->fileCollectionRepository;
353 }
354
355 /**
356 * @return FileRepository
357 */
358 protected function getFileRepository()
359 {
360 if ($this->fileRepository === null) {
361 $this->fileRepository = GeneralUtility::makeInstance(FileRepository::class);
362 }
363 return $this->fileRepository;
364 }
365 }