Skip to content

Commit

Permalink
feat(forms): Add export and import functionality for translations
Browse files Browse the repository at this point in the history
  • Loading branch information
ccailly committed Jan 6, 2025
1 parent 1d73acf commit 2ddf4bf
Show file tree
Hide file tree
Showing 11 changed files with 508 additions and 3 deletions.
68 changes: 68 additions & 0 deletions phpunit/functional/Glpi/Form/Export/FormSerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,18 @@
use Glpi\Form\Export\Result\ImportError;
use Glpi\Form\Export\Serializer\FormSerializer;
use Glpi\Form\Form;
use Glpi\Form\FormTranslation;
use Glpi\Form\Question;
use Glpi\Form\QuestionType\QuestionTypeActorsExtraDataConfig;
use Glpi\Form\QuestionType\QuestionTypeActorsDefaultValueConfig;
use Glpi\Form\QuestionType\QuestionTypeCheckbox;
use Glpi\Form\QuestionType\QuestionTypeDropdown;
use Glpi\Form\QuestionType\QuestionTypeDropdownExtraDataConfig;
use Glpi\Form\QuestionType\QuestionTypeItemDefaultValueConfig;
use Glpi\Form\QuestionType\QuestionTypeItemExtraDataConfig;
use Glpi\Form\QuestionType\QuestionTypeItemDropdown;
use Glpi\Form\QuestionType\QuestionTypeRequester;
use Glpi\Form\QuestionType\QuestionTypeSelectableExtraDataConfig;
use Glpi\Form\QuestionType\QuestionTypeShortText;
use Glpi\Form\Section;
use Glpi\Tests\FormBuilder;
Expand Down Expand Up @@ -577,6 +580,71 @@ public function testExportAndImportDestinations(): void
);
}

public function testExportAndImportTranslations(): void
{
// Arrange: create a form with multiple blocks and sections
$builder = new FormBuilder();
$builder->addSection("My first section")
->addComment("My first comment", "My first comment in my first section")
->addQuestion(
"My text question",
QuestionTypeShortText::class,
'Test default value',
'',
'My text question description'
)
->addSection("My second section")
->addQuestion(
"My multiple choice question",
QuestionTypeCheckbox::class,
'123456789',
json_encode((new QuestionTypeSelectableExtraDataConfig([
'123456789' => 'Option 1',
'987654321' => 'Option 2',
], true))->jsonSerialize()),
);
$form = $this->createForm($builder);

// Add translations to the form
$handlers = $form->listTranslationsHandlers();
array_walk_recursive(
$handlers,
function ($handler) {
$this->addTranslationToForm(
$handler->getParentItem(),
'fr_FR',
$handler->getKey(),
$handler->getKey() . ' in fr_FR'
);

$this->addTranslationToForm(
$handler->getParentItem(),
'es_ES',
$handler->getKey(),
$handler->getKey() . ' in es_ES'
);
}
);

// Act: export and import the form
$form_copy = $this->exportAndImportForm($form);

// Assert: validate translations
$translations = FormTranslation::getTranslationsForForm($form);
$translations_copy = FormTranslation::getTranslationsForForm($form_copy);
$keys_to_exclude = ['id' => '', 'items_id' => ''];
$this->assertEquals(
array_map(
fn($translation) => array_diff_key($translation->fields, $keys_to_exclude),
$translations
),
array_map(
fn($translation) => array_diff_key($translation->fields, $keys_to_exclude),
$translations_copy
),
);
}

public function testPreviewImportWithValidForm(): void
{
// Arrange: create a valid form
Expand Down
1 change: 1 addition & 0 deletions src/Glpi/Form/BlockInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@
interface BlockInterface extends ProvideTranslationsInterface
{
public function displayBlockForEditor(): void;
public function getUniqueIDInForm(): string;
}
9 changes: 9 additions & 0 deletions src/Glpi/Form/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ public function getForm(): Form
return $this->getItem()->getItem();
}

public function getUniqueIDInForm(): string
{
return sprintf(
"%s-%s",
$this->getItem()->fields['rank'],
$this->fields['rank']
);
}

/**
* Manually update logs of the parent form item
*
Expand Down
8 changes: 7 additions & 1 deletion src/Glpi/Form/Export/Context/DatabaseMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@
namespace Glpi\Form\Export\Context;

use CommonDBTM;
use Glpi\Form\Comment;
use Glpi\Form\Export\Specification\DataRequirementSpecification;
use Glpi\Form\Question;
use Glpi\Form\Section;
use InvalidArgumentException;

final class DatabaseMapper
Expand Down Expand Up @@ -135,7 +137,11 @@ private function isValidItemtype(string $itemtype): bool

private function contextExist(string $itemtype, string $name): bool
{
if ($itemtype === Question::class) {
if (
$itemtype === Question::class
|| $itemtype === Comment::class
|| $itemtype === Section::class
) {
return true;
}

Expand Down
113 changes: 113 additions & 0 deletions src/Glpi/Form/Export/Context/ForeignKey/CommentForeignKeyHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

/**
* ---------------------------------------------------------------------
*
* GLPI - Gestionnaire Libre de Parc Informatique
*
* http://glpi-project.org
*
* @copyright 2015-2024 Teclib' and contributors.
* @copyright 2003-2014 by the INDEPNET Development Team.
* @licence https://www.gnu.org/licenses/gpl-3.0.html
*
* ---------------------------------------------------------------------
*
* LICENSE
*
* This file is part of GLPI.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* ---------------------------------------------------------------------
*/

namespace Glpi\Form\Export\Context\ForeignKey;

use Glpi\Form\Export\Context\DatabaseMapper;
use Glpi\Form\Export\Specification\DataRequirementSpecification;
use Glpi\Form\Comment;

/**
* Handle a foreign keys.
*/
final class CommentForeignKeyHandler 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 Comment();
if ($item->getFromDB($foreign_key)) {
$requirements[] = new DataRequirementSpecification(
Comment::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 Comment();
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(
Comment::class,
$serialized_data[$this->key]
);

return $serialized_data;
}

private function keyExistInSerializedData(array $serialized_data): bool
{
return isset($serialized_data[$this->key]);
}
}
113 changes: 113 additions & 0 deletions src/Glpi/Form/Export/Context/ForeignKey/SectionForeignKeyHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

/**
* ---------------------------------------------------------------------
*
* GLPI - Gestionnaire Libre de Parc Informatique
*
* http://glpi-project.org
*
* @copyright 2015-2024 Teclib' and contributors.
* @copyright 2003-2014 by the INDEPNET Development Team.
* @licence https://www.gnu.org/licenses/gpl-3.0.html
*
* ---------------------------------------------------------------------
*
* LICENSE
*
* This file is part of GLPI.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* ---------------------------------------------------------------------
*/

namespace Glpi\Form\Export\Context\ForeignKey;

use Glpi\Form\Export\Context\DatabaseMapper;
use Glpi\Form\Export\Specification\DataRequirementSpecification;
use Glpi\Form\Section;

/**
* Handle a foreign keys.
*/
final class SectionForeignKeyHandler 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 Section();
if ($item->getFromDB($foreign_key)) {
$requirements[] = new DataRequirementSpecification(
Section ::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 Section();
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(
Section::class,
$serialized_data[$this->key]
);

return $serialized_data;
}

private function keyExistInSerializedData(array $serialized_data): bool
{
return isset($serialized_data[$this->key]);
}
}
Loading

0 comments on commit 2ddf4bf

Please sign in to comment.