From 67faf42f3e784b84476318f04b714e07e9f8e244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Cailly?= Date: Tue, 13 Aug 2024 10:09:56 +0200 Subject: [PATCH] feat(forms): Add Entity field configuration for form destination --- .../CommonITILField/EntityFieldTest.php | 376 ++++++++++++++++++ src/Dropdown.php | 2 + src/Entity.php | 2 +- .../AbstractCommonITILFormDestination.php | 4 +- .../CommonITILField/EntityField.php | 190 +++++++++ .../CommonITILField/EntityFieldConfig.php | 94 +++++ .../CommonITILField/EntityFieldStrategy.php | 162 ++++++++ .../Form/QuestionType/QuestionTypeItem.php | 8 +- .../layout/parts/profile_selector.html.twig | 4 +- .../form/itil_config_fields/entity.html.twig | 82 ++++ .../destination_config_fields/entity.cy.js | 143 +++++++ 11 files changed, 1059 insertions(+), 8 deletions(-) create mode 100644 phpunit/functional/Glpi/Form/Destination/CommonITILField/EntityFieldTest.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/EntityField.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/EntityFieldConfig.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/EntityFieldStrategy.php create mode 100644 templates/pages/admin/form/itil_config_fields/entity.html.twig create mode 100644 tests/cypress/e2e/form/destination_config_fields/entity.cy.js diff --git a/phpunit/functional/Glpi/Form/Destination/CommonITILField/EntityFieldTest.php b/phpunit/functional/Glpi/Form/Destination/CommonITILField/EntityFieldTest.php new file mode 100644 index 00000000000..d866fc46543 --- /dev/null +++ b/phpunit/functional/Glpi/Form/Destination/CommonITILField/EntityFieldTest.php @@ -0,0 +1,376 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace tests\units\Glpi\Form\Destination\CommonITILField; + +use DbTestCase; +use Entity; +use Glpi\Form\AnswersHandler\AnswersHandler; +use Glpi\Form\Destination\CommonITILField\EntityField; +use Glpi\Form\Destination\CommonITILField\EntityFieldConfig; +use Glpi\Form\Destination\CommonITILField\EntityFieldStrategy; +use Glpi\Form\Destination\FormDestinationTicket; +use Glpi\Form\Form; +use Glpi\Form\QuestionType\QuestionTypeItem; +use Glpi\Form\QuestionType\QuestionTypeRequester; +use Glpi\Tests\FormBuilder; +use Glpi\Tests\FormTesterTrait; +use Group; +use Group_User; +use Session; +use User; + +final class EntityFieldTest extends DbTestCase +{ + use FormTesterTrait; + + public function setUp(): void + { + parent::setUp(); + $this->login(); + } + + private function getAnswers() + { + $entities = $this->createItems(Entity::class, [ + ['name' => 'Entity 1', 'entities_id' => $this->getTestRootEntity(true)], + ['name' => 'Entity 2', 'entities_id' => $this->getTestRootEntity(true)], + ['name' => 'Entity 3', 'entities_id' => $this->getTestRootEntity(true)], + ['name' => 'Entity 4', 'entities_id' => $this->getTestRootEntity(true)], + ['name' => 'Entity 5', 'entities_id' => $this->getTestRootEntity(true)], + ]); + $users = $this->createItems(User::class, [ + ['name' => 'User 1', 'entities_id' => $entities[2]->getId()], + ['name' => 'User 2', 'entities_id' => $entities[3]->getId()], + ]); + $groups = $this->createItems(Group::class, [ + ['name' => "Group 1", 'entities_id' => $entities[4]->getId()], + ]); + + return [ + 'answers' => [ + "Entity 1" => [ + 'itemtype' => Entity::getType(), + 'items_id' => $entities[0]->getId(), + ], + "Entity 2" => [ + 'itemtype' => Entity::getType(), + 'items_id' => $entities[1]->getId(), + ], + "Requester 1" => [ + 'users_id-' . $users[0]->getId(), + ], + "Requester 2" => [ + 'groups_id-' . $groups[0]->getId(), + ], + "Requester 3" => [ + 'users_id-' . $users[1]->getId(), + 'groups_id-' . $groups[0]->getId(), + ], + ], + 'entities' => $entities, + 'users' => $users, + 'groups' => $groups, + ]; + } + + public function testEntityFromForm() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers()['answers']; + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::FROM_FORM + ), + answers: $answers, + expected_entity_id: $form->fields['entities_id'] + ); + } + + public function testEntityFromFormWithSpecificEntityId() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers()['answers']; + + // Change the entity to a new one + $new_entity = $this->createItem( + Entity::class, + ['name' => 'New entity', 'entities_id' => $this->getTestRootEntity(true)] + ); + $this->updateItem( + $form::getType(), + $form->getId(), + ['entities_id' => $new_entity->getId()] + ); + + // Refresh object + $form->getFromDB($form->getId()); + + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::FROM_FORM + ), + answers: $answers, + expected_entity_id: $new_entity->getId() + ); + } + + public function testEntityFromFirstRequester() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers(); + + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::FROM_FIRST_REQUESTER + ), + answers: $answers['answers'], + expected_entity_id: $answers['users'][0]->fields['entities_id'] + ); + } + + public function testEntityFromFirstRequesterGroup() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers(); + + // Drop the "Requester 1" answer + unset($answers['answers']["Requester 1"]); + + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::FROM_FIRST_REQUESTER + ), + answers: $answers['answers'], + expected_entity_id: $answers['groups'][0]->fields['entities_id'] + ); + } + + public function testEntityFromFirstRequesters() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers(); + + // Drop the "Requester 1" and "Requester 2" answers + unset($answers['answers']["Requester 1"]); + unset($answers['answers']["Requester 2"]); + + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::FROM_FIRST_REQUESTER + ), + answers: $answers['answers'], + expected_entity_id: $answers['users'][1]->fields['entities_id'] + ); + } + + public function testEntityFromUser() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers(); + + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::FROM_USER + ), + answers: $answers['answers'], + expected_entity_id: Session::getActiveEntity() + ); + } + + public function testEntityFromSpecificValue() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers(); + + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::SPECIFIC_VALUE, + specific_entity_id: $answers['entities'][2]->getId() + ), + answers: $answers['answers'], + expected_entity_id: $answers['entities'][2]->getId() + ); + } + + public function testEntityFromSpecificAnswer() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers(); + + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::SPECIFIC_ANSWER, + specific_question_id: $this->getQuestionId($form, "Entity 2") + ), + answers: $answers['answers'], + expected_entity_id: $answers['entities'][1]->getId() + ); + } + + public function testEntityFromLastValidAnswer() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers(); + + // With multiple answers submitted + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::LAST_VALID_ANSWER + ), + answers: $answers['answers'], + expected_entity_id: $answers['entities'][1]->getId() + ); + } + + public function testEntityFromLastValidAnswerWithOnlyFirstAnswer() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers(); + + // Only first answer was submitted + unset($answers['answers']["Entity 2"]); + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::LAST_VALID_ANSWER + ), + answers: $answers['answers'], + expected_entity_id: $answers['entities'][0]->getId() + ); + } + + public function testEntityFromLastValidAnswerWithOnlySecondAnswer() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + $answers = $this->getAnswers(); + + // Only second answer was submitted + unset($answers['answers']["Entity 1"]); + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::LAST_VALID_ANSWER + ), + answers: $answers['answers'], + expected_entity_id: $answers['entities'][1]->getId() + ); + } + + public function testEntityFromLastValidAnswerWithNoAnswer() + { + $form = $this->createAndGetFormWithMultipleEntityAndRequesterQuestions(); + + // No answers, fallback to default value db value + $this->sendFormAndAssertTicketEntity( + form: $form, + config: new EntityFieldConfig( + EntityFieldStrategy::LAST_VALID_ANSWER + ), + answers: [], + expected_entity_id: 0 + ); + } + + private function sendFormAndAssertTicketEntity( + Form $form, + EntityFieldConfig $config, + array $answers, + int $expected_entity_id, + ): void { + // Insert config + $destinations = $form->getDestinations(); + $this->assertCount(1, $destinations); + $destination = current($destinations); + $this->updateItem( + $destination::getType(), + $destination->getId(), + ['config' => ['entity' => $config->jsonSerialize()]], + ["config"], + ); + + // The provider use a simplified answer format to be more readable. + // Rewrite answers into expected format. + $formatted_answers = []; + foreach ($answers as $question => $answer) { + $key = $this->getQuestionId($form, $question); + $formatted_answers[$key] = $answer; + } + + // Submit form + $answers_handler = AnswersHandler::getInstance(); + $answers = $answers_handler->saveAnswers( + $form, + $formatted_answers, + getItemByTypeName(\User::class, TU_USER, true) + ); + + // Get created ticket + $created_items = $answers->getCreatedItems(); + $this->assertCount(1, $created_items); + $ticket = current($created_items); + + // Check request type + $this->assertEquals($expected_entity_id, $ticket->fields['entities_id']); + } + + private function createAndGetFormWithMultipleEntityAndRequesterQuestions(): Form + { + $builder = new FormBuilder(); + $builder->addQuestion("Entity 1", QuestionTypeItem::class, [ + 'itemtype' => Entity::getType(), + ]); + $builder->addQuestion("Entity 2", QuestionTypeItem::class, [ + 'itemtype' => Entity::getType(), + ]); + $builder->addQuestion("Requester 1", QuestionTypeRequester::class); + $builder->addQuestion("Requester 2", QuestionTypeRequester::class); + $builder->addQuestion("Requester 3", QuestionTypeRequester::class); + $builder->addDestination( + FormDestinationTicket::class, + "My ticket", + ); + return $this->createForm($builder); + } +} diff --git a/src/Dropdown.php b/src/Dropdown.php index fb6d0374431..c8cf2b64bc0 100644 --- a/src/Dropdown.php +++ b/src/Dropdown.php @@ -133,6 +133,7 @@ public static function show($itemtype, $options = []) $params['parent_id_field'] = null; $params['multiple'] = false; $params['init'] = true; + $params['aria_label'] = ''; if (is_array($options) && count($options)) { foreach ($options as $key => $val) { @@ -266,6 +267,7 @@ public static function show($itemtype, $options = []) 'parent_id_field' => $params['parent_id_field'], 'multiple' => $params['multiple'] ?? false, 'init' => $params['init'] ?? true, + 'aria_label' => $params['aria_label'], ]; if ($params['multiple']) { diff --git a/src/Entity.php b/src/Entity.php index ff29d07337a..4cfb45f8237 100644 --- a/src/Entity.php +++ b/src/Entity.php @@ -3088,7 +3088,7 @@ public static function getEntitySelectorTree(): array if (isset($entity['tree']) && count($entity['tree']) > 0) { $entity['folder'] = true; - $entity['title'] .= " + $entity['title'] .= " "; diff --git a/src/Glpi/Form/Destination/AbstractCommonITILFormDestination.php b/src/Glpi/Form/Destination/AbstractCommonITILFormDestination.php index 473b3474a20..7c93b8956a1 100644 --- a/src/Glpi/Form/Destination/AbstractCommonITILFormDestination.php +++ b/src/Glpi/Form/Destination/AbstractCommonITILFormDestination.php @@ -39,6 +39,7 @@ use Glpi\Application\View\TemplateRenderer; use Glpi\Form\AnswersSet; use Glpi\Form\Destination\CommonITILField\ContentField; +use Glpi\Form\Destination\CommonITILField\EntityField; use Glpi\Form\Destination\CommonITILField\TitleField; use Glpi\Form\Form; use Override; @@ -79,8 +80,6 @@ final public function createDestinationItems( $input = [ 'name' => '', 'content' => '', - // Temporary as entity configuration is not yet available - 'entities_id' => $form->fields['entities_id'] ]; // Compute and apply template predefined template fields @@ -143,6 +142,7 @@ public function getConfigurableFields(): array return [ new TitleField(), new ContentField(), + new EntityField(), ]; } diff --git a/src/Glpi/Form/Destination/CommonITILField/EntityField.php b/src/Glpi/Form/Destination/CommonITILField/EntityField.php new file mode 100644 index 00000000000..df1165b501c --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/EntityField.php @@ -0,0 +1,190 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Entity; +use Glpi\Application\View\TemplateRenderer; +use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\AnswersSet; +use Glpi\Form\Destination\AbstractConfigField; +use Glpi\Form\Form; +use Glpi\Form\QuestionType\QuestionTypeItem; +use InvalidArgumentException; +use Override; + +class EntityField extends AbstractConfigField +{ + #[Override] + public function getKey(): string + { + return 'entity'; + } + + #[Override] + public function getLabel(): string + { + return _n("Entity", "Entities", 1); + } + + #[Override] + public function getConfigClass(): string + { + return EntityFieldConfig::class; + } + + #[Override] + public function renderConfigForm( + Form $form, + JsonFieldInterface $config, + string $input_name, + array $display_options + ): string { + if (!$config instanceof EntityFieldConfig) { + throw new InvalidArgumentException("Unexpected config class"); + } + + $twig = TemplateRenderer::getInstance(); + return $twig->render('pages/admin/form/itil_config_fields/entity.html.twig', [ + // Possible configuration constant that will be used to to hide/show additional fields + 'CONFIG_SPECIFIC_VALUE' => EntityFieldStrategy::SPECIFIC_VALUE->value, + 'CONFIG_SPECIFIC_ANSWER' => EntityFieldStrategy::SPECIFIC_ANSWER->value, + + // General display options + 'options' => $display_options, + + // Main config field + 'main_config_field' => [ + 'label' => $this->getLabel(), + 'value' => $config->getStrategy()->value, + 'input_name' => $input_name . "[" . EntityFieldConfig::STRATEGY . "]", + 'possible_values' => $this->getMainConfigurationValuesforDropdown(), + ], + + // Specific additional config for SPECIFIC_VALUE strategy + 'specific_value_extra_field' => [ + 'aria_label' => __("Select an entity..."), + 'value' => $config->getSpecificEntityId() ?? 0, + 'input_name' => $input_name . "[" . EntityFieldConfig::ENTITY_ID . "]", + ], + + // Specific additional config for SPECIFIC_ANSWER strategy + 'specific_answer_extra_field' => [ + 'empty_label' => __("Select a question..."), + 'value' => $config->getSpecificQuestionId(), + 'input_name' => $input_name . "[" . EntityFieldConfig::QUESTION_ID . "]", + 'possible_values' => $this->getEntityQuestionsValuesForDropdown($form), + ], + ]); + } + + #[Override] + public function applyConfiguratedValueToInputUsingAnswers( + JsonFieldInterface $config, + array $input, + AnswersSet $answers_set + ): array { + if (!$config instanceof EntityFieldConfig) { + throw new InvalidArgumentException("Unexpected config class"); + } + + // Compute value according to strategy + $entity_id = $config->getStrategy()->getEntityID($config, $answers_set); + + // Do not edit input if invalid value was found + if (Entity::getById($entity_id) === false) { + return $input; + } + + // Apply value + $input['entities_id'] = $entity_id; + return $input; + } + + #[Override] + public function getDefaultConfig(Form $form): EntityFieldConfig + { + // Returne last valid answer by default and fallback + // to form entity if no valid answer was found + $valid_answers = array_filter( + $form->getQuestionsByType( + QuestionTypeItem::class + ), + fn($question) => (new QuestionTypeItem())->getDefaultValueItemtype($question) === Entity::getType() + ); + + if (count($valid_answers) == 0) { + return new EntityFieldConfig( + EntityFieldStrategy::FROM_FORM + ); + } + + return new EntityFieldConfig( + EntityFieldStrategy::LAST_VALID_ANSWER + ); + } + + private function getMainConfigurationValuesforDropdown(): array + { + $values = []; + foreach (EntityFieldStrategy::cases() as $strategies) { + $values[$strategies->value] = $strategies->getLabel(); + } + return $values; + } + + private function getEntityQuestionsValuesForDropdown(Form $form): array + { + $values = []; + $questions = $form->getQuestionsByType(QuestionTypeItem::class); + + foreach ($questions as $question) { + // Only keep questions that are Entity + if ((new QuestionTypeItem())->getDefaultValueItemtype($question) !== Entity::getType()) { + continue; + } + + $values[$question->getId()] = $question->fields['name']; + } + + return $values; + } + + #[Override] + public function getWeight(): int + { + return 30; + } +} diff --git a/src/Glpi/Form/Destination/CommonITILField/EntityFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/EntityFieldConfig.php new file mode 100644 index 00000000000..e9d7c633075 --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/EntityFieldConfig.php @@ -0,0 +1,94 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Glpi\DBAL\JsonFieldInterface; +use Override; + +final class EntityFieldConfig implements JsonFieldInterface +{ + // Unique reference to hardcoded names used for serialization and forms input names + public const STRATEGY = 'strategy'; + public const QUESTION_ID = 'question_id'; + public const ENTITY_ID = 'entity_id'; + + public function __construct( + private EntityFieldStrategy $strategy, + private ?int $specific_question_id = null, + private ?int $specific_entity_id = null, + ) { + } + + #[Override] + public static function jsonDeserialize(array $data): self + { + $strategy = EntityFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); + if ($strategy === null) { + $strategy = EntityFieldStrategy::LAST_VALID_ANSWER; + } + + return new self( + strategy: $strategy, + specific_question_id: $data[self::QUESTION_ID], + specific_entity_id: $data[self::ENTITY_ID], + ); + } + + #[Override] + public function jsonSerialize(): array + { + return [ + self::STRATEGY => $this->strategy->value, + self::QUESTION_ID => $this->specific_question_id, + self::ENTITY_ID => $this->specific_entity_id, + ]; + } + + public function getStrategy(): EntityFieldStrategy + { + return $this->strategy; + } + + public function getSpecificQuestionId(): ?int + { + return $this->specific_question_id; + } + + public function getSpecificEntityId(): ?int + { + return $this->specific_entity_id; + } +} diff --git a/src/Glpi/Form/Destination/CommonITILField/EntityFieldStrategy.php b/src/Glpi/Form/Destination/CommonITILField/EntityFieldStrategy.php new file mode 100644 index 00000000000..c45b23779e0 --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/EntityFieldStrategy.php @@ -0,0 +1,162 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Entity; +use Glpi\Form\AnswersSet; +use Glpi\Form\QuestionType\QuestionTypeItem; +use Glpi\Form\QuestionType\QuestionTypeRequester; +use Group; +use Group_User; +use Session; +use User; + +enum EntityFieldStrategy: string +{ + case FROM_FORM = 'from_form'; + case FROM_FIRST_REQUESTER = 'from_first_requester'; + case FROM_USER = 'from_user'; + case SPECIFIC_VALUE = 'specific_value'; + case SPECIFIC_ANSWER = 'specific_answer'; + case LAST_VALID_ANSWER = 'last_valid_answer'; + + public function getLabel(): string + { + return match ($this) { + self::FROM_FORM => __("From form"), + self::FROM_FIRST_REQUESTER => __("From first requester"), + self::FROM_USER => __("From user"), + self::SPECIFIC_VALUE => __("Specific entity"), + self::SPECIFIC_ANSWER => __("Answer from a specific question"), + self::LAST_VALID_ANSWER => __('Answer to last "Entity" item question'), + }; + } + + public function getEntityID( + EntityFieldConfig $config, + AnswersSet $answers_set, + ): ?int { + return match ($this) { + self::FROM_FORM => $answers_set->getItem()->fields['entities_id'], + self::FROM_FIRST_REQUESTER => $this->getEntityIDForFirstRequester($answers_set), + self::FROM_USER => Session::getActiveEntity(), + self::SPECIFIC_VALUE => $config->getSpecificEntityId(), + self::SPECIFIC_ANSWER => $this->getEntityIDForSpecificAnswer( + $config->getSpecificQuestionId(), + $answers_set + ), + self::LAST_VALID_ANSWER => $this->getEntityIDForLastValidAnswer($answers_set), + }; + } + + private function getEntityIDForFirstRequester( + AnswersSet $answers_set, + ): ?int { + $valid_answers = $answers_set->getAnswersByType( + QuestionTypeRequester::class + ); + + if (count($valid_answers) == 0) { + return null; + } + + $answer = current($valid_answers); + $requester_type = explode('-', $answer->getRawAnswer()[0])[0]; + $requester_id = explode('-', $answer->getRawAnswer()[0])[1]; + if (!is_numeric($requester_id)) { + return null; + } + + switch ($requester_type) { + case 'users_id': + $requester = new User(); + break; + case 'groups_id': + $requester = new Group(); + break; + } + + if (!isset($requester) || !$requester->getFromDB($requester_id)) { + return null; + } + + return $requester->fields['entities_id']; + } + + private function getEntityIDForSpecificAnswer( + ?int $question_id, + AnswersSet $answers_set, + ): ?int { + if ($question_id === null) { + return null; + } + + $answer = $answers_set->getAnswerByQuestionId($question_id); + if ($answer === null) { + return null; + } + + $value = $answer->getRawAnswer(); + if ($value['itemtype'] !== Entity::getType() || !is_numeric($value['items_id'])) { + return null; + } + + return (int) $value['items_id']; + } + + public function getEntityIDForLastValidAnswer( + AnswersSet $answers_set, + ): ?int { + $valid_answers = array_filter( + $answers_set->getAnswersByType( + QuestionTypeItem::class + ), + fn($answer) => $answer->getRawAnswer()['itemtype'] === Entity::getType() + ); + + if (count($valid_answers) == 0) { + return null; + } + + $answer = end($valid_answers); + $value = $answer->getRawAnswer(); + if (!is_numeric($value['items_id'])) { + return null; + } + + return (int) $value['items_id']; + } +} diff --git a/src/Glpi/Form/QuestionType/QuestionTypeItem.php b/src/Glpi/Form/QuestionType/QuestionTypeItem.php index b517de07839..bffe72c38c6 100644 --- a/src/Glpi/Form/QuestionType/QuestionTypeItem.php +++ b/src/Glpi/Form/QuestionType/QuestionTypeItem.php @@ -210,15 +210,17 @@ public function renderEndUserTemplate(Question $question): string 'no_label' : true, 'display_emptychoice': true, 'right' : 'all', + 'aria_label' : items_id_aria_label, } ) }} TWIG; $twig = TemplateRenderer::getInstance(); return $twig->renderFromStringTemplate($template, [ - 'question' => $question, - 'itemtype' => $this->getDefaultValueItemtype($question) ?? '0', - 'default_items_id' => $this->getDefaultValueItemId($question), + 'question' => $question, + 'itemtype' => $this->getDefaultValueItemtype($question) ?? '0', + 'default_items_id' => $this->getDefaultValueItemId($question), + 'items_id_aria_label' => $this->items_id_aria_label, ]); } diff --git a/templates/layout/parts/profile_selector.html.twig b/templates/layout/parts/profile_selector.html.twig index a56e7e45c46..530074920b2 100644 --- a/templates/layout/parts/profile_selector.html.twig +++ b/templates/layout/parts/profile_selector.html.twig @@ -66,7 +66,7 @@ {% else %}