[!!!][TASK] Improve flex and TCA handling in FormEngine
[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\Clipboard\Clipboard;
18 use TYPO3\CMS\Backend\Form\InlineStackProcessor;
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21 use TYPO3\CMS\Core\Imaging\Icon;
22 use TYPO3\CMS\Core\Messaging\FlashMessage;
23 use TYPO3\CMS\Core\Messaging\FlashMessageService;
24 use TYPO3\CMS\Core\Resource\ProcessedFile;
25 use TYPO3\CMS\Core\Resource\ResourceFactory;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27 use TYPO3\CMS\Core\Utility\MathUtility;
28 use TYPO3\CMS\Core\Utility\StringUtility;
29
30 /**
31 * Generation of TCEform elements of the type "group"
32 */
33 class GroupElement extends AbstractFormElement
34 {
35 /**
36 * @var Clipboard
37 */
38 protected $clipboard;
39
40 /**
41 * This will render a selector box into which elements from either
42 * the file system or database can be inserted. Relations.
43 *
44 * @return array As defined in initializeResultArray() of AbstractNode
45 * @throws \RuntimeException
46 */
47 public function render()
48 {
49 $languageService = $this->getLanguageService();
50 $backendUser = $this->getBackendUserAuthentication();
51
52 $table = $this->data['tableName'];
53 $fieldName = $this->data['fieldName'];
54 $row = $this->data['databaseRow'];
55 $parameterArray = $this->data['parameterArray'];
56 $config = $parameterArray['fieldConf']['config'];
57 $elementName = $parameterArray['itemFormElName'];
58
59 $resultArray = $this->initializeResultArray();
60
61 $selectedItems = $parameterArray['itemFormElValue'];
62 $selectedItemsCount = count($selectedItems);
63
64 $maxItems = $config['maxitems'];
65 $autoSizeMax = MathUtility::forceIntegerInRange($config['autoSizeMax'], 0);
66 $size = 5;
67 if (isset($config['size'])) {
68 $size = (int)$config['size'];
69 }
70 if ($autoSizeMax >= 1) {
71 $size = MathUtility::forceIntegerInRange($selectedItemsCount + 1, MathUtility::forceIntegerInRange($size, 1), $autoSizeMax);
72 }
73
74 $isDisabled = false;
75 if (isset($config['readOnly']) && $config['readOnly']) {
76 $isDisabled = true;
77 }
78 $showMoveIcons = true;
79 if (isset($config['hideMoveIcons']) && $config['hideMoveIcons']) {
80 $showMoveIcons = false;
81 }
82
83 $internalType = (string)$config['internal_type'];
84 $showThumbs = (bool)$config['show_thumbs'];
85 $allowed = GeneralUtility::trimExplode(',', $config['allowed'], true);
86 $disallowed = GeneralUtility::trimExplode(',', $config['disallowed'], true);
87 $uploadFieldId = $parameterArray['itemFormElID'] . '_files';
88 $itemCanBeSelectedMoreThanOnce = !empty($config['multiple']);
89 $maxTitleLength = $backendUser->uc['titleLen'];
90 $isDirectFileUploadEnabled = (bool)$backendUser->uc['edit_docModuleUpload'];
91 $clipboardElements = $config['clipboardElements'];
92
93 $disableControls = [];
94 if (isset($config['disable_controls'])) {
95 $disableControls = GeneralUtility::trimExplode(',', $config['disable_controls'], true);
96 }
97 $showListControl = true;
98 if (in_array('list', $disableControls, true)) {
99 $showListControl = false;
100 }
101 $showDeleteControl = true;
102 if (in_array('delete', $disableControls, true)) {
103 $showDeleteControl = false;
104 }
105 $showBrowseControl = true;
106 if (in_array('browser', $disableControls, true)) {
107 $showBrowseControl = false;
108 }
109 $showAllowedTables = true;
110 if (in_array('allowedTables', $disableControls, true)) {
111 $showAllowedTables = false;
112 }
113 $showUploadField = true;
114 if (in_array('upload', $disableControls, true)) {
115 $showUploadField = false;
116 }
117
118 if ($maxItems === 1) {
119 // If maxitems==1 then automatically replace the current item (in list and file selector)
120 $resultArray['additionalJavaScriptPost'][] =
121 'TBE_EDITOR.clearBeforeSettingFormValueFromBrowseWin[' . GeneralUtility::quoteJSvalue($elementName) . '] = {'
122 . 'itemFormElID_file: ' . GeneralUtility::quoteJSvalue($uploadFieldId)
123 . '}';
124 $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] =
125 'setFormValueManipulate(' . GeneralUtility::quoteJSvalue($elementName) . ', \'Remove\');'
126 . $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
127 } elseif (!$showListControl) {
128 // If the list controls have been removed and the maximum number is reached, remove the first entry to avoid "write once" field
129 $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] =
130 'setFormValueManipulate(' . GeneralUtility::quoteJSvalue($elementName) . ', \'RemoveFirstIfFull\', ' . $maxItems . ');'
131 . $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
132 }
133
134 $listOfSelectedValues = [];
135 $thumbnailsHtml = [];
136 $recordsOverviewHtml = [];
137 $selectorOptionsHtml = [];
138 $clipboardOnClick = [];
139 if ($internalType === 'file_reference' || $internalType === 'file') {
140 $fileFactory = ResourceFactory::getInstance();
141 foreach ($selectedItems as $selectedItem) {
142 $uidOrPath = $selectedItem['uidOrPath'];
143 $listOfSelectedValues[] = $uidOrPath;
144 $title = $selectedItem['title'];
145 $shortenedTitle = GeneralUtility::fixed_lgd_cs($title, $maxTitleLength);
146 $selectorOptionsHtml[] =
147 '<option value="' . htmlspecialchars($uidOrPath) . '" title="' . htmlspecialchars($title) . '">'
148 . htmlspecialchars($shortenedTitle)
149 . '</option>';
150 if ($showThumbs) {
151 if (MathUtility::canBeInterpretedAsInteger($uidOrPath)) {
152 $fileObject = $fileFactory->getFileObject($uidOrPath);
153 if (!$fileObject->isMissing()) {
154 $extension = $fileObject->getExtension();
155 if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'],
156 $extension)
157 ) {
158 $thumbnailsHtml[] =
159 '<li>'
160 . '<span class="thumbnail">'
161 . $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, [])->getPublicUrl(true)
162 . '</span>'
163 . '</li>';
164 }
165 }
166 } else {
167 $rowCopy = [];
168 $rowCopy[$fieldName] = $uidOrPath;
169 try {
170 $icon = BackendUtility::thumbCode(
171 $rowCopy,
172 $table,
173 $fieldName,
174 '',
175 '',
176 $config['uploadfolder'],
177 0,
178 ' align="middle"'
179 );
180 $thumbnailsHtml[] =
181 '<li>'
182 . '<span class="thumbnail">'
183 . $icon
184 . '</span>'
185 . '</li>';
186 } catch (\Exception $exception) {
187 $message = $exception->getMessage();
188 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $message, '',
189 FlashMessage::ERROR, true);
190 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
191 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
192 $defaultFlashMessageQueue->enqueue($flashMessage);
193 $logMessage = $message . ' (' . $table . ':' . $row['uid'] . ')';
194 GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_WARNING);
195 }
196 }
197 }
198 }
199 foreach ($clipboardElements as $clipboardElement) {
200 $value = $clipboardElement['value'];
201 $title = 'unescape(' . GeneralUtility::quoteJSvalue(rawurlencode(basename($clipboardElement['title']))) . ')';
202 $clipboardOnClick[] = 'setFormValueFromBrowseWin('
203 . GeneralUtility::quoteJSvalue($elementName) . ','
204 . 'unescape(' . GeneralUtility::quoteJSvalue(rawurlencode(str_replace('%20', ' ', $value))) . '),'
205 . $title . ','
206 . $title
207 . ');';
208 }
209 } elseif ($internalType === 'folder') {
210 foreach ($selectedItems as $selectedItem) {
211 $folder = $selectedItem['folder'];
212 $listOfSelectedValues[] = $folder;
213 $selectorOptionsHtml[] =
214 '<option value="' . htmlspecialchars($folder) . '" title="' . htmlspecialchars($folder) . '">'
215 . htmlspecialchars($folder)
216 . '</option>';
217 }
218 } else {
219 // 'db'
220 foreach ($selectedItems as $selectedItem) {
221 $tableWithUid = $selectedItem['table'] . '_' . $selectedItem['uid'];
222 $listOfSelectedValues[] = $tableWithUid;
223 $title = $selectedItem['title'];
224 if (empty($title)) {
225 $title = '[' . $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title') . ']';
226 }
227 $shortenedTitle = GeneralUtility::fixed_lgd_cs($title, $maxTitleLength);
228 $selectorOptionsHtml[] =
229 '<option value="' . htmlspecialchars($tableWithUid) . '" title="' . htmlspecialchars($title) . '">'
230 . htmlspecialchars($shortenedTitle)
231 . '</option>';
232 if (!$isDisabled && $showThumbs) {
233 $linkedIcon = BackendUtility::wrapClickMenuOnIcon(
234 $this->iconFactory->getIconForRecord($selectedItem['table'], $selectedItem['row'], Icon::SIZE_SMALL)->render(),
235 $selectedItem['table'],
236 $selectedItem['uid'],
237 1,
238 '',
239 '+copy,info,edit,view'
240 );
241 $linkedTitle = BackendUtility::wrapClickMenuOnIcon(
242 $shortenedTitle,
243 $selectedItem['table'],
244 $selectedItem['uid'],
245 1,
246 '',
247 '+copy,info,edit,view'
248 );
249 $recordsOverviewHtml[] =
250 '<tr>'
251 . '<td class="col-icon">'
252 . $linkedIcon
253 . '</td>'
254 . '<td class="col-title">'
255 . $linkedTitle
256 . '<span class="text-muted">'
257 . ' [' . $selectedItem['uid'] . ']'
258 . '</span>'
259 . '</td>'
260 . '</tr>';
261 }
262 }
263 foreach ($clipboardElements as $clipboardElement) {
264 $value = $clipboardElement['value'];
265 $title = GeneralUtility::quoteJSvalue($clipboardElement['title']);
266 $clipboardOnClick[] = 'setFormValueFromBrowseWin('
267 . GeneralUtility::quoteJSvalue($elementName) . ','
268 . 'unescape(' . GeneralUtility::quoteJSvalue(rawurlencode(str_replace('%20', ' ', $value))) . '),'
269 . $title . ','
270 . $title
271 . ');';
272 }
273 }
274
275 // Check against inline uniqueness - Create some onclick js for delete control and element browser
276 // to override record selection in some FAL scenarios - See 'appearance' docs of group element
277 $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
278 $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
279 $elementBrowserOnClickInline = '';
280 $deleteControlOnClick = '';
281 if ($this->data['isInlineChild']
282 && $this->data['inlineParentUid']
283 && $this->data['inlineParentConfig']['foreign_table'] === $table
284 && $this->data['inlineParentConfig']['foreign_unique'] === $fieldName
285 ) {
286 $objectPrefix = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']) . '-' . $table;
287 $elementBrowserOnClickInline = $objectPrefix . '|inline.checkUniqueElement|inline.setUniqueElement';
288 $deleteControlOnClick = 'inline.revertUnique(' . GeneralUtility::quoteJSvalue($objectPrefix) . ',null,' . GeneralUtility::quoteJSvalue($row['uid']) . ');';
289 }
290 $elementBrowserType = $internalType;
291 if (is_array($config['appearance']) && isset($config['appearance']['elementBrowserType'])) {
292 $elementBrowserType = $config['appearance']['elementBrowserType'];
293 }
294 $elementBrowserAllowed = implode(',', $allowed);
295 if (is_array($config['appearance']) && isset($config['appearance']['elementBrowserAllowed'])) {
296 $elementBrowserAllowed = $config['appearance']['elementBrowserAllowed'];
297 }
298 $elementBrowserOnClick = 'setFormValueOpenBrowser('
299 . GeneralUtility::quoteJSvalue($elementBrowserType) . ','
300 . GeneralUtility::quoteJSvalue($elementName . '|||' . $elementBrowserAllowed . '|' . $elementBrowserOnClickInline)
301 . ');'
302 . ' return false;';
303
304 $allowedTablesHtml = [];
305 if ($allowed[0] === '*') {
306 $allowedTablesHtml[] =
307 '<span>'
308 . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.allTables'))
309 . '</span>';
310 } else {
311 foreach ($allowed as $tableName) {
312 $label = $languageService->sL($GLOBALS['TCA'][$tableName]['ctrl']['title']);
313 if (!$isDisabled) {
314 $icon = $this->iconFactory->getIconForRecord($tableName, [], Icon::SIZE_SMALL);
315 $onClick = 'setFormValueOpenBrowser(\'db\', ' . GeneralUtility::quoteJSvalue($elementName . '|||' . $tableName) . '); return false;';
316 $allowedTablesHtml[] =
317 '<a href="#" onClick="' . htmlspecialchars($onClick) . '" class="btn btn-default">'
318 . $icon->render() . htmlspecialchars($label) . '</a> '
319 . '</a>';
320 } else {
321 $allowedTablesHtml[] = '<span>' . htmlspecialchars($label) . '</span> ';
322 }
323 }
324 }
325
326 $allowedHtml = [];
327 foreach ($allowed as $item) {
328 $allowedHtml[] = '<span class="label label-success">' . htmlspecialchars(strtoupper($item)) . '</span> ';
329 }
330
331 $disallowedHtml = [];
332 foreach ($disallowed as $item) {
333 $disallowedHtml[] = '<span class="label label-danger">' . htmlspecialchars(strtoupper($item)) . '</span> ';
334 }
335
336 $selectorStyles = [];
337 $selectorAttributes = [];
338 $selectorAttributes[] = 'id="' . StringUtility::getUniqueId('tceforms-multiselect-') . '"';
339 $selectorAttributes[] = 'data-formengine-input-name="' . htmlspecialchars($elementName) . '"';
340 $selectorAttributes[] = $this->getValidationDataAsDataAttribute($config);
341 if ($maxItems !== 1 && $size !== 1) {
342 $selectorAttributes[] = 'multiple="multiple"';
343 }
344 if ($isDisabled) {
345 $selectorAttributes[] = 'disabled="disabled"';
346 }
347 if ($showListControl) {
348 $selectorClasses = [];
349 $selectorClasses[] = 'form-control';
350 $selectorClasses[] = 'tceforms-multiselect';
351 if ($maxItems === 1) {
352 $selectorClasses[] = 'form-select-no-siblings';
353 }
354 $selectorAttributes[] = 'class="' . implode(' ', $selectorClasses) . '"';
355 $selectorAttributes[] = 'size="' . $size . '"';
356 } else {
357 $selectorStyles[] = 'display: none';
358 }
359 if (isset($config['selectedListStyle'])) {
360 $selectorStyles[] = $config['selectedListStyle'];
361 }
362 $selectorAttributes[] = 'style="' . implode(';', $selectorStyles) . '"';
363
364 $html = [];
365 $html[] = '<input type="hidden" class="t3js-group-hidden-field" data-formengine-input-name="' . htmlspecialchars($elementName) . '" value="' . $itemCanBeSelectedMoreThanOnce . '" />';
366 $html[] = '<div class="form-wizards-wrap form-wizards-aside">';
367 $html[] = '<div class="form-wizards-element">';
368 $html[] = '<select ' . implode(' ', $selectorAttributes) . '>';
369 $html[] = implode(LF, $selectorOptionsHtml);
370 $html[] = '</select>';
371 if ($showListControl && $showAllowedTables && $internalType === 'db' && !empty($allowedTablesHtml)) {
372 $html[] = '<div class="help-block">';
373 $html[] = implode(LF, $allowedTablesHtml);
374 $html[] = '</div>';
375 }
376 if ($showListControl && $internalType === 'file' && (!empty($allowedHtml) || !empty($disallowedHtml)) && !$isDisabled) {
377 $html[] = '<div class="help-block">';
378 $html[] = implode(LF, $allowedHtml);
379 $html[] = implode(LF, $disallowedHtml);
380 $html[] = '</div>';
381 }
382 $html[] = '</div>';
383
384 $html[] = '<div class="form-wizards-items">';
385 $html[] = '<div class="btn-group-vertical">';
386 if ($maxItems > 1 && $size >=5 && $selectedItemsCount >=5 && !$isDisabled && $showMoveIcons) {
387 $html[] = '<a href="#"';
388 $html[] = ' class="btn btn-default t3js-btn-moveoption-top"';
389 $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
390 $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.move_to_top')) . '"';
391 $html[] = '>';
392 $html[] = $this->iconFactory->getIcon('actions-move-to-top', Icon::SIZE_SMALL)->render();
393 $html[] = '</a>';
394 }
395 if ($maxItems > 1 && !$isDisabled && $showMoveIcons) {
396 $html[] = '<a href="#"';
397 $html[] = ' class="btn btn-default t3js-btn-moveoption-up"';
398 $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
399 $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.move_up')) . '"';
400 $html[] = '>';
401 $html[] = $this->iconFactory->getIcon('actions-move-up', Icon::SIZE_SMALL)->render();
402 $html[] = '</a>';
403 $html[] = '<a href="#"';
404 $html[] = ' class="btn btn-default t3js-btn-moveoption-down"';
405 $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
406 $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.move_down')) . '"';
407 $html[] = '>';
408 $html[] = $this->iconFactory->getIcon('actions-move-down', Icon::SIZE_SMALL)->render();
409 $html[] = '</a>';
410 }
411 if ($maxItems > 1 && $size >=5 && $selectedItemsCount >=5 && !$isDisabled && $showMoveIcons) {
412 $html[] = '<a href="#"';
413 $html[] = ' class="btn btn-default t3js-btn-moveoption-bottom"';
414 $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
415 $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.move_to_bottom')) . '"';
416 $html[] = '>';
417 $html[] = $this->iconFactory->getIcon('actions-move-to-bottom', Icon::SIZE_SMALL)->render();
418 $html[] = '</a>';
419 }
420 if ($showDeleteControl && !$isDisabled) {
421 $html[] = '<a href="#"';
422 $html[] = ' class="btn btn-default t3js-btn-removeoption"';
423 $html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
424 $html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.remove_selected')) . '"';
425 $html[] = ' onClick="' . $deleteControlOnClick . '"';
426 $html[] = '>';
427 $html[] = $this->iconFactory->getIcon('actions-selection-delete', Icon::SIZE_SMALL)->render();
428 $html[] = '</a>';
429 }
430 $html[] = '</div>';
431 $html[] = '</div>';
432
433 $html[] = '<div class="form-wizards-items">';
434 $html[] = '<div class="btn-group-vertical">';
435 if ($showListControl && $showBrowseControl && !$isDisabled) {
436 if ($internalType === 'db') {
437 $elementBrowserLabel = $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.browse_db');
438 } else {
439 $elementBrowserLabel = $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.browse_file');
440 }
441 $html[] = '<a href="#"';
442 $html[] = ' onclick="' . htmlspecialchars($elementBrowserOnClick) . '"';
443 $html[] = ' class="btn btn-default"';
444 $html[] = ' title="' . htmlspecialchars($elementBrowserLabel) . '"';
445 $html[] = '>';
446 $html[] = $this->iconFactory->getIcon('actions-insert-record', Icon::SIZE_SMALL)->render();
447 $html[] = '</a>';
448 }
449 if ($showListControl && $showBrowseControl && !$isDisabled && !empty($clipboardElements)) {
450 if ($internalType === 'db') {
451 $clipboardLabel = sprintf($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.clipInsert_db'), count($clipboardElements));
452 } else {
453 $clipboardLabel = sprintf($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.clipInsert_file'), count($clipboardElements));
454 }
455 $html[] = '<a href="#"';
456 $html[] = ' onclick="' . htmlspecialchars(implode(LF, $clipboardOnClick)) . ' return false;"';
457 $html[] = ' class="btn btn-default"';
458 $html[] = ' title="' . htmlspecialchars($clipboardLabel) . '"';
459 $html[] = '>';
460 $html[] = $this->iconFactory->getIcon('actions-document-paste-into', Icon::SIZE_SMALL)->render();
461 $html[] = '</a>';
462 }
463 $html[] = '</div>';
464 $html[] = '</div>';
465 $html[] = '</div>';
466
467 if (!empty($thumbnailsHtml)) {
468 $html[] = '<ul class="list-inline">';
469 $html[] = implode(LF, $thumbnailsHtml);
470 $html[] = '</ul>';
471 }
472 if (!empty($recordsOverviewHtml)) {
473 $html[] = '<div class="table-fit">';
474 $html[] = '<table class="table table-white">';
475 $html[] = '<tbody>';
476 $html[] = implode(LF, $recordsOverviewHtml);
477 $html[] = '</tbody>';
478 $html[] = '</table>';
479 $html[] = '</div>';
480 }
481
482 if (!$isDisabled && $showUploadField) {
483 // Adding the upload field
484 if ($isDirectFileUploadEnabled && !empty($config['uploadfolder'])) {
485 // Insert the multiple attribute to enable HTML5 multiple file upload
486 $selectorMultipleAttribute = '';
487 $multipleFilenameSuffix = '';
488 if ($maxItems > 1) {
489 $selectorMultipleAttribute = ' multiple="multiple"';
490 $multipleFilenameSuffix = '[]';
491 }
492 $html[] = '<div id="' . $uploadFieldId . '">';
493 $html[] = '<input';
494 $html[] = ' type="file"';
495 $html[] = $selectorMultipleAttribute;
496 $html[] = ' name="data_files' . $this->data['elementBaseName'] . $multipleFilenameSuffix . '"';
497 $html[] = ' size="35"';
498 $html[] = ' onchange="' . implode('', $parameterArray['fieldChangeFunc']) . '"';
499 $html[] = '/>';
500 $html[] = '</div>';
501 }
502 }
503
504 $html[] = '<input type="hidden" name="' . htmlspecialchars($elementName) . '" value="' . htmlspecialchars(implode(',', $listOfSelectedValues)) . '" />';
505
506 $html = implode(LF, $html);
507
508 if (!$config['readOnly']) {
509 $html = $this->renderWizards(
510 [ $html ],
511 $config['wizards'],
512 $table,
513 $row,
514 $fieldName,
515 $parameterArray,
516 $elementName,
517 BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras'])
518 );
519 }
520
521 $resultArray['html'] = $html;
522 return $resultArray;
523 }
524
525 /**
526 * @return BackendUserAuthentication
527 */
528 protected function getBackendUserAuthentication()
529 {
530 return $GLOBALS['BE_USER'];
531 }
532 }