1<?php
2
3declare(strict_types=1);
4
5/*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18namespace TYPO3\CMS\Backend\Form\Element;
19
20use TYPO3\CMS\Core\Utility\StringUtility;
21
22/**
23 * Generation of TCEform elements of the type "check"
24 */
25class CheckboxLabeledToggleElement extends AbstractFormElement
26{
27    /**
28     * Default field information enabled for this element.
29     *
30     * @var array
31     */
32    protected $defaultFieldInformation = [
33        'tcaDescription' => [
34            'renderType' => 'tcaDescription',
35        ],
36    ];
37
38    /**
39     * Default field wizards enabled for this element.
40     *
41     * @var array
42     */
43    protected $defaultFieldWizard = [
44        'localizationStateSelector' => [
45            'renderType' => 'localizationStateSelector',
46        ],
47        'otherLanguageContent' => [
48            'renderType' => 'otherLanguageContent',
49            'after' => [
50                'localizationStateSelector',
51            ],
52        ],
53        'defaultLanguageDifferences' => [
54            'renderType' => 'defaultLanguageDifferences',
55            'after' => [
56                'otherLanguageContent',
57            ],
58        ],
59    ];
60
61    /**
62     * This will render a checkbox or an array of checkboxes
63     *
64     * @return array As defined in initializeResultArray() of AbstractNode
65     */
66    public function render(): array
67    {
68        $resultArray = $this->initializeResultArray();
69
70        $elementHtml = '';
71        $disabled = false;
72        if ($this->data['parameterArray']['fieldConf']['config']['readOnly'] ?? false) {
73            $disabled = true;
74        }
75        // Traversing the array of items
76        $items = $this->data['parameterArray']['fieldConf']['config']['items'];
77
78        $numberOfItems = \count($items);
79        if ($numberOfItems === 0) {
80            $items[] = ['', ''];
81            $numberOfItems = 1;
82        }
83        $formElementValue = (int)$this->data['parameterArray']['itemFormElValue'];
84        $cols = (int)($this->data['parameterArray']['fieldConf']['config']['cols'] ?? 0);
85        if ($cols > 1) {
86            [$colClass, $colClear] = $this->calculateColumnMarkup($cols);
87            $elementHtml .= '<div class="checkbox-row row">';
88            $counter = 0;
89            // $itemKey is important here, because items could have been removed via TSConfig
90            foreach ($items as $itemKey => $itemDefinition) {
91                $label = $itemDefinition[0];
92                $elementHtml .=
93                    '<div class="checkbox-column ' . $colClass . '">'
94                    . $this->renderSingleCheckboxElement($label, $itemKey, $formElementValue, $numberOfItems, $this->data['parameterArray'], $disabled) .
95                    '</div>';
96                ++$counter;
97                if ($counter < $numberOfItems && !empty($colClear)) {
98                    foreach ($colClear as $rowBreakAfter => $clearClass) {
99                        if ($counter % $rowBreakAfter === 0) {
100                            $elementHtml .= '<div class="clearfix ' . $clearClass . '"></div>';
101                        }
102                    }
103                }
104            }
105            $elementHtml .= '</div>';
106        } else {
107            $counter = 0;
108            foreach ($items as $itemKey => $itemDefinition) {
109                $label = $itemDefinition[0];
110                $elementHtml .= $this->renderSingleCheckboxElement($label, $counter, $formElementValue, $numberOfItems, $this->data['parameterArray'], $disabled);
111                ++$counter;
112            }
113        }
114        if (!$disabled) {
115            $elementHtml .= '<input type="hidden" name="' . htmlspecialchars($this->data['parameterArray']['itemFormElName']) . '" value="' . htmlspecialchars((string)$formElementValue) . '" />';
116        }
117
118        $fieldInformationResult = $this->renderFieldInformation();
119        $fieldInformationHtml = $fieldInformationResult['html'];
120        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
121
122        $fieldWizardResult = $this->renderFieldWizard();
123        $fieldWizardHtml = $fieldWizardResult['html'];
124        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
125
126        $html = [];
127        $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
128        $html[] = $fieldInformationHtml;
129        $html[] =   '<div class="form-wizards-wrap">';
130        $html[] =       '<div class="form-wizards-element">';
131        $html[] =           $elementHtml;
132        $html[] =       '</div>';
133        if (!$disabled && !empty($fieldWizardHtml)) {
134            $html[] =   '<div class="form-wizards-items-bottom">';
135            $html[] =       $fieldWizardHtml;
136            $html[] =   '</div>';
137        }
138        $html[] =   '</div>';
139        $html[] = '</div>';
140
141        $resultArray['html'] = implode(LF, $html);
142        return $resultArray;
143    }
144
145    /**
146     * This functions builds the HTML output for the checkbox
147     *
148     * @param string $label Label of this item
149     * @param int $itemCounter Number of this element in the list of all elements
150     * @param int $formElementValue Value of this element
151     * @param int $numberOfItems Full number of items
152     * @param array $additionalInformation Information with additional configuration options.
153     * @param bool $disabled TRUE if form element is disabled
154     * @return string Single element HTML
155     */
156    protected function renderSingleCheckboxElement($label, $itemCounter, $formElementValue, $numberOfItems, $additionalInformation, $disabled): string
157    {
158        $config = $additionalInformation['fieldConf']['config'];
159        $inline = !empty($config['cols']) && $config['cols'] === 'inline';
160        $invert = isset($config['items'][$itemCounter]['invertStateDisplay']) && $config['items'][$itemCounter]['invertStateDisplay'] === true;
161        $checkboxParameters = $this->checkBoxParams(
162            $additionalInformation['itemFormElName'],
163            $formElementValue,
164            $itemCounter,
165            $numberOfItems,
166            $additionalInformation['fieldChangeFunc'] ?? [],
167            $invert
168        );
169        $uniqueId = StringUtility::getUniqueId('_');
170        $checkboxId = $additionalInformation['itemFormElID'] . '_' . $itemCounter . $uniqueId;
171        return '
172            <div class="form-check form-check-type-labeled-toggle' . ($inline ? ' form-check-inline' : '') . (!$disabled ? '' : ' disabled') . '">
173                <input type="checkbox"
174                    class="form-check-input"
175                    value="1"
176                    data-formengine-input-name="' . htmlspecialchars($additionalInformation['itemFormElName']) . '"
177                    ' . $checkboxParameters . '
178                    ' . (!$disabled ? '' : ' disabled="disabled"') . '
179                    id="' . $checkboxId . '" />
180                <label class="form-check-label" for="' . $checkboxId . '">
181                    <span class="form-check-label-switch">
182                        <span class="form-check-label-switch-checked">
183                            ' . $config['items'][$itemCounter]['labelChecked'] . '
184                        </span>
185                        <span class="form-check-label-switch-unchecked">
186                            ' . $config['items'][$itemCounter]['labelUnchecked'] . '
187                        </span>
188                    </span>
189                    <span class="form-check-label-text">' . $this->appendValueToLabelInDebugMode(($label ? htmlspecialchars($label) : '&nbsp;'), $formElementValue) . '</span>
190                </label>
191            </div>';
192    }
193}
194