[TASK] Add prepended icon to SelectSingleElement
[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\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Core\Utility\StringUtility;
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
21 use TYPO3\CMS\Backend\Form\InlineStackProcessor;
22
23 /**
24 * Creates a widget where only one item can be selected.
25 * This is either a select drop-down if no size config is given or set to 1, or a select box.
26 *
27 * This is rendered for type=select, maxitems=1
28 */
29 class SelectSingleElement extends AbstractFormElement {
30
31 /**
32 * Render single element
33 *
34 * @return array As defined in initializeResultArray() of AbstractNode
35 */
36 public function render() {
37 $table = $this->data['tableName'];
38 $field = $this->data['fieldName'];
39 $row = $this->data['databaseRow'];
40 $parameterArray = $this->data['parameterArray'];
41 $config = $parameterArray['fieldConf']['config'];
42
43 $selectItems = $parameterArray['fieldConf']['config']['items'];
44
45 // Creating the label for the "No Matching Value" entry.
46 $noMatchingLabel = isset($parameterArray['fieldTSConfig']['noMatchingValue_label'])
47 ? $this->getLanguageService()->sL($parameterArray['fieldTSConfig']['noMatchingValue_label'])
48 : '[ ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue') . ' ]';
49
50 // Check against inline uniqueness
51 /** @var InlineStackProcessor $inlineStackProcessor */
52 $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
53 $inlineStackProcessor->initializeByGivenStructure($this->data['inlineStructure']);
54 $inlineParent = $inlineStackProcessor->getStructureLevel(-1);
55 $uniqueIds = NULL;
56 if (is_array($inlineParent) && $inlineParent['uid']) {
57 $inlineObjectName = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
58 $inlineFormName = $inlineStackProcessor->getCurrentStructureFormPrefix();
59 if ($inlineParent['config']['foreign_table'] == $table && $inlineParent['config']['foreign_unique'] == $field) {
60 $uniqueIds = $this->data['inlineData']['unique'][$inlineObjectName . '-' . $table]['used'];
61 $parameterArray['fieldChangeFunc']['inlineUnique'] = 'inline.updateUnique(this,'
62 . GeneralUtility::quoteJSvalue($inlineObjectName . '-' . $table) . ','
63 . GeneralUtility::quoteJSvalue($inlineFormName) . ','
64 . GeneralUtility::quoteJSvalue($row['uid']) . ');';
65 }
66 // hide uid of parent record for symmetric relations
67 if (
68 $inlineParent['config']['foreign_table'] == $table
69 && ($inlineParent['config']['foreign_field'] == $field || $inlineParent['config']['symmetric_field'] == $field)
70 ) {
71 $uniqueIds[] = $inlineParent['uid'];
72 }
73 }
74
75 // Initialization:
76 $selectId = StringUtility::getUniqueId('tceforms-select-');
77 $selectedIndex = 0;
78 $selectedIcon = '';
79 $selectedValueFound = FALSE;
80 $onlySelectedIconShown = FALSE;
81 $size = (int)$config['size'];
82
83 // Style set on <select/>
84 $options = '';
85 $disabled = FALSE;
86 if (!empty($config['readOnly'])) {
87 $disabled = TRUE;
88 $onlySelectedIconShown = TRUE;
89 }
90
91 // Icon configuration:
92 if ($config['suppress_icons'] === 'IF_VALUE_FALSE') {
93 $suppressIcons = empty($parameterArray['itemFormElValue']);
94 } elseif ($config['suppress_icons'] === 'ONLY_SELECTED') {
95 $suppressIcons = FALSE;
96 $onlySelectedIconShown = TRUE;
97 } elseif ($config['suppress_icons']) {
98 $suppressIcons = TRUE;
99 } else {
100 $suppressIcons = FALSE;
101 }
102
103 // Prepare groups
104 $selectItemCounter = 0;
105 $selectItemGroupCount = 0;
106 $selectItemGroups = array();
107 $selectIcons = array();
108 $selectedValue = '';
109 $hasIcons = FALSE;
110
111 if (!empty($parameterArray['itemFormElValue'])) {
112 $selectedValue = (string)$parameterArray['itemFormElValue'][0];
113 }
114
115 foreach ($selectItems as $item) {
116 if ($item[1] === '--div--') {
117 // IS OPTGROUP
118 if ($selectItemCounter !== 0) {
119 $selectItemGroupCount++;
120 }
121 $selectItemGroups[$selectItemGroupCount]['header'] = array(
122 'title' => $item[0],
123 );
124 } else {
125 // IS ITEM
126 $title = htmlspecialchars($item['0'], ENT_COMPAT, 'UTF-8', FALSE);
127 $icon = !empty($item[2]) ? FormEngineUtility::getIconHtml($item[2], $title, $title) : '';
128 $selected = $selectedValue === (string)$item[1];
129
130 if ($selected) {
131 $selectedIndex = $selectItemCounter;
132 $selectedIcon = $icon;
133 $selectedValueFound = TRUE;
134 }
135
136 $selectItemGroups[$selectItemGroupCount]['items'][] = array(
137 'title' => $title,
138 'value' => $item[1],
139 'icon' => $icon,
140 'selected' => $selected,
141 'index' => $selectItemCounter
142 );
143
144 // ICON
145 if ($icon && !$suppressIcons && (!$onlySelectedIconShown || $selected)) {
146 $selectIcons[] = array(
147 'title' => $title,
148 'icon' => $icon,
149 'index' => $selectItemCounter,
150 );
151 }
152
153 $selectItemCounter++;
154 }
155
156 }
157
158 // No-matching-value:
159 if ($selectedValue && !$selectedValueFound && !$parameterArray['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
160 $noMatchingLabel = @sprintf($noMatchingLabel, $selectedValue);
161 $options = '<option value="' . htmlspecialchars($selectedValue) . '" selected="selected">' . htmlspecialchars($noMatchingLabel) . '</option>';
162 } elseif (!$selectedIcon && $selectItemGroups[0]['items'][0]['icon']) {
163 $selectedIcon = $selectItemGroups[0]['items'][0]['icon'];
164 }
165
166 // Process groups
167 foreach ($selectItemGroups as $selectItemGroup) {
168 // suppress groups without items
169 if (empty($selectItemGroup['items'])) {
170 continue;
171 }
172
173 $optionGroup = is_array($selectItemGroup['header']);
174 $options .= ($optionGroup ? '<optgroup label="' . htmlspecialchars($selectItemGroup['header']['title'], ENT_COMPAT, 'UTF-8', FALSE) . '">' : '');
175
176 if (is_array($selectItemGroup['items'])) {
177 foreach ($selectItemGroup['items'] as $item) {
178 $options .= '<option value="' . htmlspecialchars($item['value']) . '" data-icon="' .
179 htmlspecialchars($item['icon']) . '"'
180 . ($item['selected'] ? ' selected="selected"' : '') . '>' . $item['title'] . '</option>';
181 }
182 $hasIcons = !empty($item['icon']);
183 }
184
185 $options .= ($optionGroup ? '</optgroup>' : '');
186 }
187
188 // Build the element
189 $html = ['<div class="form-control-wrap">'];
190
191 if ($hasIcons) {
192 $html[] = '<div class="input-group">';
193 $html[] = '<span class="input-group-addon input-group-icon">';
194 $html[] = $selectedIcon;
195 $html[] = '</span>';
196 }
197
198 $html[] = '<select'
199 . ' id="' . $selectId . '"'
200 . ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'
201 . $this->getValidationDataAsDataAttribute($config)
202 . ' class="form-control form-control-adapt"'
203 . ($size ? ' size="' . $size . '"' : '')
204 . ($disabled ? ' disabled="disabled"' : '')
205 . '>';
206 $html[] = $options;
207 $html[] = '</select>';
208
209 if ($hasIcons) {
210 $html[] = '</div>';
211 }
212
213 $html[] = '</div>';
214
215 // Create icon table:
216 if (!empty($selectIcons) && !$config['noIconsBelowSelect']) {
217 $selectIconColumns = (int)$config['selicon_cols'];
218
219 if (!$selectIconColumns) {
220 $selectIconColumns = count($selectIcons);
221 }
222
223 $selectIconColumns = ($selectIconColumns > 12 ? 12 : $selectIconColumns);
224 $selectIconRows = ceil(count($selectIcons) / $selectIconColumns);
225 $selectIcons = array_pad($selectIcons, $selectIconRows * $selectIconColumns, '');
226
227 $html[] = '<div class="t3js-forms-select-single-icons table-fit table-fit-inline-block">';
228 $html[] = '<table class="table table-condensed table-white table-center">';
229 $html[] = '<tbody>';
230 $html[] = '<tr>';
231
232 foreach ($selectIcons as $i => $selectIcon) {
233 if ($i % $selectIconColumns === 0 && $i !== 0) {
234 $html[] = '</tr>';
235 $html[] = '<tr>';
236 }
237
238 $html[] = '<td>';
239
240 if (is_array($selectIcon)) {
241 $html[] = (!$onlySelectedIconShown ? '<a href="#" title="' . $selectIcon['title'] . '" data-select-index="' . $selectIcon['index'] . '">' : '');
242 $html[] = $selectIcon['icon'];
243 $html[] = (!$onlySelectedIconShown ? '</a>' : '');
244 }
245
246 $html[] = '</td>';
247 }
248
249 $html[] = '</tr>';
250 $html[] = '</tbody>';
251 $html[] = '</table>';
252 $html[] = '</div>';
253 }
254
255 $html = implode(LF, $html);
256
257 // Wizards:
258 if (!$disabled) {
259 $html = $this->renderWizards(
260 array($html),
261 $config['wizards'],
262 $table,
263 $row,
264 $field,
265 $parameterArray,
266 $parameterArray['itemFormElName'],
267 BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras'])
268 );
269 }
270
271 $resultArray = $this->initializeResultArray();
272 $resultArray['html'] = $html;
273 $resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectSingleElement' => implode(LF, [
274 'function(SelectSingleElement) {',
275 'SelectSingleElement.initialize(',
276 GeneralUtility::quoteJSvalue('#' . $selectId) . ',',
277 '{',
278 'onChange: function() {',
279 implode('', $parameterArray['fieldChangeFunc']),
280 '},',
281 'onFocus: function() {',
282 $parameterArray['onFocus'],
283 '},',
284 '}',
285 ');',
286 '}',
287 ])];
288
289 return $resultArray;
290 }
291 }