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
22 changes: 17 additions & 5 deletions inc/destinationfield.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,25 @@ public function applyConfiguratedValueToInputUsingAnswers(
$field_name = $field->fields['name'];
}

$raw_answer = $answer->getRawAnswer();
$is_multiple = (bool) $field->fields['multiple'];
if (!$is_multiple && $question->getQuestionType() instanceof PluginFieldsQuestionType) {
$extra = json_decode($question->fields['extra_data'] ?? '{}', true) ?? [];
$is_multiple = (bool) ($extra[PluginFieldsQuestionTypeExtraDataConfig::IS_MULTIPLE] ?? false);
}

if ($field->fields['type'] == 'glpi_item') {
$input[sprintf('itemtype_%s', $field_name)] = $answer->getRawAnswer()['itemtype'];
$input[sprintf('items_id_%s', $field_name)] = $answer->getRawAnswer()['items_id'];
} elseif ($field->fields['type'] == 'dropdown') {
$input[$field_name] = $answer->getRawAnswer()['items_id'];
$input[sprintf('itemtype_%s', $field_name)] = $raw_answer['itemtype'];
$input[sprintf('items_id_%s', $field_name)] = $raw_answer['items_id'];
} elseif (str_starts_with((string) $field->fields['type'], 'dropdown')) {
$items_id = $raw_answer['items_id'] ?? $raw_answer;
if ($is_multiple && is_array($items_id)) {
$input[$field_name] = json_encode(array_values(array_map(intval(...), $items_id)));
} else {
$input[$field_name] = $items_id;
}
} else {
$input[$field_name] = $value ?? $answer->getRawAnswer();
$input[$field_name] = $value ?? $raw_answer;
}
}
}
Expand Down
92 changes: 88 additions & 4 deletions inc/questiontype.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use Glpi\DBAL\JsonFieldInterface;
use Glpi\Form\Condition\ConditionHandler\ItemAsTextConditionHandler;
use Glpi\Form\Condition\ConditionHandler\ItemConditionHandler;
use Glpi\Form\Condition\ConditionHandler\MultipleChoiceFromValuesConditionHandler;
use Glpi\Form\Form;
use Glpi\Form\Migration\FormQuestionDataConverterInterface;
use Glpi\Form\Question;
Expand Down Expand Up @@ -85,6 +86,41 @@ public function formatDefaultValueForDB(mixed $value): string
return json_encode($value);
}

#[Override]
public function prepareEndUserAnswer(Question $question, mixed $answer): mixed
{
if ($this->isMultipleForQuestion($question) && (isset($answer['items_id']) && is_array($answer['items_id']))) {
$answer['items_id'] = array_values(array_map(intval(...), $answer['items_id']));
}

return $answer;
}

#[Override]
public function renderAdministrationOptionsTemplate(?Question $question): string
{
$is_multiple = $this->isMultipleForQuestion($question);

$template = <<<TWIG
<div class="d-flex gap-2">
<label class="form-check form-switch mb-0">
<input type="hidden" name="is_multiple" value="0"
data-glpi-form-editor-specific-question-extra-data>
<input class="form-check-input" type="checkbox" name="is_multiple"
value="1" {{ is_multiple ? 'checked' : '' }}
data-glpi-form-editor-specific-question-extra-data>
<span class="form-check-label">{{ label }}</span>
</label>
</div>
TWIG;

$twig = TemplateRenderer::getInstance();
return $twig->renderFromStringTemplate($template, [
'is_multiple' => $is_multiple,
'label' => __('Allow multiple options'),
]);
}

#[Override]
public function validateExtraDataInput(array $input): bool
{
Expand Down Expand Up @@ -134,14 +170,17 @@ public function renderAdministrationTemplate(?Question $question): string
$default_value = json_decode($question->fields['default_value'], true);
}

$field_data = $current_field->fields;
$field_data['multiple'] = $this->isMultipleForQuestion($question) ? 1 : 0;

$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,
'field' => $field_data,
]);
}

Expand Down Expand Up @@ -181,10 +220,13 @@ public function renderEndUserTemplate(Question $question): string
}
}

$field_data = $current_field->fields;
$field_data['multiple'] = $this->isMultipleForQuestion($question) ? 1 : 0;

$twig = TemplateRenderer::getInstance();
return $twig->render('@fields/question_type_end_user.html.twig', [
'question' => $question,
'field' => $current_field->fields,
'field' => $field_data,
'default_value' => $default_value,
'item' => new Form(),
'itemtype' => $itemtype,
Expand Down Expand Up @@ -312,7 +354,6 @@ public function getConditionHandlers(
return parent::getConditionHandlers($question_config);
}

// If the question is configured with a dropdown field, we add condition handlers to handle item and item as text conditions on the dropdown options
$field = PluginFieldsField::getById($question_config->getFieldId());
if ($field && str_starts_with((string) $field->fields['type'], 'dropdown')) {
if ($field->fields['type'] == 'dropdown') {
Expand All @@ -330,11 +371,29 @@ public function getConditionHandlers(
new ItemAsTextConditionHandler($itemtype),
],
);

if ($field->fields['multiple'] || $question_config->isMultiple()) {
$condition_handlers[] = new MultipleChoiceFromValuesConditionHandler(
$this->getDropdownValuesForCondition($itemtype),
);
}
}

return $condition_handlers;
}

private function getDropdownValuesForCondition(string $itemtype): array
{
$values = [];
$item = new $itemtype();
$rows = $item->find([], 'name');
foreach ($rows as $row) {
$values[(string) $row['id']] = $row['name'];
}

return $values;
}

/**
* Retrieve the default value block from the question's extra data
*
Expand Down Expand Up @@ -375,6 +434,31 @@ public function getDefaultValueFieldId(?Question $question): ?int
return $config->getFieldId();
}

private function getFieldForQuestion(Question $question): ?PluginFieldsField
{
$field_id = $this->getDefaultValueFieldId($question);
if ($field_id === null) {
return null;
}
return PluginFieldsField::getById($field_id) ?: null;
}

private function isMultipleForQuestion(?Question $question): bool
{
if (!$question instanceof Question) {
return false;
}

/** @var ?PluginFieldsQuestionTypeExtraDataConfig $config */
$config = $this->getExtraDataConfig(json_decode($question->fields['extra_data'], true) ?? []);
if ($config !== null && $config->isMultiple()) {
return true;
}

$field = $this->getFieldForQuestion($question);
return $field !== null && (bool) $field->fields['multiple'];
}

private function getAvailableBlocks(): array
{
$field_container = new PluginFieldsContainer();
Expand All @@ -391,7 +475,7 @@ private function getAvailableBlocks(): array

$result = $field_container->find([
'is_active' => 1,
'type' => 'dom',
'type' => ['dom', 'tab'],
'OR' => [
['itemtypes' => ['LIKE', '%\"Ticket\"%']],
['itemtypes' => ['LIKE', '%\"Change\"%']],
Expand Down
10 changes: 10 additions & 0 deletions inc/questiontypeextradataconfig.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@ class PluginFieldsQuestionTypeExtraDataConfig implements JsonFieldInterface

public const FIELD_ID = "field_id";

public const IS_MULTIPLE = "is_multiple";

public function __construct(
private readonly ?int $block_id = null,
private readonly ?int $field_id = null,
private readonly bool $is_multiple = false,
) {}

#[Override]
Expand All @@ -48,6 +51,7 @@ public static function jsonDeserialize(array $data): self
return new self(
block_id: $data[self::BLOCK_ID] ?? null,
field_id: $data[self::FIELD_ID] ?? null,
is_multiple: (bool) ($data[self::IS_MULTIPLE] ?? false),
);
}

Expand All @@ -57,6 +61,7 @@ public function jsonSerialize(): array
return [
self::BLOCK_ID => $this->block_id,
self::FIELD_ID => $this->field_id,
self::IS_MULTIPLE => $this->is_multiple,
];
}

Expand All @@ -69,4 +74,9 @@ public function getFieldId(): ?int
{
return $this->field_id;
}

public function isMultiple(): bool
{
return $this->is_multiple;
}
}
8 changes: 8 additions & 0 deletions templates/fields.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@
{% set dropdown_options = {'entity': item.getEntityID()} %}
{% if field['multiple'] %}
{% set dropdown_options = dropdown_options|merge({'multiple': true}) %}
{% if value is iterable %}
{% set dropdown_options = dropdown_options|merge({'values': value}) %}
{% set value = '' %}
{% endif %}
{% endif %}
{% if item.isRecursive() %}
{% set dropdown_options = dropdown_options|merge({'entity_sons': true}) %}
Expand All @@ -168,6 +172,10 @@
{% endif %}
{% if field['multiple'] %}
{% set dropdown_options = dropdown_options|merge({'multiple': true}) %}
{% if value is iterable %}
{% set dropdown_options = dropdown_options|merge({'values': value}) %}
{% set value = '' %}
{% endif %}
{% endif %}
{{ macros.dropdownField(field['dropdown_class'], input_name, value, label, field_options|merge(dropdown_options|default({}))) }}

Expand Down