Serializes Item metadata.

@param Item  $question
@param array $options

@return array
348321
private function serializeMetadata(Item $question, array $options = [])
    {
        $metadata = ['protectQuestion' => $question->getProtectUpdate()];

        $creator = $question->getCreator();

        if (!empty($creator)) {
            $metadata['creator'] = $this->userSerializer->serialize($creator, $options);
            // TODO : remove me. for retro compatibility with old schema
            $metadata['authors'] = [$this->userSerializer->serialize($creator, $options)];
        }

        if ($question->getDateCreate()) {
            $metadata['created'] = DateNormalizer::normalize($question->getDateCreate());
        }

        if ($question->getDateModify()) {
            $metadata['updated'] = DateNormalizer::normalize($question->getDateModify());
        }

        if (in_array(Transfer::INCLUDE_ADMIN_META, $options)) {
            /** @var ExerciseRepository $exerciseRepo */
            $exerciseRepo = $this->om->getRepository(Exercise::class);

            // Gets exercises that use this item
            $exercises = $exerciseRepo->findByQuestion($question);
            $metadata['usedBy'] = array_map(function (Exercise $exercise) {
                return $exercise->getUuid();
            }, $exercises);

            // Gets users who have access to this item
            $users = $this->om->getRepository(Shared::class)->findBy(['question' => $question]);
            $metadata['sharedWith'] = array_map(function (Shared $sharedQuestion) use ($options) {
                $shared = [
                    'adminRights' => $sharedQuestion->hasAdminRights(),
                    'user' => $this->userSerializer->serialize($sharedQuestion->getUser(), $options),
                ];

                return $shared;
            }, $users);
        }

        return $metadata;
    }
353539
public function ask(string $question, $default = 'n', ?array $options = null)
	{
		$options = $options === null ? ['y' => true, 'n' => false] : $this->normalizeKeys($options);

		$input = parent::ask(trim($question) . ' [' . $this->getOptions($options, $default) . '] ');

		$input = mb_strtolower(empty($input) ? $default : $input);

		if(!isset($options[$input]))
		{
			return $this->ask($question, $default, $options);
		}

		return $options[$input];
	}
745667
public function validateAfterSchema($question, array $options = [])
    {
        $errors = [];

        if (empty($question['content'])) {
            // No blank content
            $errors[] = [
                'path' => '/content',
                'message' => 'Question content can not be empty',
            ];
        }

        if (!isset($question['score'])) {
            // No question with no score
            // this is not in the schema because this will become optional when exercise without scores will be implemented
            $errors[] = [
                'path' => '/score',
                'message' => 'Question score is required',
            ];
        }

        if (in_array(Validation::REQUIRE_SOLUTIONS, $options) && !isset($question['solutions'])) {
            // No question without solutions
            $errors[] = [
                'path' => '/solutions',
                'message' => 'Question requires a "solutions" property',
            ];
        }

        if (!$this->itemDefinitions->has($question['type'])) {
            $errors[] = [
                'path' => '/type',
                'message' => 'Unknown question type "'.$question['type'].'"',
            ];
        }

        // Validate hints
        if (isset($question['hints'])) {
            array_map(function ($hint) use (&$errors, $options) {
                $errors = array_merge($errors, $this->hintValidator->validateAfterSchema($hint, $options));
            }, $question['hints']);
        }

        // Validate objects
        if (isset($question['objects'])) {
            array_map(function ($object) use (&$errors, $options) {
                $errors = array_merge($errors, $this->contentValidator->validateAfterSchema($object, $options));
            }, $question['objects']);
        }

        // Validates specific data of the question type
        if (empty($errors)) {
            // Forward to the correct definition
            $definition = $this->itemDefinitions->get($question['type']);

            $errors = array_merge(
                $errors,
                $definition->validateQuestion($question, array_merge($options, [Validation::NO_SCHEMA]))
            );
        }

        return $errors;
    }
247058
private function deserializeHints(Item $question, array $hints = [], array $options = [])
    {
        $hintEntities = $question->getHints()->toArray();

        foreach ($hints as $hintData) {
            $existingHint = null;

            // Searches for an existing hint entity.
            foreach ($hintEntities as $entityIndex => $entityHint) {
                /** @var Hint $entityHint */
                if ($entityHint->getUuid() === $hintData['id']) {
                    $existingHint = $entityHint;
                    unset($hintEntities[$entityIndex]);
                    break;
                }
            }

            $entity = $this->hintSerializer->deserialize($hintData, $existingHint, $options);

            if (empty($existingHint)) {
                // Creation of a new hint (we need to link it to the question)
                $question->addHint($entity);
            }
        }

        // Remaining hints are no longer in the Exercise
        if (0 < count($hintEntities)) {
            foreach ($hintEntities as $hintToRemove) {
                $question->removeHint($hintToRemove);
            }
        }
    }
583333
public function deserialize($data, OpenQuestion $wordsQuestion = null, array $options = [])
    {
        if (empty($wordsQuestion)) {
            $wordsQuestion = new OpenQuestion();
        }

        $this->deserializeSolutions($wordsQuestion, $data['solutions'], $options);

        return $wordsQuestion;
    }
1077879
public function serialize(ChoiceQuestion $choiceQuestion, array $options = [])
    {
        $serialized = [
            'random' => $choiceQuestion->getShuffle(),
            'multiple' => $choiceQuestion->isMultiple(),
            'numbering' => $choiceQuestion->getNumbering(),
            'direction' => $choiceQuestion->getDirection(),
        ];

        // Serializes choices
        $choices = $this->serializeChoices($choiceQuestion, $options);

        if ($choiceQuestion->getShuffle() && in_array(Transfer::SHUFFLE_ANSWERS, $options)) {
            shuffle($choices);
        }

        $serialized['choices'] = $choices;

        if (in_array(Transfer::INCLUDE_SOLUTIONS, $options)) {
            $serialized['solutions'] = $this->serializeSolutions($choiceQuestion);
        }

        return $serialized;
    }
850463
public function apiSerialize($object, $options = [])
    {
        if (!empty($object)) {
            return $this->serializerProvider->serialize($object, $options);
        }

        return $object;
    }
567284
private function serializeItems(OrderingQuestion $question, array $options = [])
    {
        return array_map(function (OrderingItem $item) use ($options) {
            $itemData = $this->contentSerializer->serialize($item, $options);
            $itemData['id'] = $item->getUuid();

            return $itemData;
        }, $question->getItems()->toArray());
    }
850890
public function validateAfterSchema($answerData, array $options = [])
    {
        $errors = [];

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

        foreach ($answerData as $rowIndex => $answerRow) {
            foreach ($answerRow as $itemIndex => $answerItem) {
                if (empty($question->getItem($answerItem))) {
                    $errors[] = [
                        'path' => "/[{$rowIndex}]/[{$itemIndex}]",
                        'message' => 'answers must reference a question item',
                    ];
                }
            }
        }

        return $errors;
    }
517695
private function serializeHints(Item $question, array $options = [])
    {
        return array_map(function (Hint $hint) use ($options) {
            return $this->hintSerializer->serialize($hint, $options);
        }, $question->getHints()->toArray());
    }
519891
protected function select(string $question, array $options): int
	{
		return (new Select($this->input, $this->output))->ask($question, $options);
	}
701192
public function serialize(MatchQuestion $setQuestion, array $options = [])
    {
        $serialized = [
            'random' => $setQuestion->getShuffle(),
            'penalty' => $setQuestion->getPenalty(),
        ];

        $items = array_map(function (Proposal $proposal) use ($options) {
            $setData = $this->contentSerializer->serialize($proposal, $options);
            $setData['id'] = $proposal->getUuid();

            return $setData;
        }, $setQuestion->getProposals()->toArray());

        $sets = array_map(function (Label $label) use ($options) {
            $itemData = $this->contentSerializer->serialize($label, $options);
            $itemData['id'] = $label->getUuid();

            return $itemData;
        }, $setQuestion->getLabels()->toArray());

        if ($setQuestion->getShuffle() && in_array(Transfer::SHUFFLE_ANSWERS, $options)) {
            shuffle($sets);
            shuffle($items);
        }

        $serialized['sets'] = $sets;
        $serialized['items'] = $items;

        if (in_array(Transfer::INCLUDE_SOLUTIONS, $options)) {
            $serialized['solutions'] = $this->serializeSolutions($setQuestion);
        }

        return $serialized;
    }
724041
public function serialize(Paper $paper, array $options = [])
    {
        // Adds user score if available and the method options do not already request it
        if (!in_array(Transfer::INCLUDE_USER_SCORE, $options)
            && $this->isScoreAvailable($paper->getExercise(), $paper)) {
            $options[] = Transfer::INCLUDE_USER_SCORE;
        }

        return $this->serializer->serialize($paper, $options);
    }
346500
public function deserializeQuestion(array $questionData, AbstractItem $question = null, array $options = [])
    {
        return $this->getQuestionSerializer()->deserialize($questionData, $question, $options);
    }
685368
public function serialize(OpenQuestion $openQuestion, array $options = [])
    {
        $serialized = [
            'contentType' => 'text',
            'maxLength' => $openQuestion->getAnswerMaxLength(),
        ];

        if (in_array(Transfer::INCLUDE_SOLUTIONS, $options)) {
            $serialized['solutions'] = [];
        }

        return $serialized;
    }
1289174
public function validateAfterSchema($answerData, array $options = [])
    {
        /** @var MatchQuestion $question */
        $question = !empty($options[Validation::QUESTION]) ? $options[Validation::QUESTION] : null;
        if (empty($question)) {
            throw new \LogicException('Answer validation : Cannot perform additional validation without question.');
        }

        $errors = [];

        $proposalIds = array_map(function (Proposal $proposal) {
            return $proposal->getUuid();
        }, $question->getProposals()->toArray());

        $labelIds = array_map(function (Label $label) {
            return $label->getUuid();
        }, $question->getLabels()->toArray());

        foreach ($answerData as $answer) {
            if (!in_array($answer['itemId'], $proposalIds)) {
                $errors[] = [
                    'path' => '/itemId',
                    'message' => 'Answer `itemId` must reference an item from `items`',
                ];
            }

            if (!in_array($answer['setId'], $labelIds)) {
                $errors[] = [
                    'path' => '/setId',
                    'message' => 'Answer `setId` must reference an item from `sets`',
                ];
            }
        }

        return $errors;
    }
605185
private function serializeSolutions(OrderingQuestion $question)
    {
        return array_map(function (OrderingItem $item) {
            $solutionData = [
                'itemId' => $item->getUuid(),
                'score' => $item->getScore(),
            ];

            if ($item->getFeedback()) {
                $solutionData['feedback'] = $item->getFeedback();
            }

            if ($item->getPosition()) {
                $solutionData['position'] = $item->getPosition();
            }

            return $solutionData;
        }, $question->getItems()->toArray());
    }