8739d0f4d8b80a780a83479bf72f34f379a74307
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / SelectCheckBoxElement.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\Backend\Utility\IconUtility;
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
21
22 /**
23 * Creates a widget with check box elements.
24 *
25 * This is rendered for config type=select, renderMode=checkbox, maxitems > 1
26 */
27 class SelectCheckBoxElement extends AbstractFormElement {
28
29 /**
30 * @var array Result array given returned by render() - This property is a helper until class is properly refactored
31 */
32 protected $resultArray = array();
33
34 /**
35 * Render check boxes
36 *
37 * @return array As defined in initializeResultArray() of AbstractNode
38 */
39 public function render() {
40 $table = $this->globalOptions['table'];
41 $field = $this->globalOptions['fieldName'];
42 $row = $this->globalOptions['databaseRow'];
43 $parameterArray = $this->globalOptions['parameterArray'];
44 // Field configuration from TCA:
45 $config = $parameterArray['fieldConf']['config'];
46 $disabled = '';
47 if ($this->isGlobalReadonly() || $config['readOnly']) {
48 $disabled = ' disabled="disabled"';
49 }
50 $this->resultArray = $this->initializeResultArray();
51 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist.
52 $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
53 $selItems = FormEngineUtility::getSelectItems($table, $field, $row, $parameterArray);
54
55 // Creating the label for the "No Matching Value" entry.
56 $noMatchingLabel = isset($parameterArray['fieldTSConfig']['noMatchingValue_label'])
57 ? $this->getLanguageService()->sL($parameterArray['fieldTSConfig']['noMatchingValue_label'])
58 : '[ ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue') . ' ]';
59
60 $html = $this->getSingleField_typeSelect_checkbox($table, $field, $row, $parameterArray, $config, $selItems, $noMatchingLabel);
61
62 // Wizards:
63 if (!$disabled) {
64 $altItem = '<input type="hidden" name="' . $parameterArray['itemFormElName'] . '" value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" />';
65 $html = $this->renderWizards(array($html, $altItem), $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 checkbox list (renderMode = "checkbox")
73 *
74 * @param string $table See getSingleField_typeSelect()
75 * @param string $field See getSingleField_typeSelect()
76 * @param array $row See getSingleField_typeSelect()
77 * @param array $parameterArray See getSingleField_typeSelect()
78 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
79 * @param array $selItems Items available for selection
80 * @param string $noMatchingLabel Label for no-matching-value
81 * @return string The HTML code for the item
82 */
83 protected function getSingleField_typeSelect_checkbox($table, $field, $row, $parameterArray, $config, $selItems, $noMatchingLabel) {
84 if (empty($selItems)) {
85 return '';
86 }
87 // Get values in an array (and make unique, which is fine because there can be no duplicates anyway):
88 $itemArray = array_flip(FormEngineUtility::extractValuesOnlyFromValueLabelList($parameterArray['itemFormElValue']));
89 $output = '';
90
91 // Disabled
92 $disabled = 0;
93 if ($this->isGlobalReadonly() || $config['readOnly']) {
94 $disabled = 1;
95 }
96 // Traverse the Array of selector box items:
97 $groups = array();
98 $currentGroup = 0;
99 $c = 0;
100 $sOnChange = '';
101 if (!$disabled) {
102 $sOnChange = implode('', $parameterArray['fieldChangeFunc']);
103 // Used to accumulate the JS needed to restore the original selection.
104 foreach ($selItems as $p) {
105 // Non-selectable element:
106 if ($p[1] === '--div--') {
107 $selIcon = '';
108 if (isset($p[2]) && $p[2] != 'empty-empty') {
109 $selIcon = FormEngineUtility::getIconHtml($p[2]);
110 }
111 $currentGroup++;
112 $groups[$currentGroup]['header'] = array(
113 'icon' => $selIcon,
114 'title' => htmlspecialchars($p[0])
115 );
116 } else {
117
118 // Check if some help text is available
119 // Since TYPO3 4.5 help text is expected to be an associative array
120 // with two key, "title" and "description"
121 // For the sake of backwards compatibility, we test if the help text
122 // is a string and use it as a description (this could happen if items
123 // are modified with an itemProcFunc)
124 $hasHelp = FALSE;
125 $help = '';
126 $helpArray = array();
127 if (is_array($p[3]) && count($p[3]) > 0 || !empty($p[3])) {
128 $hasHelp = TRUE;
129 if (is_array($p[3])) {
130 $helpArray = $p[3];
131 } else {
132 $helpArray['description'] = $p[3];
133 }
134 }
135 if ($hasHelp) {
136 $help = BackendUtility::wrapInHelp('', '', '', $helpArray);
137 }
138
139 // Selected or not by default:
140 $checked = 0;
141 if (isset($itemArray[$p[1]])) {
142 $checked = 1;
143 unset($itemArray[$p[1]]);
144 }
145
146 // Build item array
147 $groups[$currentGroup]['items'][] = array(
148 'id' => str_replace('.', '', uniqid('select_checkbox_row_', TRUE)),
149 'name' => $parameterArray['itemFormElName'] . '[' . $c . ']',
150 'value' => $p[1],
151 'checked' => $checked,
152 'disabled' => $disabled,
153 'class' => '',
154 'icon' => (!empty($p[2]) ? FormEngineUtility::getIconHtml($p[2]) : IconUtility::getSpriteIcon('empty-empty')),
155 'title' => htmlspecialchars($p[0], ENT_COMPAT, 'UTF-8', FALSE),
156 'help' => $help
157 );
158 $c++;
159 }
160 }
161 }
162 // Remaining values (invalid):
163 if (count($itemArray) && !$parameterArray['fieldTSConfig']['disableNoMatchingValueElement'] && !$config['disableNoMatchingValueElement']) {
164 $currentGroup++;
165 foreach ($itemArray as $theNoMatchValue => $temp) {
166 // Build item array
167 $groups[$currentGroup]['items'][] = array(
168 'id' => str_replace('.', '', uniqid('select_checkbox_row_', TRUE)),
169 'name' => $parameterArray['itemFormElName'] . '[' . $c . ']',
170 'value' => $theNoMatchValue,
171 'checked' => 1,
172 'disabled' => $disabled,
173 'class' => 'danger',
174 'icon' => '',
175 'title' => htmlspecialchars(@sprintf($noMatchingLabel, $theNoMatchValue), ENT_COMPAT, 'UTF-8', FALSE),
176 'help' => ''
177 );
178 $c++;
179 }
180 }
181 // Add an empty hidden field which will send a blank value if all items are unselected.
182 $output .= '<input type="hidden" class="select-checkbox" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="" />';
183
184 // Building the checkboxes
185 foreach ($groups as $groupKey => $group) {
186 $groupId = htmlspecialchars($parameterArray['itemFormElID']) . '-group-' . $groupKey;
187 $output .= '<div class="panel panel-default">';
188 if (is_array($group['header'])) {
189 $output .= '
190 <div class="panel-heading">
191 <a data-toggle="collapse" href="#' . $groupId . '" aria-expanded="true" aria-controls="' . $groupId . '">
192 ' . $group['header']['icon'] . '
193 ' . $group['header']['title'] . '
194 </a>
195 </div>
196 ';
197 }
198 if (is_array($group['items']) && !empty($group['items'])) {
199 $tableRows = '';
200 $checkGroup = array();
201 $uncheckGroup = array();
202 $resetGroup = array();
203
204 // Render rows
205 foreach ($group['items'] as $item) {
206 $tableRows .= '
207 <tr class="' . $item['class'] . '">
208 <td class="col-checkbox">
209 <input type="checkbox"
210 id="' . $item['id'] . '"
211 name="' . htmlspecialchars($item['name']) . '"
212 value="' . htmlspecialchars($item['value']) . '"
213 onclick="' . htmlspecialchars($sOnChange) . '"
214 ' . ($item['checked'] ? ' checked=checked' : '') . '
215 ' . ($item['disabled'] ? ' disabled=disabled' : '') . '
216 ' . $parameterArray['onFocus'] . ' />
217 </td>
218 <td class="col-icon">
219 <label class="label-block" for="' . $item['id'] . '">' . $item['icon'] . '</label>
220 </td>
221 <td class="col-title">
222 <label class="label-block" for="' . $item['id'] . '">' . $item['title'] . '</label>
223 </td>
224 <td>' . $item['help'] . '</td>
225 </tr>
226 ';
227 $checkGroup[] = 'document.editform[' . GeneralUtility::quoteJSvalue($item['name']) . '].checked=1;';
228 $uncheckGroup[] = 'document.editform[' . GeneralUtility::quoteJSvalue($item['name']) . '].checked=0;';
229 $resetGroup[] = 'document.editform[' . GeneralUtility::quoteJSvalue($item['name']) . '].checked='.$item['checked'] . ';';
230 }
231
232 // Build toggle group checkbox
233 $toggleGroupCheckbox = '';
234 if (!empty($resetGroup)) {
235 $toggleGroupCheckbox = '
236 <input type="checkbox" class="checkbox" onclick="if (checked) {' . htmlspecialchars(implode('', $checkGroup) . '} else {' . implode('', $uncheckGroup)) . '}">
237 ';
238 }
239
240 // Build reset group button
241 $resetGroupBtn = '';
242 if (!empty($resetGroup)) {
243 $resetGroupBtn = '
244 <a href="#" class="btn btn-default" onclick="' . implode('', $resetGroup) . ' return false;' . '">
245 ' . IconUtility::getSpriteIcon('actions-edit-undo', array('title' => htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.revertSelection')))) . '
246 ' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.revertSelection') . '
247 </a>
248 ';
249 }
250
251 $output .= '
252 <div id="' . $groupId . '" class="panel-collapse collapse in" role="tabpanel">
253 <div class="table-fit">
254 <table class="table table-transparent table-hover">
255 <thead>
256 <tr>
257 <th class="col-checkbox">' . $toggleGroupCheckbox . '</th>
258 <th class="col-icon"></th>
259 <th class="text-right" colspan="2">' . $resetGroupBtn . '</th>
260 </tr>
261 </thead>
262 <tbody>' . $tableRows . '</tbody>
263 </table>
264 </div>
265 </div>
266 ';
267 }
268 $output .= '</div>';
269 }
270
271 return $output;
272 }
273
274 }