1<?php 2declare(strict_types = 1); 3namespace TYPO3\CMS\Form\Domain\Model\FormElements; 4 5/* 6 * This file is part of the TYPO3 CMS project. 7 * 8 * It originated from the Neos.Form package (www.neos.io) 9 * 10 * It is free software; you can redistribute it and/or modify it under 11 * the terms of the GNU General Public License, either version 2 12 * of the License, or any later version. 13 * 14 * For the full copyright and license information, please read the 15 * LICENSE.txt file that was distributed with this source code. 16 * 17 * The TYPO3 project - inspiring people to share! 18 */ 19 20use TYPO3\CMS\Core\Utility\GeneralUtility; 21use TYPO3\CMS\Extbase\Object\ObjectManager; 22use TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException; 23use TYPO3\CMS\Form\Domain\Exception\TypeDefinitionNotFoundException; 24use TYPO3\CMS\Form\Domain\Exception\TypeDefinitionNotValidException; 25use TYPO3\CMS\Form\Domain\Model\Exception\FormDefinitionConsistencyException; 26use TYPO3\CMS\Form\Domain\Model\Renderable\AbstractCompositeRenderable; 27 28/** 29 * A base class for "section-like" form parts like "Page" or "Section" (which 30 * is rendered as "Fieldset") 31 * 32 * This class contains multiple FormElements ({@link FormElementInterface}). 33 * 34 * Please see {@link FormDefinition} for an in-depth explanation. 35 * 36 * **This class is NOT meant to be sub classed by developers.** 37 * Scope: frontend 38 */ 39abstract class AbstractSection extends AbstractCompositeRenderable 40{ 41 42 /** 43 * Constructor. Needs the identifier and type of this element 44 * 45 * @param string $identifier The Section identifier 46 * @param string $type The Section type 47 * @throws IdentifierNotValidException if the identifier was no non-empty string 48 */ 49 public function __construct(string $identifier, string $type) 50 { 51 if (!is_string($identifier) || strlen($identifier) === 0) { 52 throw new IdentifierNotValidException('The given identifier was not a string or the string was empty.', 1477082501); 53 } 54 55 $this->identifier = $identifier; 56 $this->type = $type; 57 } 58 59 /** 60 * Get the child Form Elements 61 * 62 * @return FormElementInterface[] The Page's elements 63 */ 64 public function getElements(): array 65 { 66 return $this->renderables; 67 } 68 69 /** 70 * Get the child Form Elements 71 * 72 * @return FormElementInterface[] The Page's elements 73 */ 74 public function getElementsRecursively(): array 75 { 76 return $this->getRenderablesRecursively(); 77 } 78 79 /** 80 * Add a new form element at the end of the section 81 * 82 * @param FormElementInterface $formElement The form element to add 83 * @throws FormDefinitionConsistencyException if FormElement is already added to a section 84 */ 85 public function addElement(FormElementInterface $formElement) 86 { 87 $this->addRenderable($formElement); 88 } 89 90 /** 91 * Create a form element with the given $identifier and attach it to this section/page. 92 * 93 * - Create Form Element object based on the given $typeName 94 * - set defaults inside the Form Element (based on the parent form's field defaults) 95 * - attach Form Element to this Section/Page 96 * - return the newly created Form Element object 97 * 98 * 99 * @param string $identifier Identifier of the new form element 100 * @param string $typeName type of the new form element 101 * @return FormElementInterface the newly created form element 102 * @throws TypeDefinitionNotFoundException 103 * @throws TypeDefinitionNotValidException 104 */ 105 public function createElement(string $identifier, string $typeName): FormElementInterface 106 { 107 $formDefinition = $this->getRootForm(); 108 109 $typeDefinitions = $formDefinition->getTypeDefinitions(); 110 if (isset($typeDefinitions[$typeName])) { 111 $typeDefinition = $typeDefinitions[$typeName]; 112 } else { 113 $renderingOptions = $formDefinition->getRenderingOptions(); 114 $skipUnknownElements = isset($renderingOptions['skipUnknownElements']) && $renderingOptions['skipUnknownElements'] === true; 115 if (!$skipUnknownElements) { 116 throw new TypeDefinitionNotFoundException(sprintf('Type "%s" not found. Probably some configuration is missing.', $typeName), 1382364019); 117 } 118 119 $element = GeneralUtility::makeInstance(ObjectManager::class) 120 ->get(UnknownFormElement::class, $identifier, $typeName); 121 $this->addElement($element); 122 return $element; 123 } 124 125 if (!isset($typeDefinition['implementationClassName'])) { 126 throw new TypeDefinitionNotFoundException(sprintf('The "implementationClassName" was not set in type definition "%s".', $typeName), 1325689855); 127 } 128 129 $implementationClassName = $typeDefinition['implementationClassName']; 130 $element = GeneralUtility::makeInstance(ObjectManager::class) 131 ->get($implementationClassName, $identifier, $typeName); 132 if (!$element instanceof FormElementInterface) { 133 throw new TypeDefinitionNotValidException(sprintf('The "implementationClassName" for element "%s" ("%s") does not implement the FormElementInterface.', $identifier, $implementationClassName), 1327318156); 134 } 135 unset($typeDefinition['implementationClassName']); 136 137 $this->addElement($element); 138 $element->setOptions($typeDefinition); 139 140 $element->initializeFormElement(); 141 return $element; 142 } 143 144 /** 145 * Move FormElement $element before $referenceElement. 146 * 147 * Both $element and $referenceElement must be direct descendants of this Section/Page. 148 * 149 * @param FormElementInterface $elementToMove 150 * @param FormElementInterface $referenceElement 151 */ 152 public function moveElementBefore(FormElementInterface $elementToMove, FormElementInterface $referenceElement) 153 { 154 $this->moveRenderableBefore($elementToMove, $referenceElement); 155 } 156 157 /** 158 * Move FormElement $element after $referenceElement 159 * 160 * Both $element and $referenceElement must be direct descendants of this Section/Page. 161 * 162 * @param FormElementInterface $elementToMove 163 * @param FormElementInterface $referenceElement 164 */ 165 public function moveElementAfter(FormElementInterface $elementToMove, FormElementInterface $referenceElement) 166 { 167 $this->moveRenderableAfter($elementToMove, $referenceElement); 168 } 169 170 /** 171 * Remove $elementToRemove from this Section/Page 172 * 173 * @param FormElementInterface $elementToRemove 174 */ 175 public function removeElement(FormElementInterface $elementToRemove) 176 { 177 $this->removeRenderable($elementToRemove); 178 } 179} 180