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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Fixed

- Fix value mapping between a multiple dropdown field and it's destination in a form

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Fix value mapping between a multiple dropdown field and it's destination in a form
- Fix value mapping between a multiple dropdown field and its destination in a form

- Fix display width for rich text fields
- Fix massive action update on CustomAssets
- Fix mandatory fields on a Tab block not being enforced when updating an item.
Expand Down
7 changes: 7 additions & 0 deletions inc/destinationfield.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ public function applyConfiguratedValueToInputUsingAnswers(
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 (str_starts_with((string) $field->fields['type'], 'dropdown') && $field->fields['multiple']) {
// Multiple dropdowns are submitted as an array of ids
$raw_ids = (array) ($answer->getRawAnswer()['items_id'] ?? []);
$input[$field_name] = array_values(array_filter(
array_map(intval(...), $raw_ids),
fn(int $id) => $id > 0,
));
} elseif (str_starts_with((string) $field->fields['type'], 'dropdown')) {
$raw_id = (int) ($answer->getRawAnswer()['items_id'] ?? 0);
$input[$field_name] = ($raw_id > 0) ? $raw_id : null;
Expand Down
4 changes: 4 additions & 0 deletions inc/questiontype.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ public function formatRawAnswer(mixed $answer, Question $question): string
return '';
}

if (is_array($answer) && array_key_exists('items_id', $answer)) {
$answer = $answer['items_id'];
}

if (!is_array($answer)) {
$answer = [$answer];
}
Expand Down
85 changes: 84 additions & 1 deletion tests/Units/FieldDestinationFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
use Override;
use PluginFieldsContainer;
use PluginFieldsDestinationField;
use PluginFieldsDropdown;
use PluginFieldsQuestionType;
use Problem;
use Ticket;
Expand Down Expand Up @@ -120,12 +121,32 @@ private function initFieldTest(): void
]);
$this->fields[] = $this->createField([
'label' => 'Location Field',
'type' => 'dropdown-Location',
'type' => 'dropdown-' . Location::class,
PluginFieldsContainer::getForeignKeyField() => $this->blocks[Ticket::class]->getID(),
'ranking' => 3,
'is_active' => 1,
'is_readonly' => 0,
]);
$this->fields[] = $this->createField([
'label' => 'Multiple dropdown',
'type' => 'dropdown',
'multiple' => 1,
'default_value' => [],
PluginFieldsContainer::getForeignKeyField() => $this->blocks[Ticket::class]->getID(),
'ranking' => 4,
'is_active' => 1,
'is_readonly' => 0,
], ['default_value']);
$this->fields[] = $this->createField([
'label' => 'Multiple locations',
'type' => 'dropdown-' . Location::class,
'multiple' => 1,
'default_value' => [],
PluginFieldsContainer::getForeignKeyField() => $this->blocks[Ticket::class]->getID(),
'ranking' => 5,
'is_active' => 1,
'is_readonly' => 0,
], ['default_value']);
}

public function setUp(): void
Expand Down Expand Up @@ -254,6 +275,68 @@ public function testDestinationWithLocationAdditonalFields(): void
$location->delete($location->fields, true);
}

public function testDestinationWithMultipleDropdownAdditionalFields(): void
{
$this->login();

$dropdown_field = $this->fields[5];
$locations_field = $this->fields[6];

$form = $this->createForm(
(new FormBuilder())
->addQuestion("Multiple dropdown", PluginFieldsQuestionType::class, extra_data: json_encode([
'block_id' => $this->blocks[Ticket::class]->getID(),
'field_id' => $dropdown_field->getID(),
]))
->addQuestion("Multiple locations", PluginFieldsQuestionType::class, extra_data: json_encode([
'block_id' => $this->blocks[Ticket::class]->getID(),
'field_id' => $locations_field->getID(),
])),
);

$dropdown_class = PluginFieldsDropdown::getClassname($dropdown_field->fields['name']);
$value1 = $this->createItem($dropdown_class, ['name' => 'Value 1', 'entities_id' => 0]);
$value2 = $this->createItem($dropdown_class, ['name' => 'Value 2', 'entities_id' => 0]);

$location1 = $this->createItem(Location::class, [
'name' => 'Location 1',
'entities_id' => $this->getTestRootEntity(true),
]);
$location2 = $this->createItem(Location::class, [
'name' => 'Location 2',
'entities_id' => $this->getTestRootEntity(true),
]);

try {
$this->sendFormAndAssertITILObjectAdditionalFields(
form: $form,
config: new SimpleValueConfig(1),
answers: [
// Multiple dropdowns are submitted as an array of ids
"Multiple dropdown" => [
'itemtype' => $dropdown_class,
'items_id' => [(string) $value1->getID(), (string) $value2->getID()],
],
"Multiple locations" => [
'itemtype' => Location::class,
'items_id' => [(string) $location1->getID(), (string) $location2->getID()],
],
],
expected_field_values: [
Ticket::class => [
'plugin_fields_' . $dropdown_field->fields['name'] . 'dropdowns_id' => json_encode([$value1->getID(), $value2->getID()]),
$locations_field->fields['name'] => json_encode([$location1->getID(), $location2->getID()]),
],
],
);
} finally {
// delete created items for another run
foreach ([$value1, $value2, $location1, $location2] as $item) {
$item->delete($item->fields, true);
}
}
}

#[Override]
public static function provideConvertFieldConfigFromFormCreator(): iterable
{
Expand Down
54 changes: 54 additions & 0 deletions tests/Units/FieldQuestionTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@
use Glpi\Form\Condition\Type;
use Glpi\Form\Condition\ValueOperator;
use Glpi\Form\Condition\VisibilityStrategy;
use Glpi\Form\Question;
use Glpi\Form\QuestionType\QuestionTypeShortText;
use Glpi\Form\QuestionType\QuestionTypesManager;
use Glpi\Tests\FormBuilder;
use GlpiPlugin\Field\Tests\QuestionTypeTestCase;
use Location;
use LogicException;
use PluginFieldsContainer;
use PluginFieldsDropdown;
Expand Down Expand Up @@ -188,6 +190,58 @@ public function testFieldsQuestionSubmitEmptyDropdown(): void
]);
}

public function testFormatRawAnswerForMultipleGlpiItemDropdown(): void
{
$this->login();

// Arrange: create a multiple "dropdown-Location" field
$this->fields['locations'] = $this->createField([
'label' => 'Locations',
'type' => 'dropdown-' . Location::class,
'multiple' => 1,
'default_value' => [],
PluginFieldsContainer::getForeignKeyField() => $this->block->getID(),
'ranking' => 2,
'is_active' => 1,
], ['default_value']);

$location1 = $this->createItem(Location::class, [
'name' => __FUNCTION__ . ' 1',
'entities_id' => $this->getTestRootEntity(true),
]);
$location2 = $this->createItem(Location::class, [
'name' => __FUNCTION__ . ' 2',
'entities_id' => $this->getTestRootEntity(true),
]);

$builder = new FormBuilder("My form");
$builder->addQuestion(
"Locations question",
PluginFieldsQuestionType::class,
extra_data: json_encode($this->getFieldExtraDataConfig('locations')),
);
$form = $this->createForm($builder);
$question = Question::getById($this->getQuestionId($form, "Locations question"));

try {
// Act: format the answer as submitted by the end user template
$formatted = (new PluginFieldsQuestionType())->formatRawAnswer([
'itemtype' => Location::class,
'items_id' => [(string) $location1->getID(), (string) $location2->getID()],
], $question);

// Assert: both location names are displayed
$this->assertEquals(
$location1->fields['name'] . ', ' . $location2->fields['name'],
$formatted,
);
} finally {
// delete locations for another run
$location1->delete($location1->fields, true);
$location2->delete($location2->fields, true);
}
}

public function testFieldDeletionWhenUsedInForm(): void
{
$this->login();
Expand Down
Loading