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