56c0c34179d22545c8ba1591f1b08effc95b3a0f
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / ContentObject / FilesContentObject.php
1 <?php
2 namespace TYPO3\CMS\Frontend\ContentObject;
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\Resource\Collection\AbstractFileCollection;
18 use TYPO3\CMS\Core\Resource\Exception;
19 use TYPO3\CMS\Core\Resource\FileCollectionRepository;
20 use TYPO3\CMS\Core\Resource\FileInterface;
21 use TYPO3\CMS\Core\Resource\FileRepository;
22 use TYPO3\CMS\Core\Resource\Folder;
23 use TYPO3\CMS\Core\Resource\ResourceFactory;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\CMS\Core\Utility\MathUtility;
26 use TYPO3\CMS\Core\Log\LogManager;
27
28 /**
29 * Contains FILES content object
30 *
31 * @author Ingmar Schlecht <ingmar@typo3.org>
32 */
33 class FilesContentObject extends AbstractContentObject {
34
35 /**
36 * @var FileCollectionRepository|NULL
37 */
38 protected $collectionRepository = NULL;
39
40 /**
41 * @var ResourceFactory|NULL
42 */
43 protected $fileFactory = NULL;
44
45 /**
46 * @var FileRepository|NULL
47 */
48 protected $fileRepository = NULL;
49
50 /**
51 * Rendering the cObject FILES
52 *
53 * @param array $conf Array of TypoScript properties
54 * @return string Output
55 */
56 public function render($conf = array()) {
57 if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
58 return '';
59 }
60
61 $fileObjects = array();
62 // Getting the files
63 if ($conf['references'] || $conf['references.']) {
64 /*
65 The TypoScript could look like this:# all items related to the page.media field:
66 references {
67 table = pages
68 uid.data = page:uid
69 fieldName = media
70 }# or: sys_file_references with uid 27:
71 references = 27
72 */
73 $referencesUid = $this->cObj->stdWrapValue('references', $conf);
74 $referencesUidArray = GeneralUtility::intExplode(',', $referencesUid, TRUE);
75 foreach ($referencesUidArray as $referenceUid) {
76 try {
77 $this->addToArray(
78 $this->getFileFactory()->getFileReferenceObject($referenceUid),
79 $fileObjects
80 );
81 } catch (Exception $e) {
82 /** @var \TYPO3\CMS\Core\Log\Logger $logger */
83 $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
84 $logger->warning('The file-reference with uid "' . $referenceUid . '" could not be found and won\'t be included in frontend output');
85 }
86 }
87
88 $this->handleFileReferences($conf, (array)$this->cObj->data, $fileObjects);
89 }
90 if ($conf['files'] || $conf['files.']) {
91 /*
92 The TypoScript could look like this:
93 # with sys_file UIDs:
94 files = 12,14,15# using stdWrap:
95 files.field = some_field
96 */
97 $fileUids = GeneralUtility::intExplode(',', $this->cObj->stdWrapValue('files', $conf), TRUE);
98 foreach ($fileUids as $fileUid) {
99 try {
100 $this->addToArray($this->getFileFactory()->getFileObject($fileUid), $fileObjects);
101 } catch (Exception $e) {
102 /** @var \TYPO3\CMS\Core\Log\Logger $logger */
103 $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
104 $logger->warning('The file with uid "' . $fileUid . '" could not be found and won\'t be included in frontend output');
105 }
106 }
107 }
108 if ($conf['collections'] || $conf['collections.']) {
109 $collectionUids = GeneralUtility::intExplode(',', $this->cObj->stdWrapValue('collections', $conf), TRUE);
110 foreach ($collectionUids as $collectionUid) {
111 try {
112 $fileCollection = $this->getCollectionRepository()->findByUid($collectionUid);
113 if ($fileCollection instanceof AbstractFileCollection) {
114 $fileCollection->loadContents();
115 $this->addToArray($fileCollection->getItems(), $fileObjects);
116 }
117 } catch (Exception $e) {
118 /** @var \TYPO3\CMS\Core\Log\Logger $logger */
119 $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
120 $logger->warning('The file-collection with uid "' . $collectionUid . '" could not be found or contents could not be loaded and won\'t be included in frontend output');
121 }
122 }
123 }
124 if ($conf['folders'] || $conf['folders.']) {
125 $folderIdentifiers = GeneralUtility::trimExplode(',', $this->cObj->stdWrapValue('folders', $conf));
126 foreach ($folderIdentifiers as $folderIdentifier) {
127 if ($folderIdentifier) {
128 try {
129 $folder = $this->getFileFactory()->getFolderObjectFromCombinedIdentifier($folderIdentifier);
130 if ($folder instanceof Folder) {
131 $this->addToArray(array_values($folder->getFiles()), $fileObjects);
132 }
133 } catch (Exception $e) {
134 /** @var \TYPO3\CMS\Core\Log\Logger $logger */
135 $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
136 $logger->warning('The folder with identifier "' . $folderIdentifier . '" could not be found and won\'t be included in frontend output');
137 }
138 }
139 }
140 }
141 // Rendering the files
142 $content = '';
143 // optionSplit applied to conf to allow differnt settings per file
144 $splitConf = $GLOBALS['TSFE']->tmpl->splitConfArray($conf, count($fileObjects));
145
146 // Enable sorting for multiple fileObjects
147 $sortingProperty = '';
148 if ($conf['sorting'] || $conf['sorting.']) {
149 $sortingProperty = $this->cObj->stdWrapValue('sorting', $conf);
150 }
151 if ($sortingProperty !== '' && count($fileObjects) > 1) {
152 @usort($fileObjects, function(FileInterface $a, FileInterface $b) use($sortingProperty) {
153 if ($a->hasProperty($sortingProperty) && $b->hasProperty($sortingProperty)) {
154 return strnatcasecmp($a->getProperty($sortingProperty), $b->getProperty($sortingProperty));
155 } else {
156 return 0;
157 }
158 });
159 $sortingDirection = isset($conf['sorting.']['direction']) ? $conf['sorting.']['direction'] : '';
160 if (isset($conf['sorting.']['direction.'])) {
161 $sortingDirection = $this->cObj->stdWrap($sortingDirection, $conf['sorting.']['direction.']);
162 }
163 if (strtolower($sortingDirection) === 'desc') {
164 $fileObjects = array_reverse($fileObjects);
165 }
166 }
167
168 $availableFileObjectCount = count($fileObjects);
169
170 $start = 0;
171 if (!empty($conf['begin'])) {
172 $start = (int)$conf['begin'];
173 }
174 if (!empty($conf['begin.'])) {
175 $start = (int)$this->cObj->stdWrap($start, $conf['begin.']);
176 }
177 $start = MathUtility::forceIntegerInRange($start, 0, $availableFileObjectCount);
178
179 $limit = $availableFileObjectCount;
180 if (!empty($conf['maxItems'])) {
181 $limit = (int)$conf['maxItems'];
182 }
183 if (!empty($conf['maxItems.'])) {
184 $limit = (int)$this->cObj->stdWrap($limit, $conf['maxItems.']);
185 }
186
187 $end = MathUtility::forceIntegerInRange($start + $limit, $start, $availableFileObjectCount);
188
189 $GLOBALS['TSFE']->register['FILES_COUNT'] = min($limit, $availableFileObjectCount);
190 $fileObjectCounter = 0;
191 $keys = array_keys($fileObjects);
192 for ($i = $start; $i < $end; $i++) {
193 $key = $keys[$i];
194 $fileObject = $fileObjects[$key];
195
196 $GLOBALS['TSFE']->register['FILE_NUM_CURRENT'] = $fileObjectCounter;
197 $this->cObj->setCurrentFile($fileObject);
198 $content .= $this->cObj->cObjGetSingle($splitConf[$key]['renderObj'], $splitConf[$key]['renderObj.']);
199 $fileObjectCounter++;
200 }
201 $content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
202 return $content;
203 }
204
205 /**
206 * Sets the file factory.
207 *
208 * @param ResourceFactory $fileFactory
209 * @return void
210 */
211 public function setFileFactory($fileFactory) {
212 $this->fileFactory = $fileFactory;
213 }
214
215 /**
216 * Returns the file factory.
217 *
218 * @return ResourceFactory
219 */
220 public function getFileFactory() {
221 if ($this->fileFactory === NULL) {
222 $this->fileFactory = GeneralUtility::makeInstance(ResourceFactory::class);
223 }
224
225 return $this->fileFactory;
226 }
227
228 /**
229 * Sets the file repository.
230 *
231 * @param FileRepository $fileRepository
232 * @return void
233 */
234 public function setFileRepository($fileRepository) {
235 $this->fileRepository = $fileRepository;
236 }
237
238 /**
239 * Returns the file repository.
240 *
241 * @return FileRepository
242 */
243 public function getFileRepository() {
244 if ($this->fileRepository === NULL) {
245 $this->fileRepository = GeneralUtility::makeInstance(FileRepository::class);
246 }
247
248 return $this->fileRepository;
249 }
250
251 /**
252 * Sets the collection repository.
253 *
254 * @param FileCollectionRepository $collectionRepository
255 * @return void
256 */
257 public function setCollectionRepository($collectionRepository) {
258 $this->collectionRepository = $collectionRepository;
259 }
260
261 /**
262 * Returns the collection repository.
263 *
264 * @return FileCollectionRepository
265 */
266 public function getCollectionRepository() {
267 if ($this->collectionRepository === NULL) {
268 $this->collectionRepository = GeneralUtility::makeInstance(FileCollectionRepository::class);
269 }
270
271 return $this->collectionRepository;
272 }
273
274 /**
275 * Handles and resolves file references.
276 *
277 * @param array $configuration TypoScript configuration
278 * @param array $element The parent element referencing to files
279 * @param array $fileObjects Collection of file objects
280 * @return void
281 */
282 protected function handleFileReferences(array $configuration, array $element, array &$fileObjects) {
283 if (empty($configuration['references.'])) {
284 return;
285 }
286
287 // It's important that this always stays "fieldName" and not be renamed to "field" as it would otherwise collide with the stdWrap key of that name
288 $referencesFieldName = $this->cObj->stdWrapValue('fieldName', $configuration['references.']);
289
290 // If no reference fieldName is set, there's nothing to do
291 if (empty($referencesFieldName)) {
292 return;
293 }
294
295 $currentId = !empty($element['uid']) ? $element['uid'] : 0;
296 $tableName = $this->cObj->getCurrentTable();
297
298 // Fetch the references of the default element
299 $referencesForeignTable = $this->cObj->stdWrapValue('table', $configuration['references.'], $tableName);
300 $referencesForeignUid = $this->cObj->stdWrapValue('uid', $configuration['references.'], $currentId);
301
302 $pageRepository = $this->getPageRepository();
303 // Fetch element if definition has been modified via TypoScript
304 if ($referencesForeignTable !== $tableName || $referencesForeignUid !== $currentId) {
305 $element = $pageRepository->getRawRecord(
306 $referencesForeignTable,
307 $referencesForeignUid,
308 '*',
309 FALSE
310 );
311
312 $pageRepository->versionOL($referencesForeignTable, $element, TRUE);
313 if ($referencesForeignTable === 'pages') {
314 $element = $pageRepository->getPageOverlay($element);
315 } else {
316 $element = $pageRepository->getRecordOverlay(
317 $referencesForeignTable,
318 $element,
319 $GLOBALS['TSFE']->sys_language_content,
320 $GLOBALS['TSFE']->sys_language_contentOL
321 );
322 }
323 }
324
325 $references = $pageRepository->getFileReferences(
326 $referencesForeignTable,
327 $referencesFieldName,
328 $element
329 );
330
331 $this->addToArray($references, $fileObjects);
332 }
333
334 /**
335 * Adds $newItems to $theArray, which is passed by reference. Array must only consist of numerical keys.
336 *
337 * @param mixed $newItems Array with new items or single object that's added.
338 * @param array $theArray The array the new items should be added to. Must only contain numeric keys (for array_merge() to add items instead of replacing).
339 */
340 protected function addToArray($newItems, array &$theArray) {
341 if (is_array($newItems)) {
342 $theArray = array_merge($theArray, $newItems);
343 } elseif (is_object($newItems)) {
344 $theArray[] = $newItems;
345 }
346 }
347
348 /**
349 * @return \TYPO3\CMS\Frontend\Page\PageRepository
350 */
351 protected function getPageRepository() {
352 return $GLOBALS['TSFE']->sys_page;
353 }
354
355 }