7ac2136eb176be7f8d7e25bd264aea6427b65f62
[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\TypoScript\TypoScriptService;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Utility\MathUtility;
20 use TYPO3\CMS\Frontend\Resource\FileCollector;
21
22 /**
23 * Contains FILES content object
24 */
25 class FilesContentObject extends AbstractContentObject
26 {
27 /**
28 * Rendering the cObject FILES
29 *
30 * @param array $conf Array of TypoScript properties
31 * @return string Output
32 */
33 public function render($conf = [])
34 {
35 if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
36 return '';
37 }
38 // Store the original "currentFile" within a variable so it can be re-applied later-on
39 $originalFileInContentObject = $this->cObj->getCurrentFile();
40
41 $fileCollector = $this->findAndSortFiles($conf);
42 $fileObjects = $fileCollector->getFiles();
43 $availableFileObjectCount = count($fileObjects);
44
45 // optionSplit applied to conf to allow different settings per file
46 $splitConf = GeneralUtility::makeInstance(TypoScriptService::class)
47 ->explodeConfigurationForOptionSplit($conf, $availableFileObjectCount);
48
49 $start = 0;
50 if (!empty($conf['begin'])) {
51 $start = (int)$conf['begin'];
52 }
53 if (!empty($conf['begin.'])) {
54 $start = (int)$this->cObj->stdWrap($start, $conf['begin.']);
55 }
56 $start = MathUtility::forceIntegerInRange($start, 0, $availableFileObjectCount);
57
58 $limit = $availableFileObjectCount;
59 if (!empty($conf['maxItems'])) {
60 $limit = (int)$conf['maxItems'];
61 }
62 if (!empty($conf['maxItems.'])) {
63 $limit = (int)$this->cObj->stdWrap($limit, $conf['maxItems.']);
64 }
65
66 $end = MathUtility::forceIntegerInRange($start + $limit, $start, $availableFileObjectCount);
67
68 $GLOBALS['TSFE']->register['FILES_COUNT'] = min($limit, $availableFileObjectCount);
69 $fileObjectCounter = 0;
70 $keys = array_keys($fileObjects);
71
72 $content = '';
73 for ($i = $start; $i < $end; $i++) {
74 $key = $keys[$i];
75 $fileObject = $fileObjects[$key];
76
77 $GLOBALS['TSFE']->register['FILE_NUM_CURRENT'] = $fileObjectCounter;
78 $this->cObj->setCurrentFile($fileObject);
79 $content .= $this->cObj->cObjGetSingle($splitConf[$key]['renderObj'], $splitConf[$key]['renderObj.']);
80 $fileObjectCounter++;
81 }
82
83 // Reset current file within cObj to the original file after rendering output of FILES
84 // so e.g. stdWrap is not working on the last current file applied, thus avoiding side-effects
85 $this->cObj->setCurrentFile($originalFileInContentObject);
86
87 return $this->cObj->stdWrap($content, $conf['stdWrap.']);
88 }
89
90 /**
91 * Function to check for references, collections, folders and
92 * accumulates into one etc.
93 *
94 * @param array $conf
95 * @return FileCollector
96 */
97 protected function findAndSortFiles(array $conf)
98 {
99 $fileCollector = $this->getFileCollector();
100
101 // Getting the files
102 if ($conf['references'] || $conf['references.']) {
103 /*
104 The TypoScript could look like this:
105 # all items related to the page.media field:
106 references {
107 table = pages
108 uid.data = page:uid
109 fieldName = media
110 }
111 # or: sys_file_references with uid 27:
112 references = 27
113 */
114 $referencesUidList = $this->cObj->stdWrapValue('references', $conf);
115 $referencesUids = GeneralUtility::intExplode(',', $referencesUidList, true);
116 $fileCollector->addFileReferences($referencesUids);
117
118 if (!empty($conf['references.'])) {
119 $this->addFileReferences($conf, (array)$this->cObj->data, $fileCollector);
120 }
121 }
122
123 if ($conf['files'] || $conf['files.']) {
124 /*
125 The TypoScript could look like this:
126 # with sys_file UIDs:
127 files = 12,14,15# using stdWrap:
128 files.field = some_field
129 */
130 $fileUids = GeneralUtility::intExplode(',', $this->cObj->stdWrapValue('files', $conf), true);
131 $fileCollector->addFiles($fileUids);
132 }
133
134 if ($conf['collections'] || $conf['collections.']) {
135 $collectionUids = GeneralUtility::intExplode(',', $this->cObj->stdWrapValue('collections', $conf), true);
136 $fileCollector->addFilesFromFileCollections($collectionUids);
137 }
138
139 if ($conf['folders'] || $conf['folders.']) {
140 $folderIdentifiers = GeneralUtility::trimExplode(',', $this->cObj->stdWrapValue('folders', $conf));
141 $fileCollector->addFilesFromFolders($folderIdentifiers, !empty($conf['folders.']['recursive']));
142 }
143
144 // Enable sorting for multiple fileObjects
145 $sortingProperty = '';
146 if ($conf['sorting'] || $conf['sorting.']) {
147 $sortingProperty = $this->cObj->stdWrapValue('sorting', $conf);
148 }
149 if ($sortingProperty !== '') {
150 $sortingDirection = isset($conf['sorting.']['direction']) ? $conf['sorting.']['direction'] : '';
151 if (isset($conf['sorting.']['direction.'])) {
152 $sortingDirection = $this->cObj->stdWrap($sortingDirection, $conf['sorting.']['direction.']);
153 }
154 $fileCollector->sort($sortingProperty, $sortingDirection);
155 }
156
157 return $fileCollector;
158 }
159
160 /**
161 * Handles and resolves file references.
162 *
163 * @param array $configuration TypoScript configuration
164 * @param array $element The parent element referencing to files
165 * @param FileCollector $fileCollector
166 * @return array
167 */
168 protected function addFileReferences(array $configuration, array $element, FileCollector $fileCollector)
169 {
170
171 // 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
172 $referencesFieldName = $this->cObj->stdWrapValue('fieldName', $configuration['references.']);
173
174 // If no reference fieldName is set, there's nothing to do
175 if (empty($referencesFieldName)) {
176 return;
177 }
178
179 $currentId = !empty($element['uid']) ? $element['uid'] : 0;
180 $tableName = $this->cObj->getCurrentTable();
181
182 // Fetch the references of the default element
183 $referencesForeignTable = $this->cObj->stdWrapValue('table', $configuration['references.'], $tableName);
184 $referencesForeignUid = $this->cObj->stdWrapValue('uid', $configuration['references.'], $currentId);
185
186 $pageRepository = $this->getPageRepository();
187 // Fetch element if definition has been modified via TypoScript
188 if ($referencesForeignTable !== $tableName || $referencesForeignUid !== $currentId) {
189 $element = $pageRepository->getRawRecord($referencesForeignTable, $referencesForeignUid);
190
191 $pageRepository->versionOL($referencesForeignTable, $element, true);
192 if ($referencesForeignTable === 'pages') {
193 $element = $pageRepository->getPageOverlay($element);
194 } else {
195 $element = $pageRepository->getRecordOverlay(
196 $referencesForeignTable,
197 $element,
198 $GLOBALS['TSFE']->sys_language_content,
199 $GLOBALS['TSFE']->sys_language_contentOL
200 );
201 }
202 }
203
204 if (is_array($element)) {
205 $fileCollector->addFilesFromRelation($referencesForeignTable, $referencesFieldName, $element);
206 }
207 }
208
209 /**
210 * @return \TYPO3\CMS\Frontend\Page\PageRepository
211 */
212 protected function getPageRepository()
213 {
214 return $GLOBALS['TSFE']->sys_page;
215 }
216
217 /**
218 * @return FileCollector
219 */
220 protected function getFileCollector()
221 {
222 return GeneralUtility::makeInstance(FileCollector::class);
223 }
224 }