From 544e097c658391ec209df24dad5d4f8822eeb721 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 16 Jun 2022 09:35:41 +0200 Subject: [PATCH] feat(form): section display mode --- css/print_form.css | 12 ++ css/styles.scss | 22 ++ inc/form.class.php | 39 +++- inc/section.class.php | 20 ++ install/mysql/plugin_formcreator_empty.sql | 1 + install/upgrade_to_2.14.php | 6 + .../components/form/fields_macros.html.twig | 21 ++ templates/pages/form.html.twig | 2 + ...orm.html.twig => userform_stack.html.twig} | 0 templates/pages/userform_wizard.html.twig | 194 ++++++++++++++++++ tests/3-unit/PluginFormcreatorForm.php | 10 + 11 files changed, 326 insertions(+), 1 deletion(-) rename templates/pages/{userform.html.twig => userform_stack.html.twig} (100%) create mode 100644 templates/pages/userform_wizard.html.twig diff --git a/css/print_form.css b/css/print_form.css index a845b4cc3c..ab9ae1f7f0 100644 --- a/css/print_form.css +++ b/css/print_form.css @@ -133,3 +133,15 @@ label[for] { height:16px; width:16px; } + +#plugin_formcreator_form.plugin_formcreator_form li.plugin_formcreator_section { + position: inherit; + top: inherit; + left: inherit; + display: inherit !important; +} + +#plugin_formcreator_form.plugin_formcreator_form li.plugin_formcreator_section .next_button, +#plugin_formcreator_form.plugin_formcreator_form li.plugin_formcreator_section .previous_button { + display: none; +} diff --git a/css/styles.scss b/css/styles.scss index e8f43ed7c7..c7c24f2e68 100644 --- a/css/styles.scss +++ b/css/styles.scss @@ -225,12 +225,34 @@ list-style-type: none; text-align: left; padding-left: 0; + overflow: hidden; } [data-itemtype = "PluginFormcreatorQuestion"]:not([hidden]), .plugin_formcreator_gap { vertical-align: top; display: inline-block; } + + li.plugin_formcreator_section { + position: relative; + width: 100%; + top: 0; + left: 0; + transition-property: left; + transition-duration: 0.3s; + } + + .plugin_formcreator_section_navigate { + padding-bottom: 8px; + .previous_button { + margin-left: 8px; + float: inherit; + } + + .next_button { + float: right; + } + } } .form_answer h1 { diff --git a/inc/form.class.php b/inc/form.class.php index ef4d8ffd27..27b56bf4f9 100644 --- a/inc/form.class.php +++ b/inc/form.class.php @@ -61,6 +61,10 @@ class PluginFormcreatorForm extends CommonDBTM implements const VALIDATION_USER = 1; const VALIDATION_GROUP = 2; + const SECTION_DISPLAY_VERTICAL_STACK = 0; + const SECTION_DISPLAY_WIZARD = 1; + const SECTION_DISPLAY_ACCORDION = 2; + public static function getEnumAccessType() { return [ self::ACCESS_PUBLIC => __('Public access', 'formcreator'), @@ -69,6 +73,14 @@ public static function getEnumAccessType() { ]; } + public static function getEnumSectionDisplayMode() { + return [ + self::SECTION_DISPLAY_VERTICAL_STACK => __('Vertical stack', 'formcreator'), + self::SECTION_DISPLAY_WIZARD => __('Wizard', 'formcreator'), + self::SECTION_DISPLAY_ACCORDION => __('Accordion', 'formcreator'), + ]; + } + public static function getEnumShowRule() : array { return PluginFormcreatorCondition::getEnumShowRule(); } @@ -958,7 +970,20 @@ public function displayUserForm() : void { } $formanswer = new PluginFormcreatorFormAnswer(); $formanswer->loadAnswersFromSession(); - TemplateRenderer::getInstance()->display('@formcreator/pages/userform.html.twig', [ + + switch ($this->fields['section_display_mode']) { + default: + $template = '@formcreator/pages/userform_stack.html.twig'; + break; + case self::SECTION_DISPLAY_WIZARD : + $template = '@formcreator/pages/userform_wizard.html.twig'; + break; + case self::SECTION_DISPLAY_ACCORDION : + $template = '@formcreator/pages/userform_acordion.html.twig'; + break; + } + + TemplateRenderer::getInstance()->display($template, [ 'item' => $this, 'options' => [ 'columns' => PluginFormcreatorSection::COLUMNS, @@ -2572,4 +2597,16 @@ public function getExtraHeader(): string { return $extra_header; } + + /** + * Show a section display mode dropdown + * + * @param string $name + * @param array $options + * @return void + */ + public static function dropdownSectionDisplayMode(string $name, array $options): void { + $modes = self::getEnumSectionDisplayMode(); + Dropdown::showFromArray($name, $modes, $options); + } } diff --git a/inc/section.class.php b/inc/section.class.php index a297e719a2..a771bb9110 100644 --- a/inc/section.class.php +++ b/inc/section.class.php @@ -449,6 +449,26 @@ public static function getSectionsFromForm($formId): \Generator { } } + /** + * Counts sections in a form + * @param int $formId ID of a form + * @return int + */ + public static function getSectionsCountFromForm($formId): int { + global $DB; + + $count = $DB->request([ + 'COUNT' => 'c', + 'FROM' => self::getTable(), + 'WHERE' => [ + 'plugin_formcreator_forms_id' => $formId + ], + 'ORDER' => 'order ASC' + ])->current(); + + return $count['c']; + } + public function showForm($ID, $options = []) { $this->initForm($ID, $options); $options['candel'] = false; diff --git a/install/mysql/plugin_formcreator_empty.sql b/install/mysql/plugin_formcreator_empty.sql index 506cc10ff0..30559e0fa0 100644 --- a/install/mysql/plugin_formcreator_empty.sql +++ b/install/mysql/plugin_formcreator_empty.sql @@ -67,6 +67,7 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_forms` ( `show_rule` int(11) NOT NULL DEFAULT '1' COMMENT 'Conditions setting to show the submit button', `formanswer_name` varchar(255) NOT NULL DEFAULT '', `is_visible` tinyint NOT NULL DEFAULT 1, + `section_display_mode` int(11) NOT NULL DEFAULT '0', `uuid` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), INDEX `entities_id` (`entities_id`), diff --git a/install/upgrade_to_2.14.php b/install/upgrade_to_2.14.php index 68c3c2d19d..8db5b81d1d 100644 --- a/install/upgrade_to_2.14.php +++ b/install/upgrade_to_2.14.php @@ -42,6 +42,7 @@ public function upgrade(Migration $migration) { $this->addRights(); $this->addPropertiesToCategories(); $this->addTargetActorUnicity(); + $this->addSectionDisplayMode(); } public function addTtoToIssues() { @@ -131,4 +132,9 @@ public function addTargetActorUnicity() { // Set unicity $this->migration->addKey($table, $unicity, 'unicity', 'UNIQUE'); } + + public function addSectionDisplayMode() { + $table = (new DBUtils())->getTableForItemType(PluginFormcreatorForm::class); + $this->migration->addField($table, 'section_display_mode', 'integer', ['after' => 'is_visible']); + } } diff --git a/templates/components/form/fields_macros.html.twig b/templates/components/form/fields_macros.html.twig index f2893c49e9..7bf797359e 100644 --- a/templates/components/form/fields_macros.html.twig +++ b/templates/components/form/fields_macros.html.twig @@ -324,3 +324,24 @@ {% endset %} {{ fields.field(name, field, label, options) }} {% endmacro %} + +{% macro dropdownSectionDisplayMode(name, value, label = '', options = {}) %} + {% import 'components/form/fields_macros.html.twig' as fields %} + {% set options = {'rand': random()}|merge(options) %} + + {% if options.disabled %} + {% set options = options|merge({specific_tags: {'disabled': 'disabled'}}) %} + {% endif %} + {% if options.fields_template.isMandatoryField(name) %} + {% set options = {'specific_tags': {'required': true}}|merge(options) %} + {% endif %} + + {% set field %} + {% do call('PluginFormcreatorForm::dropdownSectionDisplayMode', [name, { + 'value': value, + 'rand': rand, + 'width': '100%' + }|merge(options)]) %} + {% endset %} + {{ fields.field(name, field, label, options) }} +{% endmacro %} \ No newline at end of file diff --git a/templates/pages/form.html.twig b/templates/pages/form.html.twig index c5b149a2de..01f8a5c6f0 100644 --- a/templates/pages/form.html.twig +++ b/templates/pages/form.html.twig @@ -98,4 +98,6 @@ {% set visible = item.fields['is_visible'] %} {% endif %} {{ fields.dropdownYesNo('is_visible', visible, __('Visible', 'formcreator'), { 'add_field_html': tooltip }) }} + + {{ formcreatorFields.dropdownSectionDisplayMode('section_display_mode', item.fields['section_display_mode'], __('Section display mode', 'formcreator')) }} {% endblock %} diff --git a/templates/pages/userform.html.twig b/templates/pages/userform_stack.html.twig similarity index 100% rename from templates/pages/userform.html.twig rename to templates/pages/userform_stack.html.twig diff --git a/templates/pages/userform_wizard.html.twig b/templates/pages/userform_wizard.html.twig new file mode 100644 index 0000000000..c57d1c1c58 --- /dev/null +++ b/templates/pages/userform_wizard.html.twig @@ -0,0 +1,194 @@ +{# + # --------------------------------------------------------------------- + # Formcreator is a plugin which allows creation of custom forms of + # easy access. + # --------------------------------------------------------------------- + # LICENSE + # + # This file is part of Formcreator. + # + # Formcreator 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 2 of the License, or + # (at your option) any later version. + # + # Formcreator 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 Formcreator. If not, see . + # --------------------------------------------------------------------- + # @copyright Copyright © 2011 - 2021 Teclib' + # @license http://www.gnu.org/licenses/gpl.txt GPLv3+ + # @link https://github.com/pluginsGLPI/formcreator/ + # @link https://pluginsglpi.github.io/formcreator/ + # @link http://plugins.glpi-project.org/#/plugin/formcreator + # --------------------------------------------------------------------- + #} + +{% import 'components/form/fields_macros.html.twig' as fields %} + + + +
+ {% set formName = 'plugin_formcreator_form' %} +
+

+ {{ __(item.fields['name'], options.domain) }} + +

+ {% if item.fields['content'] != '' or item.getExtraHeader() != "" %} +
+ {{ __(item.fields['content'], options.domain)|safe_html }} + {{ item.getExtraHeader()|safe_html }} +
+ {% endif %} +
    + + + + {% set sections = call('PluginFormcreatorSection::getSectionsFromForm', [ + item.fields['id'] + ]) %} + {% set sectionsCount = call('PluginFormcreatorSection::getSectionsCountFromForm', [ + item.fields['id'] + ]) %} + {% set currentSection = 0 %} + {% for section in sections %} + {% set currentSection = currentSection + 1 %} + {% set sectionId = section.fields['id'] %} +
  1. +
    +

    + {% if section.fields['name'] == '' %} + ({{ sectionId }}) + {% else %} + {{ __(section.fields['name'], options.domain) }} + {% endif %} +

    +
    +
    + {% set lastQuestion = null %} + {% set questions = call('PluginFormcreatorQuestion::getQuestionsFromSection', [ + sectionId + ]) %} + {% for question in questions %} + {% if not (lastQuestion is null) %} + {% if lastQuestion.fields['row'] < question.fields['row'] %} +
    + {% else %} + {% set x = lastQuestion.fields['col'] + lastQuestion.fields['width'] %} + {% set width = question.fields['col'] - x %} + {% if x < question.fields['col'] %} +
    + {% endif %} + {% endif %} + {% endif %} + {% if not options.public or question.getSubField().isPublicFormCompatible() %} + {% set sessionData = session('formcreator') %} + {{ question.getRenderedHtml(options.domain, true, options.formanswer)|raw }} + {% endif %} + {% set lastQuestion = question %} + {% endfor %} +
    + + {% if currentSection == sectionsCount %} + {% if options.use_captcha %} + {% set captchaTime = call('time') %} + {% set catchaId = call('md5', [captchaTime ~ item.fields['id']]) %} + {% set captcha = call('PluginFormcreatorCommon::getCaptcha', [captchaId]) %} +
    +
    {{ __('Are you a robot ?', 'formcreator') }}
    +
    +
      +
    + {{ fields.textField('plugin_formcreator_captcha', '', '', { 'no_label': true}) }} + {{ fields.hiddenField('plugin_formcreator_captcha_id', captchaId) }} +
    +
    +
    +
    + {% endif %} + + {{ call('PluginFormcreatorForm_Validator::dropdownValidator', [ + item + ])|raw }} + {% endif %} + +
    + {% if currentSection > 1%} + + {% else %} + {% endif %} + {% if currentSection < sectionsCount %} + + {% else %} + {{ call('Html::submit', [ + __('Send'), + {'name': 'submit_formcreator', + 'class': 'btn btn-primary me-2 next_button', + 'icon': 'far fa-save'} + ])|raw }} + {% endif %} +
    +
  2. + {% endfor %} +
+ + + {{ fields.hiddenField('plugin_formcreator_forms_id', item.fields['id']) }} + +
+
diff --git a/tests/3-unit/PluginFormcreatorForm.php b/tests/3-unit/PluginFormcreatorForm.php index 2873fd039b..aa59c04dc2 100644 --- a/tests/3-unit/PluginFormcreatorForm.php +++ b/tests/3-unit/PluginFormcreatorForm.php @@ -106,6 +106,15 @@ public function testGetEnumAccessType() { ]); } + public function testGetEnumSectionDisplayMode() { + $output = \PluginFormcreatorForm::getEnumSectionDisplayMode(); + $this->array($output)->isEqualTo([ + \PluginFormcreatorForm::SECTION_DISPLAY_VERTICAL_STACK => __('Vertical stack', 'formcreator'), + \PluginFormcreatorForm::SECTION_DISPLAY_WIZARD => __('Wizard', 'formcreator'), + \PluginFormcreatorForm::SECTION_DISPLAY_ACCORDION => __('Accordion', 'formcreator'), + ]); + } + public function testCanCreate() { $this->login('glpi', 'glpi'); $output = \PluginFormcreatorForm::canCreate(); @@ -533,6 +542,7 @@ public function testExport() { 'profiles', 'users', 'groups', + 'section_display_mode', ]; $extraFields = [ '_entity',