Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 105 additions & 13 deletions inc/questiontype.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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,
]);
}

Expand Down
25 changes: 25 additions & 0 deletions templates/question_type_embedded_administration.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{% import 'components/form/fields_macros.html.twig' as fields %}

<div class="d-flex align-items-center gap-2 w-100">
{{ 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': ''
}
}
) }}
</div>
13 changes: 9 additions & 4 deletions templates/question_type_end_user.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -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
}) %}
Expand All @@ -36,7 +38,7 @@
{% set name_suffix = is_dropdown ? '[items_id]' : '' %}

{{ fields.hiddenField(
question.getEndUserInputName() ~ '[itemtype]',
input_name ~ '[itemtype]',
itemtype
) }}

Expand All @@ -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 }}
Expand Down