[BUGFIX] FormEngine: Exception on inline/section in flex container
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / GroupElement.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\Element;
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\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
19 use TYPO3\CMS\Core\Imaging\Icon;
20 use TYPO3\CMS\Core\Messaging\FlashMessage;
21 use TYPO3\CMS\Core\Messaging\FlashMessageService;
22 use TYPO3\CMS\Core\Resource\ProcessedFile;
23 use TYPO3\CMS\Core\Resource\ResourceFactory;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\CMS\Core\Utility\MathUtility;
26
27 /**
28 * Generation of TCEform elements of the type "group"
29 */
30 class GroupElement extends AbstractFormElement
31 {
32 /**
33 * This will render a selector box into which elements from either
34 * the file system or database can be inserted. Relations.
35 *
36 * @return array As defined in initializeResultArray() of AbstractNode
37 */
38 public function render()
39 {
40 $table = $this->data['tableName'];
41 $fieldName = $this->data['fieldName'];
42 $row = $this->data['databaseRow'];
43 $parameterArray = $this->data['parameterArray'];
44 $config = $parameterArray['fieldConf']['config'];
45 $show_thumbs = $config['show_thumbs'];
46 $resultArray = $this->initializeResultArray();
47
48 $size = isset($config['size']) ? (int)$config['size'] : $this->minimumInputWidth;
49 $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 0);
50 if (!$maxitems) {
51 $maxitems = 100000;
52 }
53 $minitems = MathUtility::forceIntegerInRange($config['minitems'], 0);
54 $thumbnails = array();
55 $allowed = GeneralUtility::trimExplode(',', $config['allowed'], true);
56 $disallowed = GeneralUtility::trimExplode(',', $config['disallowed'], true);
57 $disabled = $config['readOnly'];
58 $info = array();
59 $parameterArray['itemFormElID_file'] = $parameterArray['itemFormElID'] . '_files';
60
61 // whether the list and delete controls should be disabled
62 $noList = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'list');
63 $noDelete = isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'delete');
64
65 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist.
66 $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
67
68 // Register properties in required elements / validation
69 $attributes['data-formengine-validation-rules'] = htmlspecialchars(
70 $this->getValidationDataAsJsonString(
71 array(
72 'minitems' => $minitems,
73 'maxitems' => $maxitems
74 )
75 )
76 );
77
78 // If maxitems==1 then automatically replace the current item (in list and file selector)
79 if ($maxitems === 1) {
80 $resultArray['additionalJavaScriptPost'][] =
81 'TBE_EDITOR.clearBeforeSettingFormValueFromBrowseWin[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . '] = {
82 itemFormElID_file: ' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElID_file']) . '
83 }';
84 $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'])
85 . ', \'Remove\'); ' . $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
86 } elseif ($noList) {
87 // If the list controls have been removed and the maximum number is reached, remove the first entry to avoid "write once" field
88 $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'setFormValueManipulate(' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'])
89 . ', \'RemoveFirstIfFull\', ' . GeneralUtility::quoteJSvalue($maxitems) . '); ' . $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
90 }
91
92 $html = '<input type="hidden" class="t3js-group-hidden-field" data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="' . ($config['multiple'] ? 1 : 0) . '"' . $disabled . ' />';
93
94 // Define parameters for all types below
95 $commonParameters = array(
96 'size' => $size,
97 'dontShowMoveIcons' => isset($config['hideMoveIcons']) || $maxitems <= 1,
98 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0),
99 'maxitems' => $maxitems,
100 'style' => isset($config['selectedListStyle'])
101 ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"'
102 : '',
103 'readOnly' => $disabled,
104 'noBrowser' => $noList || isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'browser'),
105 'noList' => $noList,
106 );
107
108 // Acting according to either "file" or "db" type:
109 switch ((string)$config['internal_type']) {
110 case 'file_reference':
111 $config['uploadfolder'] = '';
112 // Fall through
113 case 'file':
114 // Creating string showing allowed types:
115 if (empty($allowed)) {
116 $allowed = array('*');
117 }
118 // Making the array of file items:
119 $itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], true);
120 $fileFactory = ResourceFactory::getInstance();
121 // Correct the filename for the FAL items
122 foreach ($itemArray as &$fileItem) {
123 list($fileUid, $fileLabel) = explode('|', $fileItem);
124 if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
125 $fileObject = $fileFactory->getFileObject($fileUid);
126 $fileLabel = $fileObject->getName();
127 }
128 $fileItem = $fileUid . '|' . $fileLabel;
129 }
130 // Showing thumbnails:
131 if ($show_thumbs) {
132 foreach ($itemArray as $imgRead) {
133 $imgP = explode('|', $imgRead);
134 $imgPath = rawurldecode($imgP[0]);
135 // FAL icon production
136 if (MathUtility::canBeInterpretedAsInteger($imgP[0])) {
137 $fileObject = $fileFactory->getFileObject($imgP[0]);
138 if ($fileObject->isMissing()) {
139 $thumbnails[] = array(
140 'message' => '<span class="label label-danger">'
141 . htmlspecialchars(static::getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_missing'))
142 . '</span>&nbsp;' . htmlspecialchars($fileObject->getName()) . '<br />'
143 );
144 } elseif (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) {
145 $thumbnails[] = array(
146 'name' => htmlspecialchars($fileObject->getName()),
147 'image' => $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, array())->getPublicUrl(true)
148 );
149 } else {
150 $name = htmlspecialchars($fileObject->getName());
151 // Icon
152 $thumbnails[] = array(
153 'name' => $name,
154 'image' => '<span title="' . $name . '">' . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL) . '</span>'
155 );
156 }
157 } else {
158 $rowCopy = array();
159 $rowCopy[$fieldName] = $imgPath;
160 try {
161 $thumbnails[] = array(
162 'name' => $imgPath,
163 'image' => BackendUtility::thumbCode(
164 $rowCopy,
165 $table,
166 $fieldName,
167 '',
168 '',
169 $config['uploadfolder'],
170 0,
171 ' align="middle"'
172 )
173 );
174 } catch (\Exception $exception) {
175 /** @var $flashMessage FlashMessage */
176 $message = $exception->getMessage();
177 $flashMessage = GeneralUtility::makeInstance(
178 FlashMessage::class,
179 $message, '', FlashMessage::ERROR, true
180 );
181 /** @var $flashMessageService FlashMessageService */
182 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
183 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
184 $defaultFlashMessageQueue->enqueue($flashMessage);
185 $logMessage = $message . ' (' . $table . ':' . $row['uid'] . ')';
186 GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_WARNING);
187 }
188 }
189 }
190 }
191 // Creating the element:
192 $params = array_merge($commonParameters, array(
193 'allowed' => $allowed,
194 'disallowed' => $disallowed,
195 'thumbnails' => $thumbnails,
196 'noDelete' => $noDelete
197 ));
198 $html .= $this->dbFileIcons(
199 $parameterArray['itemFormElName'],
200 'file',
201 implode(',', $allowed),
202 $itemArray,
203 '',
204 $params,
205 $parameterArray['onFocus'],
206 '',
207 '',
208 '',
209 $config);
210 if (!$disabled && !(isset($config['disable_controls']) && GeneralUtility::inList($config['disable_controls'], 'upload'))) {
211 // Adding the upload field:
212 $isDirectFileUploadEnabled = (bool)$this->getBackendUserAuthentication()->uc['edit_docModuleUpload'];
213 if ($isDirectFileUploadEnabled && $config['uploadfolder']) {
214 // Insert the multiple attribute to enable HTML5 multiple file upload
215 $multipleAttribute = '';
216 $multipleFilenameSuffix = '';
217 if (isset($config['maxitems']) && $config['maxitems'] > 1) {
218 $multipleAttribute = ' multiple="multiple"';
219 $multipleFilenameSuffix = '[]';
220 }
221 $html .= '
222 <div id="' . $parameterArray['itemFormElID_file'] . '">
223 <input type="file"' . $multipleAttribute . '
224 name="data_files' . $this->data['elementBaseName'] . $multipleFilenameSuffix . '"
225 size="35" onchange="' . implode('', $parameterArray['fieldChangeFunc']) . '"
226 />
227 </div>';
228 }
229 }
230 break;
231 case 'folder':
232 // If the element is of the internal type "folder":
233 // Array of folder items:
234 $itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], true);
235 // Creating the element:
236 $params = $commonParameters;
237 $html .= $this->dbFileIcons(
238 $parameterArray['itemFormElName'],
239 'folder',
240 '',
241 $itemArray,
242 '',
243 $params,
244 $parameterArray['onFocus']
245 );
246 break;
247 case 'db':
248 // If the element is of the internal type "db":
249 // Creating string showing allowed types:
250 $languageService = $this->getLanguageService();
251
252 $allowedTables = array();
253 if ($allowed[0] === '*') {
254 $allowedTables = array(
255 'name' => htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.allTables'))
256 );
257 } elseif ($allowed) {
258 foreach ($allowed as $allowedTable) {
259 $allowedTables[] = array(
260 // @todo: access to globals!
261 'name' => htmlspecialchars($languageService->sL($GLOBALS['TCA'][$allowedTable]['ctrl']['title'])),
262 'icon' => $this->iconFactory->getIconForRecord($allowedTable, array(), Icon::SIZE_SMALL)->render(),
263 'onClick' => 'setFormValueOpenBrowser(\'db\', ' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'] . '|||' . $allowedTable) . '); return false;'
264 );
265 }
266 }
267 $perms_clause = $this->getBackendUserAuthentication()->getPagePermsClause(1);
268 $itemArray = array();
269
270 // Thumbnails:
271 // @todo: this is data processing - must be extracted
272 $temp_itemArray = GeneralUtility::trimExplode(',', $parameterArray['itemFormElValue'], true);
273 foreach ($temp_itemArray as $dbRead) {
274 $recordParts = explode('|', $dbRead);
275 list($this_table, $this_uid) = BackendUtility::splitTable_Uid($recordParts[0]);
276 $itemArray[] = array('table' => $this_table, 'id' => $this_uid);
277 if (!$disabled && $show_thumbs) {
278 $rr = BackendUtility::getRecordWSOL($this_table, $this_uid);
279 $thumbnails[] = array(
280 'name' => BackendUtility::getRecordTitle($this_table, $rr, true),
281 'image' => $this->iconFactory->getIconForRecord($this_table, $rr, Icon::SIZE_SMALL)->render(),
282 'path' => BackendUtility::getRecordPath($rr['pid'], $perms_clause, 15),
283 'uid' => $rr['uid'],
284 'table' => $this_table
285 );
286 }
287 }
288 // Creating the element:
289 $params = array_merge($commonParameters, array(
290 'info' => $info,
291 'allowedTables' => $allowedTables,
292 'thumbnails' => $thumbnails,
293 ));
294 $html .= $this->dbFileIcons(
295 $parameterArray['itemFormElName'],
296 'db',
297 implode(',', $allowed),
298 $itemArray,
299 '',
300 $params,
301 $parameterArray['onFocus'],
302 $table,
303 $fieldName,
304 $row['uid'],
305 $config
306 );
307 break;
308 }
309 // Wizards:
310 if (!$disabled) {
311 $html = $this->renderWizards(
312 array($html),
313 $config['wizards'],
314 $table,
315 $row,
316 $fieldName,
317 $parameterArray,
318 $parameterArray['itemFormElName'],
319 $specConf
320 );
321 }
322 $resultArray['html'] = $html;
323 return $resultArray;
324 }
325
326 /**
327 * @return BackendUserAuthentication
328 */
329 protected function getBackendUserAuthentication()
330 {
331 return $GLOBALS['BE_USER'];
332 }
333 }