diff --git a/inc/questiontype.class.php b/inc/questiontype.class.php index a38daf1b..b5232f91 100644 --- a/inc/questiontype.class.php +++ b/inc/questiontype.class.php @@ -37,11 +37,14 @@ use Glpi\Form\Question; use Glpi\Form\QuestionType\AbstractQuestionType; use Glpi\Form\QuestionType\QuestionTypeCategoryInterface; +use Glpi\Form\QuestionType\QuestionTypeEmbeddableInterface; use function Safe\json_decode; use function Safe\json_encode; -final class PluginFieldsQuestionType extends AbstractQuestionType implements FormQuestionDataConverterInterface +final class PluginFieldsQuestionType extends AbstractQuestionType implements + FormQuestionDataConverterInterface, + QuestionTypeEmbeddableInterface { #[Override] public function getCategory(): QuestionTypeCategoryInterface @@ -112,6 +115,48 @@ public function validateExtraDataInput(array $input): bool #[Override] public function renderAdministrationTemplate(?Question $question): string { + return $this->renderAdministrationTemplateWithInputNames( + $question, + field_id_input_name: 'field_id', + default_value_input_name: 'default_value', + embedded: false, + ); + } + + /** Render the complete Fields configuration inside another question type. */ + public function renderEmbeddedAdministrationTemplate( + ?Question $question, + string $extra_data_input_prefix, + ): string { + $block_id = $this->getDefaultValueBlockId($question); + $available_blocks = $this->getAvailableBlocks(); + if ($block_id === null) { + $block_id = current(array_keys($available_blocks)); + } + + $available_fields = self::getFieldsFromBlock($block_id); + $current_field_id = $this->getDefaultValueFieldId($question); + if ($current_field_id === null || !isset($available_fields[$current_field_id])) { + $current_field_id = current(array_keys($available_fields)); + } + + return TemplateRenderer::getInstance()->render('@fields/question_type_embedded_administration.html.twig', [ + 'block_input_name' => $extra_data_input_prefix . '[block_id]', + 'field_input_name' => $extra_data_input_prefix . '[field_id]', + 'selected_block_id' => $block_id, + 'selected_field_id' => $current_field_id, + 'available_blocks' => $available_blocks, + 'available_fields' => $available_fields, + ]); + } + + private function renderAdministrationTemplateWithInputNames( + + ?Question $question, + string $field_id_input_name, + string $default_value_input_name, + bool $embedded, + ): string { // Get the block_id from the question's extra data or use the first available block $block_id = $this->getDefaultValueBlockId($question); if ($block_id === null) { @@ -127,6 +172,9 @@ public function renderAdministrationTemplate(?Question $question): string } $current_field = PluginFieldsField::getById($current_field_id); + if (!$current_field) { + return ''; + } // Compute default value for the field $default_value = null; @@ -136,33 +184,75 @@ public function renderAdministrationTemplate(?Question $question): string $twig = TemplateRenderer::getInstance(); return $twig->render('@fields/question_type_administration.html.twig', [ - 'question' => $question, - 'default_value' => $default_value, - 'selected_field_id' => $current_field_id, - 'available_fields' => $available_fields, - 'item' => new Form(), - 'field' => $current_field->fields, + 'question' => $question, + 'default_value' => $default_value, + 'selected_field_id' => $current_field_id, + 'available_fields' => $available_fields, + 'item' => new Form(), + 'field' => $current_field->fields, + 'field_id_input_name' => $field_id_input_name, + 'default_value_input_name' => $default_value_input_name, + 'embedded' => $embedded, ]); } #[Override] public function renderEndUserTemplate(Question $question): string { - // Get the block_id from the question's extra data or use the first available block + return $this->renderEndUserTemplateWithInputName( + $question, + $question->getEndUserInputName(), + false, + ); + } + + /** Render the end-user control with an input name supplied by an embedding question type. */ + public function renderEmbeddedEndUserTemplate(Question $question, string $input_name): string + { + return $this->renderEndUserTemplateWithInputName($question, $input_name, true); + } + + private function renderEndUserTemplateWithInputName( + Question $question, + string $input_name, + bool $embedded, + ): string + { + // Resolve a valid block. Embedded table questions may contain an empty, + // stale or no longer accessible block_id. + $available_blocks = $this->getAvailableBlocks(); + if ($available_blocks === []) { + return ''; + } + $block_id = $this->getDefaultValueBlockId($question); - if ($block_id === null) { - $block_id = current(array_keys($this->getAvailableBlocks())); + if ( + $block_id === null + || !array_key_exists($block_id, $available_blocks) + ) { + $block_id = array_key_first($available_blocks); } $available_fields = self::getFieldsFromBlock($block_id); + if ($available_fields === []) { + return ''; + } - // Retrieve current field + // Resolve a valid field for the selected block. This also handles + // deleted fields and configurations created before automatic block + // selection was added. $current_field_id = $this->getDefaultValueFieldId($question); - if ($current_field_id === null) { - $current_field_id = current(array_keys($available_fields)); + if ( + $current_field_id === null + || !array_key_exists($current_field_id, $available_fields) + ) { + $current_field_id = array_key_first($available_fields); } $current_field = PluginFieldsField::getById($current_field_id); + if (!$current_field instanceof PluginFieldsField) { + return ''; + } // Compute default value for the field $default_value = null; @@ -188,6 +278,8 @@ public function renderEndUserTemplate(Question $question): string 'default_value' => $default_value, 'item' => new Form(), 'itemtype' => $itemtype, + 'input_name' => $input_name, + 'embedded' => $embedded, ]); } diff --git a/templates/question_type_embedded_administration.html.twig b/templates/question_type_embedded_administration.html.twig new file mode 100644 index 00000000..99c5ef9e --- /dev/null +++ b/templates/question_type_embedded_administration.html.twig @@ -0,0 +1,25 @@ +{% import 'components/form/fields_macros.html.twig' as fields %} + +
+ {{ fields.hiddenField( + block_input_name, + selected_block_id + ) }} + + {{ fields.dropdownArrayField( + field_input_name, + selected_field_id, + available_fields, + '', + { + 'no_label': true, + 'field_class': 'flex-grow-1', + 'class': 'form-select form-select-sm w-100', + 'mb': '', + 'init': true, + 'add_data_attributes': { + 'af-embedded-question-config-control': '' + } + } + ) }} +
diff --git a/templates/question_type_end_user.html.twig b/templates/question_type_end_user.html.twig index 4e583ba5..05da2d68 100644 --- a/templates/question_type_end_user.html.twig +++ b/templates/question_type_end_user.html.twig @@ -28,6 +28,8 @@ {% import 'components/form/fields_macros.html.twig' as fields %} +{% set input_name = input_name|default(question.getEndUserInputName()) %} + {% set field = field|merge({ 'default_value': default_value.items_id ?? default_value ?? field.default_value }) %} @@ -36,7 +38,7 @@ {% set name_suffix = is_dropdown ? '[items_id]' : '' %} {{ fields.hiddenField( - question.getEndUserInputName() ~ '[itemtype]', + input_name ~ '[itemtype]', itemtype ) }} @@ -48,11 +50,14 @@ true, false, { - 'input_name' : question.getEndUserInputName() ~ name_suffix, - 'full_width' : not (field.type starts with 'dropdown' or field.type == 'glpi_item'), + 'input_name' : input_name ~ name_suffix, + 'full_width' : embedded|default(false) + or not (field.type starts with 'dropdown' or field.type == 'glpi_item'), 'no_label' : true, 'mb' : '', - 'add_field_class': 'glpi-fields-plugin-question-type-glpi-item-field', + 'add_field_class': embedded|default(false) + ? 'glpi-fields-plugin-question-type-glpi-item-field w-100' + : 'glpi-fields-plugin-question-type-glpi-item-field', 'comments' : false } ])|raw }}