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