Skip to content

Commit

Permalink
feat(forms): Adding file category and related sub question type
Browse files Browse the repository at this point in the history
  • Loading branch information
ccailly committed May 13, 2024
1 parent 7b436b3 commit df69399
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 16 deletions.
7 changes: 4 additions & 3 deletions src/Form/AnswersHandler/AnswersHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,12 @@ protected function createAnswserSet(
// We need to keep track of some extra data like label and type because
// the linked question might be deleted one day but the answer must still
// be readable.
$question = $questions[$question_id];
$formatted_answers[] = [
'question' => $question_id,
'value' => $answer,
'label' => $questions[$question_id]->fields['name'],
'type' => $questions[$question_id]->fields['type'],
'value' => $question->getQuestionType()->prepareEndUserAnswer($question, $answer),
'label' => $question->fields['name'],
'type' => $question->fields['type'],
];
}

Expand Down
6 changes: 3 additions & 3 deletions src/Form/Question.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public function getExtraDatas(): ?array
*
* @return Form
*/
protected function getForm(): Form
public function getForm(): Form
{
return $this->getItem()->getItem();
}
Expand Down Expand Up @@ -154,7 +154,7 @@ private function prepareInput(&$input)

if ($question_type) {
if (isset($input['default_value'])) {
$input['default_value'] = $question_type::formatDefaultValueForDB($input['default_value']);
$input['default_value'] = $question_type->formatDefaultValueForDB($input['default_value']);
}

$extra_data = $input['extra_data'] ?? [];
Expand All @@ -167,7 +167,7 @@ private function prepareInput(&$input)
}
}

$is_extra_data_valid = $question_type::validateExtraDataInput($extra_data);
$is_extra_data_valid = $question_type->validateExtraDataInput($extra_data);

if (!$is_extra_data_valid) {
throw new \InvalidArgumentException("Invalid extra data for question");
Expand Down
10 changes: 8 additions & 2 deletions src/Form/QuestionType/AbstractQuestionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,19 @@ public function __construct()
}

#[Override]
public static function formatDefaultValueForDB(mixed $value): ?string
public function formatDefaultValueForDB(mixed $value): ?string
{
return $value; // Default value is already formatted
}

#[Override]
public static function validateExtraDataInput(array $input): bool
public function prepareEndUserAnswer(Question $question, mixed $answer): mixed
{
return $answer;
}

#[Override]
public function validateExtraDataInput(array $input): bool
{
return empty($input); // No extra data by default
}
Expand Down
4 changes: 2 additions & 2 deletions src/Form/QuestionType/AbstractQuestionTypeActors.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ abstract class AbstractQuestionTypeActors extends AbstractQuestionType
abstract public function getAllowedActorTypes(): array;

#[Override]
public static function formatDefaultValueForDB(mixed $value): ?string
public function formatDefaultValueForDB(mixed $value): ?string
{
if (is_array($value)) {
return implode(',', $value);
Expand All @@ -62,7 +62,7 @@ public static function formatDefaultValueForDB(mixed $value): ?string
}

#[Override]
public static function validateExtraDataInput(array $input): bool
public function validateExtraDataInput(array $input): bool
{
$allowed_keys = [
'is_multiple_actors'
Expand Down
6 changes: 6 additions & 0 deletions src/Form/QuestionType/QuestionTypeCategory.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ enum QuestionTypeCategory: string
*/
case REQUEST_TYPE = "request_type";

/**
* Question that expect a file upload
*/
case FILE = "file";
/**
* Get category label
* @return string
Expand All @@ -83,6 +88,7 @@ public function getLabel(): string
self::ACTORS => __("Actors"),
self::URGENCY => __("Urgency"),
self::REQUEST_TYPE => __("Request type"),
self::FILE => __("File")
};
}
}
2 changes: 1 addition & 1 deletion src/Form/QuestionType/QuestionTypeDateTime.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public function isTimeEnabled(?Question $question): bool
}

#[Override]
public static function validateExtraDataInput(array $input): bool
public function validateExtraDataInput(array $input): bool
{
$allowed_keys = [
'is_default_value_current_time',
Expand Down
140 changes: 140 additions & 0 deletions src/Form/QuestionType/QuestionTypeFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?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\QuestionType;

use Document;
use Glpi\Application\View\TemplateRenderer;
use Glpi\Form\Question;
use Override;

final class QuestionTypeFile extends AbstractQuestionType
{
#[Override]
public function prepareEndUserAnswer(Question $question, mixed $answer): mixed
{
$form = $question->getForm();
$document = new Document();
$document_ids = [];
foreach ($answer as $file) {
$document_ids[] = $document->add([
'name' => sprintf('%s - %s', $form->getName(), $question->getName()),
'entities_id' => $form->getEntityID(),
'is_recursive' => $form->isRecursive(),
'_filename' => [$file],
'_prefix_filename' => [$_POST['_prefix_' . $question->getEndUserInputName()]],
]);
}

return $document_ids;
}

#[Override]
public function renderAdministrationTemplate(?Question $question): string
{
$template = <<<TWIG
{% import 'components/form/fields_macros.html.twig' as fields %}
{{ fields.fileField(
'default_value',
'',
'',
{
'init' : question is not null ? true: false,
'no_label' : true,
'full_width' : true,
}
) }}
TWIG;

$twig = TemplateRenderer::getInstance();
return $twig->renderFromStringTemplate($template, [
'question' => $question
]);
}

#[Override]
public function renderAdministrationOptionsTemplate(?Question $question): string
{
return '';
}

#[Override]
public function renderEndUserTemplate(Question $question): string
{
$template = <<<TWIG
{% import 'components/form/fields_macros.html.twig' as fields %}
{{ fields.fileField(
question.getEndUserInputName(),
"",
"",
{
'init' : true,
'no_label' : true,
'full_width' : true,
}
) }}
TWIG;

$twig = TemplateRenderer::getInstance();
return $twig->renderFromStringTemplate($template, [
'question' => $question
]);
}

#[Override]
public function renderAnswerTemplate($answer): string
{
$template = <<<TWIG
{% for document in documents %}
<div class="form-control-plaintext">
{{ document.getLink()|raw }}
</div>
{% endfor %}
TWIG;

$twig = TemplateRenderer::getInstance();
return $twig->renderFromStringTemplate($template, [
'documents' => array_map(fn($document_id) => (new Document())->getById($document_id), $answer)
]);
}

#[Override]
public function getCategory(): QuestionTypeCategory
{
return QuestionTypeCategory::FILE;
}
}
14 changes: 12 additions & 2 deletions src/Form/QuestionType/QuestionTypeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function __construct();
* @param mixed $value The default value to format.
* @return string
*/
public static function formatDefaultValueForDB(mixed $value): ?string;
public function formatDefaultValueForDB(mixed $value): ?string;

/**
* Validate the input for extra data of the question.
Expand All @@ -61,7 +61,17 @@ public static function formatDefaultValueForDB(mixed $value): ?string;
*
* @return bool
*/
public static function validateExtraDataInput(array $input): bool;
public function validateExtraDataInput(array $input): bool;

/**
* Prepare the answer for the end user.
* This method is called before saving the answer.
*
* @param Question $question The question data.
* @param mixed $answer The raw answer data.
* @return mixed The prepared answer data.
*/
public function prepareEndUserAnswer(Question $question, mixed $answer): mixed;

/**
* Render the administration template for the given question.
Expand Down
14 changes: 12 additions & 2 deletions tests/units/Glpi/Form/AnswersSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@
use Glpi\Form\Destination\FormDestinationTicket;
use Glpi\Form\QuestionType\QuestionTypeDateTime;
use Glpi\Form\QuestionType\QuestionTypeEmail;
use Glpi\Form\QuestionType\QuestionTypeFile;
use Glpi\Form\QuestionType\QuestionTypeLongText;
use Glpi\Form\QuestionType\QuestionTypeNumber;
use Glpi\Form\QuestionType\QuestionTypeRequestType;
use Glpi\Form\QuestionType\QuestionTypeShortText;
use Glpi\Form\QuestionType\QuestionTypesManager;
use Glpi\Form\QuestionType\QuestionTypeTime;
use Glpi\Form\QuestionType\QuestionTypeUrgency;
use Glpi\Tests\FormBuilder;
use Glpi\Tests\FormTesterTrait;
Expand Down Expand Up @@ -259,7 +259,16 @@ public function testShowForm(): void
->addQuestion("Assignee", QuestionTypeAssignee::class)
->addQuestion("Urgency", QuestionTypeUrgency::class)
->addQuestion("Request type", QuestionTypeRequestType::class)
->addQuestion("File", QuestionTypeFile::class)
);

// File question type requires an uploaded file
$unique_id = uniqid();
$filename = $unique_id . '-test-show-form-question-type-file.txt';
copy(__DIR__ . '/../../../fixtures/uploads/bar.txt', GLPI_TMP_DIR . '/' . $filename);
$question = \Glpi\Form\Question::getById($this->getQuestionId($form, "File"));
$_POST['_prefix_' . $question->getEndUserInputName()] = $unique_id;

$answers_set = $answers_handler->saveAnswers($form, [
$this->getQuestionId($form, "Name") => "Pierre Paul Jacques",
$this->getQuestionId($form, "Age") => 20,
Expand All @@ -282,7 +291,8 @@ public function testShowForm(): void
Supplier::getForeignKeyField() . '-1'
],
$this->getQuestionId($form, "Urgency") => 2,
$this->getQuestionId($form, "Request type") => 1
$this->getQuestionId($form, "Request type") => 1,
$this->getQuestionId($form, "File") => [$filename]
], \Session::getLoginUserID());

// Ensure we used every possible questions types
Expand Down
10 changes: 9 additions & 1 deletion tests/units/Glpi/Form/QuestionType/QuestionTypesManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ public function testGetCategories(): void
QuestionTypeCategory::DATE_AND_TIME,
QuestionTypeCategory::ACTORS,
QuestionTypeCategory::URGENCY,
QuestionTypeCategory::REQUEST_TYPE
QuestionTypeCategory::REQUEST_TYPE,
QuestionTypeCategory::FILE,
];

// Manual array comparison, `isEqualTo` doesn't seem to work properly
Expand Down Expand Up @@ -146,6 +147,13 @@ protected function testGetTypesForCategoryProvider(): iterable
new \Glpi\Form\QuestionType\QuestionTypeRequestType(),
]
];

yield [
QuestionTypeCategory::FILE,
[
new \Glpi\Form\QuestionType\QuestionTypeFile(),
]
];
}

/**
Expand Down

0 comments on commit df69399

Please sign in to comment.