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\NodeExpansion; 19 20use TYPO3\CMS\Backend\Form\AbstractNode; 21use TYPO3\CMS\Core\Imaging\Icon; 22use TYPO3\CMS\Core\Imaging\IconFactory; 23use TYPO3\CMS\Core\Localization\LanguageService; 24use TYPO3\CMS\Core\Service\DependencyOrderingService; 25use TYPO3\CMS\Core\Utility\GeneralUtility; 26 27/** 28 * Field controls are additional HTML on a single element level that are typically 29 * shown right aside the main element HTML. 30 * 31 * They are restricted to only allow an icon as output. 32 * 33 * The "link popup" button next to the input field of a renderType "inputLink" 34 * is an example of such an additional control. 35 * 36 * The element itself must position any field controls at an appropriate place. 37 * For instance the "group" element shows them in a row vertically, while others 38 * display single controls next to each other. 39 */ 40class FieldControl extends AbstractNode 41{ 42 /** 43 * Order the list of field wizards to be rendered with the ordering service, 44 * then call each wizard element through the node factory and merge their 45 * results. 46 * 47 * @return array Result array 48 */ 49 public function render(): array 50 { 51 $result = $this->initializeResultArray(); 52 if (!isset($this->data['renderData']['fieldControl'])) { 53 return $result; 54 } 55 56 $languageService = $this->getLanguageService(); 57 58 $iconFactory = GeneralUtility::makeInstance(IconFactory::class); 59 $fieldControl = $this->data['renderData']['fieldControl']; 60 $orderingService = GeneralUtility::makeInstance(DependencyOrderingService::class); 61 $orderedFieldControl = $orderingService->orderByDependencies($fieldControl, 'before', 'after'); 62 63 foreach ($orderedFieldControl as $anOrderedFieldControl => $orderedFieldControlConfiguration) { 64 if (isset($orderedFieldControlConfiguration['disabled']) && $orderedFieldControlConfiguration['disabled'] 65 || !isset($fieldControl[$anOrderedFieldControl]['renderType']) 66 ) { 67 // Don't consider this control if disabled. 68 // Also ignore if renderType is not given. 69 // Missing renderType may happen if an element registers a default field control 70 // as disabled, and TCA enabled that. If then additionally for instance the 71 // element renderType is changed to an element that doesn't register the control 72 // by default anymore, this would then fatal if we don't continue here. 73 // @todo: the above scenario indicates a small configuration flaw, maybe log an error somewhere? 74 continue; 75 } 76 77 $options = $this->data; 78 $options['renderType'] = $fieldControl[$anOrderedFieldControl]['renderType']; 79 $options['renderData']['fieldControlOptions'] = $orderedFieldControlConfiguration['options'] ?? []; 80 $controlResult = $this->nodeFactory->create($options)->render(); 81 82 if (!is_array($controlResult)) { 83 throw new \RuntimeException( 84 'Field controls must return an array', 85 1484838560 86 ); 87 } 88 89 // If the controlResult is empty (this control rendered nothing), continue to next one 90 if (empty($controlResult)) { 91 continue; 92 } 93 94 if (empty($controlResult['iconIdentifier'])) { 95 throw new \RuntimeException( 96 'Field controls must return an iconIdentifier', 97 1483890332 98 ); 99 } 100 if (empty($controlResult['title'])) { 101 throw new \RuntimeException( 102 'Field controls must return a title', 103 1483890482 104 ); 105 } 106 if (empty($controlResult['linkAttributes'])) { 107 throw new \RuntimeException( 108 'Field controls must return link attributes', 109 1483891272 110 ); 111 } 112 113 $icon = $controlResult['iconIdentifier']; 114 $title = $languageService->sL($controlResult['title']); 115 $linkAttributes = $controlResult['linkAttributes']; 116 if (!isset($linkAttributes['class'])) { 117 $linkAttributes['class'] = 'btn btn-default'; 118 } else { 119 $linkAttributes['class'] .= ' btn btn-default'; 120 } 121 if (!isset($linkAttributes['href'])) { 122 $linkAttributes['href'] = '#'; 123 } 124 125 unset($controlResult['iconIdentifier']); 126 unset($controlResult['title']); 127 unset($controlResult['linkAttributes']); 128 129 $html = []; 130 $html[] = '<a ' . GeneralUtility::implodeAttributes($linkAttributes, true) . '>'; 131 $html[] = '<span title="' . htmlspecialchars($title) . '">'; 132 $html[] = $iconFactory->getIcon($icon, Icon::SIZE_SMALL)->render(); 133 $html[] = '</span>'; 134 $html[] = '</a>'; 135 136 $finalControlResult = $this->initializeResultArray(); 137 $finalControlResult = array_merge($finalControlResult, $controlResult); 138 $finalControlResult['html'] = implode(LF, $html); 139 140 $result = $this->mergeChildReturnIntoExistingResult($result, $finalControlResult); 141 } 142 return $result; 143 } 144 145 /** 146 * @return LanguageService 147 */ 148 protected function getLanguageService() 149 { 150 return $GLOBALS['LANG']; 151 } 152} 153