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