1<?php 2 3/* 4 * This file is part of the TYPO3 CMS project. 5 * 6 * It is free software; you can redistribute it and/or modify it under 7 * the terms of the GNU General Public License, either version 2 8 * of the License, or any later version. 9 * 10 * For the full copyright and license information, please read the 11 * LICENSE.txt file that was distributed with this source code. 12 * 13 * The TYPO3 project - inspiring people to share! 14 */ 15 16namespace TYPO3\CMS\Backend\Form\Element; 17 18use TYPO3\CMS\Core\Utility\GeneralUtility; 19 20/** 21 * Render data as a tree. 22 * 23 * Typically rendered for config type=select, renderType=selectTree 24 */ 25class SelectTreeElement 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 * @var array Default wizards 40 */ 41 protected $defaultFieldWizard = [ 42 'localizationStateSelector' => [ 43 'renderType' => 'localizationStateSelector', 44 ], 45 ]; 46 47 /** 48 * Default number of tree nodes to show (determines tree height) 49 * when no ['config']['size'] is set 50 * 51 * @var int 52 */ 53 protected $itemsToShow = 15; 54 55 /** 56 * Number of items to show at last 57 * e.g. when you have only 2 items in a tree 58 * 59 * @var int 60 */ 61 protected $minItemsToShow = 5; 62 63 /** 64 * Pixel height of a single tree node 65 * 66 * @var int 67 */ 68 protected $itemHeight = 20; 69 70 /** 71 * Render tree widget 72 * 73 * @return array As defined in initializeResultArray() of AbstractNode 74 * @see AbstractNode::initializeResultArray() 75 */ 76 public function render() 77 { 78 $resultArray = $this->initializeResultArray(); 79 $parameterArray = $this->data['parameterArray']; 80 $formElementId = md5($parameterArray['itemFormElName']); 81 82 // Field configuration from TCA: 83 $config = $parameterArray['fieldConf']['config']; 84 $readOnly = !empty($config['readOnly']) && $config['readOnly']; 85 $exclusiveKeys = !empty($config['exclusiveKeys']) ? $config['exclusiveKeys'] : ''; 86 $exclusiveKeys = $exclusiveKeys . ','; 87 $appearance = !empty($config['treeConfig']['appearance']) ? $config['treeConfig']['appearance'] : []; 88 $expanded = !empty($appearance['expandAll']); 89 $showHeader = !empty($appearance['showHeader']); 90 if (isset($config['size']) && (int)$config['size'] > 0) { 91 $height = max($this->minItemsToShow, (int)$config['size']); 92 } else { 93 $height = $this->itemsToShow; 94 } 95 $heightInPx = $height * $this->itemHeight; 96 $treeWrapperId = 'tree_' . $formElementId; 97 $fieldId = 'tree_record_' . $formElementId; 98 99 $fieldName = $this->data['fieldName']; 100 101 $dataStructureIdentifier = ''; 102 $flexFormSheetName = ''; 103 $flexFormFieldName = ''; 104 $flexFormContainerName = ''; 105 $flexFormContainerIdentifier = ''; 106 $flexFormContainerFieldName = ''; 107 $flexFormSectionContainerIsNew = false; 108 if ($this->data['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') { 109 $dataStructureIdentifier = $this->data['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier']; 110 if (isset($this->data['flexFormSheetName'])) { 111 $flexFormSheetName = $this->data['flexFormSheetName']; 112 } 113 if (isset($this->data['flexFormFieldName'])) { 114 $flexFormFieldName = $this->data['flexFormFieldName']; 115 } 116 if (isset($this->data['flexFormContainerName'])) { 117 $flexFormContainerName = $this->data['flexFormContainerName']; 118 } 119 if (isset($this->data['flexFormContainerFieldName'])) { 120 $flexFormContainerFieldName = $this->data['flexFormContainerFieldName']; 121 } 122 if (isset($this->data['flexFormContainerIdentifier'])) { 123 $flexFormContainerIdentifier = $this->data['flexFormContainerIdentifier']; 124 } 125 // Add a flag this is a tree in a new flex section container element. This is needed to initialize 126 // the databaseRow with this container again so the tree data provider is able to calculate tree items. 127 if (!empty($this->data['flexSectionContainerPreparation'])) { 128 $flexFormSectionContainerIsNew = true; 129 } 130 } 131 132 $fieldInformationResult = $this->renderFieldInformation(); 133 $fieldInformationHtml = $fieldInformationResult['html']; 134 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false); 135 136 $fieldWizardResult = $this->renderFieldWizard(); 137 $fieldWizardHtml = $fieldWizardResult['html']; 138 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); 139 140 $html = []; 141 $html[] = '<div class="formengine-field-item t3js-formengine-field-item">'; 142 $html[] = $fieldInformationHtml; 143 $html[] = '<div class="form-control-wrap">'; 144 $html[] = '<div class="form-wizards-wrap">'; 145 $html[] = '<div class="form-wizards-element">'; 146 $html[] = '<div class="typo3-tceforms-tree">'; 147 $html[] = '<input class="treeRecord" type="hidden" id="' . htmlspecialchars($fieldId) . '"'; 148 $html[] = ' data-formengine-validation-rules="' . htmlspecialchars($this->getValidationDataAsJsonString($config)) . '"'; 149 $html[] = ' data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'; 150 $html[] = ' data-tablename="' . htmlspecialchars($this->data['tableName']) . '"'; 151 $html[] = ' data-fieldname="' . htmlspecialchars($this->data['fieldName']) . '"'; 152 $html[] = ' data-uid="' . (int)$this->data['vanillaUid'] . '"'; 153 $html[] = ' data-recordtypevalue="' . htmlspecialchars($this->data['recordTypeValue']) . '"'; 154 $html[] = ' data-datastructureidentifier="' . htmlspecialchars($dataStructureIdentifier) . '"'; 155 $html[] = ' data-flexformsheetname="' . htmlspecialchars($flexFormSheetName) . '"'; 156 $html[] = ' data-flexformfieldname="' . htmlspecialchars($flexFormFieldName) . '"'; 157 $html[] = ' data-flexformcontainername="' . htmlspecialchars($flexFormContainerName) . '"'; 158 $html[] = ' data-flexformcontaineridentifier="' . htmlspecialchars($flexFormContainerIdentifier) . '"'; 159 $html[] = ' data-flexformcontainerfieldname="' . htmlspecialchars($flexFormContainerFieldName) . '"'; 160 $html[] = ' data-flexformsectioncontainerisnew="' . htmlspecialchars((string)$flexFormSectionContainerIsNew) . '"'; 161 $html[] = ' data-command="' . htmlspecialchars($this->data['command']) . '"'; 162 $html[] = ' data-read-only="' . ($readOnly ? '1' : '0') . '"'; 163 $html[] = ' data-tree-exclusive-keys="' . htmlspecialchars($exclusiveKeys) . '"'; 164 $html[] = ' data-tree-expand-up-to-level="' . ($expanded ? '999' : '1') . '"'; 165 $html[] = ' data-tree-show-toolbar="' . $showHeader . '"'; 166 $html[] = ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'; 167 $html[] = ' id="treeinput' . $formElementId . '"'; 168 $html[] = ' value="' . htmlspecialchars(implode(',', $parameterArray['itemFormElValue'])) . '"'; 169 $html[] = '/>'; 170 $html[] = '</div>'; 171 $html[] = '<div id="' . $treeWrapperId . '" class="svg-tree-wrapper" style="height: ' . $heightInPx . 'px;"></div>'; 172 $html[] = '</div>'; 173 if (!$readOnly && !empty($fieldWizardHtml)) { 174 $html[] = '<div class="form-wizards-items-bottom">'; 175 $html[] = $fieldWizardHtml; 176 $html[] = '</div>'; 177 } 178 $html[] = '</div>'; 179 $html[] = '</div>'; 180 $html[] = '</div>'; 181 182 $resultArray['html'] = implode(LF, $html); 183 184 // add necessary labels for tree header 185 if ($showHeader) { 186 $resultArray['additionalInlineLanguageLabelFiles'][] = 'EXT:core/Resources/Private/Language/locallang_csh_corebe.xlf'; 187 } 188 $resultArray['requireJsModules']['selectTreeElement'] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectTreeElement' => ' 189 function(SelectTreeElement) { 190 require([\'jquery\'], function($) { 191 $(function() { 192 new SelectTreeElement(' . GeneralUtility::quoteJSvalue($treeWrapperId) . ', ' . GeneralUtility::quoteJSvalue($fieldId) . ', ' . $this->getTreeOnChangeJs() . '); 193 }); 194 }); 195 }' 196 ]; 197 198 return $resultArray; 199 } 200 201 /** 202 * Generates JS code triggered on change of the tree 203 * 204 * @return string 205 */ 206 protected function getTreeOnChangeJs() 207 { 208 $parameterArray = $this->data['parameterArray']; 209 $onChange = !empty($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']) ? $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] : ''; 210 $onChange .= !empty($parameterArray['fieldChangeFunc']['alert']) ? $parameterArray['fieldChangeFunc']['alert'] : ''; 211 return 'function () {' . $onChange . '}'; 212 } 213} 214