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\Form\Domain\Configuration\FormDefinition\Validators; 19 20use TYPO3\CMS\Core\Utility\ArrayUtility; 21use TYPO3\CMS\Form\Domain\Configuration\Exception\PropertyException; 22 23/** 24 * @internal 25 */ 26class CreatableFormElementPropertiesValidator extends ElementBasedValidator 27{ 28 29 /** 30 * Checks if the form element property is defined within the form editor setup 31 * or if the property is defined within the "predefinedDefaults" in the form editor setup 32 * and the property value matches the predefined value 33 * or if there is a valid hmac hash for the value. 34 * If the form element property is defined within the form editor setup 35 * and there is no valid hmac hash for the value 36 * and is the form element property configured to only allow a limited set of values, 37 * check the current (submitted) value against the allowed set of values (defined within the form setup). 38 * 39 * @param string $key 40 * @param mixed $value 41 */ 42 public function __invoke(string $key, $value) 43 { 44 $dto = $this->validationDto->withPropertyPath($key); 45 46 if ($this->getConfigurationService()->isFormElementPropertyDefinedInFormEditorSetup($dto)) { 47 if ($this->getConfigurationService()->formElementPropertyHasLimitedAllowedValuesDefinedWithinFormEditorSetup($dto)) { 48 $this->validateFormElementValue($value, $dto); 49 } 50 } elseif ( 51 $this->getConfigurationService()->isFormElementPropertyDefinedInPredefinedDefaultsInFormEditorSetup($dto) 52 && !ArrayUtility::isValidPath($this->currentElement, $this->buildHmacDataPath($dto->getPropertyPath()), '.') 53 ) { 54 $this->validateFormElementPredefinedDefaultValue($value, $dto); 55 } else { 56 $this->validateFormElementPropertyValueByHmacData( 57 $this->currentElement, 58 $value, 59 $this->sessionToken, 60 $dto 61 ); 62 } 63 } 64 65 /** 66 * Throws an exception if the value from a form element property 67 * does not match the default value from the form editor setup. 68 * 69 * @param mixed $value 70 * @param ValidationDto $dto 71 * @throws PropertyException 72 */ 73 protected function validateFormElementPredefinedDefaultValue( 74 $value, 75 ValidationDto $dto 76 ): void { 77 // If the form element is newly created, we have to compare the $value (form definition) with $predefinedDefaultValue (form setup) 78 // to check the integrity (at this time we don't have a hmac for the $value to check the integrity) 79 $predefinedDefaultValue = $this->getConfigurationService()->getFormElementPredefinedDefaultValueFromFormEditorSetup($dto); 80 if ($value !== $predefinedDefaultValue) { 81 $throwException = true; 82 83 if (is_string($predefinedDefaultValue)) { 84 // Last chance: 85 // Get all translations (from all backend languages) for the untranslated! $predefinedDefaultValue and 86 // compare the (already translated) $value (from the form definition) against the possible 87 // translations from $predefinedDefaultValue. 88 // Usecase: 89 // * backend language is EN 90 // * open the form editor and add a ContentElement form element 91 // * switch to another browser tab and change the backend language to DE 92 // * clear the cache 93 // * go back to the form editor and click the save button 94 // Out of scope: 95 // * the same scenario as above + delete the previous chosen backend language within the maintenance tool 96 $untranslatedPredefinedDefaultValue = $this->getConfigurationService()->getFormElementPredefinedDefaultValueFromFormEditorSetup($dto, false); 97 $translations = $this->getConfigurationService()->getAllBackendTranslationsForTranslationKey( 98 $untranslatedPredefinedDefaultValue, 99 $dto->getPrototypeName() 100 ); 101 102 if (in_array($value, $translations, true)) { 103 $throwException = false; 104 } 105 } 106 107 if ($throwException) { 108 $message = 'The value "%s" of property "%s" (form element "%s") is not equal to the default value "%s" #1528588035'; 109 throw new PropertyException( 110 sprintf( 111 $message, 112 $value, 113 $dto->getPropertyPath(), 114 $dto->getFormElementIdentifier(), 115 $predefinedDefaultValue 116 ), 117 1528588035 118 ); 119 } 120 } 121 } 122 123 /** 124 * Throws an exception if the value from a form element property 125 * does not match the allowed set of values (defined within the form setup). 126 * 127 * @param mixed $value 128 * @param ValidationDto $dto 129 * @throws PropertyException 130 */ 131 protected function validateFormElementValue( 132 $value, 133 ValidationDto $dto 134 ): void { 135 $allowedValues = $this->getConfigurationService()->getAllowedValuesForFormElementPropertyFromFormEditorSetup($dto); 136 137 if (!in_array($value, $allowedValues, true)) { 138 $untranslatedAllowedValues = $this->getConfigurationService()->getAllowedValuesForFormElementPropertyFromFormEditorSetup($dto, false); 139 // Compare the $value against the untranslated set of allowed values 140 if (in_array($value, $untranslatedAllowedValues, true)) { 141 // All good, $value is within the untranslated set of allowed values 142 return; 143 } 144 // Get all translations (from all backend languages) for the untranslated! $allowedValues and 145 // compare the (already translated) $value (from the form definition) against all possible 146 // translations for $untranslatedAllowedValues. 147 $allPossibleAllowedValuesTranslations = $this->getConfigurationService()->getAllBackendTranslationsForTranslationKeys( 148 $untranslatedAllowedValues, 149 $dto->getPrototypeName() 150 ); 151 152 foreach ($allPossibleAllowedValuesTranslations as $translations) { 153 if (in_array($value, $translations, true)) { 154 // All good, $value is within the set of translated allowed values 155 return; 156 } 157 } 158 159 // Last chance: 160 // If $value is not configured within the form setup as an allowed value 161 // but was written within the form definition by hand (and therefore contains a hmac), 162 // check if $value is manipulated. 163 // If $value has no hmac or if the hmac exists but is not valid, 164 // then $this->validatePropertyCollectionElementPropertyValueByHmacData() will 165 // throw an exception. 166 $this->validateFormElementPropertyValueByHmacData( 167 $this->currentElement, 168 $value, 169 $this->sessionToken, 170 $dto 171 ); 172 } 173 } 174} 175