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