‪TYPO3CMS  11.5
SelectCheckBoxElement.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use TYPO3\CMS\Backend\Form\Behavior\OnFieldChangeTrait;
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
25 
32 {
33  use OnFieldChangeTrait;
34 
40  protected ‪$defaultFieldInformation = [
41  'tcaDescription' => [
42  'renderType' => 'tcaDescription',
43  ],
44  ];
45 
51  protected ‪$defaultFieldWizard = [
52  'localizationStateSelector' => [
53  'renderType' => 'localizationStateSelector',
54  ],
55  'otherLanguageContent' => [
56  'renderType' => 'otherLanguageContent',
57  'after' => [
58  'localizationStateSelector',
59  ],
60  ],
61  'defaultLanguageDifferences' => [
62  'renderType' => 'defaultLanguageDifferences',
63  'after' => [
64  'otherLanguageContent',
65  ],
66  ],
67  ];
68 
74  public function ‪render()
75  {
76  $resultArray = $this->‪initializeResultArray();
77 
78  // Field configuration from TCA:
79  $parameterArray = $this->data['parameterArray'];
80  $config = $parameterArray['fieldConf']['config'];
81  $readOnly = (bool)($config['readOnly'] ?? false);
82 
83  $selectItems = $config['items'] ?? [];
84  if (empty($selectItems)) {
85  // Early return in case the field does not contain any items
86  return $resultArray;
87  }
88 
89  // Get item value as array and make unique, which is fine because there can be no duplicates anyway.
90  $itemArray = array_flip($parameterArray['itemFormElValue']);
91 
92  // Initialize variables and traverse the items
93  $groups = [];
94  $currentGroup = 0;
95  $counter = 0;
96  foreach ($selectItems as $item) {
97  // Non-selectable element:
98  if ($item[1] === '--div--') {
99  $selIcon = '';
100  if (isset($item[2]) && $item[2] !== 'empty-empty') {
101  $selIcon = ‪FormEngineUtility::getIconHtml($item[2]);
102  }
103  $currentGroup++;
104  $groups[$currentGroup]['header'] = [
105  'icon' => $selIcon,
106  'title' => $item[0],
107  ];
108  } else {
109  // Check if some help text is available
110  // Help text is expected to be an associative array
111  // with two key, "title" and "description"
112  // For the sake of backwards compatibility, we test if the help text
113  // is a string and use it as a description (this could happen if items
114  // are modified with an itemProcFunc)
115  $help = '';
116  if (!empty($item[4])) {
117  if (is_array($item[4])) {
118  $helpArray = $item[4];
119  } else {
120  $helpArray['description'] = $item[4];
121  }
122  $help = BackendUtility::wrapInHelp('', '', '', $helpArray);
123  }
124 
125  // Check if current item is selected. If found, unset the key in the $itemArray.
126  $checked = isset($itemArray[$item[1]]);
127  if ($checked) {
128  unset($itemArray[$item[1]]);
129  }
130 
131  // Build item array
132  $groups[$currentGroup]['items'][] = [
133  'id' => ‪StringUtility::getUniqueId('select_checkbox_row_'),
134  'name' => $parameterArray['itemFormElName'] . '[' . $counter . ']',
135  'value' => $item[1],
136  'checked' => $checked,
137  'icon' => ‪FormEngineUtility::getIconHtml(!empty($item[2]) ? $item[2] : 'empty-empty'),
138  'title' => $item[0],
139  'help' => $help,
140  ];
141  $counter++;
142  }
143  }
144 
145  $fieldInformationResult = $this->‪renderFieldInformation();
146  $fieldInformationHtml = $fieldInformationResult['html'];
147  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
148 
149  $fieldWizardResult = $this->‪renderFieldWizard();
150  $fieldWizardHtml = $fieldWizardResult['html'];
151  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
152 
153  $html[] = '<div class="formengine-field-item t3js-formengine-field-item" data-formengine-validation-rules="' . htmlspecialchars($this->‪getValidationDataAsJsonString($config)) . '">';
154  $html[] = $fieldInformationHtml;
155  $html[] = '<div class="form-wizards-wrap">';
156  $html[] = '<div class="form-wizards-element">';
157 
158  if (!$readOnly) {
159  // Add an empty hidden field which will send a blank value if all items are unselected.
160  $html[] = '<input type="hidden" class="select-checkbox" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="">';
161  }
162 
163  // Building the checkboxes
164  foreach ($groups as $groupKey => $group) {
165  $groupId = htmlspecialchars($parameterArray['itemFormElID']) . '-group-' . $groupKey;
166  $groupIdCollapsible = $groupId . '-collapse';
167  $hasGroupHeader = is_array($group['header'] ?? false);
168 
169  $html[] = '<div id="' . $groupId . '" class="panel panel-default">';
170  if ($hasGroupHeader) {
171  $html[] = '<div class="panel-heading">';
172  $html[] = '<a data-bs-toggle="collapse" href="#' . $groupIdCollapsible . '" aria-expanded="false" aria-controls="' . $groupIdCollapsible . '">';
173  $html[] = $group['header']['icon'];
174  $html[] = htmlspecialchars($group['header']['title']);
175  $html[] = '</a>';
176  $html[] = '</div>';
177  }
178  if (!empty($group['items']) && is_array($group['items'])) {
179  $tableRows = [];
180 
181  // Render rows
182  foreach ($group['items'] as $item) {
183  $inputElementAttrs = [
184  'type' => 'checkbox',
185  'class' => 't3js-checkbox',
186  'id' => $item['id'],
187  'name' => $item['name'],
188  'value' => $item['value'],
189  ];
190 
191  if ($item['checked']) {
192  $inputElementAttrs['checked'] = 'checked';
193  }
194 
195  if ($readOnly) {
196  // Disable item if the element is readonly
197  $inputElementAttrs['disabled'] = 'disabled';
198  } else {
199  // Add fieldChange attributes if element is not readOnly
200  $inputElementAttrs = array_merge(
201  $inputElementAttrs,
202  $this->getOnFieldChangeAttrs('click', $parameterArray['fieldChangeFunc'] ?? [])
203  );
204  }
205 
206  $tableRows[] = '<tr>';
207  $tableRows[] = '<td class="col-checkbox">';
208  $tableRows[] = '<input ' . GeneralUtility::implodeAttributes($inputElementAttrs, true) . '>';
209  $tableRows[] = '</td>';
210  $tableRows[] = '<td class="col-title">';
211  $tableRows[] = '<label class="label-block nowrap-disabled" for="' . $item['id'] . '">';
212  $tableRows[] = '<span class="inline-icon">' . $item['icon'] . '</span>';
213  $tableRows[] = htmlspecialchars($this->‪appendValueToLabelInDebugMode($item['title'], $item['value']), ENT_COMPAT, 'UTF-8', false);
214  $tableRows[] = '</label>';
215  $tableRows[] = '</td>';
216  $tableRows[] = '<td class="text-end">' . $item['help'] . '</td>';
217  $tableRows[] = '</tr>';
218  }
219 
220  if ($hasGroupHeader) {
221  $expandAll = ($config['appearance']['expandAll'] ?? false) ? 'show' : '';
222  $html[] = '<div id="' . $groupIdCollapsible . '" class="panel-collapse collapse ' . $expandAll . '" role="tabpanel">';
223  }
224 
225  $html[] = '<div class="table-responsive">';
226  $html[] = '<table class="table table-transparent table-hover">';
227  if (!$readOnly) {
228  $checkboxId = ‪StringUtility::getUniqueId($groupId);
229  $title = htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.toggleall'));
230 
231  // Add table header with actions, in case the element is not readOnly
232  $html[] = '<thead>';
233  $html[] = '<tr>';
234  $html[] = '<th class="col-checkbox">';
235  $html[] = '<input type="checkbox" id="' . $checkboxId . '" class="t3js-toggle-checkboxes" data-bs-trigger="hover" data-bs-placement="right" title="' . $title . '" data-bs-toggle="tooltip" />';
236  $html[] = '</th>';
237  $html[] = '<th class="col-title"><label for="' . $checkboxId . '">' . $title . '</label></th>';
238  $html[] = '<th class="text-end">';
239  $html[] = '<button type="button" class="btn btn-default btn-sm t3js-revert-selection">';
240  $html[] = $this->iconFactory->getIcon('actions-edit-undo', ‪Icon::SIZE_SMALL)->render() . ' ';
241  $html[] = htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.revertSelection'));
242  $html[] = '</button>';
243  $html[] = '</th>';
244  $html[] = '</tr>';
245  $html[] = '</thead>';
246 
247  // Add RequireJS module. This is only needed, in case the element
248  // is not readOnly, since otherwise no checkbox changes take place.
249  $resultArray['requireJsModules'][] = ‪JavaScriptModuleInstruction::forRequireJS(
250  'TYPO3/CMS/Backend/FormEngine/Element/SelectCheckBoxElement'
251  )->instance($checkboxId);
252  }
253  $html[] = '<tbody>' . implode(LF, $tableRows) . '</tbody>';
254  $html[] = '</table>';
255  $html[] = '</div>';
256  if ($hasGroupHeader) {
257  $html[] = '</div>';
258  }
259  }
260  $html[] = '</div>';
261  }
262 
263  $html[] = '</div>';
264  if (!$readOnly && !empty($fieldWizardHtml)) {
265  $html[] = '<div class="form-wizards-items-bottom">';
266  $html[] = $fieldWizardHtml;
267  $html[] = '</div>';
268  }
269  $html[] = '</div>';
270  $html[] = '</div>';
271 
272  $resultArray['html'] = implode(LF, $html);
273  $resultArray['requireJsModules'][] = ‪JavaScriptModuleInstruction::forRequireJS('TYPO3/CMS/Backend/Tooltip');
274  return $resultArray;
275  }
276 }
‪TYPO3\CMS\Backend\Form\Element\SelectCheckBoxElement
Definition: SelectCheckBoxElement.php:32
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldInformation
‪array renderFieldInformation()
Definition: AbstractFormElement.php:76
‪TYPO3\CMS\Backend\Form\AbstractNode\mergeChildReturnIntoExistingResult
‪array mergeChildReturnIntoExistingResult(array $existing, array $childReturn, bool $mergeHtml=true)
Definition: AbstractNode.php:120
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪array initializeResultArray()
Definition: AbstractNode.php:91
‪TYPO3\CMS\Backend\Form\Element\SelectCheckBoxElement\render
‪array render()
Definition: SelectCheckBoxElement.php:71
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Backend\Form\Element\SelectCheckBoxElement\$defaultFieldWizard
‪array $defaultFieldWizard
Definition: SelectCheckBoxElement.php:48
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement
Definition: AbstractFormElement.php:35
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction
Definition: JavaScriptModuleInstruction.php:23
‪TYPO3\CMS\Backend\Form\Element
Definition: AbstractFormElement.php:16
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction\forRequireJS
‪static self forRequireJS(string $name, string $exportName=null)
Definition: JavaScriptModuleInstruction.php:49
‪TYPO3\CMS\Backend\Form\Utility\FormEngineUtility
Definition: FormEngineUtility.php:39
‪TYPO3\CMS\Backend\Form\AbstractNode\getValidationDataAsJsonString
‪string getValidationDataAsJsonString(array $config)
Definition: AbstractNode.php:156
‪TYPO3\CMS\Backend\Form\Utility\FormEngineUtility\getIconHtml
‪static string getIconHtml($icon, $alt='', $title='')
Definition: FormEngineUtility.php:122
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\appendValueToLabelInDebugMode
‪string appendValueToLabelInDebugMode($label, $value)
Definition: AbstractFormElement.php:436
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:128
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\getLanguageService
‪LanguageService getLanguageService()
Definition: AbstractFormElement.php:448
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldWizard
‪array renderFieldWizard()
Definition: AbstractFormElement.php:108
‪TYPO3\CMS\Backend\Form\Element\SelectCheckBoxElement\$defaultFieldInformation
‪array $defaultFieldInformation
Definition: SelectCheckBoxElement.php:38