[!!!][FEATURE] FormEngine element level refactoring
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / SelectSingleElement.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\Form\InlineStackProcessor;
18 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20 use TYPO3\CMS\Core\Utility\StringUtility;
21
22 /**
23 * Creates a widget where only one item can be selected.
24 * This is either a select drop-down if no size config is given or set to 1, or a select box.
25 *
26 * This is rendered for type=select, renderType=selectSingle
27 */
28 class SelectSingleElement extends AbstractFormElement
29 {
30 /**
31 * Default field wizards enabled for this element.
32 *
33 * @var array
34 */
35 protected $defaultFieldWizard = [
36 'selectIcons' => [
37 'renderType' => 'selectIcons',
38 'disabled' => true,
39 ],
40 'otherLanguageContent' => [
41 'renderType' => 'otherLanguageContent',
42 'after' => [ 'selectIcons' ],
43 ],
44 'defaultLanguageDifferences' => [
45 'renderType' => 'defaultLanguageDifferences',
46 'after' => [ 'otherLanguageContent' ],
47 ],
48 ];
49
50 /**
51 * Render single element
52 *
53 * @return array As defined in initializeResultArray() of AbstractNode
54 */
55 public function render()
56 {
57 $resultArray = $this->initializeResultArray();
58
59 $table = $this->data['tableName'];
60 $field = $this->data['fieldName'];
61 $row = $this->data['databaseRow'];
62 $parameterArray = $this->data['parameterArray'];
63 $config = $parameterArray['fieldConf']['config'];
64
65 $selectItems = $parameterArray['fieldConf']['config']['items'];
66
67 // Check against inline uniqueness
68 /** @var InlineStackProcessor $inlineStackProcessor */
69 $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
70 $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
71 $uniqueIds = null;
72 if ($this->data['isInlineChild'] && $this->data['inlineParentUid']) {
73 $inlineObjectName = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
74 $inlineFormName = $inlineStackProcessor->getCurrentStructureFormPrefix();
75 if ($this->data['inlineParentConfig']['foreign_table'] === $table
76 && $this->data['inlineParentConfig']['foreign_unique'] === $field
77 ) {
78 $uniqueIds = $this->data['inlineData']['unique'][$inlineObjectName . '-' . $table]['used'];
79 $parameterArray['fieldChangeFunc']['inlineUnique'] = 'inline.updateUnique(this,'
80 . GeneralUtility::quoteJSvalue($inlineObjectName . '-' . $table) . ','
81 . GeneralUtility::quoteJSvalue($inlineFormName) . ','
82 . GeneralUtility::quoteJSvalue($row['uid']) . ');';
83 }
84 // hide uid of parent record for symmetric relations
85 if ($this->data['inlineParentConfig']['foreign_table'] === $table
86 && (
87 $this->data['inlineParentConfig']['foreign_field'] === $field
88 || $this->data['inlineParentConfig']['symmetric_field'] === $field
89 )
90 ) {
91 $uniqueIds[] = $this->data['inlineParentUid'];
92 }
93 }
94
95 // Initialization:
96 $selectId = StringUtility::getUniqueId('tceforms-select-');
97 $selectedIcon = '';
98 $size = (int)$config['size'];
99
100 // Style set on <select/>
101 $options = '';
102 $disabled = false;
103 if (!empty($config['readOnly'])) {
104 $disabled = true;
105 }
106
107 // Prepare groups
108 $selectItemCounter = 0;
109 $selectItemGroupCount = 0;
110 $selectItemGroups = [];
111 $selectedValue = '';
112 $hasIcons = false;
113
114 if (!empty($parameterArray['itemFormElValue'])) {
115 $selectedValue = (string)$parameterArray['itemFormElValue'][0];
116 }
117
118 foreach ($selectItems as $item) {
119 if ($item[1] === '--div--') {
120 // IS OPTGROUP
121 if ($selectItemCounter !== 0) {
122 $selectItemGroupCount++;
123 }
124 $selectItemGroups[$selectItemGroupCount]['header'] = [
125 'title' => $item[0],
126 ];
127 } else {
128 // IS ITEM
129 $icon = !empty($item[2]) ? FormEngineUtility::getIconHtml($item[2], $item[0], $item[0]) : '';
130 $selected = $selectedValue === (string)$item[1];
131
132 if ($selected) {
133 $selectedIcon = $icon;
134 }
135
136 $selectItemGroups[$selectItemGroupCount]['items'][] = [
137 'title' => $item[0],
138 'value' => $item[1],
139 'icon' => $icon,
140 'selected' => $selected,
141 ];
142 $selectItemCounter++;
143 }
144 }
145
146 // Fallback icon
147 // @todo: assign a special icon for non matching values?
148 if (!$selectedIcon && $selectItemGroups[0]['items'][0]['icon']) {
149 $selectedIcon = $selectItemGroups[0]['items'][0]['icon'];
150 }
151
152 // Process groups
153 foreach ($selectItemGroups as $selectItemGroup) {
154 // suppress groups without items
155 if (empty($selectItemGroup['items'])) {
156 continue;
157 }
158
159 $optionGroup = is_array($selectItemGroup['header']);
160 $options .= ($optionGroup ? '<optgroup label="' . htmlspecialchars($selectItemGroup['header']['title'], ENT_COMPAT, 'UTF-8', false) . '">' : '');
161
162 if (is_array($selectItemGroup['items'])) {
163 foreach ($selectItemGroup['items'] as $item) {
164 $options .= '<option value="' . htmlspecialchars($item['value']) . '" data-icon="' .
165 htmlspecialchars($item['icon']) . '"'
166 . ($item['selected'] ? ' selected="selected"' : '') . '>' . htmlspecialchars($item['title'], ENT_COMPAT, 'UTF-8', false) . '</option>';
167 }
168 $hasIcons = !empty($item['icon']);
169 }
170
171 $options .= ($optionGroup ? '</optgroup>' : '');
172 }
173
174 $selectAttributes = [
175 'id' => $selectId,
176 'name' => $parameterArray['itemFormElName'],
177 'data-formengine-validation-rules' => $this->getValidationDataAsJsonString($config),
178 'class' => 'form-control form-control-adapt',
179 ];
180 if ($size) {
181 $selectAttributes['size'] = $size;
182 }
183 if ($disabled) {
184 $selectAttributes['disabled'] = 'disabled';
185 }
186
187 $legacyWizards = $this->renderWizards();
188 $legacyFieldWizardHtml = implode(LF, $legacyWizards['fieldWizard']);
189
190 $fieldInformationResult = $this->renderFieldInformation();
191 $fieldInformationHtml = $fieldInformationResult['html'];
192 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
193
194 $fieldWizardResult = $this->renderFieldWizard();
195 $fieldWizardHtml = $legacyFieldWizardHtml . $fieldWizardResult['html'];
196 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
197
198 $html = [];
199 $html[] = '<div class="t3js-formengine-field-item">';
200 if (!$disabled) {
201 $html[] = $fieldInformationHtml;
202 }
203 $html[] = '<div class="form-control-wrap">';
204 $html[] = '<div class="form-wizards-wrap">';
205 $html[] = '<div class="form-wizards-element">';
206 if ($hasIcons) {
207 $html[] = '<div class="input-group">';
208 $html[] = '<span class="input-group-addon input-group-icon">';
209 $html[] = $selectedIcon;
210 $html[] = '</span>';
211 }
212 $html[] = '<select ' . GeneralUtility::implodeAttributes($selectAttributes, true) . '>';
213 $html[] = $options;
214 $html[] = '</select>';
215 if ($hasIcons) {
216 $html[] = '</div>';
217 }
218 $html[] = '</div>';
219 if (!$disabled) {
220 $html[] = '<div class="form-wizards-items-bottom">';
221 $html[] = $fieldWizardHtml;
222 $html[] = '</div>';
223 }
224 $html[] = '</div>';
225 $html[] = '</div>';
226 $html[] = '</div>';
227
228 $resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectSingleElement' => implode(LF, [
229 'function(SelectSingleElement) {',
230 'SelectSingleElement.initialize(',
231 GeneralUtility::quoteJSvalue('#' . $selectId) . ',',
232 '{',
233 'onChange: function() {',
234 implode('', $parameterArray['fieldChangeFunc']),
235 '}',
236 '}',
237 ');',
238 '}',
239 ])];
240
241 $resultArray['html'] = implode(LF, $html);
242 return $resultArray;
243 }
244 }