116caa68eff1ab22071f4ba25c8b538147e92d9b
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / SelectMultipleSideBySideElement.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\Core\Utility\StringUtility;
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
21 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
22 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
23
24 /**
25 * Render a widget with two boxes side by side.
26 *
27 * This is rendered for config type=select, maxitems > 1, no other renderMode set
28 */
29 class SelectMultipleSideBySideElement 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 side by side element.
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
54 // Creating the label for the "No Matching Value" entry.
55 $noMatchingLabel = isset($parameterArray['fieldTSConfig']['noMatchingValue_label'])
56 ? $this->getLanguageService()->sL($parameterArray['fieldTSConfig']['noMatchingValue_label'])
57 : '[ ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue') . ' ]';
58
59 $html = $this->getSingleField_typeSelect_multiple($table, $field, $parameterArray, $config, $noMatchingLabel);
60
61 // Wizards:
62 if (!$disabled) {
63 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist.
64 $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
65 $html = $this->renderWizards(array($html), $config['wizards'], $table, $row, $field, $parameterArray, $parameterArray['itemFormElName'], $specConf);
66 }
67 $this->resultArray['html'] = $html;
68 return $this->resultArray;
69 }
70
71 /**
72 * Creates a multiple-selector box (two boxes, side-by-side)
73 *
74 * @param string $table See getSingleField_typeSelect()
75 * @param string $field See getSingleField_typeSelect()
76 * @param array $parameterArray See getSingleField_typeSelect()
77 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
78 * @param string $noMatchingLabel Label for no-matching-value
79 * @return string The HTML code for the item
80 */
81 protected function getSingleField_typeSelect_multiple($table, $field, $parameterArray, $config, $noMatchingLabel) {
82 $languageService = $this->getLanguageService();
83 $selItems = $parameterArray['fieldConf']['config']['items'];
84 $item = '';
85 $disabled = '';
86 if ($config['readOnly']) {
87 $disabled = ' disabled="disabled"';
88 }
89 // Setting this hidden field (as a flag that JavaScript can read out)
90 if (!$disabled) {
91 $item .= '<input type="hidden" data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="' . ($config['multiple'] ? 1 : 0) . '" />';
92 }
93 // Set max and min items:
94 $maxitems = MathUtility::forceIntegerInRange($config['maxitems'], 0);
95 if (!$maxitems) {
96 $maxitems = 100000;
97 }
98 // Get "removeItems":
99 $removeItems = GeneralUtility::trimExplode(',', $parameterArray['fieldTSConfig']['removeItems'], TRUE);
100 // Get the array with selected items:
101 $itemsArray = $parameterArray['itemFormElValue'];
102
103 // Perform modification of the selected items array:
104 // @todo: this part should probably be moved to TcaSelectValues provider?!
105 foreach ($itemsArray as $itemNumber => $itemValue) {
106 $itemArray = array(
107 0 => $itemValue,
108 1 => '',
109 );
110 $itemIcon = NULL;
111 $isRemoved = in_array($itemValue, $removeItems)
112 || $config['type'] == 'select' && $config['authMode']
113 && !$this->getBackendUserAuthentication()->checkAuthMode($table, $field, $itemValue, $config['authMode']);
114 if ($isRemoved && !$parameterArray['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
115 $itemArray[1] = rawurlencode(@sprintf($noMatchingLabel, $itemValue));
116 } else {
117 if (isset($parameterArray['fieldTSConfig']['altLabels.'][$itemValue])) {
118 $itemArray[1] = rawurlencode($languageService->sL($parameterArray['fieldTSConfig']['altLabels.'][$itemValue]));
119 }
120 if (isset($parameterArray['fieldTSConfig']['altIcons.'][$itemValue])) {
121 $itemArray[2] = $parameterArray['fieldTSConfig']['altIcons.'][$itemValue];
122 }
123 }
124 if ($itemArray[1] === '') {
125 foreach ($selItems as $selItem) {
126 if ($selItem[1] == $itemValue) {
127 $itemArray[1] = $selItem[0];
128 break;
129 }
130 }
131 }
132 $itemsArray[$itemNumber] = implode('|', $itemArray);
133 }
134
135 // size must be at least two, as there are always maxitems > 1 (see parent function)
136 if (isset($config['size'])) {
137 $size = (int)$config['size'];
138 } else {
139 $size = 2;
140 }
141 $size = $config['autoSizeMax'] ? MathUtility::forceIntegerInRange(count($itemsArray) + 1, MathUtility::forceIntegerInRange($size, 1), $config['autoSizeMax']) : $size;
142
143 $itemsToSelect = '';
144 $filterTextfield = '';
145 $filterSelectbox = '';
146 if (!$disabled) {
147 // Create option tags:
148 $opt = array();
149 foreach ($selItems as $p) {
150 $opt[] = '<option value="' . htmlspecialchars($p[1]) . '"'
151 . ' title="' . $p[0] . '">' . $p[0] . '</option>';
152 }
153 // Put together the selector box:
154 $selector_itemListStyle = isset($config['itemListStyle'])
155 ? ' style="' . htmlspecialchars($config['itemListStyle']) . '"'
156 : '';
157 $sOnChange = implode('', $parameterArray['fieldChangeFunc']);
158
159 $multiSelectId = StringUtility::getUniqueId('tceforms-multiselect-');
160 $itemsToSelect = '
161 <select data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '" data-exclusivevalues="'
162 . htmlspecialchars($config['exclusiveKeys']) . '" id="' . $multiSelectId . '" data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" '
163 . ' class="form-control t3js-formengine-select-itemstoselect" '
164 . ($size ? ' size="' . $size . '"' : '') . ' onchange="' . htmlspecialchars($sOnChange) . '"'
165 . $parameterArray['onFocus'] . $this->getValidationDataAsDataAttribute($config) . $selector_itemListStyle . '>
166 ' . implode('
167 ', $opt) . '
168 </select>';
169
170 // enable filter functionality via a text field
171 if ($config['enableMultiSelectFilterTextfield']) {
172 $filterTextfield = '
173 <span class="input-group input-group-sm">
174 <span class="input-group-addon">
175 <span class="fa fa-filter"></span>
176 </span>
177 <input class="t3js-formengine-multiselect-filter-textfield form-control" value="" />
178 </span>';
179 }
180
181 // enable filter functionality via a select
182 if (isset($config['multiSelectFilterItems']) && is_array($config['multiSelectFilterItems']) && count($config['multiSelectFilterItems']) > 1) {
183 $filterDropDownOptions = array();
184 foreach ($config['multiSelectFilterItems'] as $optionElement) {
185 $optionValue = $languageService->sL(isset($optionElement[1]) && $optionElement[1] != '' ? $optionElement[1]
186 : $optionElement[0]);
187 $filterDropDownOptions[] = '<option value="' . htmlspecialchars($languageService->sL($optionElement[0])) . '">'
188 . htmlspecialchars($optionValue) . '</option>';
189 }
190 $filterSelectbox = '<select class="form-control input-sm t3js-formengine-multiselect-filter-dropdown">
191 ' . implode('
192 ', $filterDropDownOptions) . '
193 </select>';
194 }
195 }
196
197 if (!empty(trim($filterSelectbox)) && !empty(trim($filterTextfield))) {
198 $filterSelectbox = '<div class="form-multigroup-item form-multigroup-element">' . $filterSelectbox . '</div>';
199 $filterTextfield = '<div class="form-multigroup-item form-multigroup-element">' . $filterTextfield . '</div>';
200 $selectBoxFilterContents = '<div class="t3js-formengine-multiselect-filter-container form-multigroup-wrap">' . $filterSelectbox . $filterTextfield . '</div>';
201 } else {
202 $selectBoxFilterContents = trim($filterSelectbox . ' ' . $filterTextfield);
203 }
204
205 // Pass to "dbFileIcons" function:
206 $params = array(
207 'size' => $size,
208 'autoSizeMax' => MathUtility::forceIntegerInRange($config['autoSizeMax'], 0),
209 'style' => isset($config['selectedListStyle'])
210 ? ' style="' . htmlspecialchars($config['selectedListStyle']) . '"'
211 : '',
212 'dontShowMoveIcons' => $maxitems <= 1,
213 'maxitems' => $maxitems,
214 'info' => '',
215 'headers' => array(
216 'selector' => $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.selected'),
217 'items' => $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.items'),
218 'selectorbox' => $selectBoxFilterContents,
219 ),
220 'noBrowser' => 1,
221 'rightbox' => $itemsToSelect,
222 'readOnly' => $disabled
223 );
224 $item .= $this->dbFileIcons($parameterArray['itemFormElName'], '', '', $itemsArray, '', $params, $parameterArray['onFocus']);
225 return $item;
226 }
227
228 /**
229 * @return BackendUserAuthentication
230 */
231 protected function getBackendUserAuthentication() {
232 return $GLOBALS['BE_USER'];
233 }
234
235 }