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\Backend\Form\NodeFactory;
21use TYPO3\CMS\Core\Imaging\IconRegistry;
22use TYPO3\CMS\Core\Utility\GeneralUtility;
23use TYPO3\CMS\Core\Utility\StringUtility;
24
25/**
26 * Generation of TCEform elements of the type "check"
27 */
28class CheckboxToggleElement extends AbstractFormElement
29{
30    /**
31     * @var IconRegistry
32     */
33    private $iconRegistry;
34
35    /**
36     * Default field information enabled for this element.
37     *
38     * @var array
39     */
40    protected $defaultFieldInformation = [
41        'tcaDescription' => [
42            'renderType' => 'tcaDescription',
43        ],
44    ];
45
46    /**
47     * Default field wizards enabled for this element.
48     *
49     * @var array
50     */
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
69    /**
70     * @param NodeFactory $nodeFactory
71     * @param array $data
72     */
73    public function __construct(NodeFactory $nodeFactory, array $data)
74    {
75        parent::__construct($nodeFactory, $data);
76        $this->iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
77    }
78
79    /**
80     * This will render a checkbox or an array of checkboxes
81     *
82     * @return array As defined in initializeResultArray() of AbstractNode
83     */
84    public function render(): array
85    {
86        $resultArray = $this->initializeResultArray();
87
88        $elementHtml = '';
89        $disabled = false;
90        if ($this->data['parameterArray']['fieldConf']['config']['readOnly']) {
91            $disabled = true;
92        }
93        // Traversing the array of items
94        $items = $this->data['parameterArray']['fieldConf']['config']['items'];
95
96        $numberOfItems = count($items);
97        if ($numberOfItems === 0) {
98            $items[] = ['', ''];
99            $numberOfItems = 1;
100        }
101        $formElementValue = (int)$this->data['parameterArray']['itemFormElValue'];
102        $cols = (int)$this->data['parameterArray']['fieldConf']['config']['cols'];
103        if ($cols > 1) {
104            [$colClass, $colClear] = $this->calculateColumnMarkup($cols);
105            $elementHtml .= '<div class="checkbox-row row">';
106            $counter = 0;
107            // $itemKey is important here, because items could have been removed via TSConfig
108            foreach ($items as $itemKey => $itemDefinition) {
109                $label = $itemDefinition[0];
110                $elementHtml .=
111                    '<div class="checkbox-column ' . $colClass . '">'
112                    . $this->renderSingleCheckboxElement($label, $itemKey, $formElementValue, $numberOfItems, $this->data['parameterArray'], $disabled) .
113                    '</div>';
114                ++$counter;
115                if ($counter < $numberOfItems && !empty($colClear)) {
116                    foreach ($colClear as $rowBreakAfter => $clearClass) {
117                        if ($counter % $rowBreakAfter === 0) {
118                            $elementHtml .= '<div class="clearfix ' . $clearClass . '"></div>';
119                        }
120                    }
121                }
122            }
123            $elementHtml .= '</div>';
124        } else {
125            $counter = 0;
126            foreach ($items as $itemKey => $itemDefinition) {
127                $label = $itemDefinition[0];
128                $elementHtml .= $this->renderSingleCheckboxElement($label, $counter, $formElementValue, $numberOfItems, $this->data['parameterArray'], $disabled);
129                ++$counter;
130            }
131        }
132        if (!$disabled) {
133            $elementHtml .= '<input type="hidden" name="' . htmlspecialchars($this->data['parameterArray']['itemFormElName']) . '" value="' . htmlspecialchars((string)$formElementValue) . '" />';
134        }
135
136        $fieldInformationResult = $this->renderFieldInformation();
137        $fieldInformationHtml = $fieldInformationResult['html'];
138        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
139
140        $fieldWizardResult = $this->renderFieldWizard();
141        $fieldWizardHtml = $fieldWizardResult['html'];
142        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
143
144        $html = [];
145        $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
146        $html[] = $fieldInformationHtml;
147        $html[] =   '<div class="form-wizards-wrap">';
148        $html[] =       '<div class="form-wizards-element">';
149        $html[] =           $elementHtml;
150        $html[] =       '</div>';
151        if (!$disabled && !empty($fieldWizardHtml)) {
152            $html[] =   '<div class="form-wizards-items-bottom">';
153            $html[] =       $fieldWizardHtml;
154            $html[] =   '</div>';
155        }
156        $html[] =   '</div>';
157        $html[] = '</div>';
158
159        $resultArray['html'] = implode(LF, $html);
160        return $resultArray;
161    }
162
163    /**
164     * This functions builds the HTML output for the checkbox
165     *
166     * @param string $label Label of this item
167     * @param int $itemCounter Number of this element in the list of all elements
168     * @param int $formElementValue Value of this element
169     * @param int $numberOfItems Full number of items
170     * @param array $additionalInformation Information with additional configuration options.
171     * @param bool $disabled TRUE if form element is disabled
172     * @return string Single element HTML
173     */
174    protected function renderSingleCheckboxElement($label, $itemCounter, $formElementValue, $numberOfItems, $additionalInformation, $disabled): string
175    {
176        $config = $additionalInformation['fieldConf']['config'];
177        $inline = !empty($config['cols']) && $config['cols'] === 'inline';
178        $invert = isset($config['items'][0]['invertStateDisplay']) && $config['items'][0]['invertStateDisplay'] === true;
179        $checkboxParameters = $this->checkBoxParams(
180            $additionalInformation['itemFormElName'],
181            $formElementValue,
182            $itemCounter,
183            $numberOfItems,
184            implode('', $additionalInformation['fieldChangeFunc'])
185        );
186        $uniqueId = StringUtility::getUniqueId('_');
187        $checkboxId = $additionalInformation['itemFormElID'] . '_' . $itemCounter . $uniqueId;
188        return '
189            <div class="checkbox checkbox-type-toggle' . ($invert ? ' checkbox-invert' : '') . ($inline ? ' checkbox-inline' : '') . (!$disabled ? '' : ' disabled') . '">
190                <input type="checkbox"
191                    class="checkbox-input"
192                    value="1"
193                    data-formengine-input-name="' . htmlspecialchars($additionalInformation['itemFormElName']) . '"
194                    ' . $checkboxParameters . '
195                    ' . (!$disabled ? '' : ' disabled="disabled"') . '
196                    id="' . $checkboxId . '" />
197                <label class="checkbox-label" for="' . $checkboxId . '">
198                    <span class="checkbox-label-text">' . $this->appendValueToLabelInDebugMode(($label ? htmlspecialchars($label) : '&nbsp;'), $formElementValue) . '</span>
199                </label>
200            </div>';
201    }
202}
203