[!!!][TASK] TCA: Remove wizard _HIDDENFIELD and hideParent
[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\MathUtility;
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 * @var array Result array given returned by render() - This property is a helper until class is properly refactored
33 */
34 protected $resultArray = array();
35
36 /**
37 * Render single element
38 *
39 * @return array As defined in initializeResultArray() of AbstractNode
40 */
41 public function render() {
42 $table = $this->globalOptions['table'];
43 $field = $this->globalOptions['fieldName'];
44 $row = $this->globalOptions['databaseRow'];
45 $parameterArray = $this->globalOptions['parameterArray'];
46 $config = $parameterArray['fieldConf']['config'];
47
48 $disabled = '';
49 if ($this->isGlobalReadonly() || $config['readOnly']) {
50 $disabled = ' disabled="disabled"';
51 }
52
53 $this->resultArray = $this->initializeResultArray();
54
55 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist.
56 $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
57 $selItems = FormEngineUtility::getSelectItems($table, $field, $row, $parameterArray);
58
59 // Creating the label for the "No Matching Value" entry.
60 $noMatchingLabel = isset($parameterArray['fieldTSConfig']['noMatchingValue_label'])
61 ? $this->getLanguageService()->sL($parameterArray['fieldTSConfig']['noMatchingValue_label'])
62 : '[ ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue') . ' ]';
63
64 $html = $this->getSingleField_typeSelect_single($table, $field, $row, $parameterArray, $config, $selItems, $noMatchingLabel);
65
66 // Wizards:
67 if (!$disabled) {
68 $html = $this->renderWizards(array($html), $config['wizards'], $table, $row, $field, $parameterArray, $parameterArray['itemFormElName'], $specConf);
69 }
70 $this->resultArray['html'] = $html;
71 return $this->resultArray;
72 }
73
74 /**
75 * Creates a single-selector box
76 *
77 * @param string $table See getSingleField_typeSelect()
78 * @param string $field See getSingleField_typeSelect()
79 * @param array $row See getSingleField_typeSelect()
80 * @param array $parameterArray See getSingleField_typeSelect()
81 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
82 * @param array $selectItems Items available for selection
83 * @param string $noMatchingLabel Label for no-matching-value
84 * @return string The HTML code for the item
85 */
86 protected function getSingleField_typeSelect_single($table, $field, $row, $parameterArray, $config, $selectItems, $noMatchingLabel) {
87 // Check against inline uniqueness
88 /** @var InlineStackProcessor $inlineStackProcessor */
89 $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
90 $inlineStackProcessor->initializeByGivenStructure($this->globalOptions['inlineStructure']);
91 $inlineParent = $inlineStackProcessor->getStructureLevel(-1);
92 $uniqueIds = NULL;
93 if (is_array($inlineParent) && $inlineParent['uid']) {
94 $inlineObjectName = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->globalOptions['inlineFirstPid']);
95 $inlineFormName = $inlineStackProcessor->getCurrentStructureFormPrefix();
96 if ($inlineParent['config']['foreign_table'] == $table && $inlineParent['config']['foreign_unique'] == $field) {
97 $uniqueIds = $this->globalOptions['inlineData']['unique'][$inlineObjectName . '-' . $table]['used'];
98 $parameterArray['fieldChangeFunc']['inlineUnique'] = 'inline.updateUnique(this,'
99 . GeneralUtility::quoteJSvalue($inlineObjectName . '-' . $table) . ','
100 . GeneralUtility::quoteJSvalue($inlineFormName) . ','
101 . GeneralUtility::quoteJSvalue($row['uid']) . ');';
102 }
103 // hide uid of parent record for symmetric relations
104 if (
105 $inlineParent['config']['foreign_table'] == $table
106 && ($inlineParent['config']['foreign_field'] == $field || $inlineParent['config']['symmetric_field'] == $field)
107 ) {
108 $uniqueIds[] = $inlineParent['uid'];
109 }
110 }
111
112 // Initialization:
113 $selectId = str_replace('.', '', uniqid('tceforms-select-', TRUE));
114 $selectedIndex = 0;
115 $selectedIcon = '';
116 $noMatchingValue = 1;
117 $onlySelectedIconShown = 0;
118 $size = (int)$config['size'];
119
120 // Style set on <select/>
121 $out = '';
122 $options = '';
123 $disabled = FALSE;
124 if ($this->isGlobalReadonly() || $config['readOnly']) {
125 $disabled = TRUE;
126 $onlySelectedIconShown = 1;
127 }
128
129 // Icon configuration:
130 if ($config['suppress_icons'] === 'IF_VALUE_FALSE') {
131 $suppressIcons = !$parameterArray['itemFormElValue'] ? 1 : 0;
132 } elseif ($config['suppress_icons'] === 'ONLY_SELECTED') {
133 $suppressIcons = 0;
134 $onlySelectedIconShown = 1;
135 } elseif ($config['suppress_icons']) {
136 $suppressIcons = 1;
137 } else {
138 $suppressIcons = 0;
139 }
140
141 // Prepare groups
142 $selectItemCounter = 0;
143 $selectItemGroupCount = 0;
144 $selectItemGroups = array();
145 $selectIcons = array();
146 foreach ($selectItems as $item) {
147 if ($item[1] === '--div--') {
148 // IS OPTGROUP
149 if ($selectItemCounter !== 0) {
150 $selectItemGroupCount++;
151 }
152 $selectItemGroups[$selectItemGroupCount]['header'] = array(
153 'title' => $item[0],
154 'icon' => (!empty($item[2]) ? FormEngineUtility::getIconHtml($item[2]) : ''),
155 );
156 } else {
157 // IS ITEM
158 $title = htmlspecialchars($item['0'], ENT_COMPAT, 'UTF-8', FALSE);
159 $icon = !empty($item[2]) ? FormEngineUtility::getIconHtml($item[2], $title, $title) : '';
160 $selected = ((string)$parameterArray['itemFormElValue'] === (string)$item[1] ? 1 : 0);
161 if ($selected) {
162 $selectedIndex = $selectItemCounter;
163 $selectedIcon = $icon;
164 $noMatchingValue = 0;
165 }
166 $selectItemGroups[$selectItemGroupCount]['items'][] = array(
167 'title' => $title,
168 'value' => $item[1],
169 'icon' => $icon,
170 'selected' => $selected,
171 'index' => $selectItemCounter
172 );
173 // ICON
174 if ($icon && !$suppressIcons && (!$onlySelectedIconShown || $selected)) {
175 $onClick = 'document.editform[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . '].selectedIndex=' . $selectItemCounter . ';';
176 if ($config['iconsInOptionTags']) {
177 $onClick .= 'document.getElementById(' . GeneralUtility::quoteJSvalue($selectId . '_icon') . ').innerHTML = '
178 . 'document.editform[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName']) . ']'
179 . '.options[' . $selectItemCounter . '].getAttribute(\'data-icon\'); ';
180 }
181 $onClick .= implode('', $parameterArray['fieldChangeFunc']);
182 $onClick .= 'this.blur();return false;';
183 $selectIcons[] = array(
184 'title' => $title,
185 'icon' => $icon,
186 'index' => $selectItemCounter,
187 'onClick' => $onClick
188 );
189 }
190 $selectItemCounter++;
191 }
192
193 }
194
195 // No-matching-value:
196 if ($parameterArray['itemFormElValue'] && $noMatchingValue && !$parameterArray['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
197 $noMatchingLabel = @sprintf($noMatchingLabel, $parameterArray['itemFormElValue']);
198 $options = '<option value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" selected="selected">' . htmlspecialchars($noMatchingLabel) . '</option>';
199 } elseif (!$selectedIcon && $selectItemGroups[0]['items'][0]['icon']) {
200 $selectedIcon = $selectItemGroups[0]['items'][0]['icon'];
201 }
202
203 // Process groups
204 foreach ($selectItemGroups as $selectItemGroup) {
205 // suppress groups without items
206 if (empty($selectItemGroup['items'])) {
207 continue;
208 }
209
210 $optionGroup = is_array($selectItemGroup['header']);
211 $options .= ($optionGroup ? '<optgroup label="' . htmlspecialchars($selectItemGroup['header']['title'], ENT_COMPAT, 'UTF-8', FALSE) . '">' : '');
212 if (is_array($selectItemGroup['items'])) {
213 foreach ($selectItemGroup['items'] as $item) {
214 $options .= '<option value="' . htmlspecialchars($item['value']) . '" data-icon="' .
215 htmlspecialchars($item['icon']) . '"'
216 . ($item['selected'] ? ' selected="selected"' : '') . '>' . $item['title'] . '</option>';
217 }
218 }
219 $options .= ($optionGroup ? '</optgroup>' : '');
220 }
221
222 // Create item form fields:
223 $sOnChange = 'if (this.options[this.selectedIndex].value==\'--div--\') {this.selectedIndex=' . $selectedIndex . ';} ';
224 if ($config['iconsInOptionTags']) {
225 $sOnChange .= 'document.getElementById(' . GeneralUtility::quoteJSvalue($selectId . '_icon') . ').innerHTML = this.options[this.selectedIndex].getAttribute(\'data-icon\'); ';
226 }
227 $sOnChange .= implode('', $parameterArray['fieldChangeFunc']);
228
229 // Add icons in option tags
230 $prepend = '';
231 $append = '';
232 if ($config['iconsInOptionTags']) {
233 $prepend = '<div class="input-group"><div id="' . $selectId . '_icon" class="input-group-addon input-group-icon t3js-formengine-select-prepend">' . $selectedIcon . '</div>';
234 $append = '</div>';
235 }
236
237 // Build the element
238 $out .= '
239 <div class="form-control-wrap">
240 ' . $prepend . '
241 <select'
242 . ' id="' . $selectId . '"'
243 . ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'
244 . $this->getValidationDataAsDataAttribute($config)
245 . ' class="form-control form-control-adapt"'
246 . ($size ? ' size="' . $size . '"' : '')
247 . ' onchange="' . htmlspecialchars($sOnChange) . '"'
248 . $parameterArray['onFocus']
249 . ($disabled ? ' disabled="disabled"' : '')
250 . '>
251 ' . $options . '
252 </select>
253 ' . $append . '
254 </div>';
255
256 // Create icon table:
257 if (!empty($selectIcons) && !$config['noIconsBelowSelect']) {
258 $selectIconColumns = (int)$config['selicon_cols'];
259 if (!$selectIconColumns) {
260 $selectIconColumns = count($selectIcons);
261 }
262 $selectIconColumns = ($selectIconColumns > 12 ? 12 : $selectIconColumns);
263 $selectIconRows = ceil(count($selectIcons) / $selectIconColumns);
264 $selectIcons = array_pad($selectIcons, $selectIconRows * $selectIconColumns, '');
265 $out .= '<div class="table-fit table-fit-inline-block"><table class="table table-condensed table-white table-center"><tbody><tr>';
266 $selectIconTotalCount = count($selectIcons);
267 for ($selectIconCount = 0; $selectIconCount < $selectIconTotalCount; $selectIconCount++) {
268 if ($selectIconCount % $selectIconColumns === 0 && $selectIconCount !== 0) {
269 $out .= '</tr><tr>';
270 }
271 $out .= '<td>';
272 if (is_array($selectIcons[$selectIconCount])) {
273 $out .= (!$onlySelectedIconShown ? '<a href="#" title="' . $selectIcons[$selectIconCount]['title'] . '" onClick="' . htmlspecialchars($selectIcons[$selectIconCount]['onClick']) . '">' : '');
274 $out .= $selectIcons[$selectIconCount]['icon'];
275 $out .= (!$onlySelectedIconShown ? '</a>' : '');
276 }
277 $out .= '</td>';
278 }
279 $out .= '</tr></tbody></table></div>';
280 }
281
282 return $out;
283 }
284
285 }