138374a23e495f1bc576d98f4727b384d3b6c97a
[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 if (!empty($parameterArray['itemFormElValue'])) {
110 $selectedValue = (string)$parameterArray['itemFormElValue'][0];
111 }
112 foreach ($selectItems as $item) {
113 if ($item[1] === '--div--') {
114 // IS OPTGROUP
115 if ($selectItemCounter !== 0) {
116 $selectItemGroupCount++;
117 }
118 $selectItemGroups[$selectItemGroupCount]['header'] = array(
119 'title' => $item[0],
120 'icon' => (!empty($item[2]) ? FormEngineUtility::getIconHtml($item[2]) : ''),
121 );
122 } else {
123 // IS ITEM
124 $title = htmlspecialchars($item['0'], ENT_COMPAT, 'UTF-8', FALSE);
125 $icon = !empty($item[2]) ? FormEngineUtility::getIconHtml($item[2], $title, $title) : '';
126 $selected = $selectedValue === (string)$item[1];
127 if ($selected) {
128 $selectedIndex = $selectItemCounter;
129 $selectedIcon = $icon;
130 $selectedValueFound = TRUE;
131 }
132 $selectItemGroups[$selectItemGroupCount]['items'][] = array(
133 'title' => $title,
134 'value' => $item[1],
135 'icon' => $icon,
136 'selected' => $selected,
137 'index' => $selectItemCounter
138 );
139 // ICON
140 if ($icon && !$suppressIcons && (!$onlySelectedIconShown || $selected)) {
141 $onClick = 'document.editform[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . '].selectedIndex=' . $selectItemCounter . ';';
142 $onClick .= implode('', $parameterArray['fieldChangeFunc']);
143 $onClick .= 'this.blur();return false;';
144 $selectIcons[] = array(
145 'title' => $title,
146 'icon' => $icon,
147 'index' => $selectItemCounter,
148 'onClick' => $onClick
149 );
150 }
151 $selectItemCounter++;
152 }
153
154 }
155
156 // No-matching-value:
157 if ($selectedValue && !$selectedValueFound && !$parameterArray['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
158 $noMatchingLabel = @sprintf($noMatchingLabel, $selectedValue);
159 $options = '<option value="' . htmlspecialchars($selectedValue) . '" selected="selected">' . htmlspecialchars($noMatchingLabel) . '</option>';
160 } elseif (!$selectedIcon && $selectItemGroups[0]['items'][0]['icon']) {
161 $selectedIcon = $selectItemGroups[0]['items'][0]['icon'];
162 }
163
164 // Process groups
165 foreach ($selectItemGroups as $selectItemGroup) {
166 // suppress groups without items
167 if (empty($selectItemGroup['items'])) {
168 continue;
169 }
170
171 $optionGroup = is_array($selectItemGroup['header']);
172 $options .= ($optionGroup ? '<optgroup label="' . htmlspecialchars($selectItemGroup['header']['title'], ENT_COMPAT, 'UTF-8', FALSE) . '">' : '');
173 if (is_array($selectItemGroup['items'])) {
174 foreach ($selectItemGroup['items'] as $item) {
175 $options .= '<option value="' . htmlspecialchars($item['value']) . '" data-icon="' .
176 htmlspecialchars($item['icon']) . '"'
177 . ($item['selected'] ? ' selected="selected"' : '') . '>' . $item['title'] . '</option>';
178 }
179 }
180 $options .= ($optionGroup ? '</optgroup>' : '');
181 }
182
183 // Create item form fields:
184 $sOnChange = 'if (this.options[this.selectedIndex].value==\'--div--\') {this.selectedIndex=' . $selectedIndex . ';} ';
185 $sOnChange .= implode('', $parameterArray['fieldChangeFunc']);
186
187 // Build the element
188 $html = '
189 <div class="form-control-wrap">
190 <select'
191 . ' id="' . $selectId . '"'
192 . ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'
193 . $this->getValidationDataAsDataAttribute($config)
194 . ' class="form-control form-control-adapt"'
195 . ($size ? ' size="' . $size . '"' : '')
196 . ' onchange="' . htmlspecialchars($sOnChange) . '"'
197 . $parameterArray['onFocus']
198 . ($disabled ? ' disabled="disabled"' : '')
199 . '>
200 ' . $options . '
201 </select>
202 </div>';
203
204 // Create icon table:
205 if (!empty($selectIcons) && !$config['noIconsBelowSelect']) {
206 $selectIconColumns = (int)$config['selicon_cols'];
207 if (!$selectIconColumns) {
208 $selectIconColumns = count($selectIcons);
209 }
210 $selectIconColumns = ($selectIconColumns > 12 ? 12 : $selectIconColumns);
211 $selectIconRows = ceil(count($selectIcons) / $selectIconColumns);
212 $selectIcons = array_pad($selectIcons, $selectIconRows * $selectIconColumns, '');
213 $html .= '<div class="table-fit table-fit-inline-block"><table class="table table-condensed table-white table-center"><tbody><tr>';
214 $selectIconTotalCount = count($selectIcons);
215 for ($selectIconCount = 0; $selectIconCount < $selectIconTotalCount; $selectIconCount++) {
216 if ($selectIconCount % $selectIconColumns === 0 && $selectIconCount !== 0) {
217 $html .= '</tr><tr>';
218 }
219 $html .= '<td>';
220 if (is_array($selectIcons[$selectIconCount])) {
221 $html .= (!$onlySelectedIconShown ? '<a href="#" title="' . $selectIcons[$selectIconCount]['title'] . '" onClick="' . htmlspecialchars($selectIcons[$selectIconCount]['onClick']) . '">' : '');
222 $html .= $selectIcons[$selectIconCount]['icon'];
223 $html .= (!$onlySelectedIconShown ? '</a>' : '');
224 }
225 $html .= '</td>';
226 }
227 $html .= '</tr></tbody></table></div>';
228 }
229
230 // Wizards:
231 if (!$disabled) {
232 $html = $this->renderWizards(
233 array($html),
234 $config['wizards'],
235 $table,
236 $row,
237 $field,
238 $parameterArray,
239 $parameterArray['itemFormElName'],
240 BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras'])
241 );
242 }
243
244 $resultArray = $this->initializeResultArray();
245 $resultArray['html'] = $html;
246
247 return $resultArray;
248 }
249 }