Performs additional validations.

@param array $answerData
@param array $options

@return array
979773
public function validateAfterSchema($answerData, array $options = [])
    {
        $errors = [];

        /** @var ChoiceQuestion $question */
        $question = !empty($options[Validation::QUESTION]) ? $options[Validation::QUESTION] : null;
        if (empty($question)) {
            throw new \LogicException('Answer validation : Cannot perform additional validation without question.');
        }

        $choiceIds = array_map(function (Choice $choice) {
            return $choice->getUuid();
        }, $question->getChoices()->toArray());

        foreach ($answerData as $index => $id) {
            if (!in_array($id, $choiceIds)) {
                $errors[] = [
                    'path' => "/[{$index}]",
                    'message' => 'Answer array identifiers must reference question choices',
                ];
            }
        }

        if (!$question->isMultiple() && count($answerData) > 1) {
            $errors[] = [
                'path' => '',
                'message' => 'This question does not allow multiple answers',
            ];
        }

        return $errors;
    }
1280296
protected function getFieldValidationResults(
        array $fields,
        array $allowedFields = [],
        array $fieldResults = [],
        array $options = []
    ): array {
        foreach ($fields as $fieldName => $value) {
            if (!in_array($fieldName, $allowedFields)) {
                $fieldResults[$fieldName] = new ValidationResultBasic(
                    false,
                    BuildOptionCode::invoke($options, static::CODE_UNRECOGNIZED_FIELD),
                    ['message' => 'Unrecognized field received: (' . $fieldName . ')']
                );
            }
        }

        return $fieldResults;
    }
276922
private function getValidationMessages(array $validationData)
    {
        $messages = array();
        $messagesData = $validationData['messages'];
        foreach ($messagesData as $messageData) {
            $message = new W3CMarkupValidatorMessage($messageData);
            $messages[] = $message;
        }

        return $messages;
    }
1033976
protected function setValidations($modelData)
    {
        $fields = '';
        $firstIteration = true;

        foreach ($modelData->fields as $field)
        {
            if ($firstIteration)
            {
                $fields .= sprintf("'%s' => '%s'," . PHP_EOL, $field->name, $field->validations);
                $firstIteration = false;
            }
            else
            {
                $fields .= sprintf($this->tab(3) . "'%s' => '%s'," . PHP_EOL, $field->name, $field->validations);
            }
        }

        $this->stub = str_replace('{{validations}}', $fields, $this->stub);

        return $this;
    }
513758
protected function parseValidatorOptions($rawValidatorOptions)
    {
        $validatorOptions = [];
        $parsedValidatorOptions = [];
        preg_match_all(self::PATTERN_MATCH_VALIDATOROPTIONS, $rawValidatorOptions, $validatorOptions, PREG_SET_ORDER);
        foreach ($validatorOptions as $validatorOption) {
            $parsedValidatorOptions[trim($validatorOption['optionName'])] = trim($validatorOption['optionValue']);
        }
        array_walk($parsedValidatorOptions, [$this, 'unquoteString']);
        return $parsedValidatorOptions;
    }
1126443
public function validate(array $requestData)
    {
        $ourData = Hash::extract($requestData, $this->getInputName());

        foreach ($this->getAttributeNames() as $name) {

            $validations = $this->extractNormalizedValidations($name);
            foreach ($validations as $validationKey => $validatorConfig) {

                $validator = Validator::factory($validationKey);
                $validator->configure($validatorConfig);
                $validator->init();

                if (!array_key_exists($name, $ourData) || !$validator->test($ourData[$name], $this)) {
                    $this->setValidationError($name, $validationKey, $validator->getMessage());
                }
            }
        }

        return !$this->hasValidationErrors();
    }
416585
protected final function validateNewData(array $data = null) {
		if ($data === null && isset($this->_formData)) {
			$data = $this->_formData;
		}
		if ($this->rules) {
			return $this->validateData($this->rules, $data);
		}

		return true;
	}
599250
private function validateRelationships(array $jsonData): self
    {
        // execute start(s)
        $this->executeStarts(array_merge(
            $this->getSerializer()::readRulesStartIndexes($this->getToOneRules()),
            $this->getSerializer()::readRulesStartIndexes($this->getToManyRules())
        ));

        if (array_key_exists(DI::KEYWORD_DATA, $jsonData) === true &&
            array_key_exists(DI::KEYWORD_RELATIONSHIPS, $data = $jsonData[DI::KEYWORD_DATA]) === true
        ) {
            if (is_array($relationships = $data[DI::KEYWORD_RELATIONSHIPS]) === false) {
                $title   = $this->formatMessage(Messages::INVALID_VALUE);
                $details = $this->formatMessage(Messages::INVALID_RELATIONSHIP_TYPE);
                $status  = JsonApiResponse::HTTP_UNPROCESSABLE_ENTITY;
                $this->getJsonApiErrorCollection()->addRelationshipsError($title, $details, (string)$status);
                $this->addErrorStatus($status);
            } else {
                // ok we got to something that could be null or a valid relationship
                $toOneIndexes  = $this->getSerializer()::readRulesIndexes($this->getToOneRules());
                $toManyIndexes = $this->getSerializer()::readRulesIndexes($this->getToManyRules());

                foreach ($relationships as $name => $relationship) {
                    if (array_key_exists($name, $toOneIndexes) === true) {
                        // it might be to1 relationship
                        $this->validateAsToOneRelationship($toOneIndexes[$name], $name, $relationship);
                    } elseif (array_key_exists($name, $toManyIndexes) === true) {
                        // it might be toMany relationship
                        $this->validateAsToManyRelationship($toManyIndexes[$name], $name, $relationship);
                    } else {
                        // unknown relationship
                        $title   = $this->formatMessage(Messages::INVALID_VALUE);
                        $details = $this->formatMessage(Messages::UNKNOWN_RELATIONSHIP);
                        $status  = JsonApiResponse::HTTP_UNPROCESSABLE_ENTITY;
                        $this->getJsonApiErrorCollection()
                            ->addRelationshipError($name, $title, $details, (string)$status);
                        $this->addErrorStatus($status);
                    }
                }
            }
        }

        // execute end(s)
        $this->executeEnds(array_merge(
            $this->getSerializer()::readRulesEndIndexes($this->getToOneRules()),
            $this->getSerializer()::readRulesEndIndexes($this->getToManyRules())
        ));

        if (count($this->getErrorAggregator()) > 0) {
            $status  = JsonApiResponse::HTTP_CONFLICT;
            foreach ($this->getErrorAggregator()->get() as $error) {
                $this->getJsonApiErrorCollection()->addValidationRelationshipError($error, $status);
            }
            $this->addErrorStatus($status);
            $this->getErrorAggregator()->clear();
        }

        return $this;
    }
271426
private function serializeSolutions(GridQuestion $gridQuestion, array $options = [])
    {
        $solutions = [];

        foreach ($gridQuestion->getCells()->toArray() as $cell) {
            $cellChoices = $cell->getChoices()->toArray();

            if (!empty($cellChoices)) {
                $solutionData = [
                    'cellId' => $cell->getUuid(),
                    'answers' => [],
                ];

                foreach ($cellChoices as $choice) {
                    $solutionData['answers'][] = $this->cellChoiceSerializer->serialize($choice, $options);
                }
                $solutions[] = $solutionData;
            }
        }

        return $solutions;
    }
296130
public function checkMessages(array $options = [])
    {
        $this->buildBody($options);

        $rawMessages = json_decode($this->getRequest()->getBody()->getContents());

        return $this->makeMessages($rawMessages->data);
    }
737538
public final function validate(array $data = null, array $rules = null) {
		if ($data === null && isset($this->_formData)) {
			$data = $this->_formData;
		}
		if (empty($data)) {
			throw new ValidateException(['error' => 'data is empty']);
		}
		if ($rules) {
			$this->rules    = [];
			$this->rulesIdx = [];
			$this->ruleKeys = [];
			foreach ($rules as $field => $rule) {
				$this->addRule($field, $rule);
			}
		}

		return $this->validateNewData($data);
	}
174843
public function deserialize($data, Answer $answer = null, array $options = [])
    {
        $answer = $answer ?: new Answer();

        $this->sipe('id', 'setUuid', $data, $answer);
        $this->sipe('questionId', 'setQuestionId', $data, $answer);
        $this->sipe('tries', 'setTries', $data, $answer);
        $this->sipe('score', 'setScore', $data, $answer);
        $this->sipe('feedback', 'setFeedback', $data, $answer);

        if (isset($data['usedHints'])) {
            foreach ($data['usedHints'] as $usedHint) {
                $answer->addUsedHint($usedHint['id']);
            }
        }
        if (!empty($data['data'])) {
            $answer->setData(json_encode($data['data']));
        }

        return $answer;
    }
356536
public function validateAfterSchema($answerData, array $options = [])
    {
        /** @var ClozeQuestion $question */
        $question = !empty($options[Validation::QUESTION]) ? $options[Validation::QUESTION] : null;

        if (empty($question)) {
            throw new \LogicException('Answer validation : Cannot perform additional validation without question.');
        }

        $holeIds = array_map(function (Hole $hole) {
            return $hole->getUuid();
        }, $question->getHoles()->toArray());

        foreach ($answerData as $answer) {
            if (empty($answer['holeId'])) {
                return ['Answer `holeId` cannot be empty'];
            }

            if (!in_array($answer['holeId'], $holeIds)) {
                return ['Answer array identifiers must reference question holes'];
            }
        }

        return [];
    }
943371
protected function _validates($options)
    {
        $defaults = ['embed' => true];
        $options += $defaults;

        if ($options['embed'] === true) {
            $options['embed'] = $this->hierarchy();
        }

        $schema = $this->schema();
        $tree = $schema->treeify($options['embed']);
        $success = true;

        foreach ($tree as $field => $value) {
            if (isset($this->{$field})) {
                $rel = $schema->relation($field);
                $success = $success && $rel->validates($this, $value ? $value + $options : $options);
            }
        }
        return $success;
    }
1299521
private function doValidateOptions(array $options = []): array
    {
        if (
            isset($this->options['curl'], $options['curl']) &&
            \is_array($this->options['curl']) &&
            \is_array($options['curl'])
        ) {
            $parameters['curl'] = \array_replace($this->options['curl'], $options['curl']);
        }

        $options = \array_replace($this->options, $options);

        foreach (static::$types as $k => $v) {
            $type = \gettype($options[$k]);
            $v = (array) $v;

            $checked = false;
            foreach ($v as $t) {
                if ($t === $type || ($t === 'callable' && \is_callable($options[$k]))) {
                    $checked = true;
                    break;
                }
            }

            if (!$checked) {
                $should = \implode(', ', $v);
                throw new InvalidArgumentException("'$k' options should be '$should', but get '$type'");
            }
        }

        return $options;
    }
674435
public function validateSubmitButtons($options = []) {
		$buttons_found = [];
		$names = [];
		foreach ($this->data as $k => $v) {
			foreach (($v['rows'] ?? []) as $k2 => $v2) {
				// find all process submit buttons
				foreach ($v2['elements'] as $k3 => $v3) {
					if (!empty($v3['options']['process_submit'])) {
						if (!isset($buttons_found[$k3])) {
							$buttons_found[$k3] = [];
						}
						$buttons_found[$k3][] = [
							'name' => $v3['options']['value'],
							'key' => [$k, 'rows', $k2, 'elements', $k3]
						];
						$names[$k3] = $v3['options']['value'];
					}
				}
			}
		}
		// validations
		if (!empty($this->buttons_model)) {
			// make a call to master object
			$result = $this->buttons_model->processButtons($this, [
				'skip_validation' => $options['skip_validation'] ?? false
			]);
			$not_allowed = $result['not_allowed'];
			$also_set_save = $result['also_set_save'];
			$all_standard_buttons = $result['all_buttons'];
		} else { // standard buttons
			$all_standard_buttons = [
				self::BUTTON_SUBMIT,
				self::BUTTON_SUBMIT_SAVE,
				self::BUTTON_SUBMIT_SAVE_AND_NEW,
				self::BUTTON_SUBMIT_SAVE_AND_CLOSE,
				self::BUTTON_SUBMIT_RESET,
				self::BUTTON_SUBMIT_DELETE,
				self::BUTTON_CONTINUE,
				self::BUTTON_STOP,
			];
			// process
			$not_allowed = [];
			$show_save_buttons = false;
			if (!empty($this->options['acl_subresource_edit'])) {
				// remove delete buttons if we do not have loaded values or do not have permission
				if (!$this->values_loaded || (empty($this->options['skip_acl']) && !$this->tempProcessACLSubresources($this->options['acl_subresource_edit'], 'Record_Delete'))) {
					$not_allowed[] = self::BUTTON_SUBMIT_DELETE;
				}
				// we need to check permissions
				if (!$this->values_loaded && (empty($this->options['skip_acl']) && $this->tempProcessACLSubresources($this->options['acl_subresource_edit'], 'Record_New'))) {
					$show_save_buttons = true;
				}
				if ($this->values_loaded && (empty($this->options['skip_acl']) && $this->tempProcessACLSubresources($this->options['acl_subresource_edit'], 'Record_Edit'))) {
					$show_save_buttons = true;
				}
			} else {
				// remove delete buttons if we do not have loaded values or do not have permission
				if (!$this->values_loaded || (empty($this->options['skip_acl']) && !\Application::$controller->can('Record_Delete', 'Edit'))) {
					$not_allowed[] = self::BUTTON_SUBMIT_DELETE;
				}
				// we need to check permissions
				if (!$this->values_loaded && (empty($this->options['skip_acl']) && \Application::$controller->can('Record_New', 'Edit'))) {
					$show_save_buttons = true;
				}
				if ($this->values_loaded && (empty($this->options['skip_acl']) && \Application::$controller->can('Record_Edit', 'Edit'))) {
					$show_save_buttons = true;
				}
			}
			if (!$show_save_buttons && empty($this->options['skip_acl'])) {
				$not_allowed[] = self::BUTTON_SUBMIT_SAVE;
				$not_allowed[] = self::BUTTON_SUBMIT_SAVE_AND_NEW;
				$not_allowed[] = self::BUTTON_SUBMIT_SAVE_AND_CLOSE;
				// if we need to make form readonly
				if (!empty($this->options['readonly_if_cannot_edit'])) {
					$this->readonly();
				}
			}
			// these buttons are considered save
			$also_set_save = [
				self::BUTTON_SUBMIT,
				self::BUTTON_SUBMIT_SAVE_AND_NEW,
				self::BUTTON_SUBMIT_SAVE_AND_CLOSE,
				self::BUTTON_SUBMIT_DELETE,
				self::BUTTON_SUBMIT_OTHER_DELETE,
				self::BUTTON_CONTINUE,
				self::BUTTON_STOP
			];
		}
		// validate if we have that button
		$result = true;
		foreach ($buttons_found as $k => $v) {
			if (empty($this->process_submit[$k])) {
				unset($this->process_submit[$k]);
			} else if (empty($buttons_found[$k]) || (!in_array($k, $all_standard_buttons) && empty($this->process_submit_other[$k])) || in_array($k, $not_allowed)) {
				// if we have validation
				if (empty($options['skip_validation'])) {
					$this->error('danger', 'Form action [action] is not allowed!', null, ['replace' => ['[action]' => i18n(null, $names[$k])]]);
					$result = false;
				}
				unset($this->process_submit[$k]);
			}
			// hide it
			if (!empty($options['skip_validation'])) {
				if (!empty($buttons_found[$k]) && in_array($k, $not_allowed)) {
					foreach ($buttons_found[$k] as $v2) {
						// we disable buttons in test mode
						if (\Application::get('flag.numbers.frontend.html.form.show_field_settings')) {
							$temp = array_key_get($this->data, $v2['key']);
							$temp['options']['class'] = ($temp['options']['class'] ?? '') . ' disabled';
							array_key_set($this->data, $v2['key'], $temp);
						} else { // remove in regular mode
							array_key_get($this->data, $v2['key'], ['unset' => true]);
						}
					}
				}
			}
		}
		if ($this->submitted) {
			$this->misc_settings['__original_submitted'] = true;
		}
		$this->submitted = !empty($this->process_submit);
		// fix for save
		foreach ($also_set_save as $v) {
			if (!empty($this->process_submit[$v])) {
				$this->process_submit[self::BUTTON_SUBMIT_SAVE] = true;
			}
		}
		return $result;
	}
277924
public function validateNotSame($data, $options, $context) {
		if (!is_array($options)) {
			$options = ['compare' => $options];
		}

		$compareValue = $context['data'][$options['compare']];
		return $compareValue !== $data;
	}