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