[BUGFIX] Handle langChildren correctly in flex form
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / SelectSingleBoxElement.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\Imaging\Icon;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Utility\MathUtility;
20 use TYPO3\CMS\Core\Utility\StringUtility;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
23
24 /**
25 * Create a widget with a select box where multiple items can be selected
26 *
27 * This is rendered for config type=select, maxitems > 1, renderMode=singlebox
28 */
29 class SelectSingleBoxElement 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 * This will render a selector box element, or possibly a special construction with two selector boxes.
38 *
39 * @return array As defined in initializeResultArray() of AbstractNode
40 */
41 public function render() {
42 $table = $this->data['tableName'];
43 $field = $this->data['fieldName'];
44 $row = $this->data['databaseRow'];
45 $parameterArray = $this->data['parameterArray'];
46 // Field configuration from TCA:
47 $config = $parameterArray['fieldConf']['config'];
48 $disabled = '';
49 if ($config['readOnly']) {
50 $disabled = ' disabled="disabled"';
51 }
52 $this->resultArray = $this->initializeResultArray();
53 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist.
54 $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
55 $selItems = $parameterArray['fieldConf']['config']['items'];
56
57 // Creating the label for the "No Matching Value" entry.
58 $noMatchingLabel = isset($parameterArray['fieldTSConfig']['noMatchingValue_label'])
59 ? $this->getLanguageService()->sL($parameterArray['fieldTSConfig']['noMatchingValue_label'])
60 : '[ ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue') . ' ]';
61
62 $html = $this->getSingleField_typeSelect_singlebox($table, $field, $row, $parameterArray, $config, $selItems, $noMatchingLabel);
63
64 // Wizards:
65 if (!$disabled) {
66 $html = $this->renderWizards(array($html), $config['wizards'], $table, $row, $field, $parameterArray, $parameterArray['itemFormElName'], $specConf);
67 }
68 $this->resultArray['html'] = $html;
69 return $this->resultArray;
70 }
71
72 /**
73 * Creates a selectorbox list (renderMode = "singlebox")
74 *
75 * @param string $table See getSingleField_typeSelect()
76 * @param string $field See getSingleField_typeSelect()
77 * @param array $row See getSingleField_typeSelect()
78 * @param array $parameterArray See getSingleField_typeSelect()
79 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
80 * @param array $selItems Items available for selection
81 * @param string $noMatchingLabel Label for no-matching-value
82 * @return string The HTML code for the item
83 */
84 protected function getSingleField_typeSelect_singlebox($table, $field, $row, $parameterArray, $config, $selItems, $noMatchingLabel) {
85 $languageService = $this->getLanguageService();
86 // Get values in an array (and make unique, which is fine because there can be no duplicates anyway):
87 $itemArray = array_flip($parameterArray['itemFormElValue']);
88 $item = '';
89 $disabled = '';
90 if ($config['readOnly']) {
91 $disabled = ' disabled="disabled"';
92 }
93 // Traverse the Array of selector box items:
94 $opt = array();
95 // Used to accumulate the JS needed to restore the original selection.
96 $restoreCmd = array();
97 $c = 0;
98 foreach ($selItems as $p) {
99 // Selected or not by default:
100 $sM = '';
101 if (isset($itemArray[$p[1]])) {
102 $sM = ' selected="selected"';
103 $restoreCmd[] = 'document.editform[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'] . '[]') . '].options[' . $c . '].selected=1;';
104 unset($itemArray[$p[1]]);
105 }
106 // Non-selectable element:
107 $nonSel = '';
108 if ((string)$p[1] === '--div--') {
109 $nonSel = ' onclick="this.selected=0;" class="formcontrol-select-divider"';
110 }
111 // Compile <option> tag:
112 $opt[] = '<option value="' . htmlspecialchars($p[1]) . '"' . $sM . $nonSel . '>'
113 . htmlspecialchars($p[0], ENT_COMPAT, 'UTF-8', FALSE) . '</option>';
114 $c++;
115 }
116 // Remaining values:
117 if (!empty($itemArray) && !$parameterArray['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
118 foreach ($itemArray as $theNoMatchValue => $temp) {
119 // Compile <option> tag:
120 array_unshift($opt, '<option value="' . htmlspecialchars($theNoMatchValue) . '" selected="selected">'
121 . htmlspecialchars(@sprintf($noMatchingLabel, $theNoMatchValue), ENT_COMPAT, 'UTF-8', FALSE) . '</option>');
122 }
123 }
124 // Compile selector box:
125 $sOnChange = implode('', $parameterArray['fieldChangeFunc']);
126 $selector_itemListStyle = isset($config['itemListStyle'])
127 ? ' style="' . htmlspecialchars($config['itemListStyle']) . '"'
128 : '';
129 $size = (int)$config['size'];
130 $cssPrefix = $size === 1 ? 'tceforms-select' : 'tceforms-multiselect';
131 $size = $config['autoSizeMax']
132 ? MathUtility::forceIntegerInRange(count($selItems) + 1, MathUtility::forceIntegerInRange($size, 1), $config['autoSizeMax'])
133 : $size;
134 $selectBox = '<select id="' . StringUtility::getUniqueId($cssPrefix) . '" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '[]" '
135 . 'class="form-control ' . $cssPrefix . '"' . ($size ? ' size="' . $size . '" ' : '')
136 . ' multiple="multiple" onchange="' . htmlspecialchars($sOnChange) . '"' . $parameterArray['onFocus']
137 . ' ' . $this->getValidationDataAsDataAttribute($config) . $selector_itemListStyle . $disabled . '>
138 ' . implode('
139 ', $opt) . '
140 </select>';
141 // Add an empty hidden field which will send a blank value if all items are unselected.
142 if (!$disabled) {
143 $item .= '<input type="hidden" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="" />';
144 }
145 // Put it all into a table:
146 $onClick = htmlspecialchars('document.editform[' . GeneralUtility::quoteJSvalue($parameterArray['itemFormElName'] . '[]') . '].selectedIndex=-1;' . implode('', $restoreCmd) . ' return false;');
147 $width = $this->formMaxWidth($this->defaultInputWidth);
148 $item .= '
149 <div class="form-control-wrap" ' . ($width ? ' style="max-width: ' . $width . 'px"' : '') . '>
150 <div class="form-wizards-wrap form-wizards-aside">
151 <div class="form-wizards-element">
152 ' . $selectBox . '
153 </div>
154 <div class="form-wizards-items">
155 <a href="#" class="btn btn-default" onclick="' . $onClick . '" title="' . $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.revertSelection', TRUE) . '">'
156 . $this->iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL) . '</a>
157 </div>
158 </div>
159 </div>
160 <p>
161 <em>' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.holdDownCTRL')) . '</em>
162 </p>
163 ';
164 return $item;
165 }
166
167 }