Skip to content

Commit a75f973

Browse files
authored
feat(forms): Validation conditions for question answers
1 parent 95db364 commit a75f973

31 files changed

+1814
-142
lines changed

.phpstan-baseline.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5002,13 +5002,13 @@
50025002
$ignoreErrors[] = [
50035003
'message' => '#^Function json_decode is unsafe to use\\. It can return FALSE instead of throwing an exception\\. Please add \'use function Safe\\\\json_decode;\' at the beginning of the file to use the variant provided by the \'thecodingmachine/safe\' library\\.$#',
50045004
'identifier' => 'theCodingMachineSafe.function',
5005-
'count' => 4,
5005+
'count' => 5,
50065006
'path' => __DIR__ . '/src/Glpi/Form/Question.php',
50075007
];
50085008
$ignoreErrors[] = [
50095009
'message' => '#^Function json_encode is unsafe to use\\. It can return FALSE instead of throwing an exception\\. Please add \'use function Safe\\\\json_encode;\' at the beginning of the file to use the variant provided by the \'thecodingmachine/safe\' library\\.$#',
50105010
'identifier' => 'theCodingMachineSafe.function',
5011-
'count' => 3,
5011+
'count' => 5,
50125012
'path' => __DIR__ . '/src/Glpi/Form/Question.php',
50135013
];
50145014
$ignoreErrors[] = [

css/includes/components/form/_form-editor.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ input.value-selector {
394394
}
395395
}
396396

397-
.visibility-dropdown-card {
397+
.visibility-dropdown-card, .validation-dropdown-card {
398398
min-width: 700px;
399399
width: fit-content;
400400

install/migrations/update_10.0.x_to_11.0.0/form.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,28 @@
355355
);
356356
}
357357

358+
if (!$DB->fieldExists('glpi_forms_questions', 'validation_strategy')) {
359+
$migration->addField(
360+
'glpi_forms_questions',
361+
'validation_strategy',
362+
"varchar(30) NOT NULL DEFAULT ''",
363+
[
364+
'after' => 'conditions',
365+
]
366+
);
367+
}
368+
369+
if (!$DB->fieldExists('glpi_forms_questions', 'validation_conditions')) {
370+
$migration->addField(
371+
'glpi_forms_questions',
372+
'validation_conditions',
373+
"JSON NOT NULL",
374+
[
375+
'after' => 'validation_strategy',
376+
]
377+
);
378+
}
379+
358380
// Add rights for the forms object
359381
$migration->addRight("form", ALLSTANDARDRIGHT, ['config' => UPDATE]);
360382

install/mysql/glpi-empty.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9555,6 +9555,8 @@ CREATE TABLE `glpi_forms_questions` (
95559555
`extra_data` text COMMENT 'JSON - Extra configuration field(s) depending on the questions type',
95569556
`visibility_strategy` varchar(30) NOT NULL DEFAULT '',
95579557
`conditions` JSON NOT NULL,
9558+
`validation_strategy` varchar(30) NOT NULL DEFAULT '',
9559+
`validation_conditions` JSON NOT NULL,
95589560
PRIMARY KEY (`id`),
95599561
UNIQUE KEY `uuid` (`uuid`),
95609562
KEY `name` (`name`),

js/modules/Forms/ConditionEditorController.js renamed to js/modules/Forms/BaseConditionEditorController.js

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,9 @@
3030
* ---------------------------------------------------------------------
3131
*/
3232

33-
34-
export class GlpiFormConditionEditorController
35-
{
33+
export class BaseConditionEditorController {
3634
/**
37-
* Target containerthat will display the condition editor
35+
* Target container that will display the condition editor
3836
* @type {HTMLElement}
3937
*/
4038
#container;
@@ -63,8 +61,10 @@ export class GlpiFormConditionEditorController
6361
/** @type {?string} */
6462
#item_type;
6563

66-
constructor(container, item_uuid, item_type, forms_sections, form_questions, form_comments)
67-
{
64+
/** @type {string} */
65+
#editorEndpoint;
66+
67+
constructor(container, item_uuid, item_type, forms_sections, form_questions, form_comments, editorEndpoint) {
6868
this.#container = container;
6969
if (this.#container.dataset.glpiConditionsEditorContainer === undefined) {
7070
console.error(this.#container); // Help debugging by printing the node.
@@ -75,6 +75,9 @@ export class GlpiFormConditionEditorController
7575
this.#item_uuid = item_uuid;
7676
this.#item_type = item_type;
7777

78+
// Set the editor endpoint URL
79+
this.#editorEndpoint = editorEndpoint;
80+
7881
// Load form sections
7982
this.#form_sections = forms_sections;
8083

@@ -94,8 +97,7 @@ export class GlpiFormConditionEditorController
9497
}
9598
}
9699

97-
async renderEditor()
98-
{
100+
async renderEditor() {
99101
const data = this.#computeData();
100102
await this.#doRenderEditor(data);
101103
}
@@ -104,32 +106,28 @@ export class GlpiFormConditionEditorController
104106
* In a dynamic environement such as the form editor, it might be necessary
105107
* to redefine the known list of available sections.
106108
*/
107-
setFormSections(form_sections)
108-
{
109+
setFormSections(form_sections) {
109110
this.#form_sections = form_sections;
110111
}
111112

112113
/**
113114
* In a dynamic environement such as the form editor, it might be necessary
114115
* to redefine the known list of available questions.
115116
*/
116-
setFormQuestions(form_questions)
117-
{
117+
setFormQuestions(form_questions) {
118118
this.#form_questions = form_questions;
119119
}
120120

121121
/**
122122
* In a dynamic environement such as the form editor, it might be necessary
123123
* to redefine the known list of available comments.
124124
*/
125-
setFormComments(form_comments)
126-
{
125+
setFormComments(form_comments) {
127126
this.#form_comments = form_comments;
128127
}
129128

130-
async #doRenderEditor(data)
131-
{
132-
const url = `${CFG_GLPI.root_doc}/Form/Condition/Editor`;
129+
async #doRenderEditor(data) {
130+
const url = this.#editorEndpoint;
133131
const content = await $.post(url, {
134132
form_data: data,
135133
});
@@ -138,8 +136,7 @@ export class GlpiFormConditionEditorController
138136
$(this.#container.querySelector('[data-glpi-conditions-editor]')).html(content);
139137
}
140138

141-
#initEventHandlers()
142-
{
139+
#initEventHandlers() {
143140
// Handle add and delete conditions
144141
this.#container.addEventListener('click', (e) => {
145142
const target = e.target;
@@ -195,24 +192,21 @@ export class GlpiFormConditionEditorController
195192
}
196193
}
197194

198-
async #addNewEmptyCondition()
199-
{
195+
async #addNewEmptyCondition() {
200196
const data = this.#computeData();
201197
data.conditions.push({'item': ''});
202198
await this.#doRenderEditor(data);
203199
}
204200

205-
async #deleteCondition(condition_index)
206-
{
201+
async #deleteCondition(condition_index) {
207202
const data = this.#computeData();
208203
data.conditions = data.conditions.filter((_condition, index) => {
209204
return index != condition_index;
210205
});
211206
await this.#doRenderEditor(data);
212207
}
213208

214-
#computeData()
215-
{
209+
#computeData() {
216210
return {
217211
sections: this.#form_sections,
218212
questions: this.#form_questions,
@@ -223,8 +217,7 @@ export class GlpiFormConditionEditorController
223217
};
224218
}
225219

226-
#computeDefinedConditions()
227-
{
220+
#computeDefinedConditions() {
228221
const conditions_data = [];
229222
const conditions = this.#container.querySelectorAll(
230223
'[data-glpi-conditions-editor-condition]'
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* ---------------------------------------------------------------------
3+
*
4+
* GLPI - Gestionnaire Libre de Parc Informatique
5+
*
6+
* http://glpi-project.org
7+
*
8+
* @copyright 2015-2025 Teclib' and contributors.
9+
* @licence https://www.gnu.org/licenses/gpl-3.0.html
10+
*
11+
* ---------------------------------------------------------------------
12+
*
13+
* LICENSE
14+
*
15+
* This file is part of GLPI.
16+
*
17+
* This program is free software: you can redistribute it and/or modify
18+
* it under the terms of the GNU General Public License as published by
19+
* the Free Software Foundation, either version 3 of the License, or
20+
* (at your option) any later version.
21+
*
22+
* This program is distributed in the hope that it will be useful,
23+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
24+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25+
* GNU General Public License for more details.
26+
*
27+
* You should have received a copy of the GNU General Public License
28+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
29+
*
30+
* ---------------------------------------------------------------------
31+
*/
32+
33+
import { BaseConditionEditorController } from './BaseConditionEditorController.js';
34+
35+
export class GlpiFormConditionValidationEditorController extends BaseConditionEditorController {
36+
constructor(container, item_uuid, item_type, forms_sections, form_questions, form_comments) {
37+
super(
38+
container,
39+
item_uuid,
40+
item_type,
41+
forms_sections,
42+
form_questions,
43+
form_comments,
44+
`${CFG_GLPI.root_doc}/Form/Condition/Validation/Editor`
45+
);
46+
}
47+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* ---------------------------------------------------------------------
3+
*
4+
* GLPI - Gestionnaire Libre de Parc Informatique
5+
*
6+
* http://glpi-project.org
7+
*
8+
* @copyright 2015-2025 Teclib' and contributors.
9+
* @licence https://www.gnu.org/licenses/gpl-3.0.html
10+
*
11+
* ---------------------------------------------------------------------
12+
*
13+
* LICENSE
14+
*
15+
* This file is part of GLPI.
16+
*
17+
* This program is free software: you can redistribute it and/or modify
18+
* it under the terms of the GNU General Public License as published by
19+
* the Free Software Foundation, either version 3 of the License, or
20+
* (at your option) any later version.
21+
*
22+
* This program is distributed in the hope that it will be useful,
23+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
24+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25+
* GNU General Public License for more details.
26+
*
27+
* You should have received a copy of the GNU General Public License
28+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
29+
*
30+
* ---------------------------------------------------------------------
31+
*/
32+
33+
import { BaseConditionEditorController } from './BaseConditionEditorController.js';
34+
35+
export class GlpiFormConditionVisibilityEditorController extends BaseConditionEditorController {
36+
constructor(container, item_uuid, item_type, forms_sections, form_questions, form_comments) {
37+
super(
38+
container,
39+
item_uuid,
40+
item_type,
41+
forms_sections,
42+
form_questions,
43+
form_comments,
44+
`${CFG_GLPI.root_doc}/Form/Condition/Visibility/Editor`
45+
);
46+
}
47+
}

0 commit comments

Comments
 (0)