[!!!][TASK] Improve flex and TCA handling in FormEngine
[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\Backend\Form\Utility\FormEngineUtility;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Imaging\Icon;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Core\Utility\StringUtility;
22
23 /**
24 * Creates a widget with check box elements.
25 *
26 * This is rendered for config type=select, renderType=selectCheckBox
27 */
28 class SelectCheckBoxElement extends AbstractFormElement
29 {
30 /**
31 * Render check boxes
32 *
33 * @return array As defined in initializeResultArray() of AbstractNode
34 */
35 public function render()
36 {
37 $html = [];
38 // Field configuration from TCA:
39 $parameterArray = $this->data['parameterArray'];
40 $config = $parameterArray['fieldConf']['config'];
41 $disabled = !empty($config['readOnly']);
42
43 $selItems = $config['items'];
44 if (!empty($selItems)) {
45 // Get values in an array (and make unique, which is fine because there can be no duplicates anyway):
46 $itemArray = array_flip($parameterArray['itemFormElValue']);
47
48 // Traverse the Array of selector box items:
49 $groups = [];
50 $currentGroup = 0;
51 $c = 0;
52 $sOnChange = '';
53 if (!$disabled) {
54 $sOnChange = implode('', $parameterArray['fieldChangeFunc']);
55 // Used to accumulate the JS needed to restore the original selection.
56 foreach ($selItems as $p) {
57 // Non-selectable element:
58 if ($p[1] === '--div--') {
59 $selIcon = '';
60 if (isset($p[2]) && $p[2] != 'empty-empty') {
61 $selIcon = FormEngineUtility::getIconHtml($p[2]);
62 }
63 $currentGroup++;
64 $groups[$currentGroup]['header'] = [
65 'icon' => $selIcon,
66 'title' => $p[0]
67 ];
68 } else {
69 // Check if some help text is available
70 // Since TYPO3 4.5 help text is expected to be an associative array
71 // with two key, "title" and "description"
72 // For the sake of backwards compatibility, we test if the help text
73 // is a string and use it as a description (this could happen if items
74 // are modified with an itemProcFunc)
75 $hasHelp = false;
76 $help = '';
77 $helpArray = [];
78 if (!empty($p[3])) {
79 $hasHelp = true;
80 if (is_array($p[3])) {
81 $helpArray = $p[3];
82 } else {
83 $helpArray['description'] = $p[3];
84 }
85 }
86 if ($hasHelp) {
87 $help = BackendUtility::wrapInHelp('', '', '', $helpArray);
88 }
89
90 // Selected or not by default:
91 $checked = 0;
92 if (isset($itemArray[$p[1]])) {
93 $checked = 1;
94 unset($itemArray[$p[1]]);
95 }
96
97 // Build item array
98 $groups[$currentGroup]['items'][] = [
99 'id' => StringUtility::getUniqueId('select_checkbox_row_'),
100 'name' => $parameterArray['itemFormElName'] . '[' . $c . ']',
101 'value' => $p[1],
102 'checked' => $checked,
103 'disabled' => false,
104 'class' => '',
105 'icon' => FormEngineUtility::getIconHtml(!empty($p[2]) ? $p[2] : 'empty-empty'),
106 'title' => $p[0],
107 'help' => $help
108 ];
109 $c++;
110 }
111 }
112 }
113 // Add an empty hidden field which will send a blank value if all items are unselected.
114 $html[] = '<input type="hidden" class="select-checkbox" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="">';
115
116 // Building the checkboxes
117 foreach ($groups as $groupKey => $group) {
118 $groupId = htmlspecialchars($parameterArray['itemFormElID']) . '-group-' . $groupKey;
119 $html[] = '<div class="panel panel-default">';
120 if (is_array($group['header'])) {
121 $html[] = '<div class="panel-heading">';
122 $html[] = '<a data-toggle="collapse" href="#' . $groupId . '" aria-expanded="false" aria-controls="' . $groupId . '">';
123 $html[] = $group['header']['icon'];
124 $html[] = htmlspecialchars($group['header']['title']);
125 $html[] = '</a>';
126 $html[] = '</div>';
127 }
128 if (is_array($group['items']) && !empty($group['items'])) {
129 $tableRows = [];
130 $resetGroup = [];
131
132 // Render rows
133 foreach ($group['items'] as $item) {
134 $tableRows[] = '<tr class="' . $item['class'] . '">';
135 $tableRows[] = '<td class="col-checkbox">';
136 $tableRows[] = '<input type="checkbox" class="t3js-checkbox" '
137 . 'id="' . $item['id'] . '" '
138 . 'name="' . htmlspecialchars($item['name']) . '" '
139 . 'value="' . htmlspecialchars($item['value']) . '" '
140 . 'onclick="' . htmlspecialchars($sOnChange) . '" '
141 . ($item['checked'] ? 'checked=checked ' : '')
142 . ($item['disabled'] ? 'disabled=disabled ' : '') . '>';
143 $tableRows[] = '</td>';
144 $tableRows[] = '<td class="col-icon">';
145 $tableRows[] = '<label class="label-block" for="' . $item['id'] . '">' . $item['icon'] . '</label>';
146 $tableRows[] = '</td>';
147 $tableRows[] = '<td class="col-title">';
148 $tableRows[] = '<label class="label-block" for="' . $item['id'] . '">' . htmlspecialchars($item['title'], ENT_COMPAT, 'UTF-8', false) . '</label>';
149 $tableRows[] = '</td>';
150 $tableRows[] = '<td class="text-right">' . $item['help'] . '</td>';
151 $tableRows[] = '</tr>';
152 $resetGroup[] = 'document.editform[' . GeneralUtility::quoteJSvalue($item['name']) . '].checked=' . $item['checked'] . ';';
153 }
154
155 // Build reset group button
156 $resetGroupBtn = '';
157 if (!empty($resetGroup)) {
158 $resetGroup[] = 'TYPO3.FormEngine.updateCheckboxState(this);';
159 $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.revertSelection'));
160 $resetGroupBtn = '<a href="#" '
161 . 'class="btn btn-default btn-sm" '
162 . 'onclick="' . implode('', $resetGroup) . ' return false;" '
163 . 'title="' . $title . '">'
164 . $this->iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL)->render() . ' '
165 . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.revertSelection') . '</a>';
166 }
167
168 if (is_array($group['header'])) {
169 $html[] = '<div id="' . $groupId . '" class="panel-collapse collapse" role="tabpanel">';
170 }
171 $checkboxId = uniqid($groupId);
172 $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.toggleall'));
173 $html[] = '<div class="table-responsive">';
174 $html[] = '<table class="table table-transparent table-hover">';
175 $html[] = '<thead>';
176 $html[] = '<tr>';
177 $html[] = '<th class="col-checkbox">';
178 $html[] = '<input type="checkbox" id="' . $checkboxId . '" class="t3js-toggle-checkboxes" data-trigger="hover" data-placement="right" data-title="' . $title . '" data-toggle="tooltip" />';
179 $html[] = '</th>';
180 $html[] = '<th class="col-title" colspan="2"><label for="' . $checkboxId . '">' . $title . '</label></th>';
181 $html[] = '<th class="text-right">' . $resetGroupBtn . '</th>';
182 $html[] = '</tr>';
183 $html[] = '</thead>';
184 $html[] = '<tbody>' . implode(LF, $tableRows) . '</tbody>';
185 $html[] = '</table>';
186 $html[] = '</div>';
187 if (is_array($group['header'])) {
188 $html[] = '</div>';
189 }
190 }
191 $html[] = '</div>';
192 }
193 }
194
195 if (!$disabled) {
196 $html = $this->renderWizards(
197 [implode(LF, $html)],
198 $config['wizards'],
199 $this->data['tableName'],
200 $this->data['databaseRow'],
201 $this->data['fieldName'],
202 $parameterArray,
203 $parameterArray['itemFormElName'],
204 BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras'])
205 );
206 }
207
208 $resultArray = $this->initializeResultArray();
209 $resultArray['html'] = $html;
210 $resultArray['requireJsModules'][] = 'TYPO3/CMS/Backend/Tooltip';
211
212 return $resultArray;
213 }
214 }