SelectCheckBoxElement.php 12.9 KB
Newer Older
1
<?php
2

3
4
5
6
7
8
9
10
11
12
13
14
15
/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

16
17
namespace TYPO3\CMS\Backend\Form\Element;

18
19
use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
use TYPO3\CMS\Backend\Utility\BackendUtility;
20
use TYPO3\CMS\Core\Imaging\Icon;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Core\Utility\StringUtility;
23
24
25
26

/**
 * Creates a widget with check box elements.
 *
27
 * This is rendered for config type=select, renderType=selectCheckBox
28
 */
29
30
class SelectCheckBoxElement extends AbstractFormElement
{
31
32
33
34
35
36
37
38
39
40
41
    /**
     * Default field information enabled for this element.
     *
     * @var array
     */
    protected $defaultFieldInformation = [
        'tcaDescription' => [
            'renderType' => 'tcaDescription',
        ],
    ];

42
43
44
45
46
47
    /**
     * Default field wizards enabled for this element.
     *
     * @var array
     */
    protected $defaultFieldWizard = [
48
49
50
        'localizationStateSelector' => [
            'renderType' => 'localizationStateSelector',
        ],
51
52
        'otherLanguageContent' => [
            'renderType' => 'otherLanguageContent',
53
54
55
            'after' => [
                'localizationStateSelector'
            ],
56
57
58
59
60
61
62
63
64
        ],
        'defaultLanguageDifferences' => [
            'renderType' => 'defaultLanguageDifferences',
            'after' => [
                'otherLanguageContent',
            ],
        ],
    ];

65
66
67
68
69
70
71
    /**
     * Render check boxes
     *
     * @return array As defined in initializeResultArray() of AbstractNode
     */
    public function render()
    {
72
73
        $resultArray = $this->initializeResultArray();

74
75
76
77
78
79
80
81
        $html = [];
        // Field configuration from TCA:
        $parameterArray = $this->data['parameterArray'];
        $config = $parameterArray['fieldConf']['config'];
        $disabled = !empty($config['readOnly']);

        $selItems = $config['items'];
        if (!empty($selItems)) {
82
83
84
85
86
87
88
89
            // Get values in an array (and make unique, which is fine because there can be no duplicates anyway)
            // In case e.g. "l10n_display" is set to "defaultAsReadonly" only one value (as string) could be handed in
            if (is_array($parameterArray['itemFormElValue'])) {
                $itemArray = $parameterArray['itemFormElValue'];
            } else {
                $itemArray = [(string)$parameterArray['itemFormElValue']];
            }
            $itemArray = array_flip($itemArray);
90
91

            // Traverse the Array of selector box items:
92
            $groups = [];
93
94
95
96
97
98
99
100
101
102
            $currentGroup = 0;
            $c = 0;
            $sOnChange = '';
            if (!$disabled) {
                $sOnChange = implode('', $parameterArray['fieldChangeFunc']);
                // Used to accumulate the JS needed to restore the original selection.
                foreach ($selItems as $p) {
                    // Non-selectable element:
                    if ($p[1] === '--div--') {
                        $selIcon = '';
103
                        if (isset($p[2]) && $p[2] !== 'empty-empty') {
104
105
106
                            $selIcon = FormEngineUtility::getIconHtml($p[2]);
                        }
                        $currentGroup++;
107
                        $groups[$currentGroup]['header'] = [
108
                            'icon' => $selIcon,
109
                            'title' => $p[0]
110
                        ];
111
112
                    } else {
                        // Check if some help text is available
113
                        // Help text is expected to be an associative array
114
115
116
117
118
119
                        // with two key, "title" and "description"
                        // For the sake of backwards compatibility, we test if the help text
                        // is a string and use it as a description (this could happen if items
                        // are modified with an itemProcFunc)
                        $hasHelp = false;
                        $help = '';
120
                        $helpArray = [];
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
                        if (!empty($p[3])) {
                            $hasHelp = true;
                            if (is_array($p[3])) {
                                $helpArray = $p[3];
                            } else {
                                $helpArray['description'] = $p[3];
                            }
                        }
                        if ($hasHelp) {
                            $help = BackendUtility::wrapInHelp('', '', '', $helpArray);
                        }

                        // Selected or not by default:
                        $checked = 0;
                        if (isset($itemArray[$p[1]])) {
                            $checked = 1;
                            unset($itemArray[$p[1]]);
                        }

                        // Build item array
141
                        $groups[$currentGroup]['items'][] = [
142
143
144
145
146
147
                            'id' => StringUtility::getUniqueId('select_checkbox_row_'),
                            'name' => $parameterArray['itemFormElName'] . '[' . $c . ']',
                            'value' => $p[1],
                            'checked' => $checked,
                            'disabled' => false,
                            'class' => '',
148
                            'icon' => FormEngineUtility::getIconHtml(!empty($p[2]) ? $p[2] : 'empty-empty'),
149
                            'title' => $p[0],
150
                            'help' => $help
151
                        ];
152
153
154
155
                        $c++;
                    }
                }
            }
156
157
158
159
160
161

            $fieldInformationResult = $this->renderFieldInformation();
            $fieldInformationHtml = $fieldInformationResult['html'];
            $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);

            $fieldWizardResult = $this->renderFieldWizard();
162
            $fieldWizardHtml = $fieldWizardResult['html'];
163
164
            $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);

165
            $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
166
            $html[] = $fieldInformationHtml;
167
168
169
            $html[] =   '<div class="form-wizards-wrap">';
            $html[] =       '<div class="form-wizards-element">';

170
171
172
173
174
175
            // Add an empty hidden field which will send a blank value if all items are unselected.
            $html[] = '<input type="hidden" class="select-checkbox" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="">';

            // Building the checkboxes
            foreach ($groups as $groupKey => $group) {
                $groupId = htmlspecialchars($parameterArray['itemFormElID']) . '-group-' . $groupKey;
176
177
                $groupIdCollapsible = $groupId . '-collapse';
                $html[] = '<div id="' . $groupId . '" class="panel panel-default">';
178
179
                if (is_array($group['header'])) {
                    $html[] = '<div class="panel-heading">';
180
                    $html[] = '<a data-bs-toggle="collapse" href="#' . $groupIdCollapsible . '" aria-expanded="false" aria-controls="' . $groupIdCollapsible . '">';
181
                    $html[] = $group['header']['icon'];
182
                    $html[] = htmlspecialchars($group['header']['title']);
183
184
185
186
187
188
189
190
191
192
                    $html[] = '</a>';
                    $html[] = '</div>';
                }
                if (is_array($group['items']) && !empty($group['items'])) {
                    $tableRows = [];

                    // Render rows
                    foreach ($group['items'] as $item) {
                        $tableRows[] = '<tr class="' . $item['class'] . '">';
                        $tableRows[] =    '<td class="col-checkbox">';
193
                        $tableRows[] =        '<input type="checkbox" class="t3js-checkbox" '
194
195
196
197
198
                                            . 'id="' . $item['id'] . '" '
                                            . 'name="' . htmlspecialchars($item['name']) . '" '
                                            . 'value="' . htmlspecialchars($item['value']) . '" '
                                            . 'onclick="' . htmlspecialchars($sOnChange) . '" '
                                            . ($item['checked'] ? 'checked=checked ' : '')
199
                                            . ($item['disabled'] ? 'disabled=disabled ' : '') . '>';
200
201
202
203
204
                        $tableRows[] =    '</td>';
                        $tableRows[] =    '<td class="col-icon">';
                        $tableRows[] =        '<label class="label-block" for="' . $item['id'] . '">' . $item['icon'] . '</label>';
                        $tableRows[] =    '</td>';
                        $tableRows[] =    '<td class="col-title">';
205
                        $tableRows[] =        '<label class="label-block nowrap-disabled" for="' . $item['id'] . '">' . htmlspecialchars($this->appendValueToLabelInDebugMode($item['title'], $item['value']), ENT_COMPAT, 'UTF-8', false) . '</label>';
206
                        $tableRows[] =    '</td>';
207
                        $tableRows[] =    '<td class="text-right">' . $item['help'] . '</td>';
208
209
210
211
212
                        $tableRows[] = '</tr>';
                    }

                    // Build reset group button
                    $resetGroupBtn = '';
213
                    if (!empty($group['items'])) {
214
                        $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.revertSelection'));
215
216
217
218
                        $resetGroupBtn = '<button type="button" '
                            . 'class="btn btn-default btn-sm t3js-revert-selection" '
                            . 'title="' . $title . '"'
                            . '>'
219
                            . $this->iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL)->render() . ' '
220
                            . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.revertSelection') . '</button>';
221
222
                    }

223
                    if (is_array($group['header'])) {
224
225
                        $expandAll = (bool)($config['appearance']['expandAll'] ?? false) ? 'in' : '';
                        $html[] = '<div id="' . $groupIdCollapsible . '" class="panel-collapse collapse ' . $expandAll . '" role="tabpanel">';
226
                    }
227
                    $checkboxId = StringUtility::getUniqueId($groupId);
228
                    $title = htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.toggleall'));
229
                    $html[] =    '<div class="table-responsive">';
230
231
232
                    $html[] =        '<table class="table table-transparent table-hover">';
                    $html[] =            '<thead>';
                    $html[] =                '<tr>';
233
                    $html[] =                    '<th class="col-checkbox">';
234
                    $html[] =                       '<input type="checkbox" id="' . $checkboxId . '" class="t3js-toggle-checkboxes" data-bs-trigger="hover" data-bs-placement="right" data-title="' . $title . '" data-bs-toggle="tooltip" />';
235
                    $html[] =                    '</th>';
236
237
                    $html[] =                    '<th class="col-title" colspan="2"><label for="' . $checkboxId . '">' . $title . '</label></th>';
                    $html[] =                    '<th class="text-right">' . $resetGroupBtn . '</th>';
238
239
240
241
242
                    $html[] =                '</tr>';
                    $html[] =            '</thead>';
                    $html[] =            '<tbody>' . implode(LF, $tableRows) . '</tbody>';
                    $html[] =        '</table>';
                    $html[] =    '</div>';
243
244
245
                    if (is_array($group['header'])) {
                        $html[] = '</div>';
                    }
Andreas Fernandez's avatar
Andreas Fernandez committed
246
247
248
249
250
251

                    $resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectCheckBoxElement' => '
                        function(SelectCheckBoxElement) {
                            new SelectCheckBoxElement(' . GeneralUtility::quoteJSvalue($checkboxId) . ');
                        }'
                    ];
252
253
254
255
                }
                $html[] = '</div>';
            }

256
            $html[] =       '</div>';
257
            if (!$disabled && !empty($fieldWizardHtml)) {
258
259
260
261
262
263
                $html[] =   '<div class="form-wizards-items-bottom">';
                $html[] =       $fieldWizardHtml;
                $html[] =   '</div>';
            }
            $html[] =   '</div>';
            $html[] = '</div>';
264
265
        }

266
        $resultArray['html'] = implode(LF, $html);
267
        $resultArray['requireJsModules'][] = 'TYPO3/CMS/Backend/Tooltip';
268
269
        return $resultArray;
    }
270
}