From 3ebae3bf27914385ed1c26ff17beb1782983a1bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Cailly?= <42278610+ccailly@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:32:01 +0100 Subject: [PATCH] feat(forms): Add export and import functionality for destinations --- .../CommonITILField/AssigneeFieldTest.php | 20 +- .../CommonITILField/OLATTOFieldTest.php | 10 +- .../CommonITILField/OLATTRFieldTest.php | 10 +- .../CommonITILField/ObserverFieldTest.php | 18 +- .../CommonITILField/RequesterFieldTest.php | 18 +- .../CommonITILField/SLATTOFieldTest.php | 10 +- .../CommonITILField/SLATTRFieldTest.php | 10 +- .../Export/FormSerializerDestinationTest.php | 924 ++++++++++++++++++ .../Glpi/Form/Export/FormSerializerTest.php | 119 ++- .../AbstractCommonITILFormDestination.php | 17 + .../CommonITILField/AssigneeField.php | 15 + .../CommonITILField/AssigneeFieldConfig.php | 68 ++ .../CommonITILField/AssociatedItemsField.php | 21 +- .../AssociatedItemsFieldConfig.php | 35 +- .../CommonITILField/EntityField.php | 4 +- .../CommonITILField/EntityFieldConfig.php | 28 +- .../CommonITILField/ITILActorField.php | 31 +- .../CommonITILField/ITILActorFieldConfig.php | 32 +- .../CommonITILField/ITILCategoryField.php | 6 +- .../ITILCategoryFieldConfig.php | 28 +- .../ITILFollowupFieldConfig.php | 15 +- .../CommonITILField/ITILTaskFieldConfig.php | 14 +- .../CommonITILField/LocationField.php | 4 +- .../CommonITILField/LocationFieldConfig.php | 28 +- .../CommonITILField/OLATTOField.php | 6 + .../CommonITILField/OLATTOFieldConfig.php | 65 ++ .../CommonITILField/OLATTRField.php | 6 + .../CommonITILField/OLATTRFieldConfig.php | 65 ++ .../CommonITILField/ObserverField.php | 15 + .../CommonITILField/ObserverFieldConfig.php | 68 ++ .../RequestSourceFieldConfig.php | 2 +- .../CommonITILField/RequestTypeField.php | 4 +- .../RequestTypeFieldConfig.php | 25 +- .../CommonITILField/RequesterField.php | 10 +- .../CommonITILField/RequesterFieldConfig.php | 68 ++ .../CommonITILField/SLATTOField.php | 6 + .../CommonITILField/SLATTOFieldConfig.php | 66 ++ .../CommonITILField/SLATTRField.php | 6 + .../CommonITILField/SLATTRFieldConfig.php | 65 ++ .../Destination/CommonITILField/SLMField.php | 10 +- .../CommonITILField/SLMFieldConfig.php | 17 +- .../CommonITILField/TemplateFieldConfig.php | 24 +- .../CommonITILField/UrgencyFieldConfig.php | 17 +- .../CommonITILField/ValidationField.php | 26 +- .../CommonITILField/ValidationFieldConfig.php | 35 +- .../ValidationFieldStrategy.php | 8 +- src/Glpi/Form/Destination/FormDestination.php | 4 + .../FormDestinationTypeManager.php | 2 +- .../Form/Export/Context/DatabaseMapper.php | 5 + .../ForeignKey/ForeignKeyArrayHandler.php | 13 +- .../Context/ForeignKey/ForeignKeyHandler.php | 6 +- .../ForeignKeyItemsArrayHandler.php | 141 +++ .../QuestionArrayForeignKeyHandler.php | 154 +++ .../ForeignKey/QuestionForeignKeyHandler.php | 113 +++ .../Form/Export/Serializer/FormSerializer.php | 96 +- .../DestinationContentSpecification.php | 43 + .../FormContentSpecification.php | 3 + src/Glpi/Form/Question.php | 15 + src/Glpi/Helpdesk/DefaultDataManager.php | 5 +- tests/src/FormTesterTrait.php | 12 +- 60 files changed, 2484 insertions(+), 227 deletions(-) create mode 100644 phpunit/functional/Glpi/Form/Export/FormSerializerDestinationTest.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/AssigneeFieldConfig.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/OLATTOFieldConfig.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/OLATTRFieldConfig.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/ObserverFieldConfig.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/RequesterFieldConfig.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/SLATTOFieldConfig.php create mode 100644 src/Glpi/Form/Destination/CommonITILField/SLATTRFieldConfig.php create mode 100644 src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyItemsArrayHandler.php create mode 100644 src/Glpi/Form/Export/Context/ForeignKey/QuestionArrayForeignKeyHandler.php create mode 100644 src/Glpi/Form/Export/Context/ForeignKey/QuestionForeignKeyHandler.php create mode 100644 src/Glpi/Form/Export/Specification/DestinationContentSpecification.php diff --git a/phpunit/functional/Glpi/Form/Destination/CommonITILField/AssigneeFieldTest.php b/phpunit/functional/Glpi/Form/Destination/CommonITILField/AssigneeFieldTest.php index c46db41bfd5..420b6016354 100644 --- a/phpunit/functional/Glpi/Form/Destination/CommonITILField/AssigneeFieldTest.php +++ b/phpunit/functional/Glpi/Form/Destination/CommonITILField/AssigneeFieldTest.php @@ -38,9 +38,9 @@ use CommonITILActor; use DbTestCase; use Glpi\Form\AnswersHandler\AnswersHandler; -use Glpi\Form\Destination\CommonITILField\ITILActorFieldConfig; use Glpi\Form\Destination\CommonITILField\ITILActorFieldStrategy; use Glpi\Form\Destination\CommonITILField\AssigneeField; +use Glpi\Form\Destination\CommonITILField\AssigneeFieldConfig; use Glpi\Form\Destination\FormDestinationTicket; use Glpi\Form\Form; use Glpi\Form\QuestionType\QuestionTypeActorsExtraDataConfig; @@ -61,7 +61,7 @@ final class AssigneeFieldTest extends DbTestCase public function testAssigneeFromTemplate(): void { $form = $this->createAndGetFormWithMultipleActorsQuestions(); - $from_template_config = new ITILActorFieldConfig( + $from_template_config = new AssigneeFieldConfig( ITILActorFieldStrategy::FROM_TEMPLATE ); @@ -123,7 +123,7 @@ public function testAssigneeFromTemplate(): void public function testAssigneeFormFiller(): void { $form = $this->createAndGetFormWithMultipleActorsQuestions(); - $form_filler_config = new ITILActorFieldConfig( + $form_filler_config = new AssigneeFieldConfig( ITILActorFieldStrategy::FORM_FILLER ); @@ -160,7 +160,7 @@ public function testSpecificActors(): void // Specific value: User $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new AssigneeFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, specific_itilactors_ids: [ User::getForeignKeyField() . '-' . $user->getID() @@ -173,7 +173,7 @@ public function testSpecificActors(): void // Specific value: User and Group $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new AssigneeFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, specific_itilactors_ids: [ User::getForeignKeyField() . '-' . $user->getID(), @@ -187,7 +187,7 @@ public function testSpecificActors(): void // Specific value: User, Group and Supplier $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new AssigneeFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, specific_itilactors_ids: [ User::getForeignKeyField() . '-' . $user->getID(), @@ -224,7 +224,7 @@ public function testActorsFromSpecificQuestions(): void // Using answer from first question $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new AssigneeFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_ANSWERS, specific_question_ids: [$this->getQuestionId($form, "Assignee 1")] ), @@ -244,7 +244,7 @@ public function testActorsFromSpecificQuestions(): void // Using answer from first and second question $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new AssigneeFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_ANSWERS, specific_question_ids: [ $this->getQuestionId($form, "Assignee 1"), @@ -271,7 +271,7 @@ public function testActorsFromLastValidQuestion(): void $this->login(); $form = $this->createAndGetFormWithMultipleActorsQuestions(); - $last_valid_answer_config = new ITILActorFieldConfig( + $last_valid_answer_config = new AssigneeFieldConfig( ITILActorFieldStrategy::LAST_VALID_ANSWER ); @@ -356,7 +356,7 @@ public function testActorsFromLastValidQuestion(): void private function sendFormAndAssertTicketActors( Form $form, - ITILActorFieldConfig $config, + AssigneeFieldConfig $config, array $answers, array $expected_actors_ids, ): void { diff --git a/phpunit/functional/Glpi/Form/Destination/CommonITILField/OLATTOFieldTest.php b/phpunit/functional/Glpi/Form/Destination/CommonITILField/OLATTOFieldTest.php index b482e8b654f..d30bafd4d24 100644 --- a/phpunit/functional/Glpi/Form/Destination/CommonITILField/OLATTOFieldTest.php +++ b/phpunit/functional/Glpi/Form/Destination/CommonITILField/OLATTOFieldTest.php @@ -38,7 +38,7 @@ use DbTestCase; use Glpi\Form\AnswersHandler\AnswersHandler; use Glpi\Form\Destination\CommonITILField\OLATTOField; -use Glpi\Form\Destination\CommonITILField\SLMFieldConfig; +use Glpi\Form\Destination\CommonITILField\OLATTOFieldConfig; use Glpi\Form\Destination\CommonITILField\SLMFieldStrategy; use Glpi\Form\Destination\FormDestinationTicket; use Glpi\Form\Form; @@ -80,7 +80,7 @@ public function testDefaultTemplateWithPredefinedField(): void $this->checkOLATTOFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new OLATTOFieldConfig( strategy: SLMFieldStrategy::FROM_TEMPLATE, ), expected_olas_tto_id: $created_ola_tto->getID() @@ -102,7 +102,7 @@ public function testSpecificOLATTO(): void $this->checkOLATTOFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new OLATTOFieldConfig( strategy: SLMFieldStrategy::SPECIFIC_VALUE, specific_slm_id: $created_ola_tto->getID() ), @@ -146,7 +146,7 @@ public function testSpecificOLATTOWithDefaultTemplateWithPredefinedField(): void $this->checkOLATTOFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new OLATTOFieldConfig( strategy: SLMFieldStrategy::SPECIFIC_VALUE, specific_slm_id: $created_ola_tto->getID() ), @@ -156,7 +156,7 @@ public function testSpecificOLATTOWithDefaultTemplateWithPredefinedField(): void private function checkOLATTOFieldConfiguration( Form $form, - SLMFieldConfig $config, + OLATTOFieldConfig $config, int $expected_olas_tto_id ): Ticket { // Insert config diff --git a/phpunit/functional/Glpi/Form/Destination/CommonITILField/OLATTRFieldTest.php b/phpunit/functional/Glpi/Form/Destination/CommonITILField/OLATTRFieldTest.php index f9810395433..fa4618e7c9a 100644 --- a/phpunit/functional/Glpi/Form/Destination/CommonITILField/OLATTRFieldTest.php +++ b/phpunit/functional/Glpi/Form/Destination/CommonITILField/OLATTRFieldTest.php @@ -38,7 +38,7 @@ use DbTestCase; use Glpi\Form\AnswersHandler\AnswersHandler; use Glpi\Form\Destination\CommonITILField\OLATTRField; -use Glpi\Form\Destination\CommonITILField\SLMFieldConfig; +use Glpi\Form\Destination\CommonITILField\OLATTRFieldConfig; use Glpi\Form\Destination\CommonITILField\SLMFieldStrategy; use Glpi\Form\Destination\FormDestinationTicket; use Glpi\Form\Form; @@ -80,7 +80,7 @@ public function testDefaultTemplateWithPredefinedField(): void $this->checkOLATTRFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new OLATTRFieldConfig( strategy: SLMFieldStrategy::FROM_TEMPLATE, ), expected_olas_ttr_id: $created_ola_ttr->getID() @@ -102,7 +102,7 @@ public function testSpecificOLATTR(): void $this->checkOLATTRFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new OLATTRFieldConfig( strategy: SLMFieldStrategy::SPECIFIC_VALUE, specific_slm_id: $created_ola_ttr->getID() ), @@ -146,7 +146,7 @@ public function testSpecificOLATTRWithDefaultTemplateWithPredefinedField(): void $this->checkOLATTRFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new OLATTRFieldConfig( strategy: SLMFieldStrategy::SPECIFIC_VALUE, specific_slm_id: $created_ola_ttr->getID() ), @@ -156,7 +156,7 @@ public function testSpecificOLATTRWithDefaultTemplateWithPredefinedField(): void private function checkOLATTRFieldConfiguration( Form $form, - SLMFieldConfig $config, + OLATTRFieldConfig $config, int $expected_olas_ttr_id ): Ticket { // Insert config diff --git a/phpunit/functional/Glpi/Form/Destination/CommonITILField/ObserverFieldTest.php b/phpunit/functional/Glpi/Form/Destination/CommonITILField/ObserverFieldTest.php index f8299fd6ca8..f0913caa872 100644 --- a/phpunit/functional/Glpi/Form/Destination/CommonITILField/ObserverFieldTest.php +++ b/phpunit/functional/Glpi/Form/Destination/CommonITILField/ObserverFieldTest.php @@ -38,7 +38,7 @@ use CommonITILActor; use DbTestCase; use Glpi\Form\AnswersHandler\AnswersHandler; -use Glpi\Form\Destination\CommonITILField\ITILActorFieldConfig; +use Glpi\Form\Destination\CommonITILField\ObserverFieldConfig; use Glpi\Form\Destination\CommonITILField\ITILActorFieldStrategy; use Glpi\Form\Destination\CommonITILField\ObserverField; use Glpi\Form\Destination\FormDestinationTicket; @@ -60,7 +60,7 @@ final class ObserverFieldTest extends DbTestCase public function testObserverFromTemplate(): void { $form = $this->createAndGetFormWithMultipleActorsQuestions(); - $from_template_config = new ITILActorFieldConfig( + $from_template_config = new ObserverFieldConfig( ITILActorFieldStrategy::FROM_TEMPLATE ); @@ -105,7 +105,7 @@ public function testObserverFromTemplate(): void public function testObserverFormFiller(): void { $form = $this->createAndGetFormWithMultipleActorsQuestions(); - $form_filler_config = new ITILActorFieldConfig( + $form_filler_config = new ObserverFieldConfig( ITILActorFieldStrategy::FORM_FILLER ); @@ -139,7 +139,7 @@ public function testSpecificActors(): void // Specific value: User $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new ObserverFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, specific_itilactors_ids: [ User::getForeignKeyField() . '-' . $user->getID() @@ -152,7 +152,7 @@ public function testSpecificActors(): void // Specific value: User and Group $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new ObserverFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, specific_itilactors_ids: [ User::getForeignKeyField() . '-' . $user->getID(), @@ -175,7 +175,7 @@ public function testActorsFromSpecificQuestions(): void // Using answer from first question $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new ObserverFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_ANSWERS, specific_question_ids: [$this->getQuestionId($form, "Observer 1")] ), @@ -194,7 +194,7 @@ public function testActorsFromSpecificQuestions(): void // Using answer from first and second question $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new ObserverFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_ANSWERS, specific_question_ids: [ $this->getQuestionId($form, "Observer 1"), @@ -217,7 +217,7 @@ public function testActorsFromSpecificQuestions(): void public function testActorsFromLastValidQuestion(): void { $form = $this->createAndGetFormWithMultipleActorsQuestions(); - $last_valid_answer_config = new ITILActorFieldConfig( + $last_valid_answer_config = new ObserverFieldConfig( ITILActorFieldStrategy::LAST_VALID_ANSWER ); @@ -290,7 +290,7 @@ public function testActorsFromLastValidQuestion(): void private function sendFormAndAssertTicketActors( Form $form, - ITILActorFieldConfig $config, + ObserverFieldConfig $config, array $answers, array $expected_actors_ids, ): void { diff --git a/phpunit/functional/Glpi/Form/Destination/CommonITILField/RequesterFieldTest.php b/phpunit/functional/Glpi/Form/Destination/CommonITILField/RequesterFieldTest.php index 39bd972f1bb..90b40b891cc 100644 --- a/phpunit/functional/Glpi/Form/Destination/CommonITILField/RequesterFieldTest.php +++ b/phpunit/functional/Glpi/Form/Destination/CommonITILField/RequesterFieldTest.php @@ -38,7 +38,7 @@ use CommonITILActor; use DbTestCase; use Glpi\Form\AnswersHandler\AnswersHandler; -use Glpi\Form\Destination\CommonITILField\ITILActorFieldConfig; +use Glpi\Form\Destination\CommonITILField\RequesterFieldConfig; use Glpi\Form\Destination\CommonITILField\ITILActorFieldStrategy; use Glpi\Form\Destination\CommonITILField\RequesterField; use Glpi\Form\Destination\FormDestinationTicket; @@ -60,7 +60,7 @@ final class RequesterFieldTest extends DbTestCase public function testRequesterFromTemplate(): void { $form = $this->createAndGetFormWithMultipleActorsQuestions(); - $from_template_config = new ITILActorFieldConfig( + $from_template_config = new RequesterFieldConfig( ITILActorFieldStrategy::FROM_TEMPLATE ); @@ -105,7 +105,7 @@ public function testRequesterFromTemplate(): void public function testRequesterFormFiller(): void { $form = $this->createAndGetFormWithMultipleActorsQuestions(); - $form_filler_config = new ITILActorFieldConfig( + $form_filler_config = new RequesterFieldConfig( ITILActorFieldStrategy::FORM_FILLER ); @@ -139,7 +139,7 @@ public function testSpecificActors(): void // Specific value: User $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new RequesterFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, specific_itilactors_ids: [ User::getForeignKeyField() . '-' . $user->getID() @@ -152,7 +152,7 @@ public function testSpecificActors(): void // Specific value: User and Group $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new RequesterFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, specific_itilactors_ids: [ User::getForeignKeyField() . '-' . $user->getID(), @@ -175,7 +175,7 @@ public function testActorsFromSpecificQuestions(): void // Using answer from first question $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new RequesterFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_ANSWERS, specific_question_ids: [$this->getQuestionId($form, "Requester 1")] ), @@ -194,7 +194,7 @@ public function testActorsFromSpecificQuestions(): void // Using answer from first and second question $this->sendFormAndAssertTicketActors( form: $form, - config: new ITILActorFieldConfig( + config: new RequesterFieldConfig( strategy: ITILActorFieldStrategy::SPECIFIC_ANSWERS, specific_question_ids: [ $this->getQuestionId($form, "Requester 1"), @@ -217,7 +217,7 @@ public function testActorsFromSpecificQuestions(): void public function testActorsFromLastValidQuestion(): void { $form = $this->createAndGetFormWithMultipleActorsQuestions(); - $last_valid_answer_config = new ITILActorFieldConfig( + $last_valid_answer_config = new RequesterFieldConfig( ITILActorFieldStrategy::LAST_VALID_ANSWER ); @@ -290,7 +290,7 @@ public function testActorsFromLastValidQuestion(): void private function sendFormAndAssertTicketActors( Form $form, - ITILActorFieldConfig $config, + RequesterFieldConfig $config, array $answers, array $expected_actors_ids, ): void { diff --git a/phpunit/functional/Glpi/Form/Destination/CommonITILField/SLATTOFieldTest.php b/phpunit/functional/Glpi/Form/Destination/CommonITILField/SLATTOFieldTest.php index 687f5892bcc..9da6df67081 100644 --- a/phpunit/functional/Glpi/Form/Destination/CommonITILField/SLATTOFieldTest.php +++ b/phpunit/functional/Glpi/Form/Destination/CommonITILField/SLATTOFieldTest.php @@ -38,7 +38,7 @@ use DbTestCase; use Glpi\Form\AnswersHandler\AnswersHandler; use Glpi\Form\Destination\CommonITILField\SLATTOField; -use Glpi\Form\Destination\CommonITILField\SLMFieldConfig; +use Glpi\Form\Destination\CommonITILField\SLATTOFieldConfig; use Glpi\Form\Destination\CommonITILField\SLMFieldStrategy; use Glpi\Form\Destination\FormDestinationTicket; use Glpi\Form\Form; @@ -80,7 +80,7 @@ public function testDefaultTemplateWithPredefinedField(): void $this->checkSLATTOFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new SLATTOFieldConfig( strategy: SLMFieldStrategy::FROM_TEMPLATE, ), expected_slas_tto_id: $created_sla_tto->getID() @@ -102,7 +102,7 @@ public function testSpecificSLATTO(): void $this->checkSLATTOFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new SLATTOFieldConfig( strategy: SLMFieldStrategy::SPECIFIC_VALUE, specific_slm_id: $created_sla_tto->getID() ), @@ -146,7 +146,7 @@ public function testSpecificSLATTOWithDefaultTemplateWithPredefinedField(): void $this->checkSLATTOFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new SLATTOFieldConfig( strategy: SLMFieldStrategy::SPECIFIC_VALUE, specific_slm_id: $created_sla_tto->getID() ), @@ -156,7 +156,7 @@ public function testSpecificSLATTOWithDefaultTemplateWithPredefinedField(): void private function checkSLATTOFieldConfiguration( Form $form, - SLMFieldConfig $config, + SLATTOFieldConfig $config, int $expected_slas_tto_id ): Ticket { // Insert config diff --git a/phpunit/functional/Glpi/Form/Destination/CommonITILField/SLATTRFieldTest.php b/phpunit/functional/Glpi/Form/Destination/CommonITILField/SLATTRFieldTest.php index 03c98830918..04ea5f05d49 100644 --- a/phpunit/functional/Glpi/Form/Destination/CommonITILField/SLATTRFieldTest.php +++ b/phpunit/functional/Glpi/Form/Destination/CommonITILField/SLATTRFieldTest.php @@ -38,7 +38,7 @@ use DbTestCase; use Glpi\Form\AnswersHandler\AnswersHandler; use Glpi\Form\Destination\CommonITILField\SLATTRField; -use Glpi\Form\Destination\CommonITILField\SLMFieldConfig; +use Glpi\Form\Destination\CommonITILField\SLATTRFieldConfig; use Glpi\Form\Destination\CommonITILField\SLMFieldStrategy; use Glpi\Form\Destination\FormDestinationTicket; use Glpi\Form\Form; @@ -80,7 +80,7 @@ public function testDefaultTemplateWithPredefinedField(): void $this->checkSLATTRFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new SLATTRFieldConfig( strategy: SLMFieldStrategy::FROM_TEMPLATE, ), expected_slas_ttr_id: $created_sla_ttr->getID() @@ -102,7 +102,7 @@ public function testSpecificSLATTR(): void $this->checkSLATTRFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new SLATTRFieldConfig( strategy: SLMFieldStrategy::SPECIFIC_VALUE, specific_slm_id: $created_sla_ttr->getID() ), @@ -146,7 +146,7 @@ public function testSpecificSLATTRWithDefaultTemplateWithPredefinedField(): void $this->checkSLATTRFieldConfiguration( form: $this->createAndGetFormWithTicketDestination(), - config: new SLMFieldConfig( + config: new SLATTRFieldConfig( strategy: SLMFieldStrategy::SPECIFIC_VALUE, specific_slm_id: $created_sla_ttr->getID() ), @@ -156,7 +156,7 @@ public function testSpecificSLATTRWithDefaultTemplateWithPredefinedField(): void private function checkSLATTRFieldConfiguration( Form $form, - SLMFieldConfig $config, + SLATTRFieldConfig $config, int $expected_slas_ttr_id ): Ticket { // Insert config diff --git a/phpunit/functional/Glpi/Form/Export/FormSerializerDestinationTest.php b/phpunit/functional/Glpi/Form/Export/FormSerializerDestinationTest.php new file mode 100644 index 00000000000..429e01be276 --- /dev/null +++ b/phpunit/functional/Glpi/Form/Export/FormSerializerDestinationTest.php @@ -0,0 +1,924 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace tests\units\Glpi\Form; + +use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Destination\AbstractCommonITILFormDestination; +use Glpi\Form\Destination\CommonITILField\AssigneeField; +use Glpi\Form\Destination\CommonITILField\AssigneeFieldConfig; +use Glpi\Form\Destination\CommonITILField\AssociatedItemsField; +use Glpi\Form\Destination\CommonITILField\AssociatedItemsFieldConfig; +use Glpi\Form\Destination\CommonITILField\AssociatedItemsFieldStrategy; +use Glpi\Form\Destination\CommonITILField\ContentField; +use Glpi\Form\Destination\CommonITILField\EntityField; +use Glpi\Form\Destination\CommonITILField\EntityFieldConfig; +use Glpi\Form\Destination\CommonITILField\EntityFieldStrategy; +use Glpi\Form\Destination\CommonITILField\ITILActorFieldStrategy; +use Glpi\Form\Destination\CommonITILField\ITILCategoryField; +use Glpi\Form\Destination\CommonITILField\ITILCategoryFieldConfig; +use Glpi\Form\Destination\CommonITILField\ITILCategoryFieldStrategy; +use Glpi\Form\Destination\CommonITILField\ITILFollowupField; +use Glpi\Form\Destination\CommonITILField\ITILFollowupFieldConfig; +use Glpi\Form\Destination\CommonITILField\ITILFollowupFieldStrategy; +use Glpi\Form\Destination\CommonITILField\ITILTaskField; +use Glpi\Form\Destination\CommonITILField\ITILTaskFieldConfig; +use Glpi\Form\Destination\CommonITILField\ITILTaskFieldStrategy; +use Glpi\Form\Destination\CommonITILField\LocationField; +use Glpi\Form\Destination\CommonITILField\LocationFieldConfig; +use Glpi\Form\Destination\CommonITILField\LocationFieldStrategy; +use Glpi\Form\Destination\CommonITILField\ObserverField; +use Glpi\Form\Destination\CommonITILField\ObserverFieldConfig; +use Glpi\Form\Destination\CommonITILField\OLATTOField; +use Glpi\Form\Destination\CommonITILField\OLATTOFieldConfig; +use Glpi\Form\Destination\CommonITILField\OLATTRField; +use Glpi\Form\Destination\CommonITILField\OLATTRFieldConfig; +use Glpi\Form\Destination\CommonITILField\RequesterField; +use Glpi\Form\Destination\CommonITILField\RequesterFieldConfig; +use Glpi\Form\Destination\CommonITILField\RequestSourceField; +use Glpi\Form\Destination\CommonITILField\RequestSourceFieldConfig; +use Glpi\Form\Destination\CommonITILField\RequestSourceFieldStrategy; +use Glpi\Form\Destination\CommonITILField\RequestTypeField; +use Glpi\Form\Destination\CommonITILField\RequestTypeFieldConfig; +use Glpi\Form\Destination\CommonITILField\RequestTypeFieldStrategy; +use Glpi\Form\Destination\CommonITILField\SimpleValueConfig; +use Glpi\Form\Destination\CommonITILField\SLATTOField; +use Glpi\Form\Destination\CommonITILField\SLATTOFieldConfig; +use Glpi\Form\Destination\CommonITILField\SLATTRFieldConfig; +use Glpi\Form\Destination\CommonITILField\SLMFieldStrategy; +use Glpi\Form\Destination\CommonITILField\TemplateField; +use Glpi\Form\Destination\CommonITILField\TemplateFieldConfig; +use Glpi\Form\Destination\CommonITILField\TemplateFieldStrategy; +use Glpi\Form\Destination\CommonITILField\TitleField; +use Glpi\Form\Destination\CommonITILField\UrgencyField; +use Glpi\Form\Destination\CommonITILField\UrgencyFieldConfig; +use Glpi\Form\Destination\CommonITILField\UrgencyFieldStrategy; +use Glpi\Form\Destination\CommonITILField\ValidationField; +use Glpi\Form\Destination\CommonITILField\ValidationFieldConfig; +use Glpi\Form\Destination\CommonITILField\ValidationFieldStrategy; +use Glpi\Form\Destination\FormDestinationTicket; +use Glpi\Form\Destination\FormDestinationTypeManager; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Serializer\FormSerializer; +use Glpi\Form\Form; +use Glpi\Form\QuestionType\QuestionTypeActorsExtraDataConfig; +use Glpi\Form\QuestionType\QuestionTypeAssignee; +use Glpi\Form\QuestionType\QuestionTypeItem; +use Glpi\Form\QuestionType\QuestionTypeItemDropdown; +use Glpi\Form\QuestionType\QuestionTypeItemExtraDataConfig; +use Glpi\Form\QuestionType\QuestionTypeObserver; +use Glpi\Form\QuestionType\QuestionTypeRequester; +use Glpi\Form\QuestionType\QuestionTypeRequestType; +use Glpi\Form\QuestionType\QuestionTypeUrgency; +use Glpi\Form\QuestionType\QuestionTypeUserDevice; +use Glpi\Tests\FormBuilder; +use Glpi\Tests\FormTesterTrait; +use PHPUnit\Framework\Attributes\DataProvider; +use SLM; +use Ticket; + +use function PHPUnit\Framework\assertEquals; + +final class FormSerializerDestinationTest extends \DbTestCase +{ + use FormTesterTrait; + + private static FormSerializer $serializer; + + public static function setUpBeforeClass(): void + { + self::$serializer = new FormSerializer(); + parent::setUpBeforeClass(); + } + + public static function getFieldDestinationsWithSpecificValueProvider(): iterable + { + yield 'TitleField' => [ + 'field_key' => TitleField::getKey(), + 'config' => new SimpleValueConfig(value: 'My title') + ]; + + yield 'ContentField' => [ + 'field_key' => ContentField::getKey(), + 'config' => new SimpleValueConfig(value: 'My content') + ]; + + yield 'TemplateField' => [ + 'field_key' => TemplateField::getKey(), + 'config_fn' => fn($created_items) => new TemplateFieldConfig( + strategy: TemplateFieldStrategy::SPECIFIC_TEMPLATE, + specific_template_id: $created_items[0]->getId() + ), + 'items_to_create' => [ + [ + 'itemtype' => \TicketTemplate::class, + 'input' => [ + 'name' => 'My ticket template', + ] + ] + ] + ]; + + yield 'ITILCategoryField' => [ + 'field_key' => ITILCategoryField::getKey(), + 'config_fn' => fn($created_items) => new ITILCategoryFieldConfig( + strategy: ITILCategoryFieldStrategy::SPECIFIC_VALUE, + specific_itilcategory_id: $created_items[0]->getId() + ), + 'items_to_create' => [ + [ + 'itemtype' => \ITILCategory::class, + 'input' => [ + 'name' => 'My ITIL category', + ] + ] + ] + ]; + + yield 'EntityField' => [ + 'field_key' => EntityField::getKey(), + 'config_fn' => fn($created_items) => new EntityFieldConfig( + strategy: EntityFieldStrategy::SPECIFIC_VALUE, + specific_entity_id: $created_items[0]->getId() + ), + 'items_to_create' => [ + [ + 'itemtype' => \Entity::class, + 'input' => [ + 'name' => 'My entity', + ] + ] + ] + ]; + + yield 'LocationField' => [ + 'field_key' => LocationField::getKey(), + 'config_fn' => fn($created_items) => new LocationFieldConfig( + strategy: LocationFieldStrategy::SPECIFIC_VALUE, + specific_location_id: $created_items[0]->getId() + ), + 'items_to_create' => [ + [ + 'itemtype' => \Location::class, + 'input' => [ + 'name' => 'My location', + ] + ] + ] + ]; + + yield 'AssociatedItemsField' => [ + 'field_key' => AssociatedItemsField::getKey(), + 'config_fn' => fn($created_items) => new AssociatedItemsFieldConfig( + strategy: AssociatedItemsFieldStrategy::SPECIFIC_VALUES, + specific_associated_items: [ + \Computer::class => [$created_items[0]->getId()], + \Monitor::class => [$created_items[1]->getId()], + ] + ), + 'items_to_create' => [ + [ + 'itemtype' => \Computer::class, + 'input' => [ + 'name' => 'My computer', + ] + ], + [ + 'itemtype' => \Monitor::class, + 'input' => [ + 'name' => 'My monitor', + ] + ] + ], + 'keys_to_ignore' => ['specific_question_ids'] + ]; + + yield 'ITILFollowupField' => [ + 'field_key' => ITILFollowupField::getKey(), + 'config_fn' => fn($created_items) => new ITILFollowupFieldConfig( + strategy: ITILFollowupFieldStrategy::SPECIFIC_VALUES, + specific_itilfollowuptemplates_ids: [$created_items[0]->getId()] + ), + 'items_to_create' => [ + [ + 'itemtype' => \ITILFollowupTemplate::class, + 'input' => [ + 'name' => 'My ITIL followup template', + ] + ] + ] + ]; + + yield 'RequestSourceField' => [ + 'field_key' => RequestSourceField::getKey(), + 'config_fn' => fn($created_items) => new RequestSourceFieldConfig( + strategy: RequestSourceFieldStrategy::SPECIFIC_VALUE, + specific_request_source: $created_items[0]->getId() + ), + 'items_to_create' => [ + [ + 'itemtype' => \RequestType::class, + 'input' => [ + 'name' => 'My request source', + ] + ] + ] + ]; + + yield 'ValidationField' => [ + 'field_key' => ValidationField::getKey(), + 'config_fn' => fn($created_items) => new ValidationFieldConfig( + strategy: ValidationFieldStrategy::SPECIFIC_ACTORS, + specific_actors: [ + getForeignKeyFieldForItemType(\User::class) . '-' . $created_items[0]->getId(), + getForeignKeyFieldForItemType(\Group::class) . '-' . $created_items[1]->getId(), + ] + ), + 'items_to_create' => [ + [ + 'itemtype' => \User::class, + 'input' => [ + 'name' => 'My user', + ] + ], + [ + 'itemtype' => \Group::class, + 'input' => [ + 'name' => 'My group', + ] + ] + ], + 'check_fn' => function ($imported_destinations, $created_items) { + assertEquals( + array_diff_key( + (new ValidationFieldConfig( + strategy: ValidationFieldStrategy::SPECIFIC_ACTORS, + specific_actors: [ + \User::class => [$created_items[0]->getId()], + \Group::class => [$created_items[1]->getId()], + ] + ))->jsonSerialize(), + ['specific_question_ids' => ''] + ), + current($imported_destinations)->getConfig()[ValidationField::getKey()] + ); + } + ]; + + yield 'ITILTaskField' => [ + 'field_key' => ITILTaskField::getKey(), + 'config_fn' => fn($created_items) => new ITILTaskFieldConfig( + strategy: ITILTaskFieldStrategy::SPECIFIC_VALUES, + specific_itiltasktemplates_ids: [$created_items[0]->getId()] + ), + 'items_to_create' => [ + [ + 'itemtype' => \TaskTemplate::class, + 'input' => [ + 'name' => 'My ITIL task', + ] + ] + ] + ]; + + yield 'RequesterField' => [ + 'field_key' => RequesterField::getKey(), + 'config_fn' => fn($created_items) => new RequesterFieldConfig( + strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, + specific_itilactors_ids: [ + getForeignKeyFieldForItemType(\User::class) . '-' . $created_items[0]->getId(), + getForeignKeyFieldForItemType(\Group::class) . '-' . $created_items[1]->getId(), + getForeignKeyFieldForItemType(\Supplier::class) . '-' . $created_items[2]->getId(), + ] + ), + 'items_to_create' => [ + [ + 'itemtype' => \User::class, + 'input' => [ + 'name' => 'My user', + ] + ], + [ + 'itemtype' => \Group::class, + 'input' => [ + 'name' => 'My group', + ] + ], + [ + 'itemtype' => \Supplier::class, + 'input' => [ + 'name' => 'My supplier', + ] + ] + ], + 'check_fn' => function ($imported_destinations, $created_items) { + assertEquals( + array_diff_key( + (new RequesterFieldConfig( + strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, + specific_itilactors_ids: [ + \User::class => [$created_items[0]->getId()], + \Group::class => [$created_items[1]->getId()], + \Supplier::class => [$created_items[2]->getId()], + ] + ))->jsonSerialize(), + ['specific_question_ids' => ''] + ), + current($imported_destinations)->getConfig()[RequesterField::getKey()] + ); + } + ]; + + yield 'ObserverField' => [ + 'field_key' => ObserverField::getKey(), + 'config_fn' => fn($created_items) => new ObserverFieldConfig( + strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, + specific_itilactors_ids: [ + getForeignKeyFieldForItemType(\User::class) . '-' . $created_items[0]->getId(), + getForeignKeyFieldForItemType(\Group::class) . '-' . $created_items[1]->getId(), + ] + ), + 'items_to_create' => [ + [ + 'itemtype' => \User::class, + 'input' => [ + 'name' => 'My user', + ] + ], + [ + 'itemtype' => \Group::class, + 'input' => [ + 'name' => 'My group', + ] + ] + ], + 'check_fn' => function ($imported_destinations, $created_items) { + assertEquals( + array_diff_key( + (new ObserverFieldConfig( + strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, + specific_itilactors_ids: [ + \User::class => [$created_items[0]->getId()], + \Group::class => [$created_items[1]->getId()], + ] + ))->jsonSerialize(), + ['specific_question_ids' => ''] + ), + current($imported_destinations)->getConfig()[ObserverField::getKey()] + ); + } + ]; + + yield 'AssigneeField' => [ + 'field_key' => AssigneeField::getKey(), + 'config_fn' => fn($created_items) => new AssigneeFieldConfig( + strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, + specific_itilactors_ids: [ + getForeignKeyFieldForItemType(\User::class) . '-' . $created_items[0]->getId(), + getForeignKeyFieldForItemType(\Group::class) . '-' . $created_items[1]->getId(), + ] + ), + 'items_to_create' => [ + [ + 'itemtype' => \User::class, + 'input' => [ + 'name' => 'My user', + ] + ], + [ + 'itemtype' => \Group::class, + 'input' => [ + 'name' => 'My group', + ] + ] + ], + 'check_fn' => function ($imported_destinations, $created_items) { + assertEquals( + array_diff_key( + (new AssigneeFieldConfig( + strategy: ITILActorFieldStrategy::SPECIFIC_VALUES, + specific_itilactors_ids: [ + \User::class => [$created_items[0]->getId()], + \Group::class => [$created_items[1]->getId()], + ] + ))->jsonSerialize(), + ['specific_question_ids' => ''] + ), + current($imported_destinations)->getConfig()[AssigneeField::getKey()] + ); + } + ]; + + yield 'SLATTOField' => [ + 'field_key' => SLATTOField::getKey(), + 'config_fn' => fn($created_items) => new SLATTOFieldConfig( + strategy: SLMFieldStrategy::SPECIFIC_VALUE, + specific_slm_id: $created_items[0]->getId() + ), + 'items_to_create' => [ + [ + 'itemtype' => \SLA::class, + 'input' => [ + 'name' => 'My SLA', + 'type' => SLM::TTO, + 'number_time' => 4, + 'definition_time' => 'hour', + ] + ] + ] + ]; + + yield 'SLATTRField' => [ + 'field_key' => SLATTOField::getKey(), + 'config_fn' => fn($created_items) => new SLATTRFieldConfig( + strategy: SLMFieldStrategy::SPECIFIC_VALUE, + specific_slm_id: $created_items[0]->getId() + ), + 'items_to_create' => [ + [ + 'itemtype' => \SLA::class, + 'input' => [ + 'name' => 'My SLA', + 'type' => SLM::TTR, + 'number_time' => 4, + 'definition_time' => 'hour', + ] + ] + ] + ]; + + yield 'OLATTRField' => [ + 'field_key' => OLATTRField::getKey(), + 'config_fn' => fn($created_items) => new OLATTRFieldConfig( + strategy: SLMFieldStrategy::SPECIFIC_VALUE, + specific_slm_id: $created_items[0]->getId() + ), + 'items_to_create' => [ + [ + 'itemtype' => \OLA::class, + 'input' => [ + 'name' => 'My OLA', + 'type' => SLM::TTR, + 'number_time' => 4, + 'definition_time' => 'hour', + ] + ] + ] + ]; + + yield 'OLATTOField' => [ + 'field_key' => OLATTOField::getKey(), + 'config_fn' => fn($created_items) => new OLATTOFieldConfig( + strategy: SLMFieldStrategy::SPECIFIC_VALUE, + specific_slm_id: $created_items[0]->getId() + ), + 'items_to_create' => [ + [ + 'itemtype' => \OLA::class, + 'input' => [ + 'name' => 'My OLA', + 'type' => SLM::TTO, + 'number_time' => 4, + 'definition_time' => 'hour', + ] + ] + ] + ]; + + yield 'UrgencyField' => [ + 'field_key' => UrgencyField::getKey(), + 'config' => new UrgencyFieldConfig( + strategy: UrgencyFieldStrategy::SPECIFIC_VALUE, + specific_urgency_value: 5 + ), + ]; + + yield 'RequestTypeField' => [ + 'field_key' => RequestTypeField::getKey(), + 'config' => new RequestTypeFieldConfig( + strategy: RequestTypeFieldStrategy::SPECIFIC_VALUE, + specific_request_type: Ticket::DEMAND_TYPE + ) + ]; + } + + #[DataProvider('getFieldDestinationsWithSpecificValueProvider')] + public function testImportAndExportForDestinationFieldWithSpecificValueStrategy( + string $field_key, + JsonFieldInterface $config = null, + callable $config_fn = null, + array $items_to_create = [], + callable $check_fn = null, + array $keys_to_ignore = [] + ) { + $this->login(); + + $created_items = []; + foreach ($items_to_create as $item) { + if ((new $item['itemtype']())->isEntityAssign()) { + $item['input']['entities_id'] = $this->getTestRootEntity(only_id: true); + } + + $created_items[] = $this->createItem( + $item['itemtype'], + $item['input'] + ); + } + + if ($config === null) { + $config = $config_fn($created_items); + } + + $imported_form = $this->checkImportAndExport( + $field_key, + $config + ); + $imported_destinations = $imported_form->getDestinations(); + + if ($check_fn === null) { + $this->assertEquals( + array_diff_key( + $config->jsonSerialize(), + array_flip($keys_to_ignore) + ), + current($imported_destinations)->getConfig()[$field_key] + ); + } else { + $check_fn($imported_destinations, $created_items); + } + } + + public static function getFieldDestinationsWithSpecificQuestionProvider(): iterable + { + yield 'ITILCategoryField' => [ + 'field_key' => ITILCategoryField::getKey(), + 'config_fn' => fn($questions) => new ITILCategoryFieldConfig( + strategy: ITILCategoryFieldStrategy::SPECIFIC_ANSWER, + specific_question_id: current($questions)->getId(), + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeItemDropdown::class, + 'extra_data' => json_encode((new QuestionTypeItemExtraDataConfig( + itemtype: \ITILCategory::class + ))->jsonSerialize()) + ] + ], + ]; + + yield 'EntityField' => [ + 'field_key' => EntityField::getKey(), + 'config_fn' => fn($questions) => new EntityFieldConfig( + strategy: EntityFieldStrategy::SPECIFIC_ANSWER, + specific_question_id: current($questions)->getId(), + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeItem::class, + 'extra_data' => json_encode((new QuestionTypeItemExtraDataConfig( + itemtype: \Entity::class + ))->jsonSerialize()) + ] + ], + ]; + + yield 'LocationField' => [ + 'field_key' => LocationField::getKey(), + 'config_fn' => fn($questions) => new LocationFieldConfig( + strategy: LocationFieldStrategy::SPECIFIC_ANSWER, + specific_question_id: current($questions)->getId(), + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeItemDropdown::class, + 'extra_data' => json_encode((new QuestionTypeItemExtraDataConfig( + itemtype: \Location::class + ))->jsonSerialize()) + ] + ], + ]; + + yield 'AssociatedItemsField' => [ + 'field_key' => AssociatedItemsField::getKey(), + 'config_fn' => fn($questions) => new AssociatedItemsFieldConfig( + strategy: AssociatedItemsFieldStrategy::SPECIFIC_ANSWERS, + specific_question_ids: [ + current($questions)->getId(), + next($questions)->getId(), + ], + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeItem::class, + 'extra_data' => json_encode((new QuestionTypeItemExtraDataConfig( + itemtype: \Computer::class + ))->jsonSerialize()) + ], + [ + 'type' => QuestionTypeUserDevice::class + ] + ], + 'question_id_key' => 'specific_question_ids' + ]; + + yield 'ValidationField' => [ + 'field_key' => ValidationField::getKey(), + 'config_fn' => fn($questions) => new ValidationFieldConfig( + strategy: ValidationFieldStrategy::SPECIFIC_ANSWERS, + specific_question_ids: [ + current($questions)->getId(), + next($questions)->getId(), + next($questions)->getId(), + next($questions)->getId(), + ], + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeItem::class, + 'extra_data' => json_encode((new QuestionTypeItemExtraDataConfig( + itemtype: \User::class + ))->jsonSerialize()) + ], + [ + 'type' => QuestionTypeItem::class, + 'extra_data' => json_encode((new QuestionTypeItemExtraDataConfig( + itemtype: \Group::class + ))->jsonSerialize()) + ], + [ + 'type' => QuestionTypeObserver::class + ], + [ + 'type' => QuestionTypeAssignee::class + ] + ], + 'question_id_key' => 'specific_question_ids' + ]; + + yield 'RequesterField' => [ + 'field_key' => RequesterField::getKey(), + 'config_fn' => fn($questions) => new RequesterFieldConfig( + strategy: ITILActorFieldStrategy::SPECIFIC_ANSWERS, + specific_question_ids: [ + current($questions)->getId(), + next($questions)->getId(), + ], + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeRequester::class, + ], + [ + 'type' => QuestionTypeRequester::class, + 'extra_data' => json_encode((new QuestionTypeActorsExtraDataConfig( + is_multiple_actors: true + ))->jsonSerialize()) + ], + ], + 'question_id_key' => 'specific_question_ids' + ]; + + yield 'ObserverField' => [ + 'field_key' => ObserverField::getKey(), + 'config_fn' => fn($questions) => new ObserverFieldConfig( + strategy: ITILActorFieldStrategy::SPECIFIC_ANSWERS, + specific_question_ids: [ + current($questions)->getId(), + next($questions)->getId(), + ], + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeObserver::class, + ], + [ + 'type' => QuestionTypeObserver::class, + 'extra_data' => json_encode((new QuestionTypeActorsExtraDataConfig( + is_multiple_actors: true + ))->jsonSerialize()) + ], + ], + 'question_id_key' => 'specific_question_ids' + ]; + + yield 'AssigneeField' => [ + 'field_key' => AssigneeField::getKey(), + 'config_fn' => fn($questions) => new AssigneeFieldConfig( + strategy: ITILActorFieldStrategy::SPECIFIC_ANSWERS, + specific_question_ids: [ + current($questions)->getId(), + next($questions)->getId(), + ], + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeAssignee::class, + ], + [ + 'type' => QuestionTypeAssignee::class, + 'extra_data' => json_encode((new QuestionTypeActorsExtraDataConfig( + is_multiple_actors: true + ))->jsonSerialize()) + ], + ], + 'question_id_key' => 'specific_question_ids' + ]; + + yield 'UrgencyField' => [ + 'field_key' => UrgencyField::getKey(), + 'config_fn' => fn($questions) => new UrgencyFieldConfig( + strategy: UrgencyFieldStrategy::SPECIFIC_ANSWER, + specific_question_id: current($questions)->getId(), + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeUrgency::class + ] + ], + ]; + + yield 'RequestTypeField' => [ + 'field_key' => RequestTypeField::getKey(), + 'config_fn' => fn($questions) => new RequestTypeFieldConfig( + strategy: RequestTypeFieldStrategy::SPECIFIC_ANSWER, + specific_question_id: current($questions)->getId(), + ), + 'questions_to_create' => [ + [ + 'type' => QuestionTypeRequestType::class + ] + ], + ]; + } + + #[DataProvider('getFieldDestinationsWithSpecificQuestionProvider')] + public function testImportAndExportForDestinationFieldWithSpecificQuestionStrategy( + string $field_key, + JsonFieldInterface $config = null, + callable $config_fn = null, + array $questions_to_create = [], + string $question_id_key = 'specific_question_id' + ) { + $this->login(); + + $question_names = []; + $form_builder = $this->getFormBuilderWithTicketDestination(); + foreach ($questions_to_create as $question_data) { + $question_names[] = sprintf('%s - %s', $question_data['type'], count($question_names)); + $form_builder->addQuestion( + $question_names[count($question_names) - 1], + $question_data['type'], + $question_data['default_value'] ?? '', + $question_data['extra_data'] ?? '' + ); + } + $form = $this->createForm($form_builder); + + if ($config === null) { + $config = $config_fn($form->getQuestions()); + } + + $imported_form = $this->checkImportAndExport( + $field_key, + $config, + $form + ); + $imported_destinations = $imported_form->getDestinations(); + + $this->assertNotEquals( + $config->jsonSerialize()[$question_id_key], + current($imported_destinations)->getConfig()[$field_key][$question_id_key] + ); + + if (is_array($config->jsonSerialize()[$question_id_key])) { + foreach ($config->jsonSerialize()[$question_id_key] as $key => $question_id) { + // Retrieve the question id from the imported form + $this->assertEquals( + $form->getQuestions()[$question_id]->getName(), + $imported_form->getQuestions()[current($imported_destinations)->getConfig()[$field_key][$question_id_key][$key]]->getName() + ); + } + } else { + $this->assertEquals( + $form->getQuestions()[$config->jsonSerialize()[$question_id_key]]->getName(), + $imported_form->getQuestions()[current($imported_destinations)->getConfig()[$field_key][$question_id_key]]->getName() + ); + } + } + + public function testAllConfigFieldsImplementConfigWithForeignKeysInterface() + { + $configs_to_exclude = [ + SimpleValueConfig::class, + RequestSourceFieldConfig::class, + ]; + + /** @var AbstractCommonITILFormDestination[] $destination_types */ + $destination_types = array_filter( + FormDestinationTypeManager::getInstance()->getDestinationTypes(), + fn($destination_type) => $destination_type instanceof AbstractCommonITILFormDestination + ); + $destination_fields = array_merge(...array_map( + fn($destination_type) => $destination_type->getConfigurableFields(), + $destination_types + )); + $destination_config_fields = array_unique(array_map( + fn($field) => $field->getConfigClass(), + $destination_fields + )); + + // Check that all config fields implement ConfigWithForeignKeysInterface + foreach ($destination_config_fields as $config_field) { + if (in_array($config_field, $configs_to_exclude)) { + continue; + } + + $this->assertTrue( + is_subclass_of($config_field, ConfigWithForeignKeysInterface::class), + sprintf( + "Config field %s must implement ConfigWithForeignKeysInterface", + $config_field + ) + ); + } + } + + /** + * Check the import and export process for a given key + * + * @param string $key + * @param JsonFieldInterface $config + * @param string[] $keys_to_skip + * @param Form|null $form + * @return Form The imported form + */ + private function checkImportAndExport( + string $key, + JsonFieldInterface $config, + Form $form = null + ): Form { + if ($form === null) { + $form = $this->createForm($this->getFormBuilderWithTicketDestination()); + } + + // Insert config + $destinations = $form->getDestinations(); + $destination = current($destinations); + $this->updateItem( + $destination::getType(), + $destination->getId(), + ['config' => [$key => $config->jsonSerialize()]], + ["config"], + ); + + // Export and import process + $imported_form = $this->exportAndImportForm($form); + $imported_destinations = $imported_form->getDestinations(); + + $this->assertCount(1, $imported_destinations); + + return $imported_form; + } + + private function getFormBuilderWithTicketDestination(): FormBuilder + { + $builder = new FormBuilder(); + $builder->addDestination( + FormDestinationTicket::class, + "My ticket", + ); + return $builder; + } +} diff --git a/phpunit/functional/Glpi/Form/Export/FormSerializerTest.php b/phpunit/functional/Glpi/Form/Export/FormSerializerTest.php index 57a5c439286..2533bbaaeae 100644 --- a/phpunit/functional/Glpi/Form/Export/FormSerializerTest.php +++ b/phpunit/functional/Glpi/Form/Export/FormSerializerTest.php @@ -35,8 +35,19 @@ namespace tests\units\Glpi\Form; +use Computer; use Entity; use Glpi\Form\Comment; +use Glpi\Form\Destination\CommonITILField\AssociatedItemsField; +use Glpi\Form\Destination\CommonITILField\AssociatedItemsFieldConfig; +use Glpi\Form\Destination\CommonITILField\AssociatedItemsFieldStrategy; +use Glpi\Form\Destination\CommonITILField\ITILCategoryField; +use Glpi\Form\Destination\CommonITILField\ITILCategoryFieldConfig; +use Glpi\Form\Destination\CommonITILField\ITILCategoryFieldStrategy; +use Glpi\Form\Destination\CommonITILField\SimpleValueConfig; +use Glpi\Form\Destination\CommonITILField\TitleField; +use Glpi\Form\Destination\FormDestination; +use Glpi\Form\Destination\FormDestinationTicket; use Glpi\Form\Export\Context\DatabaseMapper; use Glpi\Form\Export\Result\ImportError; use Glpi\Form\Export\Serializer\FormSerializer; @@ -54,7 +65,9 @@ use Glpi\Form\Section; use Glpi\Tests\FormBuilder; use Glpi\Tests\FormTesterTrait; +use ITILCategory; use Location; +use Monitor; use Session; final class FormSerializerTest extends \DbTestCase @@ -454,13 +467,117 @@ public function testExportAndImportQuestions(): void 'is_mandatory' => (int) false, 'rank' => 1, 'description' => '', - 'default_value' => json_encode($actors_default_value_config->jsonSerialize()), + 'default_value' => json_encode(array_intersect_key( + $actors_default_value_config->jsonSerialize(), + ['users_ids' => ''] + )), 'extra_data' => json_encode($actors_extra_data_config->jsonSerialize()), 'forms_sections_id' => array_values($form_copy->getSections())[1]->fields['id'], ] ], $questions_data); } + public function testExportAndImportDestinations(): void + { + $this->login(); + + // Create an ITIL category + $itil_category = $this->createItem('ITILCategory', ['name' => 'My ITIL Category']); + + // Create a Computer + $computer = $this->createItem('Computer', [ + 'name' => 'My computer', + 'entities_id' => $this->getTestRootEntity(only_id: true) + ]); + + // Create a monitor + $monitor = $this->createItem('Monitor', [ + 'name' => 'My monitor', + 'entities_id' => $this->getTestRootEntity(only_id: true) + ]); + + $form = $this->createForm((new FormBuilder())->addQuestion( + "My ITIL Category question", + QuestionTypeItemDropdown::class, + json_encode((new QuestionTypeItemDefaultValueConfig($itil_category->getID()))->jsonSerialize()), + json_encode((new QuestionTypeItemExtraDataConfig(ITILCategory::class))->jsonSerialize()), + )->addDestination(FormDestinationTicket::class, 'My ticket destination')); + + $title_field_config = new SimpleValueConfig("My ticket title"); + $itil_category_field_config = new ITILCategoryFieldConfig( + ITILCategoryFieldStrategy::SPECIFIC_ANSWER, + specific_question_id: $this->getQuestionId($form, "My ITIL Category question") + ); + $associated_items_config = new AssociatedItemsFieldConfig( + AssociatedItemsFieldStrategy::SPECIFIC_VALUES, + specific_associated_items: [ + Computer::class => [ + $computer->getID() + ], + Monitor::class => [ + $monitor->getID() + ] + ] + ); + + // Insert config + $destinations = $form->getDestinations(); + $destination = current($destinations); + $this->updateItem( + $destination::getType(), + $destination->getId(), + [ + 'config' => [ + TitleField::getKey() => $title_field_config->jsonSerialize(), + ITILCategoryField::getKey() => $itil_category_field_config->jsonSerialize(), + AssociatedItemsField::getKey() => $associated_items_config->jsonSerialize() + ] + ], + ["config"], + ); + + // Export and import process + $imported_form = $this->exportAndImportForm($form); + $imported_destinations = $imported_form->getDestinations(); + $imported_configs = current($imported_destinations)->getConfig(); + + $this->assertCount(1, $imported_destinations); + + // Check that the imported form has the same destination + $this->assertEquals($title_field_config->jsonSerialize(), $imported_configs[TitleField::getKey()]); + $this->assertEquals( + array_diff_key( + $itil_category_field_config->jsonSerialize(), + ['specific_question_id' => ''] + ), + array_diff_key( + $imported_configs[ITILCategoryField::getKey()], + ['specific_question_id' => ''] + ) + ); + $this->assertEquals( + array_diff_key( + $associated_items_config->jsonSerialize(), + ['specific_question_ids' => ''] + ), + array_diff_key( + $imported_configs[AssociatedItemsField::getKey()], + ['specific_question_ids' => ''] + ) + ); + + // Extra check to ensure that the specific_question_id is the same + $this->assertNotEquals( + $itil_category_field_config->getSpecificQuestionId(), + $imported_configs[ITILCategoryField::getKey()][ITILCategoryFieldConfig::SPECIFIC_QUESTION_ID] + ); + + $this->assertEquals( + $this->getQuestionId($imported_form, "My ITIL Category question"), + $imported_configs[ITILCategoryField::getKey()][ITILCategoryFieldConfig::SPECIFIC_QUESTION_ID] + ); + } + public function testPreviewImportWithValidForm(): void { // Arrange: create a valid form diff --git a/src/Glpi/Form/Destination/AbstractCommonITILFormDestination.php b/src/Glpi/Form/Destination/AbstractCommonITILFormDestination.php index 6e212378781..0c1bcf9871d 100644 --- a/src/Glpi/Form/Destination/AbstractCommonITILFormDestination.php +++ b/src/Glpi/Form/Destination/AbstractCommonITILFormDestination.php @@ -199,6 +199,23 @@ public function getConfigurableFields(): array ]; } + /** + * Get a configurable field by its key. + * + * @param string $key + * @return \Glpi\Form\Destination\AbstractConfigField|null + */ + public function getConfigurableFieldByKey(string $key): ?AbstractConfigField + { + foreach ($this->getConfigurableFields() as $field) { + if ($field::getKey() === $key) { + return $field; + } + } + + return null; + } + final public function formatConfigInputName(string $field_key): string { // Handle array fields diff --git a/src/Glpi/Form/Destination/CommonITILField/AssigneeField.php b/src/Glpi/Form/Destination/CommonITILField/AssigneeField.php index 19d2312b19f..c24f83841c9 100644 --- a/src/Glpi/Form/Destination/CommonITILField/AssigneeField.php +++ b/src/Glpi/Form/Destination/CommonITILField/AssigneeField.php @@ -35,6 +35,7 @@ namespace Glpi\Form\Destination\CommonITILField; +use Glpi\Form\Form; use Glpi\Form\QuestionType\QuestionTypeAssignee; use Override; use Session; @@ -64,4 +65,18 @@ public function getWeight(): int { return 30; } + + #[Override] + public function getConfigClass(): string + { + return AssigneeFieldConfig::class; + } + + #[Override] + public function getDefaultConfig(Form $form): AssigneeFieldConfig + { + return new AssigneeFieldConfig( + ITILActorFieldStrategy::FROM_TEMPLATE, + ); + } } diff --git a/src/Glpi/Form/Destination/CommonITILField/AssigneeFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/AssigneeFieldConfig.php new file mode 100644 index 00000000000..128dc4c19b3 --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/AssigneeFieldConfig.php @@ -0,0 +1,68 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyItemsArrayHandler; +use Glpi\Form\Export\Context\ForeignKey\QuestionArrayForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use Override; + +final class AssigneeFieldConfig extends ITILActorFieldConfig +{ + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyItemsArrayHandler(self::SPECIFIC_ITILACTORS_IDS), + new QuestionArrayForeignKeyHandler(self::SPECIFIC_QUESTION_IDS) + ]; + } + + #[Override] + public static function jsonDeserialize(array $data): self + { + $strategy = ITILActorFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); + if ($strategy === null) { + $strategy = ITILActorFieldStrategy::FROM_TEMPLATE; + } + + return new self( + strategy: $strategy, + specific_itilactors_ids: $data[self::SPECIFIC_ITILACTORS_IDS] ?? [], + specific_question_ids: $data[self::SPECIFIC_QUESTION_IDS] ?? [], + ); + } +} diff --git a/src/Glpi/Form/Destination/CommonITILField/AssociatedItemsField.php b/src/Glpi/Form/Destination/CommonITILField/AssociatedItemsField.php index 3a3675983fe..4a485913fea 100644 --- a/src/Glpi/Form/Destination/CommonITILField/AssociatedItemsField.php +++ b/src/Glpi/Form/Destination/CommonITILField/AssociatedItemsField.php @@ -45,7 +45,6 @@ use Glpi\Form\QuestionType\QuestionTypeUserDevice; use InvalidArgumentException; use Override; -use Ticket; class AssociatedItemsField extends AbstractConfigField { @@ -93,7 +92,7 @@ public function renderConfigForm( 'specific_values_extra_field' => [ 'itemtype_aria_label' => __("Select the itemtype of the item to associate..."), 'items_id_aria_label' => __("Select the item to associate..."), - 'input_name' => $input_name . "[" . AssociatedItemsFieldConfig::ASSOCIATED_ITEMS . "]", + 'input_name' => $input_name . "[" . AssociatedItemsFieldConfig::SPECIFIC_ASSOCIATED_ITEMS . "]", 'itemtypes' => array_keys(CommonITILObject::getAllTypesForHelpdesk()), 'associated_items' => $config->getSpecificAssociatedItems(), ], @@ -102,7 +101,7 @@ public function renderConfigForm( 'specific_answer_extra_field' => [ 'aria_label' => __("Select questions..."), 'values' => $config->getSpecificQuestionIds(), - 'input_name' => $input_name . "[" . AssociatedItemsFieldConfig::QUESTION_IDS . "]", + 'input_name' => $input_name . "[" . AssociatedItemsFieldConfig::SPECIFIC_QUESTION_IDS . "]", 'possible_values' => $this->getAssociatedItemsQuestionsValuesForDropdown($form), ], ]); @@ -208,18 +207,18 @@ public function prepareInput(array $input): array $input = parent::prepareInput($input); // Ensure that question_ids is an array - if (!is_array($input[$this->getKey()][AssociatedItemsFieldConfig::QUESTION_IDS] ?? null)) { - unset($input[$this->getKey()][AssociatedItemsFieldConfig::QUESTION_IDS]); + if (!is_array($input[$this->getKey()][AssociatedItemsFieldConfig::SPECIFIC_QUESTION_IDS] ?? null)) { + $input[$this->getKey()][AssociatedItemsFieldConfig::SPECIFIC_QUESTION_IDS] = null; } // Compute associated_items as an array of itemtype => [item_id, ...] if ( - isset($input[$this->getKey()][AssociatedItemsFieldConfig::ASSOCIATED_ITEMS]) - && isset($input[$this->getKey()][AssociatedItemsFieldConfig::ASSOCIATED_ITEMS]['itemtype']) - && isset($input[$this->getKey()][AssociatedItemsFieldConfig::ASSOCIATED_ITEMS]['items_id']) + isset($input[$this->getKey()][AssociatedItemsFieldConfig::SPECIFIC_ASSOCIATED_ITEMS]) + && isset($input[$this->getKey()][AssociatedItemsFieldConfig::SPECIFIC_ASSOCIATED_ITEMS]['itemtype']) + && isset($input[$this->getKey()][AssociatedItemsFieldConfig::SPECIFIC_ASSOCIATED_ITEMS]['items_id']) ) { - $itemtypes = $input[$this->getKey()][AssociatedItemsFieldConfig::ASSOCIATED_ITEMS]['itemtype']; - $items_ids = $input[$this->getKey()][AssociatedItemsFieldConfig::ASSOCIATED_ITEMS]['items_id']; + $itemtypes = $input[$this->getKey()][AssociatedItemsFieldConfig::SPECIFIC_ASSOCIATED_ITEMS]['itemtype']; + $items_ids = $input[$this->getKey()][AssociatedItemsFieldConfig::SPECIFIC_ASSOCIATED_ITEMS]['items_id']; $result = []; @@ -233,7 +232,7 @@ public function prepareInput(array $input): array $result[$itemtype][] = $item_id; } - $input[$this->getKey()][AssociatedItemsFieldConfig::ASSOCIATED_ITEMS] = $result; + $input[$this->getKey()][AssociatedItemsFieldConfig::SPECIFIC_ASSOCIATED_ITEMS] = $result; } return $input; diff --git a/src/Glpi/Form/Destination/CommonITILField/AssociatedItemsFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/AssociatedItemsFieldConfig.php index 2aa7cc30d1b..ea4c4bdaf52 100644 --- a/src/Glpi/Form/Destination/CommonITILField/AssociatedItemsFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/AssociatedItemsFieldConfig.php @@ -36,22 +36,35 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyItemsArrayHandler; +use Glpi\Form\Export\Context\ForeignKey\QuestionArrayForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; use Override; -final class AssociatedItemsFieldConfig implements JsonFieldInterface +final class AssociatedItemsFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names public const STRATEGY = 'strategy'; - public const QUESTION_IDS = 'question_ids'; - public const ASSOCIATED_ITEMS = 'associated_items'; + public const SPECIFIC_QUESTION_IDS = 'specific_question_ids'; + public const SPECIFIC_ASSOCIATED_ITEMS = 'specific_associated_items'; public function __construct( private AssociatedItemsFieldStrategy $strategy, - private ?array $specific_question_ids = null, - private ?array $specific_associated_items = null, + private array $specific_question_ids = [], + private array $specific_associated_items = [], ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyItemsArrayHandler(self::SPECIFIC_ASSOCIATED_ITEMS), + new QuestionArrayForeignKeyHandler(self::SPECIFIC_QUESTION_IDS) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { @@ -62,8 +75,8 @@ public static function jsonDeserialize(array $data): self return new self( strategy: $strategy, - specific_question_ids: $data[self::QUESTION_IDS] ?? null, - specific_associated_items: $data[self::ASSOCIATED_ITEMS] ?? null, + specific_question_ids: $data[self::SPECIFIC_QUESTION_IDS] ?? [], + specific_associated_items: $data[self::SPECIFIC_ASSOCIATED_ITEMS] ?? [], ); } @@ -72,8 +85,8 @@ public function jsonSerialize(): array { return [ self::STRATEGY => $this->strategy->value, - self::QUESTION_IDS => $this->specific_question_ids, - self::ASSOCIATED_ITEMS => $this->specific_associated_items, + self::SPECIFIC_QUESTION_IDS => $this->specific_question_ids, + self::SPECIFIC_ASSOCIATED_ITEMS => $this->specific_associated_items, ]; } @@ -82,12 +95,12 @@ public function getStrategy(): AssociatedItemsFieldStrategy return $this->strategy; } - public function getSpecificQuestionIds(): ?array + public function getSpecificQuestionIds(): array { return $this->specific_question_ids; } - public function getSpecificAssociatedItems(): ?array + public function getSpecificAssociatedItems(): array { return $this->specific_associated_items; } diff --git a/src/Glpi/Form/Destination/CommonITILField/EntityField.php b/src/Glpi/Form/Destination/CommonITILField/EntityField.php index a97dd181701..deb39bea66c 100644 --- a/src/Glpi/Form/Destination/CommonITILField/EntityField.php +++ b/src/Glpi/Form/Destination/CommonITILField/EntityField.php @@ -91,14 +91,14 @@ public function renderConfigForm( 'specific_value_extra_field' => [ 'aria_label' => __("Select an entity..."), 'value' => $config->getSpecificEntityId() ?? 0, - 'input_name' => $input_name . "[" . EntityFieldConfig::ENTITY_ID . "]", + 'input_name' => $input_name . "[" . EntityFieldConfig::SPECIFIC_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 . "]", + 'input_name' => $input_name . "[" . EntityFieldConfig::SPECIFIC_QUESTION_ID . "]", 'possible_values' => $this->getEntityQuestionsValuesForDropdown($form), ], ]); diff --git a/src/Glpi/Form/Destination/CommonITILField/EntityFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/EntityFieldConfig.php index e9d7c633075..560605eb227 100644 --- a/src/Glpi/Form/Destination/CommonITILField/EntityFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/EntityFieldConfig.php @@ -35,15 +35,20 @@ namespace Glpi\Form\Destination\CommonITILField; +use Entity; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyHandler; +use Glpi\Form\Export\Context\ForeignKey\QuestionForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; use Override; -final class EntityFieldConfig implements JsonFieldInterface +final class EntityFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // 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 const SPECIFIC_QUESTION_ID = 'specific_question_id'; + public const SPECIFIC_ENTITY_ID = 'specific_entity_id'; public function __construct( private EntityFieldStrategy $strategy, @@ -52,6 +57,15 @@ public function __construct( ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyHandler(self::SPECIFIC_ENTITY_ID, Entity::class), + new QuestionForeignKeyHandler(self::SPECIFIC_QUESTION_ID) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { @@ -62,8 +76,8 @@ public static function jsonDeserialize(array $data): self return new self( strategy: $strategy, - specific_question_id: $data[self::QUESTION_ID], - specific_entity_id: $data[self::ENTITY_ID], + specific_question_id: $data[self::SPECIFIC_QUESTION_ID] ?? null, + specific_entity_id: $data[self::SPECIFIC_ENTITY_ID] ?? null, ); } @@ -72,8 +86,8 @@ public function jsonSerialize(): array { return [ self::STRATEGY => $this->strategy->value, - self::QUESTION_ID => $this->specific_question_id, - self::ENTITY_ID => $this->specific_entity_id, + self::SPECIFIC_QUESTION_ID => $this->specific_question_id, + self::SPECIFIC_ENTITY_ID => $this->specific_entity_id, ]; } diff --git a/src/Glpi/Form/Destination/CommonITILField/ITILActorField.php b/src/Glpi/Form/Destination/CommonITILField/ITILActorField.php index 9b39890146d..66f8ff0deb2 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ITILActorField.php +++ b/src/Glpi/Form/Destination/CommonITILField/ITILActorField.php @@ -40,7 +40,6 @@ use Glpi\Form\AnswersSet; use Glpi\Form\Destination\AbstractConfigField; use Glpi\Form\Form; -use Glpi\Form\QuestionType\AbstractQuestionTypeActors; use InvalidArgumentException; use Override; @@ -49,12 +48,6 @@ abstract class ITILActorField extends AbstractConfigField abstract public function getAllowedQuestionType(): string; abstract public function getActorType(): string; - #[Override] - public function getConfigClass(): string - { - return ITILActorFieldConfig::class; - } - public function getAllowedActorTypes(): array { return (new ($this->getAllowedQuestionType())())->getAllowedActorTypes(); @@ -97,7 +90,7 @@ public function renderConfigForm( 'specific_value_extra_field' => [ 'aria_label' => __("Select actors..."), 'values' => $specific_actors, - 'input_name' => $input_name . "[" . ITILActorFieldConfig::ITILACTORS_IDS . "]", + 'input_name' => $input_name . "[" . ITILActorFieldConfig::SPECIFIC_ITILACTORS_IDS . "]", 'allowed_types' => $this->getAllowedActorTypes(), ], @@ -105,7 +98,7 @@ public function renderConfigForm( 'specific_answer_extra_field' => [ 'aria_label' => __("Select questions..."), 'values' => $config->getSpecificQuestionIds() ?? [], - 'input_name' => $input_name . "[" . ITILActorFieldConfig::QUESTION_IDS . "]", + 'input_name' => $input_name . "[" . ITILActorFieldConfig::SPECIFIC_QUESTION_IDS . "]", 'possible_values' => $this->getITILActorQuestionsValuesForDropdown($form), ], ]); @@ -142,25 +135,17 @@ public function applyConfiguratedValueToInputUsingAnswers( return $input; } - #[Override] - public function getDefaultConfig(Form $form): ITILActorFieldConfig - { - return new ITILActorFieldConfig( - ITILActorFieldStrategy::FROM_TEMPLATE, - ); - } - #[Override] public function prepareInput(array $input): array { $input = parent::prepareInput($input); // Ensure that itilactors_ids is an array - if (!is_array($input[$this->getKey()][ITILActorFieldConfig::ITILACTORS_IDS] ?? null)) { - unset($input[$this->getKey()][ITILActorFieldConfig::ITILACTORS_IDS]); + if (!is_array($input[$this->getKey()][ITILActorFieldConfig::SPECIFIC_ITILACTORS_IDS] ?? null)) { + $input[$this->getKey()][ITILActorFieldConfig::SPECIFIC_ITILACTORS_IDS] = null; } else { - $input[$this->getKey()][ITILActorFieldConfig::ITILACTORS_IDS] = array_reduce( - $input[$this->getKey()][ITILActorFieldConfig::ITILACTORS_IDS], + $input[$this->getKey()][ITILActorFieldConfig::SPECIFIC_ITILACTORS_IDS] = array_reduce( + $input[$this->getKey()][ITILActorFieldConfig::SPECIFIC_ITILACTORS_IDS], function ($carry, $value) { $parts = explode("-", $value); $carry[getItemtypeForForeignKeyField($parts[0])][] = (int) $parts[1]; @@ -171,8 +156,8 @@ function ($carry, $value) { } // Ensure that question_ids is an array - if (!is_array($input[$this->getKey()][ITILActorFieldConfig::QUESTION_IDS] ?? null)) { - unset($input[$this->getKey()][ITILActorFieldConfig::QUESTION_IDS]); + if (!is_array($input[$this->getKey()][ITILActorFieldConfig::SPECIFIC_QUESTION_IDS] ?? null)) { + $input[$this->getKey()][ITILActorFieldConfig::SPECIFIC_QUESTION_IDS] = null; } return $input; diff --git a/src/Glpi/Form/Destination/CommonITILField/ITILActorFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/ITILActorFieldConfig.php index 6a617a5cf4b..300e382d793 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ITILActorFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/ITILActorFieldConfig.php @@ -36,44 +36,30 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; use Override; -final class ITILActorFieldConfig implements JsonFieldInterface +abstract class ITILActorFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names - public const STRATEGY = 'strategy'; - public const ITILACTORS_IDS = 'itilactors_ids'; - public const QUESTION_IDS = 'question_ids'; + public const STRATEGY = 'strategy'; + public const SPECIFIC_ITILACTORS_IDS = 'specific_itilactors_ids'; + public const SPECIFIC_QUESTION_IDS = 'specific_question_ids'; public function __construct( private ITILActorFieldStrategy $strategy, - private ?array $specific_itilactors_ids = null, - private ?array $specific_question_ids = null, + private array $specific_itilactors_ids = [], + private array $specific_question_ids = [], ) { } - #[Override] - public static function jsonDeserialize(array $data): self - { - $strategy = ITILActorFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); - if ($strategy === null) { - $strategy = ITILActorFieldStrategy::FROM_TEMPLATE; - } - - return new self( - strategy: $strategy, - specific_itilactors_ids: $data[self::ITILACTORS_IDS] ?? [], - specific_question_ids: $data[self::QUESTION_IDS] ?? [], - ); - } - #[Override] public function jsonSerialize(): array { return [ self::STRATEGY => $this->strategy->value, - self::ITILACTORS_IDS => $this->specific_itilactors_ids, - self::QUESTION_IDS => $this->specific_question_ids, + self::SPECIFIC_ITILACTORS_IDS => $this->specific_itilactors_ids, + self::SPECIFIC_QUESTION_IDS => $this->specific_question_ids, ]; } diff --git a/src/Glpi/Form/Destination/CommonITILField/ITILCategoryField.php b/src/Glpi/Form/Destination/CommonITILField/ITILCategoryField.php index f7483757caf..90dda429e15 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ITILCategoryField.php +++ b/src/Glpi/Form/Destination/CommonITILField/ITILCategoryField.php @@ -40,9 +40,7 @@ use Glpi\Form\AnswersSet; use Glpi\Form\Destination\AbstractConfigField; use Glpi\Form\Form; -use Glpi\Form\QuestionType\QuestionTypeItem; use Glpi\Form\QuestionType\QuestionTypeItemDropdown; -use Glpi\Form\QuestionType\QuestionTypeITILCategory; use InvalidArgumentException; use ITILCategory; use Override; @@ -93,14 +91,14 @@ public function renderConfigForm( 'specific_value_extra_field' => [ 'empty_label' => __("Select an ITIL category..."), 'value' => $config->getSpecificITILCategoryID() ?? 0, - 'input_name' => $input_name . "[" . ITILCategoryFieldConfig::ITILCATEGORY_ID . "]", + 'input_name' => $input_name . "[" . ITILCategoryFieldConfig::SPECIFIC_ITILCATEGORY_ID . "]", ], // Specific additional config for SPECIFIC_VALUE strategy 'specific_answer_extra_field' => [ 'empty_label' => __("Select a question..."), 'value' => $config->getSpecificQuestionId(), - 'input_name' => $input_name . "[" . ITILCategoryFieldConfig::QUESTION_ID . "]", + 'input_name' => $input_name . "[" . ITILCategoryFieldConfig::SPECIFIC_QUESTION_ID . "]", 'possible_values' => $this->getITILCategoryQuestionsValuesForDropdown($form), ], ]); diff --git a/src/Glpi/Form/Destination/CommonITILField/ITILCategoryFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/ITILCategoryFieldConfig.php index cb3b212909d..ec3136d63e7 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ITILCategoryFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/ITILCategoryFieldConfig.php @@ -36,14 +36,19 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyHandler; +use Glpi\Form\Export\Context\ForeignKey\QuestionForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use ITILCategory; use Override; -final class ITILCategoryFieldConfig implements JsonFieldInterface +final class ITILCategoryFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names public const STRATEGY = 'strategy'; - public const QUESTION_ID = 'question_id'; - public const ITILCATEGORY_ID = 'itilcategory_id'; + public const SPECIFIC_QUESTION_ID = 'specific_question_id'; + public const SPECIFIC_ITILCATEGORY_ID = 'specific_itilcategory_id'; public function __construct( private ITILCategoryFieldStrategy $strategy, @@ -52,6 +57,15 @@ public function __construct( ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyHandler(self::SPECIFIC_ITILCATEGORY_ID, ITILCategory::class), + new QuestionForeignKeyHandler(self::SPECIFIC_QUESTION_ID) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { @@ -62,8 +76,8 @@ public static function jsonDeserialize(array $data): self return new self( strategy: $strategy, - specific_question_id: $data[self::QUESTION_ID], - specific_itilcategory_id: $data[self::ITILCATEGORY_ID], + specific_question_id: $data[self::SPECIFIC_QUESTION_ID] ?? null, + specific_itilcategory_id: $data[self::SPECIFIC_ITILCATEGORY_ID] ?? null ); } @@ -72,8 +86,8 @@ public function jsonSerialize(): array { return [ self::STRATEGY => $this->strategy->value, - self::QUESTION_ID => $this->specific_question_id, - self::ITILCATEGORY_ID => $this->specific_itilcategory_id, + self::SPECIFIC_QUESTION_ID => $this->specific_question_id, + self::SPECIFIC_ITILCATEGORY_ID => $this->specific_itilcategory_id, ]; } diff --git a/src/Glpi/Form/Destination/CommonITILField/ITILFollowupFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/ITILFollowupFieldConfig.php index d67e93ff64d..3a13254e528 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ITILFollowupFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/ITILFollowupFieldConfig.php @@ -36,9 +36,13 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyArrayHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use ITILFollowupTemplate; use Override; -final class ITILFollowupFieldConfig implements JsonFieldInterface +final class ITILFollowupFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names public const STRATEGY = 'strategy'; @@ -50,6 +54,15 @@ public function __construct( ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + + return [ + new ForeignKeyArrayHandler(key: self::ITILFOLLOWUPTEMPLATE_IDS, itemtype: ITILFollowupTemplate::class) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { diff --git a/src/Glpi/Form/Destination/CommonITILField/ITILTaskFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/ITILTaskFieldConfig.php index 5e5570bc05a..677844c4a89 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ITILTaskFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/ITILTaskFieldConfig.php @@ -36,9 +36,13 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyArrayHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; use Override; +use TaskTemplate; -final class ITILTaskFieldConfig implements JsonFieldInterface +final class ITILTaskFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names public const STRATEGY = 'strategy'; @@ -50,6 +54,14 @@ public function __construct( ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyArrayHandler(key: self::TASKTEMPLATE_IDS, itemtype: TaskTemplate::class) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { diff --git a/src/Glpi/Form/Destination/CommonITILField/LocationField.php b/src/Glpi/Form/Destination/CommonITILField/LocationField.php index 8c9eaf9dd4a..ef2ed0cc366 100644 --- a/src/Glpi/Form/Destination/CommonITILField/LocationField.php +++ b/src/Glpi/Form/Destination/CommonITILField/LocationField.php @@ -91,14 +91,14 @@ public function renderConfigForm( 'specific_value_extra_field' => [ 'empty_label' => __("Select a location..."), 'value' => $config->getSpecificLocationID(), - 'input_name' => $input_name . "[" . LocationFieldConfig::LOCATION_ID . "]", + 'input_name' => $input_name . "[" . LocationFieldConfig::SPECIFIC_LOCATION_ID . "]", ], // Specific additional config for SPECIFIC_VALUE strategy 'specific_answer_extra_field' => [ 'empty_label' => __("Select a question..."), 'value' => $config->getSpecificQuestionId(), - 'input_name' => $input_name . "[" . LocationFieldConfig::QUESTION_ID . "]", + 'input_name' => $input_name . "[" . LocationFieldConfig::SPECIFIC_QUESTION_ID . "]", 'possible_values' => $this->getLocationQuestionsValuesForDropdown($form), ], ]); diff --git a/src/Glpi/Form/Destination/CommonITILField/LocationFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/LocationFieldConfig.php index a49fa8e1bc1..10c0ea7f71f 100644 --- a/src/Glpi/Form/Destination/CommonITILField/LocationFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/LocationFieldConfig.php @@ -36,14 +36,19 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyHandler; +use Glpi\Form\Export\Context\ForeignKey\QuestionForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use Location; use Override; -final class LocationFieldConfig implements JsonFieldInterface +final class LocationFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names public const STRATEGY = 'strategy'; - public const QUESTION_ID = 'question_id'; - public const LOCATION_ID = 'location_id'; + public const SPECIFIC_QUESTION_ID = 'specific_question_id'; + public const SPECIFIC_LOCATION_ID = 'specific_location_id'; public function __construct( private LocationFieldStrategy $strategy, @@ -52,6 +57,15 @@ public function __construct( ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyHandler(self::SPECIFIC_LOCATION_ID, Location::class), + new QuestionForeignKeyHandler(self::SPECIFIC_QUESTION_ID) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { @@ -62,8 +76,8 @@ public static function jsonDeserialize(array $data): self return new self( strategy: $strategy, - specific_question_id: $data[self::QUESTION_ID], - specific_location_id: $data[self::LOCATION_ID], + specific_question_id: $data[self::SPECIFIC_QUESTION_ID] ?? null, + specific_location_id: $data[self::SPECIFIC_LOCATION_ID] ?? null ); } @@ -72,8 +86,8 @@ public function jsonSerialize(): array { return [ self::STRATEGY => $this->strategy->value, - self::QUESTION_ID => $this->specific_question_id, - self::LOCATION_ID => $this->specific_location_id, + self::SPECIFIC_QUESTION_ID => $this->specific_question_id, + self::SPECIFIC_LOCATION_ID => $this->specific_location_id, ]; } diff --git a/src/Glpi/Form/Destination/CommonITILField/OLATTOField.php b/src/Glpi/Form/Destination/CommonITILField/OLATTOField.php index f6cad915f9f..9c4533eb0c2 100644 --- a/src/Glpi/Form/Destination/CommonITILField/OLATTOField.php +++ b/src/Glpi/Form/Destination/CommonITILField/OLATTOField.php @@ -64,4 +64,10 @@ public function getType(): int { return SLM::TTO; } + + #[Override] + public function getConfigClass(): string + { + return OLATTOFieldConfig::class; + } } diff --git a/src/Glpi/Form/Destination/CommonITILField/OLATTOFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/OLATTOFieldConfig.php new file mode 100644 index 00000000000..736c408c037 --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/OLATTOFieldConfig.php @@ -0,0 +1,65 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use OLA; +use Override; + +class OLATTOFieldConfig extends SLMFieldConfig +{ + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyHandler(key: self::SLM_ID, itemtype: OLA::class) + ]; + } + + #[Override] + public static function jsonDeserialize(array $data): self + { + $strategy = SLMFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); + if ($strategy === null) { + $strategy = SLMFieldStrategy::FROM_TEMPLATE; + } + + return new self( + strategy: $strategy, + specific_slm_id: $data[self::SLM_ID] ?? null + ); + } +} diff --git a/src/Glpi/Form/Destination/CommonITILField/OLATTRField.php b/src/Glpi/Form/Destination/CommonITILField/OLATTRField.php index 7c3cc4bce49..8939bf3ee73 100644 --- a/src/Glpi/Form/Destination/CommonITILField/OLATTRField.php +++ b/src/Glpi/Form/Destination/CommonITILField/OLATTRField.php @@ -64,4 +64,10 @@ public function getType(): int { return SLM::TTR; } + + #[Override] + public function getConfigClass(): string + { + return OLATTRFieldConfig::class; + } } diff --git a/src/Glpi/Form/Destination/CommonITILField/OLATTRFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/OLATTRFieldConfig.php new file mode 100644 index 00000000000..6cd5c262ead --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/OLATTRFieldConfig.php @@ -0,0 +1,65 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use OLA; +use Override; + +class OLATTRFieldConfig extends SLMFieldConfig +{ + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyHandler(key: self::SLM_ID, itemtype: OLA::class) + ]; + } + + #[Override] + public static function jsonDeserialize(array $data): self + { + $strategy = SLMFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); + if ($strategy === null) { + $strategy = SLMFieldStrategy::FROM_TEMPLATE; + } + + return new self( + strategy: $strategy, + specific_slm_id: $data[self::SLM_ID] ?? null + ); + } +} diff --git a/src/Glpi/Form/Destination/CommonITILField/ObserverField.php b/src/Glpi/Form/Destination/CommonITILField/ObserverField.php index a00197d3b5a..734d718f832 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ObserverField.php +++ b/src/Glpi/Form/Destination/CommonITILField/ObserverField.php @@ -35,6 +35,7 @@ namespace Glpi\Form\Destination\CommonITILField; +use Glpi\Form\Form; use Glpi\Form\QuestionType\QuestionTypeObserver; use Override; use Session; @@ -64,4 +65,18 @@ public function getWeight(): int { return 30; } + + #[Override] + public function getConfigClass(): string + { + return ObserverFieldConfig::class; + } + + #[Override] + public function getDefaultConfig(Form $form): ObserverFieldConfig + { + return new ObserverFieldConfig( + ITILActorFieldStrategy::FROM_TEMPLATE, + ); + } } diff --git a/src/Glpi/Form/Destination/CommonITILField/ObserverFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/ObserverFieldConfig.php new file mode 100644 index 00000000000..6fd6ce34d83 --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/ObserverFieldConfig.php @@ -0,0 +1,68 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyItemsArrayHandler; +use Glpi\Form\Export\Context\ForeignKey\QuestionArrayForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use Override; + +final class ObserverFieldConfig extends ITILActorFieldConfig +{ + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyItemsArrayHandler(self::SPECIFIC_ITILACTORS_IDS), + new QuestionArrayForeignKeyHandler(self::SPECIFIC_QUESTION_IDS) + ]; + } + + #[Override] + public static function jsonDeserialize(array $data): self + { + $strategy = ITILActorFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); + if ($strategy === null) { + $strategy = ITILActorFieldStrategy::FROM_TEMPLATE; + } + + return new self( + strategy: $strategy, + specific_itilactors_ids: $data[self::SPECIFIC_ITILACTORS_IDS] ?? [], + specific_question_ids: $data[self::SPECIFIC_QUESTION_IDS] ?? [], + ); + } +} diff --git a/src/Glpi/Form/Destination/CommonITILField/RequestSourceFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/RequestSourceFieldConfig.php index 4dda26bce06..70029f317ec 100644 --- a/src/Glpi/Form/Destination/CommonITILField/RequestSourceFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/RequestSourceFieldConfig.php @@ -60,7 +60,7 @@ public static function jsonDeserialize(array $data): self return new self( strategy: $strategy, - specific_request_source: $data[self::REQUEST_SOURCE], + specific_request_source: $data[self::REQUEST_SOURCE] ?? null, ); } diff --git a/src/Glpi/Form/Destination/CommonITILField/RequestTypeField.php b/src/Glpi/Form/Destination/CommonITILField/RequestTypeField.php index 1b31fb460ae..f80437c21a9 100644 --- a/src/Glpi/Form/Destination/CommonITILField/RequestTypeField.php +++ b/src/Glpi/Form/Destination/CommonITILField/RequestTypeField.php @@ -91,7 +91,7 @@ public function renderConfigForm( 'specific_value_extra_field' => [ 'empty_label' => __("Select a request type..."), 'value' => $config->getSpecificRequestType(), - 'input_name' => $input_name . "[" . RequestTypeFieldConfig::REQUEST_TYPE . "]", + 'input_name' => $input_name . "[" . RequestTypeFieldConfig::SPECIFIC_REQUEST_TYPE . "]", 'possible_values' => Ticket::getTypes(), ], @@ -99,7 +99,7 @@ public function renderConfigForm( 'specific_answer_extra_field' => [ 'empty_label' => __("Select a question..."), 'value' => $config->getSpecificQuestionId(), - 'input_name' => $input_name . "[" . RequestTypeFieldConfig::QUESTION_ID . "]", + 'input_name' => $input_name . "[" . RequestTypeFieldConfig::SPECIFIC_QUESTION_ID . "]", 'possible_values' => $this->getRequestTypeQuestionsValuesForDropdown($form), ], ]); diff --git a/src/Glpi/Form/Destination/CommonITILField/RequestTypeFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/RequestTypeFieldConfig.php index 6850d8552cb..d7a12048816 100644 --- a/src/Glpi/Form/Destination/CommonITILField/RequestTypeFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/RequestTypeFieldConfig.php @@ -36,14 +36,17 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\QuestionForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; use Override; -final class RequestTypeFieldConfig implements JsonFieldInterface +final class RequestTypeFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names public const STRATEGY = 'strategy'; - public const QUESTION_ID = 'question_id'; - public const REQUEST_TYPE = 'request_type'; + public const SPECIFIC_QUESTION_ID = 'specific_question_id'; + public const SPECIFIC_REQUEST_TYPE = 'specific_request_type'; public function __construct( private RequestTypeFieldStrategy $strategy, @@ -52,6 +55,14 @@ public function __construct( ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new QuestionForeignKeyHandler(self::SPECIFIC_QUESTION_ID) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { @@ -62,8 +73,8 @@ public static function jsonDeserialize(array $data): self return new self( strategy: $strategy, - specific_question_id: $data[self::QUESTION_ID], - specific_request_type: $data[self::REQUEST_TYPE], + specific_question_id: $data[self::SPECIFIC_QUESTION_ID] ?? null, + specific_request_type: $data[self::SPECIFIC_REQUEST_TYPE] ?? null, ); } @@ -72,8 +83,8 @@ public function jsonSerialize(): array { return [ self::STRATEGY => $this->strategy->value, - self::QUESTION_ID => $this->specific_question_id, - self::REQUEST_TYPE => $this->specific_request_type, + self::SPECIFIC_QUESTION_ID => $this->specific_question_id, + self::SPECIFIC_REQUEST_TYPE => $this->specific_request_type, ]; } diff --git a/src/Glpi/Form/Destination/CommonITILField/RequesterField.php b/src/Glpi/Form/Destination/CommonITILField/RequesterField.php index 2525b217621..d53e231410e 100644 --- a/src/Glpi/Form/Destination/CommonITILField/RequesterField.php +++ b/src/Glpi/Form/Destination/CommonITILField/RequesterField.php @@ -61,9 +61,15 @@ public function getLabel(): string } #[Override] - public function getDefaultConfig(Form $form): ITILActorFieldConfig + public function getConfigClass(): string { - return new ITILActorFieldConfig( + return RequesterFieldConfig::class; + } + + #[Override] + public function getDefaultConfig(Form $form): RequesterFieldConfig + { + return new RequesterFieldConfig( ITILActorFieldStrategy::FORM_FILLER, ); } diff --git a/src/Glpi/Form/Destination/CommonITILField/RequesterFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/RequesterFieldConfig.php new file mode 100644 index 00000000000..b0fe9e6e63a --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/RequesterFieldConfig.php @@ -0,0 +1,68 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyItemsArrayHandler; +use Glpi\Form\Export\Context\ForeignKey\QuestionArrayForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use Override; + +final class RequesterFieldConfig extends ITILActorFieldConfig +{ + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyItemsArrayHandler(self::SPECIFIC_ITILACTORS_IDS), + new QuestionArrayForeignKeyHandler(self::SPECIFIC_QUESTION_IDS) + ]; + } + + #[Override] + public static function jsonDeserialize(array $data): self + { + $strategy = ITILActorFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); + if ($strategy === null) { + $strategy = ITILActorFieldStrategy::FROM_TEMPLATE; + } + + return new self( + strategy: $strategy, + specific_itilactors_ids: $data[self::SPECIFIC_ITILACTORS_IDS] ?? [], + specific_question_ids: $data[self::SPECIFIC_QUESTION_IDS] ?? [], + ); + } +} diff --git a/src/Glpi/Form/Destination/CommonITILField/SLATTOField.php b/src/Glpi/Form/Destination/CommonITILField/SLATTOField.php index a63004386aa..7791a8c131c 100644 --- a/src/Glpi/Form/Destination/CommonITILField/SLATTOField.php +++ b/src/Glpi/Form/Destination/CommonITILField/SLATTOField.php @@ -64,4 +64,10 @@ public function getType(): int { return SLM::TTO; } + + #[Override] + public function getConfigClass(): string + { + return SLATTOFieldConfig::class; + } } diff --git a/src/Glpi/Form/Destination/CommonITILField/SLATTOFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/SLATTOFieldConfig.php new file mode 100644 index 00000000000..326f9dd9d9e --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/SLATTOFieldConfig.php @@ -0,0 +1,66 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use Override; +use SLA; + +class SLATTOFieldConfig extends SLMFieldConfig +{ + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyHandler(key: self::SLM_ID, itemtype: SLA::class) + ]; + } + + #[Override] + public static function jsonDeserialize(array $data): self + { + $strategy = SLMFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); + if ($strategy === null) { + $strategy = SLMFieldStrategy::FROM_TEMPLATE; + } + + return new self( + strategy: $strategy, + specific_slm_id: $data[self::SLM_ID] ?? null, + ); + } +} diff --git a/src/Glpi/Form/Destination/CommonITILField/SLATTRField.php b/src/Glpi/Form/Destination/CommonITILField/SLATTRField.php index 8a3f4f7b898..63c7cf082f4 100644 --- a/src/Glpi/Form/Destination/CommonITILField/SLATTRField.php +++ b/src/Glpi/Form/Destination/CommonITILField/SLATTRField.php @@ -64,4 +64,10 @@ public function getType(): int { return SLM::TTR; } + + #[Override] + public function getConfigClass(): string + { + return SLATTRFieldConfig::class; + } } diff --git a/src/Glpi/Form/Destination/CommonITILField/SLATTRFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/SLATTRFieldConfig.php new file mode 100644 index 00000000000..8b945274c91 --- /dev/null +++ b/src/Glpi/Form/Destination/CommonITILField/SLATTRFieldConfig.php @@ -0,0 +1,65 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Destination\CommonITILField; + +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use Override; +use SLA; + +class SLATTRFieldConfig extends SLMFieldConfig +{ + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyHandler(key: self::SLM_ID, itemtype: SLA::class) + ]; + } + + #[Override] + public static function jsonDeserialize(array $data): self + { + $strategy = SLMFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); + if ($strategy === null) { + $strategy = SLMFieldStrategy::FROM_TEMPLATE; + } + + return new self( + strategy: $strategy, + specific_slm_id: $data[self::SLM_ID] ?? null, + ); + } +} diff --git a/src/Glpi/Form/Destination/CommonITILField/SLMField.php b/src/Glpi/Form/Destination/CommonITILField/SLMField.php index 61de65c3144..de481c9dda4 100644 --- a/src/Glpi/Form/Destination/CommonITILField/SLMField.php +++ b/src/Glpi/Form/Destination/CommonITILField/SLMField.php @@ -47,12 +47,8 @@ abstract class SLMField extends AbstractConfigField { abstract public function getSLMClass(): string; abstract public function getType(): int; - - #[Override] - public function getConfigClass(): string - { - return SLMFieldConfig::class; - } + /** @return class-string */ + abstract public function getConfigClass(): string; #[Override] public function renderConfigForm( @@ -120,7 +116,7 @@ public function applyConfiguratedValueToInputUsingAnswers( #[Override] public function getDefaultConfig(Form $form): SLMFieldConfig { - return new SLMFieldConfig( + return new ($this->getConfigClass())( SLMFieldStrategy::FROM_TEMPLATE ); } diff --git a/src/Glpi/Form/Destination/CommonITILField/SLMFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/SLMFieldConfig.php index a3dee2940d5..ec94f590302 100644 --- a/src/Glpi/Form/Destination/CommonITILField/SLMFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/SLMFieldConfig.php @@ -36,9 +36,10 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; use Override; -final class SLMFieldConfig implements JsonFieldInterface +abstract class SLMFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names public const STRATEGY = 'strategy'; @@ -50,20 +51,6 @@ public function __construct( ) { } - #[Override] - public static function jsonDeserialize(array $data): self - { - $strategy = SLMFieldStrategy::tryFrom($data[self::STRATEGY] ?? ""); - if ($strategy === null) { - $strategy = SLMFieldStrategy::FROM_TEMPLATE; - } - - return new self( - strategy: $strategy, - specific_slm_id: $data[self::SLM_ID], - ); - } - #[Override] public function jsonSerialize(): array { diff --git a/src/Glpi/Form/Destination/CommonITILField/TemplateFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/TemplateFieldConfig.php index b64f50f3e90..2a0cc178507 100644 --- a/src/Glpi/Form/Destination/CommonITILField/TemplateFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/TemplateFieldConfig.php @@ -36,9 +36,13 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; +use Glpi\Form\Export\Specification\DestinationContentSpecification; use Override; -final class TemplateFieldConfig implements JsonFieldInterface +final class TemplateFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names public const STRATEGY = 'strategy'; @@ -50,6 +54,22 @@ public function __construct( ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + if (!($content_spec instanceof DestinationContentSpecification)) { + throw new \InvalidArgumentException( + "Content specification must be an instance of " . DestinationContentSpecification::class + ); + } + + $destination_itemtype = $content_spec->itemtype; + $destination_target = new ($destination_itemtype::getTargetItemtype())(); + return [ + new ForeignKeyHandler(self::TEMPLATE_ID, $destination_target->getTemplateClass()) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { @@ -60,7 +80,7 @@ public static function jsonDeserialize(array $data): self return new self( strategy: $strategy, - specific_template_id: $data[self::TEMPLATE_ID], + specific_template_id: $data[self::TEMPLATE_ID] ?? null ); } diff --git a/src/Glpi/Form/Destination/CommonITILField/UrgencyFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/UrgencyFieldConfig.php index b30b21570d4..8cac0055e23 100644 --- a/src/Glpi/Form/Destination/CommonITILField/UrgencyFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/UrgencyFieldConfig.php @@ -36,9 +36,12 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\QuestionForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; use Override; -final class UrgencyFieldConfig implements JsonFieldInterface +final class UrgencyFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names public const STRATEGY = 'strategy'; @@ -52,6 +55,14 @@ public function __construct( ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new QuestionForeignKeyHandler(self::SPECIFIC_QUESTION_ID) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { @@ -62,8 +73,8 @@ public static function jsonDeserialize(array $data): self return new self( strategy: $strategy, - specific_question_id: $data[self::SPECIFIC_QUESTION_ID], - specific_urgency_value: $data[self::SPECIFIC_URGENCY_VALUE], + specific_question_id: $data[self::SPECIFIC_QUESTION_ID] ?? null, + specific_urgency_value: $data[self::SPECIFIC_URGENCY_VALUE] ?? null, ); } diff --git a/src/Glpi/Form/Destination/CommonITILField/ValidationField.php b/src/Glpi/Form/Destination/CommonITILField/ValidationField.php index 8d57a17225e..9ca2fcc1627 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ValidationField.php +++ b/src/Glpi/Form/Destination/CommonITILField/ValidationField.php @@ -74,6 +74,13 @@ public function renderConfigForm( throw new InvalidArgumentException("Unexpected config class"); } + // Specific actors are stored as an array of itemtype => items_ids to be generic. + // We need to convert keys to foreign keys to be able to use them with the actors component. + $specific_actors = []; + foreach ($config->getSpecificActors() as $itemtype => $items_ids) { + $specific_actors[getForeignKeyFieldForItemType($itemtype)] = $items_ids; + } + $twig = TemplateRenderer::getInstance(); return $twig->render('pages/admin/form/itil_config_fields/validation.html.twig', [ // Possible configuration constant that will be used to to hide/show additional fields @@ -93,7 +100,7 @@ public function renderConfigForm( // Specific additional config for SPECIFIC_ACTORS strategy 'specific_values_extra_field' => [ - 'values' => $config->getSpecificActors() ?? [], + 'values' => $specific_actors, 'input_name' => $input_name . "[" . ValidationFieldConfig::SPECIFIC_ACTORS . "]", 'allowed_types' => [User::class, Group::class], 'aria_label' => __("Select actors..."), @@ -101,8 +108,8 @@ public function renderConfigForm( // Specific additional config for SPECIFIC_ANSWERS strategy 'specific_answers_extra_field' => [ - 'values' => $config->getSpecificQuestionIds() ?? [], - 'input_name' => $input_name . "[" . ValidationFieldConfig::QUESTION_IDS . "]", + 'values' => $config->getSpecificQuestionIds(), + 'input_name' => $input_name . "[" . ValidationFieldConfig::SPECIFIC_QUESTION_IDS . "]", 'possible_values' => $this->getActorsQuestionsValuesForDropdown($form), 'aria_label' => __("Select questions..."), ], @@ -193,13 +200,13 @@ public function prepareInput(array $input): array $input = parent::prepareInput($input); // Ensure that question_ids is an array - if (!is_array($input[$this->getKey()][ValidationFieldConfig::QUESTION_IDS] ?? null)) { - unset($input[$this->getKey()][ValidationFieldConfig::QUESTION_IDS]); + if (!is_array($input[$this->getKey()][ValidationFieldConfig::SPECIFIC_QUESTION_IDS] ?? null)) { + $input[$this->getKey()][ValidationFieldConfig::SPECIFIC_QUESTION_IDS] = null; } // Ensure that specific_actors is an array if (!is_array($input[$this->getKey()][ValidationFieldConfig::SPECIFIC_ACTORS] ?? null)) { - unset($input[$this->getKey()][ValidationFieldConfig::SPECIFIC_ACTORS]); + $input[$this->getKey()][ValidationFieldConfig::SPECIFIC_ACTORS] = null; } // Format specific_actors @@ -223,11 +230,8 @@ public function prepareInput(array $input): array continue; } - if (!isset($actors[$actor_parts[0]])) { - $actors[$actor_parts[0]] = []; - } - - $actors[$actor_parts[0]][] = $actor_parts[1]; + $itemtype = array_search($actor_parts[0], $available_actor_types); + $actors[$itemtype][] = $actor_parts[1]; } $input[$this->getKey()][ValidationFieldConfig::SPECIFIC_ACTORS] = $actors; diff --git a/src/Glpi/Form/Destination/CommonITILField/ValidationFieldConfig.php b/src/Glpi/Form/Destination/CommonITILField/ValidationFieldConfig.php index 3b0905a6292..491ce262cf6 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ValidationFieldConfig.php +++ b/src/Glpi/Form/Destination/CommonITILField/ValidationFieldConfig.php @@ -36,22 +36,35 @@ namespace Glpi\Form\Destination\CommonITILField; use Glpi\DBAL\JsonFieldInterface; +use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\ForeignKeyItemsArrayHandler; +use Glpi\Form\Export\Context\ForeignKey\QuestionArrayForeignKeyHandler; +use Glpi\Form\Export\Specification\ContentSpecificationInterface; use Override; -final class ValidationFieldConfig implements JsonFieldInterface +final class ValidationFieldConfig implements JsonFieldInterface, ConfigWithForeignKeysInterface { // Unique reference to hardcoded names used for serialization and forms input names - public const STRATEGY = 'strategy'; - public const QUESTION_IDS = 'question_ids'; - public const SPECIFIC_ACTORS = 'specific_actors'; + public const STRATEGY = 'strategy'; + public const SPECIFIC_QUESTION_IDS = 'specific_question_ids'; + public const SPECIFIC_ACTORS = 'specific_actors'; public function __construct( private ValidationFieldStrategy $strategy, - private ?array $specific_question_ids = null, - private ?array $specific_actors = null, + private array $specific_question_ids = [], + private array $specific_actors = [], ) { } + #[Override] + public static function listForeignKeysHandlers(ContentSpecificationInterface $content_spec): array + { + return [ + new ForeignKeyItemsArrayHandler(key: self::SPECIFIC_ACTORS), + new QuestionArrayForeignKeyHandler(self::SPECIFIC_QUESTION_IDS) + ]; + } + #[Override] public static function jsonDeserialize(array $data): self { @@ -62,8 +75,8 @@ public static function jsonDeserialize(array $data): self return new self( strategy: $strategy, - specific_question_ids: $data[self::QUESTION_IDS] ?? null, - specific_actors: $data[self::SPECIFIC_ACTORS] ?? null, + specific_question_ids: $data[self::SPECIFIC_QUESTION_IDS] ?? [], + specific_actors: $data[self::SPECIFIC_ACTORS] ?? [], ); } @@ -72,7 +85,7 @@ public function jsonSerialize(): array { return [ self::STRATEGY => $this->strategy->value, - self::QUESTION_IDS => $this->specific_question_ids, + self::SPECIFIC_QUESTION_IDS => $this->specific_question_ids, self::SPECIFIC_ACTORS => $this->specific_actors, ]; } @@ -82,12 +95,12 @@ public function getStrategy(): ValidationFieldStrategy return $this->strategy; } - public function getSpecificQuestionIds(): ?array + public function getSpecificQuestionIds(): array { return $this->specific_question_ids; } - public function getSpecificActors(): ?array + public function getSpecificActors(): array { return $this->specific_actors; } diff --git a/src/Glpi/Form/Destination/CommonITILField/ValidationFieldStrategy.php b/src/Glpi/Form/Destination/CommonITILField/ValidationFieldStrategy.php index 31d1596e2d7..2fb3ce4460c 100644 --- a/src/Glpi/Form/Destination/CommonITILField/ValidationFieldStrategy.php +++ b/src/Glpi/Form/Destination/CommonITILField/ValidationFieldStrategy.php @@ -59,10 +59,10 @@ public function getValidation( return match ($this) { self::NO_VALIDATION => null, self::SPECIFIC_ACTORS => $this->getActorsFromSpecificActors( - $config->getSpecificActors() ?? [] + $config->getSpecificActors() ), self::SPECIFIC_ANSWERS => $this->getActorsForSpecificAnswers( - $config->getSpecificQuestionIds() ?? [], + $config->getSpecificQuestionIds(), $answers_set ), }; @@ -135,10 +135,10 @@ private function getActorsFromSpecificActors( } $actors = []; - foreach ($specific_actors as $fk => $actor_ids) { + foreach ($specific_actors as $itemtype => $actor_ids) { foreach ($actor_ids as $actor_id) { $actors[] = [ - 'itemtype' => getItemtypeForForeignKeyField($fk), + 'itemtype' => $itemtype, 'items_id' => $actor_id, ]; } diff --git a/src/Glpi/Form/Destination/FormDestination.php b/src/Glpi/Form/Destination/FormDestination.php index 683fee51a5a..fba9b501c77 100644 --- a/src/Glpi/Form/Destination/FormDestination.php +++ b/src/Glpi/Form/Destination/FormDestination.php @@ -240,6 +240,10 @@ public function prepareInput($input): array $destination_item = $this->getConcreteDestinationItem(); if ($destination_item instanceof AbstractCommonITILFormDestination) { foreach ($destination_item->getConfigurableFields() as $field) { + if ($input['_from_import'] ?? false) { + continue; + } + $input['config'] = $field->prepareInput($input['config']); } } diff --git a/src/Glpi/Form/Destination/FormDestinationTypeManager.php b/src/Glpi/Form/Destination/FormDestinationTypeManager.php index 166c18ac6b0..d222f4f5e66 100644 --- a/src/Glpi/Form/Destination/FormDestinationTypeManager.php +++ b/src/Glpi/Form/Destination/FormDestinationTypeManager.php @@ -72,7 +72,7 @@ public static function getInstance(): FormDestinationTypeManager public function getDestinationTypes(): array { // TODO: support plugin types - return [ + return [ new FormDestinationTicket(), new FormDestinationProblem(), new FormDestinationChange(), diff --git a/src/Glpi/Form/Export/Context/DatabaseMapper.php b/src/Glpi/Form/Export/Context/DatabaseMapper.php index 8001913bddc..5b16083d734 100644 --- a/src/Glpi/Form/Export/Context/DatabaseMapper.php +++ b/src/Glpi/Form/Export/Context/DatabaseMapper.php @@ -37,6 +37,7 @@ use CommonDBTM; use Glpi\Form\Export\Specification\DataRequirementSpecification; +use Glpi\Form\Question; use InvalidArgumentException; final class DatabaseMapper @@ -134,6 +135,10 @@ private function isValidItemtype(string $itemtype): bool private function contextExist(string $itemtype, string $name): bool { + if ($itemtype === Question::class) { + return true; + } + return isset($this->values[$itemtype][$name]); } diff --git a/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyArrayHandler.php b/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyArrayHandler.php index acced88b508..9952362f014 100644 --- a/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyArrayHandler.php +++ b/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyArrayHandler.php @@ -85,7 +85,9 @@ public function getDataRequirements(array $serialized_data): array public function replaceForeignKeysByNames(array $serialized_data): array { if (!$this->keyExistInSerializedData($serialized_data)) { - return []; + // Key can be exist but defined as null, so we need to unset it + unset($serialized_data[$this->key]); + return $serialized_data; } $data_with_names = []; @@ -106,7 +108,12 @@ public function replaceForeignKeysByNames(array $serialized_data): array } } - $serialized_data[$this->key] = $data_with_names; + if (!empty($data_with_names)) { + $serialized_data[$this->key] = $data_with_names; + } else { + unset($serialized_data[$this->key]); + } + return $serialized_data; } @@ -115,7 +122,7 @@ public function replaceNamesByForeignKeys( DatabaseMapper $mapper, ): array { if (!$this->keyExistInSerializedData($serialized_data)) { - return []; + return $serialized_data; } $data_with_fkeys = []; diff --git a/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyHandler.php b/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyHandler.php index 9fb71b76f58..ca799fee459 100644 --- a/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyHandler.php +++ b/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyHandler.php @@ -74,7 +74,7 @@ public function getDataRequirements(array $serialized_data): array public function replaceForeignKeysByNames(array $serialized_data): array { if (!$this->keyExistInSerializedData($serialized_data)) { - return []; + return $serialized_data; } $foreign_key = $serialized_data[$this->key]; @@ -83,6 +83,8 @@ public function replaceForeignKeysByNames(array $serialized_data): array $item = new $this->itemtype(); if ($item->getFromDB($foreign_key)) { $serialized_data[$this->key] = $item->getName(); + } else { + unset($serialized_data[$this->key]); } return $serialized_data; @@ -93,7 +95,7 @@ public function replaceNamesByForeignKeys( DatabaseMapper $mapper, ): array { if (!$this->keyExistInSerializedData($serialized_data)) { - return []; + return $serialized_data; } // Replace name by its database id diff --git a/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyItemsArrayHandler.php b/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyItemsArrayHandler.php new file mode 100644 index 00000000000..ca652b690f3 --- /dev/null +++ b/src/Glpi/Form/Export/Context/ForeignKey/ForeignKeyItemsArrayHandler.php @@ -0,0 +1,141 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Export\Context\ForeignKey; + +use Glpi\Form\Export\Context\DatabaseMapper; +use Glpi\Form\Export\Specification\DataRequirementSpecification; + +/** + * Handle an array of foreign keys. + * If the array contains some mixed values that are not 100% foreign keys (e.g. + * a special 'all' value), you can ignore these values using the $ignored_values + * parameter of the constructor. + */ +final class ForeignKeyItemsArrayHandler implements JsonConfigForeignKeyHandlerInterface +{ + public function __construct( + private string $key, + ) { + } + + public function getDataRequirements(array $serialized_data): array + { + if (!$this->keyExistInSerializedData($serialized_data)) { + return []; + } + + $requirements = []; + $items = $serialized_data[$this->key]; + + // Create a data requirement for each item + foreach ($items as $itemtype => $items_ids) { + foreach ($items_ids as $item_id) { + $item = new $itemtype(); + if ($item->getFromDB($item_id)) { + $requirements[] = new DataRequirementSpecification( + $itemtype, + $item->getName(), + ); + }; + } + } + + return $requirements; + } + + public function replaceForeignKeysByNames(array $serialized_data): array + { + if (!$this->keyExistInSerializedData($serialized_data)) { + // Key can be exist but defined as null, so we need to unset it + unset($serialized_data[$this->key]); + return $serialized_data; + } + + $data_with_names = []; + $items = $serialized_data[$this->key]; + + // Replace each item id by its name + foreach ($items as $itemtype => $items_ids) { + $items_names = []; + foreach ($items_ids as $item_id) { + $item = new $itemtype(); + if ($item->getFromDB($item_id)) { + $items_names[] = $item->getName(); + } + } + if (!empty($items_names)) { + $data_with_names[$itemtype] = $items_names; + } + } + + if (!empty($data_with_names)) { + $serialized_data[$this->key] = $data_with_names; + } else { + unset($serialized_data[$this->key]); + } + + return $serialized_data; + } + + public function replaceNamesByForeignKeys( + array $serialized_data, + DatabaseMapper $mapper, + ): array { + if (!$this->keyExistInSerializedData($serialized_data)) { + return $serialized_data; + } + + $data_with_fkeys = []; + $items = $serialized_data[$this->key]; + + // Replace each item name by its id + foreach ($items as $itemtype => $items_names) { + $items_ids = []; + foreach ($items_names as $item_name) { + $items_ids[] = $mapper->getItemId($itemtype, $item_name); + } + $data_with_fkeys[$itemtype] = $items_ids; + } + + $serialized_data[$this->key] = $data_with_fkeys; + return $serialized_data; + } + + private function keyExistInSerializedData(array $serialized_data): bool + { + return isset($serialized_data[$this->key]); + } +} diff --git a/src/Glpi/Form/Export/Context/ForeignKey/QuestionArrayForeignKeyHandler.php b/src/Glpi/Form/Export/Context/ForeignKey/QuestionArrayForeignKeyHandler.php new file mode 100644 index 00000000000..25fe9a046bb --- /dev/null +++ b/src/Glpi/Form/Export/Context/ForeignKey/QuestionArrayForeignKeyHandler.php @@ -0,0 +1,154 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Export\Context\ForeignKey; + +use Glpi\Form\Export\Context\DatabaseMapper; +use Glpi\Form\Export\Specification\DataRequirementSpecification; +use Glpi\Form\Question; + +/** + * Handle an array of foreign keys for Question items. + */ +final class QuestionArrayForeignKeyHandler implements JsonConfigForeignKeyHandlerInterface +{ + public function __construct( + private string $key, + private array $ignored_values = [], + ) { + } + + public function getDataRequirements(array $serialized_data): array + { + if (!$this->keyExistInSerializedData($serialized_data)) { + return []; + } + + $requirements = []; + $foreign_keys = $serialized_data[$this->key]; + + // Create a data requirement for each foreign key + foreach ($foreign_keys as $foreign_key) { + if ($this->isIgnoredValue($foreign_key)) { + continue; + } + + // Load item + $item = new Question(); + if ($item->getFromDB($foreign_key)) { + $requirements[] = new DataRequirementSpecification( + Question::class, + $item->getUniqueIDInForm(), + ); + } + } + + return $requirements; + } + + public function replaceForeignKeysByNames(array $serialized_data): array + { + if (!$this->keyExistInSerializedData($serialized_data)) { + // Key can be exist but defined as null, so we need to unset it + unset($serialized_data[$this->key]); + return $serialized_data; + } + + $data_with_names = []; + $foreign_keys = $serialized_data[$this->key]; + + // Replace each foreign key by the name of the item it references + foreach ($foreign_keys as $foreign_key) { + if ($this->isIgnoredValue($foreign_key)) { + // Value isn't a fkey, keep it as it is + $data_with_names[] = $foreign_key; + continue; + } + + // Load item + $item = new Question(); + if ($item->getFromDB($foreign_key)) { + $data_with_names[] = $item->getUniqueIDInForm(); + } + } + + if (!empty($data_with_names)) { + $serialized_data[$this->key] = $data_with_names; + } else { + unset($serialized_data[$this->key]); + } + + return $serialized_data; + } + + public function replaceNamesByForeignKeys( + array $serialized_data, + DatabaseMapper $mapper, + ): array { + if (!$this->keyExistInSerializedData($serialized_data)) { + return $serialized_data; + } + + $data_with_fkeys = []; + $names = $serialized_data[$this->key]; + + // Replace names by its database id + foreach ($names as $name) { + if ($this->isIgnoredValue($name)) { + // Value isn't a name, keep it as it is + $data_with_fkeys[] = $name; + continue; + } + + $data_with_fkeys[] = $mapper->getItemId( + Question::class, + $name + ); + } + + $serialized_data[$this->key] = $data_with_fkeys; + return $serialized_data; + } + + private function keyExistInSerializedData(array $serialized_data): bool + { + return isset($serialized_data[$this->key]); + } + + private function isIgnoredValue(mixed $value): bool + { + return in_array($value, $this->ignored_values); + } +} diff --git a/src/Glpi/Form/Export/Context/ForeignKey/QuestionForeignKeyHandler.php b/src/Glpi/Form/Export/Context/ForeignKey/QuestionForeignKeyHandler.php new file mode 100644 index 00000000000..ab058045e8b --- /dev/null +++ b/src/Glpi/Form/Export/Context/ForeignKey/QuestionForeignKeyHandler.php @@ -0,0 +1,113 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Export\Context\ForeignKey; + +use Glpi\Form\Export\Context\DatabaseMapper; +use Glpi\Form\Export\Specification\DataRequirementSpecification; +use Glpi\Form\Question; + +/** + * Handle a foreign keys. + */ +final class QuestionForeignKeyHandler implements JsonConfigForeignKeyHandlerInterface +{ + public function __construct( + private string $key + ) { + } + + public function getDataRequirements(array $serialized_data): array + { + if (!$this->keyExistInSerializedData($serialized_data)) { + return []; + } + + $requirements = []; + $foreign_key = $serialized_data[$this->key]; + + // Create a data requirement for the foreign key and load item + $item = new Question(); + if ($item->getFromDB($foreign_key)) { + $requirements[] = new DataRequirementSpecification( + Question::class, + $item->getUniqueIDInForm() + ); + } + + return $requirements; + } + + public function replaceForeignKeysByNames(array $serialized_data): array + { + if (!$this->keyExistInSerializedData($serialized_data)) { + return $serialized_data; + } + + $foreign_key = $serialized_data[$this->key]; + + // Replace the foreign key by the name of the item it references and load item + $item = new Question(); + if ($item->getFromDB($foreign_key)) { + $serialized_data[$this->key] = $item->getUniqueIDInForm(); + } else { + unset($serialized_data[$this->key]); + } + + return $serialized_data; + } + + public function replaceNamesByForeignKeys( + array $serialized_data, + DatabaseMapper $mapper, + ): array { + if (!$this->keyExistInSerializedData($serialized_data)) { + return $serialized_data; + } + + // Replace name by its database id + $serialized_data[$this->key] = $mapper->getItemId( + Question::class, + $serialized_data[$this->key] + ); + + return $serialized_data; + } + + private function keyExistInSerializedData(array $serialized_data): bool + { + return isset($serialized_data[$this->key]); + } +} diff --git a/src/Glpi/Form/Export/Serializer/FormSerializer.php b/src/Glpi/Form/Export/Serializer/FormSerializer.php index 4447fe9d5a4..9b247143146 100644 --- a/src/Glpi/Form/Export/Serializer/FormSerializer.php +++ b/src/Glpi/Form/Export/Serializer/FormSerializer.php @@ -39,8 +39,10 @@ use Glpi\DBAL\JsonFieldInterface; use Glpi\Form\AccessControl\FormAccessControl; use Glpi\Form\Comment; +use Glpi\Form\Destination\FormDestination; use Glpi\Form\Export\Context\DatabaseMapper; use Glpi\Form\Export\Context\ConfigWithForeignKeysInterface; +use Glpi\Form\Export\Context\ForeignKey\QuestionForeignKeyHandler; use Glpi\Form\Export\Result\ExportResult; use Glpi\Form\Export\Result\ImportError; use Glpi\Form\Export\Result\ImportResult; @@ -48,6 +50,7 @@ use Glpi\Form\Export\Result\ImportResultPreview; use Glpi\Form\Export\Specification\AccesControlPolicyContentSpecification; use Glpi\Form\Export\Specification\CommentContentSpecification; +use Glpi\Form\Export\Specification\DestinationContentSpecification; use Glpi\Form\Export\Specification\ExportContentSpecification; use Glpi\Form\Export\Specification\FormContentSpecification; use Glpi\Form\Export\Specification\QuestionContentSpecification; @@ -213,6 +216,7 @@ private function exportFormToSpec(Form $form, int $form_export_id): FormContentS $form_spec = $this->exportComments($form, $form_spec); $form_spec = $this->exportQuestions($form, $form_spec); $form_spec = $this->exportAccesControlPolicies($form, $form_spec); + $form_spec = $this->exportDestinations($form, $form_spec); return $form_spec; } @@ -247,6 +251,7 @@ private function doImportFormFormSpecs( $form = $this->importComments($form, $form_spec); $form = $this->importQuestions($form, $form_spec, $mapper); $form = $this->importAccessControlPolicices($form, $form_spec, $mapper); + $form = $this->importDestinations($form, $form_spec, $mapper); return $form; } @@ -508,9 +513,16 @@ private function importQuestions( 'forms_sections_id' => $section->fields['id'], ]); - if (!$id) { + if (!$id || $question->getFromDB($id) === false) { throw new RuntimeException("Failed to create question"); } + + // Questions can be required for other items, so we need to map them + $mapper->addMappedItem( + Question::class, + $question->getUniqueIDInForm(), + $id + ); } // Reload form to clear lazy loaded data @@ -598,4 +610,86 @@ private function importAccessControlPolicices( $form->getFromDB($form->getID()); return $form; } + + private function exportDestinations( + Form $form, + FormContentSpecification $form_spec, + ): FormContentSpecification { + foreach ($form->getDestinations() as $destination) { + $destination_spec = new DestinationContentSpecification(); + $destination_spec->itemtype = $destination->fields['itemtype']; + $destination_spec->name = $destination->fields['name']; + $destination_spec->config = $destination->getConfig(); + + $config = $destination->getConfig(); + foreach ($config as $field_key => $field_config_data) { + $field = (new $destination->fields['itemtype']())->getConfigurableFieldByKey($field_key); + if ($field === null) { + continue; + } + + $field_config_class = $field->getConfigClass(); + $field_config = $field_config_class::jsonDeserialize($field_config_data); + if ($field_config instanceof ConfigWithForeignKeysInterface) { + $requirements = $this->extractDataRequirementsFromSerializedJsonConfig( + $field_config::listForeignKeysHandlers($destination_spec), + $field_config_data + ); + array_push($form_spec->data_requirements, ...$requirements); + + $destination_spec->config[$field_key] = $this->replaceForeignKeysByNameInSerializedJsonConfig( + $field_config::listForeignKeysHandlers($destination_spec), + $field_config_data + ); + } + } + + $form_spec->destinations[] = $destination_spec; + } + + return $form_spec; + } + + private function importDestinations( + Form $form, + FormContentSpecification $form_spec, + DatabaseMapper $mapper, + ): Form { + foreach ($form_spec->destinations as $destination_spec) { + $config = $destination_spec->config; + foreach ($config as $field_key => $field_config_data) { + $field = (new $destination_spec->itemtype())->getConfigurableFieldByKey($field_key); + if ($field === null) { + continue; + } + + $field_config_class = $field->getConfigClass(); + if (is_a($field_config_class, ConfigWithForeignKeysInterface::class, true)) { + $field_config_data = $this->replaceNamesByForeignKeysInSerializedJsonConfig( + $field_config_class::listForeignKeysHandlers($destination_spec), + $field_config_data, + $mapper + ); + $config[$field_key] = $field_config_data; + } + } + + $destination = new FormDestination(); + $id = $destination->add([ + '_from_import' => true, + 'itemtype' => $destination_spec->itemtype, + 'name' => $destination_spec->name, + 'config' => $config, + Form::getForeignKeyField() => $form->getID(), + ]); + + if (!$id) { + throw new RuntimeException("Failed to create destination"); + } + } + + // Reload form to clear lazy loaded data + $form->getFromDB($form->getID()); + return $form; + } } diff --git a/src/Glpi/Form/Export/Specification/DestinationContentSpecification.php b/src/Glpi/Form/Export/Specification/DestinationContentSpecification.php new file mode 100644 index 00000000000..37977a3a82c --- /dev/null +++ b/src/Glpi/Form/Export/Specification/DestinationContentSpecification.php @@ -0,0 +1,43 @@ +. + * + * --------------------------------------------------------------------- + */ + +namespace Glpi\Form\Export\Specification; + +final class DestinationContentSpecification implements ContentSpecificationInterface +{ + public string $itemtype; + public string $name; + public array $config; +} diff --git a/src/Glpi/Form/Export/Specification/FormContentSpecification.php b/src/Glpi/Form/Export/Specification/FormContentSpecification.php index 740d30f8083..b64f9693a85 100644 --- a/src/Glpi/Form/Export/Specification/FormContentSpecification.php +++ b/src/Glpi/Form/Export/Specification/FormContentSpecification.php @@ -55,6 +55,9 @@ final class FormContentSpecification /** @var AccesControlPolicyContentSpecification[] $policies */ public array $policies = []; + /** @var DestinationContentSpecification[] $destinations */ + public array $destinations = []; + /** @var DataRequirementSpecification[] $data_requirements */ public array $data_requirements = []; diff --git a/src/Glpi/Form/Question.php b/src/Glpi/Form/Question.php index fa924a5d0aa..80c4529cf37 100644 --- a/src/Glpi/Form/Question.php +++ b/src/Glpi/Form/Question.php @@ -61,6 +61,12 @@ public static function getTypeName($nb = 0) return _n('Question', 'Questions', $nb); } + #[Override] + public function isEntityAssign() + { + return false; + } + #[Override] public function post_addItem() { @@ -129,6 +135,15 @@ public function getEndUserInputName(): string return (new EndUserInputNameProvider())->getEndUserInputName($this); } + public function getUniqueIDInForm(): string + { + return sprintf( + "%s-%s", + $this->getItem()->fields['rank'], + $this->fields['rank'] + ); + } + #[Override] public function prepareInputForAdd($input) { diff --git a/src/Glpi/Helpdesk/DefaultDataManager.php b/src/Glpi/Helpdesk/DefaultDataManager.php index 19479312d4d..b23f4fcefae 100644 --- a/src/Glpi/Helpdesk/DefaultDataManager.php +++ b/src/Glpi/Helpdesk/DefaultDataManager.php @@ -46,6 +46,7 @@ use Glpi\Form\Destination\CommonITILField\ITILActorFieldConfig; use Glpi\Form\Destination\CommonITILField\ITILActorFieldStrategy; use Glpi\Form\Destination\CommonITILField\ObserverField; +use Glpi\Form\Destination\CommonITILField\ObserverFieldConfig; use Glpi\Form\Destination\CommonITILField\RequestTypeField; use Glpi\Form\Destination\CommonITILField\RequestTypeFieldConfig; use Glpi\Form\Destination\CommonITILField\RequestTypeFieldStrategy; @@ -202,7 +203,7 @@ private function createIncidentForm(): Form ))->jsonSerialize(), // Set last valid answer as observer - ObserverField::getKey() => (new ITILActorFieldConfig( + ObserverField::getKey() => (new ObserverFieldConfig( strategy: ITILActorFieldStrategy::LAST_VALID_ANSWER, ))->jsonSerialize(), ]; @@ -265,7 +266,7 @@ private function createRequestForm(): void ))->jsonSerialize(), // Set last valid answer as observer - ObserverField::getKey() => (new ITILActorFieldConfig( + ObserverField::getKey() => (new ObserverFieldConfig( strategy: ITILActorFieldStrategy::LAST_VALID_ANSWER, ))->jsonSerialize(), ]; diff --git a/tests/src/FormTesterTrait.php b/tests/src/FormTesterTrait.php index 9a02d666c7b..663cb503281 100644 --- a/tests/src/FormTesterTrait.php +++ b/tests/src/FormTesterTrait.php @@ -49,6 +49,7 @@ use Glpi\Session\SessionInfo; use Glpi\Tests\FormBuilder; use Profile; +use Session; use Ticket; use User; @@ -429,11 +430,12 @@ private function exportAndImportForm(Form $form): Form { // Export and import process $json = $this->exportForm($form); - $form_copy = $this->importForm( - $json, - new DatabaseMapper([$this->getTestRootEntity(only_id: true)]), - [], - ); + $active_entities = Session::getActiveEntities(); + $mapper = !empty($active_entities) + ? new DatabaseMapper($active_entities) + : new DatabaseMapper([$this->getTestRootEntity(only_id: true)]); + + $form_copy = $this->importForm($json, $mapper, []); // Make sure it was not the same form object that was returned. $this->assertNotEquals($form_copy->getId(), $form->getId());