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 CreatablePropertyCollectionElementPropertiesValidator extends CollectionBasedValidator 27{ 28 29 /** 30 * Checks if the property collection element property is defined 31 * within the form editor setup or if the property is defined within 32 * the "predefinedDefaults" in the form editor setup 33 * and the property value matches the predefined value 34 * or if there is a valid hmac hash for the value. 35 * If the property collection element property is defined within the form editor setup 36 * and there is no valid hmac hash for the value 37 * and is the form property collection element property configured to only allow a limited set of values, 38 * check the current (submitted) value against the allowed set of values (defined within the form setup). 39 * 40 * @param string $key 41 * @param mixed $value 42 */ 43 public function __invoke(string $key, $value) 44 { 45 $dto = $this->validationDto->withPropertyPath($key); 46 47 if ($this->getConfigurationService()->isPropertyCollectionPropertyDefinedInFormEditorSetup($dto)) { 48 if ($this->getConfigurationService()->propertyCollectionPropertyHasLimitedAllowedValuesDefinedWithinFormEditorSetup($dto)) { 49 $this->validatePropertyCollectionPropertyValue($value, $dto); 50 } 51 } elseif ( 52 $this->getConfigurationService()->isPropertyCollectionPropertyDefinedInPredefinedDefaultsInFormEditorSetup($dto) 53 && !ArrayUtility::isValidPath($this->currentElement, $this->buildHmacDataPath($dto->getPropertyPath()), '.') 54 ) { 55 $this->validatePropertyCollectionElementPredefinedDefaultValue($value, $dto); 56 } else { 57 $this->validatePropertyCollectionElementPropertyValueByHmacData( 58 $this->currentElement, 59 $value, 60 $this->sessionToken, 61 $dto 62 ); 63 } 64 } 65 66 /** 67 * Throws an exception if the value from a property collection property 68 * does not match the default value from the form editor setup. 69 * 70 * @param mixed $value 71 * @param ValidationDto $dto 72 * @throws PropertyException 73 */ 74 protected function validatePropertyCollectionElementPredefinedDefaultValue( 75 $value, 76 ValidationDto $dto 77 ): void { 78 // If the property collection element is newly created, we have to compare the $value (form definition) with $predefinedDefaultValue (form setup) 79 // to check the integrity (at this time we don't have a hmac on the value to check the integrity) 80 $predefinedDefaultValue = $this->getConfigurationService()->getPropertyCollectionPredefinedDefaultValueFromFormEditorSetup($dto); 81 if ($value !== $predefinedDefaultValue) { 82 $throwException = true; 83 84 if (is_string($predefinedDefaultValue)) { 85 // Last chance: 86 // Get all translations (from all backend languages) for the untranslated! $predefinedDefaultValue and 87 // compare the (already translated) $value (from the form definition) against the possible 88 // translations from $predefinedDefaultValue. 89 $untranslatedPredefinedDefaultValue = $this->getConfigurationService()->getPropertyCollectionPredefinedDefaultValueFromFormEditorSetup($dto, false); 90 $translations = $this->getConfigurationService()->getAllBackendTranslationsForTranslationKey( 91 $untranslatedPredefinedDefaultValue, 92 $dto->getPrototypeName() 93 ); 94 95 if (in_array($value, $translations, true)) { 96 $throwException = false; 97 } 98 } 99 100 if ($throwException) { 101 $message = 'The value "%s" of property "%s" (form element "%s" / "%s.%s") is not equal to the default value "%s" #1528591502'; 102 throw new PropertyException( 103 sprintf( 104 $message, 105 $value, 106 $dto->getPropertyPath(), 107 $dto->getFormElementIdentifier(), 108 $dto->getPropertyCollectionName(), 109 $dto->getPropertyCollectionElementIdentifier(), 110 $predefinedDefaultValue 111 ), 112 1528591502 113 ); 114 } 115 } 116 } 117 118 /** 119 * Throws an exception if the value from a property collection property 120 * does not match the allowed set of values (defined within the form setup). 121 * 122 * @param mixed $value 123 * @param ValidationDto $dto 124 * @throws PropertyException 125 */ 126 protected function validatePropertyCollectionPropertyValue( 127 $value, 128 ValidationDto $dto 129 ): void { 130 $allowedValues = $this->getConfigurationService()->getAllowedValuesForPropertyCollectionPropertyFromFormEditorSetup($dto); 131 132 if (!in_array($value, $allowedValues, true)) { 133 $untranslatedAllowedValues = $this->getConfigurationService()->getAllowedValuesForPropertyCollectionPropertyFromFormEditorSetup($dto, false); 134 // Compare the $value against the untranslated set of allowed values 135 if (in_array($value, $untranslatedAllowedValues, true)) { 136 // All good, $value is within the untranslated set of allowed values 137 return; 138 } 139 // Get all translations (from all backend languages) for the untranslated! $allowedValues and 140 // compare the (already translated) $value (from the form definition) against all possible 141 // translations for $untranslatedAllowedValues. 142 $allPossibleAllowedValuesTranslations = $this->getConfigurationService()->getAllBackendTranslationsForTranslationKeys( 143 $untranslatedAllowedValues, 144 $dto->getPrototypeName() 145 ); 146 147 foreach ($allPossibleAllowedValuesTranslations as $translations) { 148 if (in_array($value, $translations, true)) { 149 // All good, $value is within the set of translated allowed values 150 return; 151 } 152 } 153 154 // Last chance: 155 // If $value is not configured within the form setup as an allowed value 156 // but was written within the form definition by hand (and therefore contains a hmac), 157 // check if $value is manipulated. 158 // If $value has no hmac or if the hmac exists but is not valid, 159 // then $this->validatePropertyCollectionElementPropertyValueByHmacData() will 160 // throw an exception. 161 $this->validatePropertyCollectionElementPropertyValueByHmacData( 162 $this->currentElement, 163 $value, 164 $this->sessionToken, 165 $dto 166 ); 167 } 168 } 169} 170