[ 'renderType' => 'editPopup', 'disabled' => true, ], 'addRecord' => [ 'renderType' => 'addRecord', 'disabled' => true, ], 'listModule' => [ 'renderType' => 'listModule', 'disabled' => true, 'after' => [ 'addRecord' ], ], ]; /** * Default field wizards enabled for this element. * * @var array */ protected $defaultFieldWizard = [ 'localizationStateSelector' => [ 'renderType' => 'localizationStateSelector', ], 'otherLanguageContent' => [ 'renderType' => 'otherLanguageContent', 'after' => [ 'localizationStateSelector', ], ], 'defaultLanguageDifferences' => [ 'renderType' => 'defaultLanguageDifferences', 'after' => [ 'otherLanguageContent', ], ], ]; public function __construct( private readonly IconFactory $iconFactory, ) {} /** * Merge field control configuration with default controls and render them. * * @return array Result array */ protected function renderFieldControl(): array { $alternativeResult = [ 'additionalInlineLanguageLabelFiles' => [], 'stylesheetFiles' => [], 'javaScriptModules' => [], 'inlineData' => [], 'html' => '', ]; $options = $this->data; $fieldControl = $this->defaultFieldControl; $fieldControlFromTca = $options['parameterArray']['fieldConf']['config']['fieldControl'] ?? []; ArrayUtility::mergeRecursiveWithOverrule($fieldControl, $fieldControlFromTca); $options['renderType'] = 'fieldControl'; if (isset($fieldControl['editPopup'])) { $editPopupControl = $fieldControl['editPopup']; unset($fieldControl['editPopup']); $alternativeOptions = $options; $alternativeOptions['renderData']['fieldControl'] = ['editPopup' => $editPopupControl]; $alternativeResult = $this->nodeFactory->create($alternativeOptions)->render(); } $options['renderData']['fieldControl'] = $fieldControl; return [$this->nodeFactory->create($options)->render(), $alternativeResult]; } /** * Render side by side element. * * @return array As defined in initializeResultArray() of AbstractNode */ public function render(): array { $parameterArray = $this->data['parameterArray']; $config = $parameterArray['fieldConf']['config']; if ($config['readOnly'] ?? false) { // Early return for the relatively simple read only case return $this->renderReadOnly(); } $filterTextfield = []; $languageService = $this->getLanguageService(); $resultArray = $this->initializeResultArray(); $elementName = $parameterArray['itemFormElName']; $possibleItems = $config['items']; $selectedItems = $parameterArray['itemFormElValue'] ?: []; $maxItems = $config['maxitems']; $size = (int)($config['size'] ?? 2); $autoSizeMax = (int)($config['autoSizeMax'] ?? 0); if ($autoSizeMax > 0) { $size = MathUtility::forceIntegerInRange($size, 1); $size = MathUtility::forceIntegerInRange(count($selectedItems) + 1, $size, $autoSizeMax); } $itemCanBeSelectedMoreThanOnce = !empty($config['multiple']); $listOfSelectedValues = []; $selectedItemsHtml = []; foreach ($selectedItems as $itemValue) { foreach ($possibleItems as $possibleItem) { if ($possibleItem['value'] == $itemValue) { $title = $possibleItem['label']; $listOfSelectedValues[] = $itemValue; $selectedItemsHtml[] = ''; break; } } } $selectableItemCounter = 0; $selectableItemGroupCounter = 0; $selectableItemGroups = []; $selectableItemsHtml = []; // Initialize groups foreach ($possibleItems as $possibleItem) { $disableAttributes = []; if (!$itemCanBeSelectedMoreThanOnce && in_array((string)$possibleItem['value'], $selectedItems, true)) { $disableAttributes = [ 'disabled' => 'disabled', 'class' => 'hidden', ]; } if ($possibleItem['value'] === '--div--') { if ($selectableItemCounter !== 0) { $selectableItemGroupCounter++; } $selectableItemGroups[$selectableItemGroupCounter]['header']['title'] = $possibleItem['label']; } else { $selectableItemGroups[$selectableItemGroupCounter]['items'][] = [ 'label' => $this->appendValueToLabelInDebugMode($possibleItem['label'], $possibleItem['value']), 'attributes' => array_merge(['title' => $possibleItem['label'], 'value' => $possibleItem['value']], $disableAttributes), ]; // In case the item is not disabled, enable the group (if any) if ($disableAttributes === [] && isset($selectableItemGroups[$selectableItemGroupCounter]['header'])) { $selectableItemGroups[$selectableItemGroupCounter]['header']['disabled'] = false; } $selectableItemCounter++; } } // Process groups foreach ($selectableItemGroups as $selectableItemGroup) { if (!is_array($selectableItemGroup['items'] ?? false)) { continue; } $optionGroup = isset($selectableItemGroup['header']); if ($optionGroup) { $selectableItemsHtml[] = ''; } foreach ($selectableItemGroup['items'] as $item) { $selectableItemsHtml[] = ' '; } if ($optionGroup) { $selectableItemsHtml[] = ''; } } // Html stuff for filter and select filter on top of right side of multi select boxes $filterTextfieldId = StringUtility::getUniqueId('tceforms-multiselect-filter-'); $filterTextfield[] = ''; $filterDropDownOptions = []; if (isset($config['multiSelectFilterItems']) && is_array($config['multiSelectFilterItems']) && count($config['multiSelectFilterItems']) > 1) { foreach ($config['multiSelectFilterItems'] as $optionElement) { $value = $languageService->sL($optionElement[0]); $label = $value; if (isset($optionElement[1]) && trim($optionElement[1]) !== '') { $label = $languageService->sL($optionElement[1]); } $filterDropDownOptions[] = ''; } } $filterHtml = []; $filterHtml[] = '
'; if (!empty($filterDropDownOptions)) { $filterHtml[] = '
'; $filterHtml[] = '
'; $filterHtml[] = ''; $filterHtml[] = '
'; $filterHtml[] = '
'; $filterHtml[] = implode(LF, $filterTextfield); $filterHtml[] = '
'; $filterHtml[] = '
'; } else { $filterHtml[] = implode(LF, $filterTextfield); } $filterHtml[] = '
'; $multipleAttribute = ''; if ($maxItems !== 1 && $size !== 1) { $multipleAttribute = ' multiple="multiple"'; } $fieldInformationResult = $this->renderFieldInformation(); $fieldInformationHtml = $fieldInformationResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false); [$fieldControlResult, $alternativeControlResult] = $this->renderFieldControl(); $fieldControlHtml = $fieldControlResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false); $alternativeFieldControlHtml = $alternativeControlResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $alternativeControlResult, false); $fieldWizardResult = $this->renderFieldWizard(); $fieldWizardHtml = $fieldWizardResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); $selectedOptionsFieldId = StringUtility::getUniqueId('tceforms-multiselect-'); $availableOptionsFieldId = StringUtility::getUniqueId('tceforms-multiselect-'); $html = []; $html[] = $this->renderLabel($selectedOptionsFieldId); $html[] = '
'; $html[] = $fieldInformationHtml; $html[] = '
'; $html[] = '
'; $html[] = ''; $html[] = '
'; $html[] = '
'; $html[] = ''; $html[] = '
'; $html[] = '
'; $html[] = ' 1 && $size >= 2) { $html[] = ''; } if ($maxItems > 1) { $html[] = ''; $html[] = ''; } if ($maxItems > 1 && $size >= 2) { $html[] = ''; } $html[] = $alternativeFieldControlHtml; $html[] = ''; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = ''; $html[] = '
'; $html[] = implode(LF, $filterHtml); $html[] = '
'; $selectElementAttrs = array_merge( [ 'size' => $size, 'id' => $availableOptionsFieldId, 'class' => 'form-select t3js-formengine-select-itemstoselect', 'data-relatedfieldname' => $elementName, 'data-exclusivevalues' => $config['exclusiveKeys'] ?? '', 'data-formengine-validation-rules' => $this->getValidationDataAsJsonString($config), ], $this->getOnFieldChangeAttrs('change', $parameterArray['fieldChangeFunc'] ?? []) ); $html[] = ''; $html[] = '
'; if (!empty($fieldControlHtml)) { $html[] = '
'; $html[] = '
'; $html[] = $fieldControlHtml; $html[] = '
'; $html[] = '
'; } $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = ''; $html[] = '
'; if (!empty($fieldWizardHtml)) { $html[] = '
'; $html[] = $fieldWizardHtml; $html[] = '
'; } $html[] = '
'; $html[] = ''; $resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create( '@typo3/backend/form-engine/element/select-multiple-side-by-side-element.js' )->instance($selectedOptionsFieldId, $availableOptionsFieldId); $resultArray['html'] = implode(LF, $html); return $resultArray; } /** * Create HTML of a read only multi select. Right side is not * rendered, but just the left side with the selected items. * * @return array */ protected function renderReadOnly() { $languageService = $this->getLanguageService(); $resultArray = $this->initializeResultArray(); $parameterArray = $this->data['parameterArray']; $config = $parameterArray['fieldConf']['config']; $fieldName = $parameterArray['itemFormElName']; $possibleItems = $config['items']; $selectedItems = $parameterArray['itemFormElValue'] ?: []; if (!is_array($selectedItems)) { $selectedItems = GeneralUtility::trimExplode(',', $selectedItems, true); } $size = (int)($config['size'] ?? 2); $autoSizeMax = (int)($config['autoSizeMax'] ?? 0); if ($autoSizeMax > 0) { $size = MathUtility::forceIntegerInRange($size, 1); $size = MathUtility::forceIntegerInRange(count($selectedItems) + 1, $size, $autoSizeMax); } $multiple = ''; if ($size !== 1) { $multiple = ' multiple="multiple"'; } $listOfSelectedValues = []; $optionsHtml = []; foreach ($selectedItems as $itemValue) { foreach ($possibleItems as $possibleItem) { if ($possibleItem['value'] == $itemValue) { $title = $possibleItem['label']; $listOfSelectedValues[] = $itemValue; $optionsHtml[] = ''; break; } } } $fieldInformationResult = $this->renderFieldInformation(); $fieldInformationHtml = $fieldInformationResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false); $selectId = StringUtility::getUniqueId('tceforms-multiselect-'); $html = []; $html[] = $this->renderLabel($selectId); $html[] = '
'; $html[] = $fieldInformationHtml; $html[] = '
'; $html[] = '
'; $html[] = ''; $html[] = '
'; $html[] = '
'; $html[] = ''; $html[] = '
'; $html[] = '
'; $html[] = '
'; $resultArray['html'] = implode(LF, $html); return $resultArray; } protected function getBackendUserAuthentication(): BackendUserAuthentication { return $GLOBALS['BE_USER']; } }