From 93ed0673a7913fb38bc296d02c2ae5a3929eebad Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 20 Oct 2022 13:33:06 +0200 Subject: [PATCH 001/248] docs: remove useless alpha/beta --- plugin.xml | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/plugin.xml b/plugin.xml index cb4a256bc..86e158f31 100644 --- a/plugin.xml +++ b/plugin.xml @@ -73,46 +73,6 @@ Features ~10.0 https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0/glpi-formcreator-2.13.0.tar.bz2 - - 2.13.0-rc.2 - ~10.0 - https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0-rc.2/glpi-formcreator-2.13.0-rc.2.tar.bz2 - - - 2.13.0-rc.1 - ~10.0 - https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0-rc.1/glpi-formcreator-2.13.0-rc.1.tar.bz2 - - - 2.13.0-beta.2 - ~10.0 - https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0-beta.2/glpi-formcreator-2.13.0-beta.2.tar.bz2 - - - 2.13.0-beta.1 - ~10.0 - https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0-beta.1/glpi-formcreator-2.13.0-beta.1.tar.bz2 - - - 2.13.0-alpha.4 - ~10.0 - https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0-alpha.4/glpi-formcreator-2.13.0-alpha.4.tar.bz2 - - - 2.13.0-alpha.3 - ~10.0 - https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0-alpha.3/glpi-formcreator-2.13.0-alpha.3.tar.bz2 - - - 2.13.0-alpha.2 - ~10.0 - https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0-alpha.2/glpi-formcreator-2.13.0-alpha.2.tar.bz2 - - - 2.13.0-alpha.1 - ~10.0 - https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0-alpha.1/glpi-formcreator-2.13.0-alpha.1.tar.bz2 - 2.12.5 ~9.5.5 From 26c78ca548e22107c6967070fe7df0d8500256d1 Mon Sep 17 00:00:00 2001 From: btry Date: Tue, 22 Nov 2022 12:58:55 +0100 Subject: [PATCH 002/248] docs: update versions list version 2.12.6 for GLPI 9.5.10+ --- plugin.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugin.xml b/plugin.xml index 86e158f31..ec02be4e8 100644 --- a/plugin.xml +++ b/plugin.xml @@ -73,6 +73,11 @@ Features ~10.0 https://github.com/pluginsGLPI/formcreator/releases/download/2.13.0/glpi-formcreator-2.13.0.tar.bz2 + + 2.12.6 + ~9.5.5 + https://github.com/pluginsGLPI/formcreator/releases/download/v2.12.6/glpi-formcreator-2.12.6.tar.bz2 + 2.12.5 ~9.5.5 From 949fca990dd7f19f633ea87b52fd5bcfa71de58e Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 20 Oct 2022 13:25:18 +0200 Subject: [PATCH 003/248] Revert "ci: disable tests against GLPI 10.1" This reverts commit 3d72e450399d8d5f64bc367ed3e73e48c2ef40a1. --- .github/workflows/tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dacdf04e6..cf27642de 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -43,10 +43,10 @@ jobs: php-versions: [7.4, 8.1] glpi-branch: [10.0/bugfixes] glpi-package: [10.0.tar.gz] - # include: - # - php-versions: 8.1 - # glpi-branch: main - # glpi-package: main.tar.gz + include: + - php-versions: 8.1 + glpi-branch: main + glpi-package: main.tar.gz # glpi-use-repo: true services: From 214d99eae6bfe2ba8c8af95c44cc00542b48d270 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 20 Oct 2022 13:54:43 +0200 Subject: [PATCH 004/248] build: switch to development version --- setup.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.php b/setup.php index 743f678f1..a1c1e7c57 100644 --- a/setup.php +++ b/setup.php @@ -33,11 +33,11 @@ global $CFG_GLPI; // Version of the plugin (major.minor.bugfix) -define('PLUGIN_FORMCREATOR_VERSION', '2.13.2'); +define('PLUGIN_FORMCREATOR_VERSION', '2.13.3-dev'); // Schema version of this version (major.minor only) define('PLUGIN_FORMCREATOR_SCHEMA_VERSION', '2.13'); // is or is not an official release of the plugin -define('PLUGIN_FORMCREATOR_IS_OFFICIAL_RELEASE', true); +define('PLUGIN_FORMCREATOR_IS_OFFICIAL_RELEASE', false); // Minimal GLPI version, inclusive define ('PLUGIN_FORMCREATOR_GLPI_MIN_VERSION', '10.0'); From bee622bc2fbe6c5c7f77af446a5e256c3c1e769b Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 20 Oct 2022 15:29:52 +0200 Subject: [PATCH 005/248] feat(install): empty SQL schema --- .../mysql/plugin_formcreator_2.13.3_empty.sql | 355 ++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 install/mysql/plugin_formcreator_2.13.3_empty.sql diff --git a/install/mysql/plugin_formcreator_2.13.3_empty.sql b/install/mysql/plugin_formcreator_2.13.3_empty.sql new file mode 100644 index 000000000..a83d74bb6 --- /dev/null +++ b/install/mysql/plugin_formcreator_2.13.3_empty.sql @@ -0,0 +1,355 @@ +-- Database schema +-- Do NOT drop anything here + +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_answers` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_formanswers_id` int unsigned NOT NULL DEFAULT '0', + `plugin_formcreator_questions_id` int unsigned NOT NULL DEFAULT '0', + `answer` longtext, + PRIMARY KEY (`id`), + INDEX `plugin_formcreator_formanswers_id` (`plugin_formcreator_formanswers_id`), + INDEX `plugin_formcreator_questions_id` (`plugin_formcreator_questions_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_categories` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `comment` mediumtext, + `completename` varchar(255) DEFAULT NULL, + `plugin_formcreator_categories_id` int unsigned NOT NULL DEFAULT '0', + `level` int(11) NOT NULL DEFAULT '1', + `sons_cache` longtext, + `ancestors_cache` longtext, + `knowbaseitemcategories_id` int unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + INDEX `name` (`name`), + INDEX `knowbaseitemcategories_id` (`knowbaseitemcategories_id`), + INDEX `plugin_formcreator_categories_id` (`plugin_formcreator_categories_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_entityconfigs` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `entities_id` int unsigned NOT NULL DEFAULT '0', + `replace_helpdesk` int(11) NOT NULL DEFAULT '-2', + `default_form_list_mode` int(11) NOT NULL DEFAULT '-2', + `sort_order` int(11) NOT NULL DEFAULT '-2', + `is_kb_separated` int(11) NOT NULL DEFAULT '-2', + `is_search_visible` int(11) NOT NULL DEFAULT '-2', + `is_dashboard_visible` int(11) NOT NULL DEFAULT '-2', + `is_header_visible` int(11) NOT NULL DEFAULT '-2', + `is_search_issue_visible` int(11) NOT NULL DEFAULT '-2', + `tile_design` int(11) NOT NULL DEFAULT '-2', + `header` text, + PRIMARY KEY (`id`), + UNIQUE KEY `unicity` (`entities_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_forms` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `entities_id` int unsigned NOT NULL DEFAULT '0', + `is_recursive` tinyint(1) NOT NULL DEFAULT '0', + `icon` varchar(255) NOT NULL DEFAULT '', + `icon_color` varchar(255) NOT NULL DEFAULT '', + `background_color` varchar(255) NOT NULL DEFAULT '', + `access_rights` tinyint(1) NOT NULL DEFAULT '1', + `description` varchar(255) DEFAULT NULL, + `content` longtext, + `plugin_formcreator_categories_id` int unsigned NOT NULL DEFAULT '0', + `is_active` tinyint(1) NOT NULL DEFAULT '0', + `language` varchar(255) NOT NULL DEFAULT '', + `helpdesk_home` tinyint(1) NOT NULL DEFAULT '0', + `is_deleted` tinyint(1) NOT NULL DEFAULT '0', + `validation_required` tinyint(1) NOT NULL DEFAULT '0', + `usage_count` int(11) NOT NULL DEFAULT '0', + `is_default` tinyint(1) NOT NULL DEFAULT '0', + `is_captcha_enabled` tinyint(1) NOT NULL DEFAULT '0', + `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, + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `entities_id` (`entities_id`), + INDEX `plugin_formcreator_categories_id` (`plugin_formcreator_categories_id`), + FULLTEXT KEY `Search` (`name`,`description`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_formanswers` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `entities_id` int unsigned NOT NULL DEFAULT '0', + `is_recursive` tinyint(1) NOT NULL DEFAULT '0', + `plugin_formcreator_forms_id` int unsigned NOT NULL DEFAULT '0', + `requester_id` int unsigned NOT NULL DEFAULT '0', + `users_id_validator` int unsigned NOT NULL DEFAULT '0' COMMENT 'User in charge of validation', + `groups_id_validator` int unsigned NOT NULL DEFAULT '0' COMMENT 'Group in charge of validation', + `request_date` timestamp NULL, + `status` int(11) NOT NULL DEFAULT '101', + `comment` mediumtext, + PRIMARY KEY (`id`), + INDEX `plugin_formcreator_forms_id` (`plugin_formcreator_forms_id`), + INDEX `entities_id_is_recursive` (`entities_id`, `is_recursive`), + INDEX `requester_id` (`requester_id`), + INDEX `users_id_validator` (`users_id_validator`), + INDEX `groups_id_validator` (`groups_id_validator`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_forms_profiles` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_forms_id` int unsigned NOT NULL DEFAULT '0', + `profiles_id` int unsigned NOT NULL DEFAULT '0', + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unicity` (`plugin_formcreator_forms_id`,`profiles_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_forms_users` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_forms_id` int unsigned NOT NULL, + `users_id` int unsigned NOT NULL, + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unicity` (`plugin_formcreator_forms_id`,`users_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_forms_groups` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_forms_id` int unsigned NOT NULL, + `groups_id` int unsigned NOT NULL, + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unicity` (`plugin_formcreator_forms_id`,`groups_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_forms_validators` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_forms_id` int unsigned NOT NULL DEFAULT '0', + `itemtype` varchar(255) NOT NULL DEFAULT '', + `items_id` int unsigned NOT NULL DEFAULT '0', + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unicity` (`plugin_formcreator_forms_id`,`itemtype`,`items_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_questions` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `plugin_formcreator_sections_id` int unsigned NOT NULL DEFAULT '0', + `fieldtype` varchar(30) NOT NULL DEFAULT 'text', + `required` tinyint(1) NOT NULL DEFAULT '0', + `show_empty` tinyint(1) NOT NULL DEFAULT '0', + `default_values` mediumtext, + `itemtype` varchar(255) NOT NULL DEFAULT '' COMMENT 'itemtype used for glpi objects and dropdown question types', + `values` mediumtext, + `description` mediumtext, + `row` int(11) NOT NULL DEFAULT '0', + `col` int(11) NOT NULL DEFAULT '0', + `width` int(11) NOT NULL DEFAULT '0', + `show_rule` int(11) NOT NULL DEFAULT '1', + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `plugin_formcreator_sections_id` (`plugin_formcreator_sections_id`), + FULLTEXT KEY `Search` (`name`,`description`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_conditions` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `itemtype` varchar(255) NOT NULL DEFAULT '' COMMENT 'itemtype of the item affected by the condition', + `items_id` int unsigned NOT NULL DEFAULT '0' COMMENT 'item ID of the item affected by the condition', + `plugin_formcreator_questions_id` int unsigned NOT NULL DEFAULT '0' COMMENT 'question to test for the condition', + `show_condition` int(11) NOT NULL DEFAULT '0', + `show_value` varchar(255) NULL DEFAULT NULL, + `show_logic` int(11) NOT NULL DEFAULT '1', + `order` int(11) NOT NULL DEFAULT '1', + `uuid` varchar(255) NULL DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `plugin_formcreator_questions_id` (`plugin_formcreator_questions_id`), + INDEX `item` (`itemtype`, `items_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_sections` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `plugin_formcreator_forms_id` int unsigned NOT NULL DEFAULT '0', + `order` int(11) NOT NULL DEFAULT '0', + `show_rule` int(11) NOT NULL DEFAULT '1', + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `plugin_formcreator_forms_id` (`plugin_formcreator_forms_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_targetchanges` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `plugin_formcreator_forms_id` int unsigned NOT NULL DEFAULT '0', + `target_name` varchar(255) NOT NULL DEFAULT '', + `changetemplates_id` int unsigned NOT NULL DEFAULT '0', + `content` longtext, + `impactcontent` longtext, + `controlistcontent` longtext, + `rolloutplancontent` longtext, + `backoutplancontent` longtext, + `checklistcontent` longtext, + `due_date_rule` int(11) NOT NULL DEFAULT '1', + `due_date_question` int unsigned NOT NULL DEFAULT '0', + `due_date_value` tinyint(4) DEFAULT NULL, + `due_date_period` int(11) NOT NULL DEFAULT '0', + `urgency_rule` int(11) NOT NULL DEFAULT '1', + `urgency_question` int unsigned NOT NULL DEFAULT '0', + `validation_followup` tinyint(1) NOT NULL DEFAULT '1', + `destination_entity` int(11) NOT NULL DEFAULT '1', + `destination_entity_value` int unsigned NOT NULL DEFAULT '0', + `tag_type` int(11) NOT NULL DEFAULT '1', + `tag_questions` varchar(255) NOT NULL DEFAULT '', + `tag_specifics` varchar(255) NOT NULL DEFAULT '', + `category_rule` int(11) NOT NULL DEFAULT '1', + `category_question` int unsigned NOT NULL DEFAULT '0', + `commonitil_validation_rule` int(11) NOT NULL DEFAULT '1', + `commonitil_validation_question` varchar(255) DEFAULT NULL, + `show_rule` int(11) NOT NULL DEFAULT '1', + `sla_rule` int(11) NOT NULL DEFAULT '1', + `sla_question_tto` int unsigned NOT NULL DEFAULT '0', + `sla_question_ttr` int unsigned NOT NULL DEFAULT '0', + `ola_rule` int(11) NOT NULL DEFAULT '1', + `ola_question_tto` int unsigned NOT NULL DEFAULT '0', + `ola_question_ttr` int unsigned NOT NULL DEFAULT '0', + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_targettickets` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `plugin_formcreator_forms_id` int unsigned NOT NULL DEFAULT '0', + `target_name` varchar(255) NOT NULL DEFAULT '', + `source_rule` int(11) NOT NULL DEFAULT '0', + `source_question` int(11) NOT NULL DEFAULT '0', + `type_rule` int(11) NOT NULL DEFAULT '0', + `type_question` int unsigned NOT NULL DEFAULT '0', + `tickettemplates_id` int unsigned NOT NULL DEFAULT '0', + `content` longtext, + `due_date_rule` int(11) NOT NULL DEFAULT '1', + `due_date_question` int unsigned NOT NULL DEFAULT '0', + `due_date_value` tinyint(4) DEFAULT NULL, + `due_date_period` int(11) NOT NULL DEFAULT '0', + `urgency_rule` int(11) NOT NULL DEFAULT '1', + `urgency_question` int unsigned NOT NULL DEFAULT '0', + `validation_followup` tinyint(1) NOT NULL DEFAULT '1', + `destination_entity` int(11) NOT NULL DEFAULT '1', + `destination_entity_value` int unsigned NOT NULL DEFAULT '0', + `tag_type` int(11) NOT NULL DEFAULT '1', + `tag_questions` varchar(255) NOT NULL DEFAULT '', + `tag_specifics` varchar(255) NOT NULL DEFAULT '', + `category_rule` int(11) NOT NULL DEFAULT '1', + `category_question` int unsigned NOT NULL DEFAULT '0', + `associate_rule` int(11) NOT NULL DEFAULT '1', + `associate_question` int unsigned NOT NULL DEFAULT '0', + `location_rule` int(11) NOT NULL DEFAULT '1', + `location_question` int unsigned NOT NULL DEFAULT '0', + `commonitil_validation_rule` int(11) NOT NULL DEFAULT '1', + `commonitil_validation_question` varchar(255) DEFAULT NULL, + `show_rule` int(11) NOT NULL DEFAULT '1', + `sla_rule` int(11) NOT NULL DEFAULT '1', + `sla_question_tto` int unsigned NOT NULL DEFAULT '0', + `sla_question_ttr` int unsigned NOT NULL DEFAULT '0', + `ola_rule` int(11) NOT NULL DEFAULT '1', + `ola_question_tto` int unsigned NOT NULL DEFAULT '0', + `ola_question_ttr` int unsigned NOT NULL DEFAULT '0', + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `tickettemplates_id` (`tickettemplates_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_targetproblems` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `plugin_formcreator_forms_id` int unsigned NOT NULL DEFAULT '0', + `target_name` varchar(255) NOT NULL DEFAULT '', + `problemtemplates_id` int unsigned NOT NULL DEFAULT '0', + `content` longtext, + `impactcontent` longtext, + `causecontent` longtext, + `symptomcontent` longtext, + `urgency_rule` int(11) NOT NULL DEFAULT '1', + `urgency_question` int unsigned NOT NULL DEFAULT '0', + `destination_entity` int(11) NOT NULL DEFAULT '1', + `destination_entity_value` int unsigned NOT NULL DEFAULT '0', + `tag_type` int(11) NOT NULL DEFAULT '1', + `tag_questions` varchar(255) NOT NULL DEFAULT '', + `tag_specifics` varchar(255) NOT NULL DEFAULT '', + `category_rule` int(11) NOT NULL DEFAULT '1', + `category_question` int unsigned NOT NULL DEFAULT '0', + `show_rule` int(11) NOT NULL DEFAULT '1', + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `problemtemplates_id` (`problemtemplates_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_targets_actors` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `itemtype` varchar(255) DEFAULT NULL, + `items_id` int unsigned NOT NULL DEFAULT '0', + `actor_role` int(11) NOT NULL DEFAULT '1', + `actor_type` int(11) NOT NULL DEFAULT '1', + `actor_value` int unsigned NOT NULL DEFAULT '0', + `use_notification` tinyint(1) NOT NULL DEFAULT '1', + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `item` (`itemtype`, `items_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_issues` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NULL DEFAULT NULL, + `display_id` varchar(255) NOT NULL, + `items_id` int unsigned NOT NULL DEFAULT '0', + `itemtype` varchar(255) NOT NULL DEFAULT '', + `status` varchar(255) NOT NULL DEFAULT '', + `date_creation` timestamp NULL, + `date_mod` timestamp NULL, + `entities_id` int unsigned NOT NULL DEFAULT '0', + `is_recursive` tinyint(1) NOT NULL DEFAULT '0', + `requester_id` int unsigned NOT NULL DEFAULT '0', + `users_id_validator` int unsigned NOT NULL DEFAULT '0', + `groups_id_validator` int unsigned NOT NULL DEFAULT '0', + `comment` longtext, + `users_id_recipient` int unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + INDEX `item` (`itemtype`, `items_id`), + INDEX `entities_id` (`entities_id`), + INDEX `requester_id` (`requester_id`), + INDEX `users_id_validator` (`users_id_validator`), + INDEX `groups_id_validator` (`groups_id_validator`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_items_targettickets` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_targettickets_id` int unsigned NOT NULL DEFAULT '0', + `link` int(11) NOT NULL DEFAULT '0', + `itemtype` varchar(255) NOT NULL DEFAULT '', + `items_id` int unsigned NOT NULL DEFAULT '0', + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `plugin_formcreator_targettickets_id` (`plugin_formcreator_targettickets_id`), + INDEX `item` (`itemtype`,`items_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_questiondependencies` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_questions_id` int unsigned NOT NULL DEFAULT '0', + `plugin_formcreator_questions_id_2` int unsigned NOT NULL DEFAULT '0', + `fieldname` varchar(255) DEFAULT NULL, + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `plugin_formcreator_questions_id` (`plugin_formcreator_questions_id`), + INDEX `plugin_formcreator_questions_id_2` (`plugin_formcreator_questions_id_2`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_questionregexes` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_questions_id` int unsigned NOT NULL DEFAULT '0', + `regex` mediumtext DEFAULT NULL, + `fieldname` varchar(255) DEFAULT NULL, + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `plugin_formcreator_questions_id` (`plugin_formcreator_questions_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_questionranges` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_questions_id` int unsigned NOT NULL DEFAULT '0', + `range_min` varchar(255) DEFAULT NULL, + `range_max` varchar(255) DEFAULT NULL, + `fieldname` varchar(255) DEFAULT NULL, + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `plugin_formcreator_questions_id` (`plugin_formcreator_questions_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_forms_languages` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `plugin_formcreator_forms_id` int unsigned NOT NULL DEFAULT '0', + `name` varchar(255) DEFAULT NULL, + `comment` text, + `uuid` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; \ No newline at end of file From 6f9137461ee7e90e728fa65f2ce7f3f40de4b92f Mon Sep 17 00:00:00 2001 From: btry Date: Fri, 21 Oct 2022 14:21:14 +0200 Subject: [PATCH 006/248] feat: handle a new case in fix tool: litteral > sign (#3048) --- inc/command/cleanticketscommand.class.php | 108 ++++++++++++++++++++-- 1 file changed, 101 insertions(+), 7 deletions(-) diff --git a/inc/command/cleanticketscommand.class.php b/inc/command/cleanticketscommand.class.php index 660694145..92691844e 100644 --- a/inc/command/cleanticketscommand.class.php +++ b/inc/command/cleanticketscommand.class.php @@ -53,6 +53,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { $this->fixBadForm_1($input, $output); $this->fixBadForm_2($input, $output); + $this->fixBadForm_3($input, $output); $output->writeln('Done.'); return 0; @@ -102,7 +103,7 @@ protected function fixBadForm_1(InputInterface $input, OutputInterface $output) return 0; } - $output->write("-> Found $count tickets to clean"); + $output->write("-> Found $count tickets to clean (double encoded < and > signs)"); $output->writeln(""); $output->write("-> Cleaning tickets..."); $output->writeln(""); @@ -130,7 +131,7 @@ protected function fixBadForm_1(InputInterface $input, OutputInterface $output) } /** - * remove HTML tag
+ * replace litteral HTML tag
with <br /> * * @param InputInterface $input * @param OutputInterface $output @@ -142,7 +143,7 @@ protected function fixBadForm_2(InputInterface $input, OutputInterface $output) // Search tickets having HTML tags
$itemTicketTable = Item_Ticket::getTable(); $ticketTable = Ticket::getTable(); - $pattern = 'br /'; + $pattern = '
'; $tickets = $DB->request([ 'SELECT' => [$ticketTable => [Ticket::getIndexName(), 'content']], 'FROM' => $ticketTable, @@ -158,7 +159,7 @@ protected function fixBadForm_2(InputInterface $input, OutputInterface $output) ], ], 'WHERE' => [ - "$ticketTable.content" => ['LIKE', '%' . $pattern . '%'], // Matches bad encoding for '<' + "$ticketTable.content" => ['LIKE', '%' . $pattern . '%'], // Matches bad encoding for 'br /' ], ]); @@ -169,7 +170,7 @@ protected function fixBadForm_2(InputInterface $input, OutputInterface $output) return 0; } - $output->write("-> Found $count tickets to clean"); + $output->write("-> Found $count tickets to clean (literal BR tag)"); $output->writeln(""); $output->write("-> Cleaning tickets..."); $output->writeln(""); @@ -177,9 +178,102 @@ protected function fixBadForm_2(InputInterface $input, OutputInterface $output) $pattern = [ '
', ]; - $replace = [ - '<br />', + // Determine if we must use legacy or new encoding + // @see Sanitizer::sanitize() + $replace = null; + if (strpos($row['content'], '<') !== false && strpos($row['content'], '#60;') === false) { + $replace = [ + '<br />', + ]; + } else if (strpos($row['content'], '#60') !== false && strpos($row['content'], '<') === false) { + $replace = [ + '<br />', + ]; + } + if ($replace === null) { + $output->write("-> Unable to determine the encoding type of ticket ID: " . $row['id']. ""); + continue; + } + $row['content'] = str_replace($pattern, $replace, $row['content']); + // Direct write to the table to avoid alteration of other fields + $DB->update( + $ticketTable, + [ + 'content' => $DB->escape($row['content']) + ], + [ + 'id' => $row['id'], + ] + ); + } + } + + /** + * replace litteral HTML tag > with #38; + * This may happen when a question gives the path to an item of a CommonTreeObject + * entities, locations, ... + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + */ + protected function fixBadForm_3(InputInterface $input, OutputInterface $output) { + global $DB; + + // Search tickets having HTML tags
+ $itemTicketTable = Item_Ticket::getTable(); + $ticketTable = Ticket::getTable(); + $pattern = ' > '; // greater than sign with a space before and after + $tickets = $DB->request([ + 'SELECT' => [$ticketTable => [Ticket::getIndexName(), 'content']], + 'FROM' => $ticketTable, + 'INNER JOIN' => [ + $itemTicketTable => [ + 'FKEY' => [ + $ticketTable => Ticket::getIndexName(), + $itemTicketTable => Ticket::getForeignKeyField(), + ], + 'AND' => [ + "$itemTicketTable.itemtype" => PluginFormcreatorFormAnswer::getType(), + ] + ], + ], + 'WHERE' => [ + "$ticketTable.content" => ['LIKE', '%' . $pattern . '%'], + ], + ]); + + $count = $tickets->count(); + if ($count < 1) { + $output->writeln('-> No ticket to fix.'); + $output->writeln(""); + return 0; + } + + $output->write("-> Found $count tickets to clean (litteral > sign)"); + $output->writeln(""); + $output->write("-> Cleaning tickets..."); + $output->writeln(""); + foreach ($tickets as $row) { + $pattern = [ + ' > ', ]; + // Determine if we must use legacy or new encoding + // @see Sanitizer::sanitize() + $replace = null; + if (strpos($row['content'], '<') !== false && strpos($row['content'], '#60;') === false) { + $replace = [ + ' > ', + ]; + } else if (strpos($row['content'], '#60') !== false && strpos($row['content'], '<') === false) { + $replace = [ + ' & ', + ]; + } + if ($replace === null) { + $output->write("-> Unable to determine the encoding type of ticket ID: " . $row['id']. ""); + continue; + } $row['content'] = str_replace($pattern, $replace, $row['content']); // Direct write to the table to avoid alteration of other fields $DB->update( From 22b9a885e1b25b6d4a2862a8a3e157249b1e37f5 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Fri, 21 Oct 2022 14:41:32 +0200 Subject: [PATCH 007/248] fix(install): handle possible null while changing fields --- install/upgrade_to_2.13.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/install/upgrade_to_2.13.php b/install/upgrade_to_2.13.php index 8bce363e6..b2fb9501b 100644 --- a/install/upgrade_to_2.13.php +++ b/install/upgrade_to_2.13.php @@ -168,6 +168,16 @@ public function fixTables(): void { ['ola_rule' => '0'], ['ola_rule' => null] ); + $DB->update( + $table, + ['tag_questions' => ''], + ['tag_questions' => null] + ); + $DB->update( + $table, + ['tag_specifics' => ''], + ['tag_specifics' => null] + ); $this->migration->changeField($table, 'validation_followup', 'validation_followup', 'bool', ['after' => 'urgency_question', 'value' => '1']); $this->migration->changeField($table, 'destination_entity', 'destination_entity', 'integer', ['after' => 'validation_followup', 'value' => '1']); $this->migration->changeField($table, 'destination_entity_value', 'destination_entity_value', $unsignedIntType, ['after' => 'destination_entity', 'default' => '1']); From 8d99e5a6dc14b78d128c6bf0243d38f3757b0353 Mon Sep 17 00:00:00 2001 From: btry Date: Mon, 24 Oct 2022 11:30:10 +0200 Subject: [PATCH 008/248] fix(checkboxesfield, radiosfield): checkboxes and radios backslashes (#3050) --- inc/field/checkboxesfield.class.php | 1 - inc/field/radiosfield.class.php | 1 - .../Formcreator/Field/CheckboxesField.php | 77 +++++++++++-------- .../Formcreator/Field/RadiosField.php | 49 ++++++++++++ 4 files changed, 96 insertions(+), 32 deletions(-) diff --git a/inc/field/checkboxesfield.class.php b/inc/field/checkboxesfield.class.php index 218deffcf..dd294bf40 100644 --- a/inc/field/checkboxesfield.class.php +++ b/inc/field/checkboxesfield.class.php @@ -187,7 +187,6 @@ public function isValidValue($value): bool { return true; } - $value = Toolbox::stripslashes_deep($value); foreach ($value as $item) { if (trim($item) == '') { return false; diff --git a/inc/field/radiosfield.class.php b/inc/field/radiosfield.class.php index 314b4d644..07ec0daa1 100644 --- a/inc/field/radiosfield.class.php +++ b/inc/field/radiosfield.class.php @@ -205,7 +205,6 @@ public function isValidValue($value): bool { if ($value == '') { return true; } - $value = Toolbox::stripslashes_deep($value); $value = trim($value); return in_array($value, $this->getAvailableValues()); } diff --git a/tests/3-unit/GlpiPlugin/Formcreator/Field/CheckboxesField.php b/tests/3-unit/GlpiPlugin/Formcreator/Field/CheckboxesField.php index bb861f97e..1f4aa2d47 100644 --- a/tests/3-unit/GlpiPlugin/Formcreator/Field/CheckboxesField.php +++ b/tests/3-unit/GlpiPlugin/Formcreator/Field/CheckboxesField.php @@ -95,37 +95,54 @@ public function providerIsValidValue() { ] ], ])); - return [ - [ - 'instance' => $instance, - 'value' => '', - 'expected' => true, - ], - [ - 'instance' => $instance, - 'value' => [], - 'expected' => true, - ], - [ - 'instance' => $instance, - 'value' => ['1'], - 'expected' => true, - ], - [ - 'instance' => $instance, - 'value' => ['1', '4'], - 'expected' => true, - ], - [ - 'instance' => $instance, - 'value' => ['1', '9'], - 'expected' => false, - ], - [ - 'instance' => $instance, - 'value' => ['9'], - 'expected' => false, + yield [ + 'instance' => $instance, + 'value' => '', + 'expected' => true, + ]; + yield [ + 'instance' => $instance, + 'value' => [], + 'expected' => true, + ]; + yield [ + 'instance' => $instance, + 'value' => ['1'], + 'expected' => true, + ]; + yield [ + 'instance' => $instance, + 'value' => ['1', '4'], + 'expected' => true, + ]; + yield [ + 'instance' => $instance, + 'value' => ['1', '9'], + 'expected' => false, + ]; + yield [ + 'instance' => $instance, + 'value' => ['9'], + 'expected' => false, + ]; + + // values are escaped by GLPI, then backslashes are doubled + $instance = $this->newTestedInstance($this->getQuestion([ + 'fieldtype' => 'checkboxes', + 'values' => implode('\r\n', ['X:\\\\path\\\\to\\\\file', 'nothing']), + '_parameters' => [ + 'checkboxes' => [ + 'range' => [ + 'range_min' => '', + 'range_max' => '', + ] + ] ], + ])); + yield [ + 'instance' => $instance, + 'value' => ['X:\\path\\to\\file'], + 'expected' => true, ]; } diff --git a/tests/3-unit/GlpiPlugin/Formcreator/Field/RadiosField.php b/tests/3-unit/GlpiPlugin/Formcreator/Field/RadiosField.php index 756c73557..53dc4ecd3 100644 --- a/tests/3-unit/GlpiPlugin/Formcreator/Field/RadiosField.php +++ b/tests/3-unit/GlpiPlugin/Formcreator/Field/RadiosField.php @@ -100,6 +100,55 @@ public function testCanRequire() { $this->boolean($output)->isTrue(); } + public function providerIsValidValue() { + $instance = $this->newTestedInstance($this->getQuestion([ + 'fieldtype' => 'radios', + 'values' => implode('\r\n', ['1', '2', '3', '4']), + ])); + yield [ + 'instance' => $instance, + 'value' => '', + 'expected' => true, + ]; + yield [ + 'instance' => $instance, + 'value' => '1', + 'expected' => true, + ]; + yield [ + 'instance' => $instance, + 'value' => '9', + 'expected' => false, + ]; + + // values are escaped by GLPI, then backslashes are doubled + $instance = $this->newTestedInstance($this->getQuestion([ + 'fieldtype' => 'radios', + 'values' => implode('\r\n', ['X:\\\\path\\\\to\\\\file', 'nothing']), + '_parameters' => [ + 'checkboxes' => [ + 'range' => [ + 'range_min' => '', + 'range_max' => '', + ] + ] + ], + ])); + yield [ + 'instance' => $instance, + 'value' => 'X:\\path\\to\\file', + 'expected' => true, + ]; + } + + /** + * @dataProvider providerIsValidValue + */ + public function testIsValidValue($instance, $value, $expected) { + $output = $instance->isValidValue($value); + $this->boolean($output)->isEqualTo($expected); + } + public function providerSerializeValue() { $question = $this->getQuestion([ 'fieldtype' => 'radios', From 9fdd7848bb49dc89fa5ec910acc42b4ea800f579 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 6 Oct 2022 09:07:37 +0200 Subject: [PATCH 009/248] refactor(targetticket): change constant name --- inc/targetticket.class.php | 12 ++++++------ tests/3-unit/PluginFormcreatorTargetTicket.php | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/inc/targetticket.class.php b/inc/targetticket.class.php index f51315493..a422c4b3b 100644 --- a/inc/targetticket.class.php +++ b/inc/targetticket.class.php @@ -49,7 +49,7 @@ class PluginFormcreatorTargetTicket extends PluginFormcreatorAbstractItilTarget const REQUESTTYPE_ANSWER = 2; const REQUESTSOURCE_NONE = 0; - const REQUESTSOURCE_SPECIFIC = 1; + const REQUESTSOURCE_FORMCREATOR = 1; public static function getTypeName($nb = 1) { return _n('Target ticket', 'Target tickets', $nb, 'formcreator'); @@ -104,7 +104,7 @@ public static function getEnumAssociateRule() { public static function getEnumRequestSourceRule(): array { return [ self::REQUESTSOURCE_NONE => __('Source from template or user default or GLPI default', 'formcreator'), - self::REQUESTSOURCE_SPECIFIC => __('Formcreator', 'formcreator'), + self::REQUESTSOURCE_FORMCREATOR => __('Formcreator', 'formcreator'), ]; } @@ -538,10 +538,10 @@ public function prepareInputForAdd($input) { } if (!isset($input['source_rule'])) { - $input['source_rule'] = self::REQUESTSOURCE_SPECIFIC; + $input['source_rule'] = self::REQUESTSOURCE_FORMCREATOR; } $input['source_question'] = 0; - if ($input['source_rule'] == self::REQUESTSOURCE_SPECIFIC) { + if ($input['source_rule'] == self::REQUESTSOURCE_FORMCREATOR) { $input['source_question'] = PluginFormcreatorCommon::getFormcreatorRequestTypeId(); } return $input; @@ -631,7 +631,7 @@ public function prepareInputForUpdate($input) { case self::REQUESTSOURCE_NONE: $input['source_question'] = 0; break; - case self::REQUESTSOURCE_SPECIFIC: + case self::REQUESTSOURCE_FORMCREATOR: $input['source_question'] = PluginFormcreatorCommon::getFormcreatorRequestTypeId(); break; } @@ -1033,7 +1033,7 @@ protected function setTargetLocation($data, $formanswer) { protected function setTargetSource(array $data, PluginFormcreatorFormAnswer $formanswer): array { switch ($this->fields['source_rule']) { - case self::REQUESTSOURCE_SPECIFIC: + case self::REQUESTSOURCE_FORMCREATOR: $data['requesttypes_id'] = $this->fields['source_question']; break; } diff --git a/tests/3-unit/PluginFormcreatorTargetTicket.php b/tests/3-unit/PluginFormcreatorTargetTicket.php index b325de3e9..eca828890 100644 --- a/tests/3-unit/PluginFormcreatorTargetTicket.php +++ b/tests/3-unit/PluginFormcreatorTargetTicket.php @@ -1298,7 +1298,7 @@ public function providerPrepareInputForAdd() { 'content' => '##FULLFORM##', 'type_rule' => \PluginFormcreatorTargetTicket::REQUESTTYPE_SPECIFIC, 'type_question' => \Ticket::INCIDENT_TYPE, - 'source_rule' => \PluginFormcreatorTargetTicket::REQUESTSOURCE_SPECIFIC, + 'source_rule' => \PluginFormcreatorTargetTicket::REQUESTSOURCE_FORMCREATOR, 'source_question' => $sourceId, ], 'message' => null, @@ -1466,7 +1466,7 @@ public function providerSetRequestSource_specific(): array { 'name' => 'target ticket', 'target_name' => 'target ticket', 'plugin_formcreator_forms_id' => $form->getID(), - 'source_rule' => \PluginFormcreatorTargetTicket::REQUESTSOURCE_SPECIFIC, + 'source_rule' => \PluginFormcreatorTargetTicket::REQUESTSOURCE_FORMCREATOR, 'source_question' => \PluginFormcreatorCommon::getFormcreatorRequestTypeId(), ]); $this->boolean($targetTicket->isNewItem())->isFalse(); From c3c0db21d679994022c71db7703d5a6afd5f3a75 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 6 Oct 2022 09:08:07 +0200 Subject: [PATCH 010/248] fix(targetticket): set request source if no rule specified --- inc/targetticket.class.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/inc/targetticket.class.php b/inc/targetticket.class.php index a422c4b3b..b186370c6 100644 --- a/inc/targetticket.class.php +++ b/inc/targetticket.class.php @@ -1033,6 +1033,10 @@ protected function setTargetLocation($data, $formanswer) { protected function setTargetSource(array $data, PluginFormcreatorFormAnswer $formanswer): array { switch ($this->fields['source_rule']) { + case self::REQUESTSOURCE_NONE: + $data['requesttypes_id'] = PluginFormcreatorCommon::getFormcreatorRequestTypeId(); + break; + case self::REQUESTSOURCE_FORMCREATOR: $data['requesttypes_id'] = $this->fields['source_question']; break; From d3f07b7d4d324e2ddaad0e872fb36ffe36faa84b Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Mon, 24 Oct 2022 15:03:02 +0200 Subject: [PATCH 011/248] fix(checkboxesfield): back to BR --- inc/field/checkboxesfield.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/field/checkboxesfield.class.php b/inc/field/checkboxesfield.class.php index dd294bf40..a41c3a173 100644 --- a/inc/field/checkboxesfield.class.php +++ b/inc/field/checkboxesfield.class.php @@ -263,7 +263,7 @@ public function getValueForTargetText($domain, $richText): ?string { } if ($richText) { - $value = '

' . implode('

', $value) . '

'; + $value = implode('
', $value); } else { $value = implode(', ', $value); } From 4d71f0c57270b10cd7142954fe3e2780a3d6d500 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Mon, 24 Oct 2022 15:03:02 +0200 Subject: [PATCH 012/248] fix(checkboxesfield): back to BR --- .../3-unit/GlpiPlugin/Formcreator/Field/CheckboxesField.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/3-unit/GlpiPlugin/Formcreator/Field/CheckboxesField.php b/tests/3-unit/GlpiPlugin/Formcreator/Field/CheckboxesField.php index 1f4aa2d47..2cdc421e5 100644 --- a/tests/3-unit/GlpiPlugin/Formcreator/Field/CheckboxesField.php +++ b/tests/3-unit/GlpiPlugin/Formcreator/Field/CheckboxesField.php @@ -301,21 +301,21 @@ public function providerGetValueForTargetText() { 'values' => "[]" ]), 'value' => json_encode(['a']), - 'expected' => '

' + 'expected' => '' ], [ 'question' => $this->getQuestion([ 'values' => json_encode(['a', 'b', 'c']) ]), 'value' => json_encode(['a']), - 'expected' => '

a

' + 'expected' => 'a' ], [ 'question' => $this->getQuestion([ 'values' => json_encode(['a', 'b', 'c']) ]), 'value' => json_encode(['a', 'c']), - 'expected' => '

a

c

' + 'expected' => 'a
c' ], ]; } From 2a1f8380af48c2ba16b33436c757221584179a24 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Mon, 24 Oct 2022 15:47:52 +0200 Subject: [PATCH 013/248] fix(install): bad command in error message --- install/install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/install.php b/install/install.php index f9cdb3d89..10fa0c6e0 100644 --- a/install/install.php +++ b/install/install.php @@ -159,7 +159,7 @@ public function upgrade(Migration $migration, $args = []): bool { $message = sprintf( __('The database schema is not consistent with the installed Formcreator %s. To see the logs run the command %s', 'formcreator'), $oldVersion, - 'bin/console glpi:plugin:install formcreator -f' + 'bin/console glpi:database:check_schema_integrity -p formcreator' ); if (!isCommandLine()) { Session::addMessageAfterRedirect($message, false, ERROR); From d8faf297e6763c7bb3176ac5cc0f5b8472b2e579 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Mon, 24 Oct 2022 15:55:50 +0200 Subject: [PATCH 014/248] fix(selectfield): too many unescaping --- inc/field/selectfield.class.php | 1 - .../Formcreator/Field/SelectField.php | 49 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/inc/field/selectfield.class.php b/inc/field/selectfield.class.php index 761966049..02f77357c 100644 --- a/inc/field/selectfield.class.php +++ b/inc/field/selectfield.class.php @@ -116,7 +116,6 @@ public function isValidValue($value): bool { if ($value == '0') { return true; } - $value = Toolbox::stripslashes_deep($value); $value = trim($value); return in_array($value, $this->getAvailableValues()); } diff --git a/tests/3-unit/GlpiPlugin/Formcreator/Field/SelectField.php b/tests/3-unit/GlpiPlugin/Formcreator/Field/SelectField.php index 56b91c20f..871bf682f 100644 --- a/tests/3-unit/GlpiPlugin/Formcreator/Field/SelectField.php +++ b/tests/3-unit/GlpiPlugin/Formcreator/Field/SelectField.php @@ -252,6 +252,55 @@ public function testGetEmptyParameters() { ->isIdenticalTo([]); } + public function providerIsValidValue() { + $instance = $this->newTestedInstance($this->getQuestion([ + 'fieldtype' => 'select', + 'values' => implode('\r\n', ['1', '2', '3', '4']), + ])); + yield [ + 'instance' => $instance, + 'value' => '', + 'expected' => false, + ]; + yield [ + 'instance' => $instance, + 'value' => '1', + 'expected' => true, + ]; + yield [ + 'instance' => $instance, + 'value' => '9', + 'expected' => false, + ]; + + // values are escaped by GLPI, then backslashes are doubled + $instance = $this->newTestedInstance($this->getQuestion([ + 'fieldtype' => 'select', + 'values' => implode('\r\n', ['X:\\\\path\\\\to\\\\file', 'nothing']), + '_parameters' => [ + 'checkboxes' => [ + 'range' => [ + 'range_min' => '', + 'range_max' => '', + ] + ] + ], + ])); + yield [ + 'instance' => $instance, + 'value' => 'X:\\path\\to\\file', + 'expected' => true, + ]; + } + + /** + * @dataProvider providerIsValidValue + */ + public function testIsValidValue($instance, $value, $expected) { + $output = $instance->isValidValue($value); + $this->boolean($output)->isEqualTo($expected); + } + public function providerSerializeValue() { return [ [ From ce876de8abcef6bb9b33ae188b4bc001a134fff6 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Wed, 26 Oct 2022 14:39:09 +0200 Subject: [PATCH 015/248] docs(locales): update locales --- locales/en_GB.po | 60 ++++++++++++++++++++++++------------------------ locales/fr_FR.po | 6 ++--- locales/glpi.pot | 58 +++++++++++++++++++++++----------------------- 3 files changed, 62 insertions(+), 62 deletions(-) diff --git a/locales/en_GB.po b/locales/en_GB.po index 1457575a0..2ce61670f 100644 --- a/locales/en_GB.po +++ b/locales/en_GB.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: glpi 100a\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-10-14 17:09+0200\n" -"PO-Revision-Date: 2022-10-14 17:09+0200\n" +"POT-Creation-Date: 2022-10-26 14:38+0200\n" +"PO-Revision-Date: 2022-10-26 14:38+0200\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: en_GB\n" @@ -193,7 +193,7 @@ msgstr "A required field is empty:" msgid "The regular expression is invalid" msgstr "The regular expression is invalid" -#: inc/field/dropdownfield.class.php:73 inc/filter/entityfilter.class.php:46 +#: inc/field/dropdownfield.class.php:73 inc/filter/entityfilter.class.php:48 msgid "User and form" msgstr "User and form" @@ -279,7 +279,7 @@ msgid "Radios" msgstr "Radios" #: inc/field/radiosfield.class.php:115 inc/field/glpiselectfield.class.php:93 -#: inc/field/fieldsfield.class.php:512 inc/field/checkboxesfield.class.php:230 +#: inc/field/fieldsfield.class.php:512 inc/field/checkboxesfield.class.php:229 msgid "The field value is required:" msgstr "The field value is required:" @@ -469,25 +469,25 @@ msgstr "Additionnal fields" msgid "Checkboxes" msgstr "Checkboxes" -#: inc/field/checkboxesfield.class.php:212 +#: inc/field/checkboxesfield.class.php:211 #, php-format msgid "The following question needs at least %d answers" msgstr "The following question needs at least %d answers" -#: inc/field/checkboxesfield.class.php:218 +#: inc/field/checkboxesfield.class.php:217 #, php-format msgid "The following question does not accept more than %d answers" msgstr "The following question does not accept more than %d answers" -#: inc/field/checkboxesfield.class.php:290 +#: inc/field/checkboxesfield.class.php:289 msgid "Range min" msgstr "Range min" -#: inc/field/checkboxesfield.class.php:291 +#: inc/field/checkboxesfield.class.php:290 msgid "Range max" msgstr "Range max" -#: inc/field/requesttypefield.class.php:86 inc/targetticket.class.php:1095 +#: inc/field/requesttypefield.class.php:86 inc/targetticket.class.php:1099 msgid "Request type" msgstr "Request type" @@ -556,7 +556,7 @@ msgstr "Displayed unless" #: inc/section.class.php:379 inc/restrictedformcriteria.class.php:200 #: inc/question.class.php:828 inc/questionparameter/range.class.php:202 #: inc/questionregex.class.php:185 inc/questiondependency.class.php:196 -#: inc/item_targetticket.class.php:154 inc/targetticket.class.php:1404 +#: inc/item_targetticket.class.php:154 inc/targetticket.class.php:1408 #, php-format msgid "Failed to add or update the %1$s %2$s" msgstr "Failed to add or update the %1$s %2$s" @@ -568,7 +568,7 @@ msgstr "Failed to add or update the %1$s %2$s" #: inc/section.class.php:404 inc/restrictedformcriteria.class.php:226 #: inc/question.class.php:877 inc/questionparameter/range.class.php:137 #: inc/questionregex.class.php:120 inc/questiondependency.class.php:211 -#: inc/item_targetticket.class.php:64 inc/targetticket.class.php:1443 +#: inc/item_targetticket.class.php:64 inc/targetticket.class.php:1447 #, php-format msgid "Cannot export an empty object: %s" msgstr "Cannot export an empty object: %s" @@ -1102,7 +1102,7 @@ msgid "Properties" msgstr "Properties" #: inc/targetproblem.class.php:522 inc/targetchange.class.php:285 -#: inc/targetticket.class.php:1386 +#: inc/targetticket.class.php:1390 #, php-format msgid "" "Failed to add or update the %1$s %2$s: a question is missing and is used in " @@ -1635,8 +1635,8 @@ msgstr "SLA" msgid "OLA" msgstr "OLA" -#: inc/question.class.php:1197 inc/question.class.php:1237 -#: inc/question.class.php:1240 +#: inc/question.class.php:1197 inc/question.class.php:1238 +#: inc/question.class.php:1241 msgid "Assets" msgstr "Assets" @@ -1644,27 +1644,27 @@ msgstr "Assets" msgid "Assistance" msgstr "Assistance" -#: inc/question.class.php:1216 +#: inc/question.class.php:1217 msgid "Management" msgstr "Management" -#: inc/question.class.php:1225 +#: inc/question.class.php:1226 msgid "Tools" msgstr "Tools" -#: inc/question.class.php:1226 +#: inc/question.class.php:1227 msgid "Notes" msgstr "Notes" -#: inc/question.class.php:1227 +#: inc/question.class.php:1228 msgid "RSS feed" msgstr "RSS feed" -#: inc/question.class.php:1229 +#: inc/question.class.php:1230 msgid "Administration" msgstr "Administration" -#: inc/question.class.php:1237 inc/question.class.php:1240 +#: inc/question.class.php:1238 inc/question.class.php:1241 msgid "Plugin" msgid_plural "Plugins" msgstr[0] "Plugin" @@ -1887,19 +1887,19 @@ msgstr "Failed to link the item" msgid "Your form has been accepted by the validator" msgstr "Your form has been accepted by the validator" -#: inc/targetticket.class.php:1082 +#: inc/targetticket.class.php:1086 msgid "Request source" msgstr "Request source" -#: inc/targetticket.class.php:1107 +#: inc/targetticket.class.php:1111 msgid "Type " msgstr "Type " -#: inc/targetticket.class.php:1135 +#: inc/targetticket.class.php:1139 msgid "Associated elements" msgstr "Associated elements" -#: inc/targetticket.class.php:1146 +#: inc/targetticket.class.php:1150 msgid "Item " msgstr "Item " @@ -2306,11 +2306,15 @@ msgstr "Add translations" msgid "An error occured while querying forms" msgstr "An error occured while querying forms" -#: js/scripts.js:1400 +#: js/scripts.js:1396 entrée standard:139 +msgid "Send" +msgstr "Send" + +#: js/scripts.js:1405 msgid "An internal error occurred. Please report it to administrator." msgstr "An internal error occurred. Please report it to administrator." -#: js/scripts.js:1441 +#: js/scripts.js:1446 msgid "Are you sure you want to delete this target:" msgstr "Are you sure you want to delete this target:" @@ -2365,10 +2369,6 @@ msgstr "Default form in service catalog" msgid "Are you a robot ?" msgstr "Are you a robot ?" -#: entrée standard:139 -msgid "Send" -msgstr "Send" - #: entrée standard:64 msgid "Condition to show the section" msgstr "Condition to show the section" diff --git a/locales/fr_FR.po b/locales/fr_FR.po index 02e51995f..b2d3ac014 100644 --- a/locales/fr_FR.po +++ b/locales/fr_FR.po @@ -2,7 +2,7 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# # Translators: # alexandre delaunay , 2021 # Tiago Graça, 2021 @@ -12,7 +12,7 @@ # tguichard25 , 2022 # 06c8e5f1ec78ded2ceb41498ec52b068, 2022 # Thierry Bugier , 2022 -# +# #, fuzzy msgid "" msgstr "" @@ -2435,7 +2435,7 @@ msgstr "Êtes-vous un robot ?" #: entrée standard:139 msgid "Send" -msgstr "EnvoyerEnvoyer" +msgstr "Envoyer" #: entrée standard:64 msgid "Condition to show the section" diff --git a/locales/glpi.pot b/locales/glpi.pot index eaf8fc80e..67c429571 100644 --- a/locales/glpi.pot +++ b/locales/glpi.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-10-14 17:09+0200\n" +"POT-Creation-Date: 2022-10-26 14:38+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -194,7 +194,7 @@ msgstr "" msgid "The regular expression is invalid" msgstr "" -#: inc/field/dropdownfield.class.php:73 inc/filter/entityfilter.class.php:46 +#: inc/field/dropdownfield.class.php:73 inc/filter/entityfilter.class.php:48 msgid "User and form" msgstr "" @@ -278,7 +278,7 @@ msgid "Radios" msgstr "" #: inc/field/radiosfield.class.php:115 inc/field/glpiselectfield.class.php:93 -#: inc/field/fieldsfield.class.php:512 inc/field/checkboxesfield.class.php:230 +#: inc/field/fieldsfield.class.php:512 inc/field/checkboxesfield.class.php:229 msgid "The field value is required:" msgstr "" @@ -468,25 +468,25 @@ msgstr "" msgid "Checkboxes" msgstr "" -#: inc/field/checkboxesfield.class.php:212 +#: inc/field/checkboxesfield.class.php:211 #, php-format msgid "The following question needs at least %d answers" msgstr "" -#: inc/field/checkboxesfield.class.php:218 +#: inc/field/checkboxesfield.class.php:217 #, php-format msgid "The following question does not accept more than %d answers" msgstr "" -#: inc/field/checkboxesfield.class.php:290 +#: inc/field/checkboxesfield.class.php:289 msgid "Range min" msgstr "" -#: inc/field/checkboxesfield.class.php:291 +#: inc/field/checkboxesfield.class.php:290 msgid "Range max" msgstr "" -#: inc/field/requesttypefield.class.php:86 inc/targetticket.class.php:1095 +#: inc/field/requesttypefield.class.php:86 inc/targetticket.class.php:1099 msgid "Request type" msgstr "" @@ -555,7 +555,7 @@ msgstr "" #: inc/section.class.php:379 inc/restrictedformcriteria.class.php:200 #: inc/question.class.php:828 inc/questionparameter/range.class.php:202 #: inc/questionregex.class.php:185 inc/questiondependency.class.php:196 -#: inc/item_targetticket.class.php:154 inc/targetticket.class.php:1404 +#: inc/item_targetticket.class.php:154 inc/targetticket.class.php:1408 #, php-format msgid "Failed to add or update the %1$s %2$s" msgstr "" @@ -567,7 +567,7 @@ msgstr "" #: inc/section.class.php:404 inc/restrictedformcriteria.class.php:226 #: inc/question.class.php:877 inc/questionparameter/range.class.php:137 #: inc/questionregex.class.php:120 inc/questiondependency.class.php:211 -#: inc/item_targetticket.class.php:64 inc/targetticket.class.php:1443 +#: inc/item_targetticket.class.php:64 inc/targetticket.class.php:1447 #, php-format msgid "Cannot export an empty object: %s" msgstr "" @@ -1101,7 +1101,7 @@ msgid "Properties" msgstr "" #: inc/targetproblem.class.php:522 inc/targetchange.class.php:285 -#: inc/targetticket.class.php:1386 +#: inc/targetticket.class.php:1390 #, php-format msgid "" "Failed to add or update the %1$s %2$s: a question is missing and is used in " @@ -1630,8 +1630,8 @@ msgstr "" msgid "OLA" msgstr "" -#: inc/question.class.php:1197 inc/question.class.php:1237 -#: inc/question.class.php:1240 +#: inc/question.class.php:1197 inc/question.class.php:1238 +#: inc/question.class.php:1241 msgid "Assets" msgstr "" @@ -1639,27 +1639,27 @@ msgstr "" msgid "Assistance" msgstr "" -#: inc/question.class.php:1216 +#: inc/question.class.php:1217 msgid "Management" msgstr "" -#: inc/question.class.php:1225 +#: inc/question.class.php:1226 msgid "Tools" msgstr "" -#: inc/question.class.php:1226 +#: inc/question.class.php:1227 msgid "Notes" msgstr "" -#: inc/question.class.php:1227 +#: inc/question.class.php:1228 msgid "RSS feed" msgstr "" -#: inc/question.class.php:1229 +#: inc/question.class.php:1230 msgid "Administration" msgstr "" -#: inc/question.class.php:1237 inc/question.class.php:1240 +#: inc/question.class.php:1238 inc/question.class.php:1241 msgid "Plugin" msgid_plural "Plugins" msgstr[0] "" @@ -1882,19 +1882,19 @@ msgstr "" msgid "Your form has been accepted by the validator" msgstr "" -#: inc/targetticket.class.php:1082 +#: inc/targetticket.class.php:1086 msgid "Request source" msgstr "" -#: inc/targetticket.class.php:1107 +#: inc/targetticket.class.php:1111 msgid "Type " msgstr "" -#: inc/targetticket.class.php:1135 +#: inc/targetticket.class.php:1139 msgid "Associated elements" msgstr "" -#: inc/targetticket.class.php:1146 +#: inc/targetticket.class.php:1150 msgid "Item " msgstr "" @@ -2278,11 +2278,15 @@ msgstr "" msgid "An error occured while querying forms" msgstr "" -#: js/scripts.js:1400 +#: js/scripts.js:1396 entrée standard:139 +msgid "Send" +msgstr "" + +#: js/scripts.js:1405 msgid "An internal error occurred. Please report it to administrator." msgstr "" -#: js/scripts.js:1441 +#: js/scripts.js:1446 msgid "Are you sure you want to delete this target:" msgstr "" @@ -2337,10 +2341,6 @@ msgstr "" msgid "Are you a robot ?" msgstr "" -#: entrée standard:139 -msgid "Send" -msgstr "" - #: entrée standard:64 msgid "Condition to show the section" msgstr "" From 2b49e37c4e892c9f78166ddc8c84560b640c248b Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Wed, 26 Oct 2022 14:52:45 +0200 Subject: [PATCH 016/248] fix(form): delete question does not reset preview tab --- js/scripts.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/scripts.js b/js/scripts.js index d613e215c..0655f4efc 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -683,6 +683,7 @@ var plugin_formcreator = new function() { if (typeof(id) === 'undefined') { return; } + var that = this; if (confirm(i18n.textdomain('formcreator').__('Are you sure you want to delete this question?', 'formcreator'))) { jQuery.ajax({ url: formcreatorRootDoc + '/ajax/question_delete.php', @@ -697,6 +698,7 @@ var plugin_formcreator = new function() { var gridstack = container.gridstack; var row = $(item).attr('data-gs-y'); gridstack.removeWidget(item); + that.resetTabs(); }); } }; From ee653ffd76912427c5ae797fb6ad4ebb85c41e4c Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 27 Oct 2022 09:38:52 +0200 Subject: [PATCH 017/248] refactor(formanswer): split validateFormAnswer --- inc/formanswer.class.php | 70 ++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/inc/formanswer.class.php b/inc/formanswer.class.php index e5c0cb51e..9a6ab3b1a 100644 --- a/inc/formanswer.class.php +++ b/inc/formanswer.class.php @@ -726,6 +726,14 @@ public function prepareInputForAdd($input) { // Validation of answers failed return false; } + if (!$this->validateCaptcha($input)) { + // Captcha verification failed + return false; + } + if (!$this->validateValidator($input)) { + // Validator requirement failed + return false; + } $input['name'] = $DB->escape($this->parseTags($form->fields['formanswer_name'])); @@ -783,7 +791,7 @@ public function prepareInputForUpdate($input) { } if ($input['status'] != self::STATUS_REFUSED) { - // The requester mai edit his answers + // The requester may edit his answers // or the validator accepts the answers and may edit the requester's answers // check if the input contains answers to validate @@ -799,9 +807,15 @@ public function prepareInputForUpdate($input) { } } - if (!$skipValidation && !$this->validateFormAnswer($input, false)) { - // Validation of answers failed - return false; + if (!$skipValidation) { + if (!$this->validateFormAnswer($input)) { + // Validation of answers failed + return false; + } + if (!$this->validateCaptcha($input)) { + // Captcha verification failed + return false; + } } return $input; @@ -1257,13 +1271,9 @@ public function parseTags(string $content, PluginFormcreatorTargetInterface $tar * Validates answers of a form * * @param array $input fields from the HTML form - * @param bolean $checkValidator True if validator input must be checked * @return boolean true if answers are valid, false otherwise */ - protected function validateFormAnswer($input, $checkValidator = true) { - // Find the form the requester is answering to - $form = PluginFormcreatorCommon::getForm(); - $form->getFromDB($input['plugin_formcreator_forms_id']); + protected function validateFormAnswer($input) { $this->getQuestionFields($input['plugin_formcreator_forms_id']); // Parse form answers @@ -1275,18 +1285,6 @@ protected function validateFormAnswer($input, $checkValidator = true) { // any invalid field will invalidate the answers $this->isAnswersValid = !in_array(false, $fieldValidities, true); - // check captcha if any - if ($this->isAnswersValid && $form->fields['access_rights'] == PluginFormcreatorForm::ACCESS_PUBLIC && $form->fields['is_captcha_enabled'] != '0') { - if (!isset($_SESSION['plugin_formcreator']['captcha'])) { - Session::addMessageAfterRedirect(__('No turing test set', 'formcreator')); - $this->isAnswersValid = false; - } - $this->isAnswersValid = PluginFormcreatorCommon::checkCaptcha($input['plugin_formcreator_captcha_id'], $input['plugin_formcreator_captcha']); - if (!$this->isAnswersValid) { - Session::addMessageAfterRedirect(__('You failed the Turing test', 'formcreator')); - } - } - if ($this->isAnswersValid) { foreach ($this->questionFields as $id => $field) { if (!$this->questionFields[$id]->isPrerequisites()) { @@ -1298,10 +1296,6 @@ protected function validateFormAnswer($input, $checkValidator = true) { } } - if ($form->validationRequired() && $checkValidator) { - $this->validateValidator($input); - } - if (!$this->isAnswersValid) { return false; } @@ -1309,6 +1303,24 @@ protected function validateFormAnswer($input, $checkValidator = true) { return true; } + public function validateCaptcha(array $input): bool { + $form = $this->getForm($input['plugin_formcreator_forms_id']); + if ($this->isAnswersValid && $form->fields['access_rights'] == PluginFormcreatorForm::ACCESS_PUBLIC && $form->fields['is_captcha_enabled'] != '0') { + if (!isset($_SESSION['plugin_formcreator']['captcha'])) { + Session::addMessageAfterRedirect(__('No turing test set', 'formcreator')); + $this->isAnswersValid = false; + return false; + } + $this->isAnswersValid = PluginFormcreatorCommon::checkCaptcha($input['plugin_formcreator_captcha_id'], $input['plugin_formcreator_captcha']); + if (!$this->isAnswersValid) { + Session::addMessageAfterRedirect(__('You failed the Turing test', 'formcreator')); + return false; + } + } + + return true; + } + /** * Validate validator * @@ -1317,11 +1329,13 @@ protected function validateFormAnswer($input, $checkValidator = true) { */ protected function validateValidator(array $input): bool { // Find the form the requester is answering to - $form = PluginFormcreatorCommon::getForm(); - $form->getFromDB($input['plugin_formcreator_forms_id']); + $form = $this->getForm($input['plugin_formcreator_forms_id']); + if (!$form->validationRequired()) { + return true; + } // Check required_validator - if ($form->validationRequired() && empty($input['formcreator_validator'])) { + if (empty($input['formcreator_validator'])) { // Check if only one validator of level 1 is available Session::addMessageAfterRedirect(__('You must select validator!', 'formcreator'), false, ERROR); $this->isAnswersValid = false; From db91aacf41e624280b7c9a14fd3376d06ace64f8 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 27 Oct 2022 09:47:22 +0200 Subject: [PATCH 018/248] fix(common): captcha check --- inc/common.class.php | 1 - 1 file changed, 1 deletion(-) diff --git a/inc/common.class.php b/inc/common.class.php index ea1c1e23c..e9115537e 100644 --- a/inc/common.class.php +++ b/inc/common.class.php @@ -522,7 +522,6 @@ public static function checkCaptcha($captchaId, $challenge, $expiration = 600) { } $result = strtolower($_SESSION['plugin_formcreator']['captcha'][$captchaId]['phrase']) == strtolower((string) $challenge); - unset($_SESSION['plugin_formcreator']['captcha'][$captchaId]); return $result; } From 5a2ad176fb491947d034f82ce8fc17877e6f125d Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 27 Oct 2022 10:11:57 +0200 Subject: [PATCH 019/248] docs: add comments, typehinting --- inc/formanswer.class.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/inc/formanswer.class.php b/inc/formanswer.class.php index 9a6ab3b1a..80c8baaff 100644 --- a/inc/formanswer.class.php +++ b/inc/formanswer.class.php @@ -1273,7 +1273,7 @@ public function parseTags(string $content, PluginFormcreatorTargetInterface $tar * @param array $input fields from the HTML form * @return boolean true if answers are valid, false otherwise */ - protected function validateFormAnswer($input) { + protected function validateFormAnswer($input): bool { $this->getQuestionFields($input['plugin_formcreator_forms_id']); // Parse form answers @@ -1303,6 +1303,12 @@ protected function validateFormAnswer($input) { return true; } + /** + * Check the captcha is resolved by the user + * + * @param array $input + * @return boolean + */ public function validateCaptcha(array $input): bool { $form = $this->getForm($input['plugin_formcreator_forms_id']); if ($this->isAnswersValid && $form->fields['access_rights'] == PluginFormcreatorForm::ACCESS_PUBLIC && $form->fields['is_captcha_enabled'] != '0') { From 7ae12dceae436fc41189cbb000238ca7e80199ec Mon Sep 17 00:00:00 2001 From: btry Date: Thu, 27 Oct 2022 15:51:06 +0200 Subject: [PATCH 020/248] fix(issue): sync issue fails when a ticket has several validators if a ticket has several validators, the sync issue fails to recreate the row the HAVING COUNT() clause finds 2 rows instead of 1 The solution is to drop the user and group validators and find them dynamically with JOIN. this also allows to find all ticket validators --- hook.php | 50 +++++--- inc/formanswer.class.php | 4 - inc/issue.class.php | 109 +++++++++++++----- install/install.php | 1 + .../mysql/plugin_formcreator_2.13.3_empty.sql | 6 +- install/upgrade_to_2.13.3.php | 54 +++++++++ tests/3-unit/PluginFormcreatorIssue.php | 101 ++++++++++++++-- 7 files changed, 260 insertions(+), 65 deletions(-) create mode 100644 install/upgrade_to_2.13.3.php diff --git a/hook.php b/hook.php index a12d973fc..bb6cf4f2a 100644 --- a/hook.php +++ b/hook.php @@ -98,6 +98,16 @@ function plugin_formcreator_addDefaultJoin($itemtype, $ref_table, &$already_link $join .= PluginAdvformCommon::addDefaultJoin($itemtype, $ref_table, $already_link_tables); } else { $issueSo = Search::getOptions($itemtype); + $join .= Search::addLeftJoin( + $itemtype, + $ref_table, + $already_link_tables, + $issueSo[9]['table'], + 'users_id_validator', + 0, + 0, + $issueSo[9]['joinparams'] + ); $join .= Search::addLeftJoin( $itemtype, $ref_table, @@ -108,7 +118,16 @@ function plugin_formcreator_addDefaultJoin($itemtype, $ref_table, &$already_link 0, $issueSo[11]['joinparams'] ); - + $join .= Search::addLeftJoin( + $itemtype, + $ref_table, + $already_link_tables, + $issueSo[16]['table'], + 'groups_id_validator', + 0, + 0, + $issueSo[16]['joinparams'] + ); } break; @@ -145,12 +164,11 @@ function plugin_formcreator_addDefaultWhere($itemtype) { $condition .= ' OR '; } // condition where current user is a validator of the issue - if (Plugin::isPluginActive(PLUGIN_FORMCREATOR_ADVANCED_VALIDATION)) { - $complexJoinId = Search::computeComplexJoinID(Search::getOptions($itemtype)[9]['joinparams']); - $condition .= "`glpi_users_$complexJoinId`.`id` = '$currentUser'"; - } else { - $condition .= "`glpi_plugin_formcreator_issues`.`users_id_validator` = '$currentUser'"; - } + // Search optin ID 9 is either from Formcreator, either from AdvForms + $issueSearchOptions = Search::getOptions($itemtype); + $complexJoinId = Search::computeComplexJoinID($issueSearchOptions[9]['joinparams']); + $colname = $issueSearchOptions[9]['linkfield']; + $condition .= "`glpi_users_${colname}_$complexJoinId`.`id` = '$currentUser'"; // condition where current user is a member of a validator group of the issue $groupList = []; @@ -159,16 +177,14 @@ function plugin_formcreator_addDefaultWhere($itemtype) { } if (count($groupList) > 0) { $groupList = implode("', '", $groupList); - if (Plugin::isPluginActive(PLUGIN_FORMCREATOR_ADVANCED_VALIDATION)) { - $complexJoinId = Search::computeComplexJoinID(Search::getOptions($itemtype)[9]['joinparams']); - $condition .= " OR `glpi_groups_$complexJoinId`.`id` IN ('$groupList')"; - } else { - $condition .= " OR `glpi_plugin_formcreator_issues`.`groups_id_validator` IN ('$groupList')"; - } + // Search option ID 16 is either from Formcreator, either from AdvForms + $complexJoinId = Search::computeComplexJoinID($issueSearchOptions[16]['joinparams']); + $colname = $issueSearchOptions[16]['linkfield']; + $condition .= " OR `glpi_groups_${colname}_$complexJoinId`.`id` IN ('$groupList')"; } // condition where current user is a validator of a issue of type ticket - $complexJoinId = Search::computeComplexJoinID(Search::getOptions($itemtype)[11]['joinparams']); + $complexJoinId = Search::computeComplexJoinID($issueSearchOptions[11]['joinparams']); $condition .= " OR `glpi_users_users_id_validate_$complexJoinId`.`id` = '$currentUser'"; // Add users_id_recipient $condition .= " OR `glpi_plugin_formcreator_issues`.`users_id_recipient` = $currentUser "; @@ -368,7 +384,7 @@ function plugin_formcreator_hook_add_ticket(CommonDBTM $item) { 'name' => $issueName, 'display_id' => 't_' . $item->getID(), 'items_id' => $item->getID(), - 'itemtype' => 'Ticket', + 'itemtype' => Ticket::class, 'status' => $validationStatus, 'date_creation' => $item->fields['date'], 'date_mod' => $item->fields['date_mod'], @@ -417,7 +433,7 @@ function plugin_formcreator_hook_update_ticket(CommonDBTM $item) { 'id' => $issue->getID(), 'items_id' => $id, 'display_id' => "t_$id", - 'itemtype' => 'Ticket', + 'itemtype' => Ticket::class, 'name' => $issueName, 'status' => $validationStatus, 'date_creation' => $item->fields['date'], @@ -581,7 +597,7 @@ function plugin_formcreator_dynamicReport($params) { case PluginFormcreatorFormAnswer::class; if ($url = parse_url($_SERVER['HTTP_REFERER'])) { if (strpos($url['path'], - Toolbox::getItemTypeFormURL("PluginFormcreatorForm")) !== false) { + Toolbox::getItemTypeFormURL(PluginFormcreatorForm::class)) !== false) { parse_str($url['query'], $query); if (isset($query['id'])) { $item = PluginFormcreatorCommon::getForm(); diff --git a/inc/formanswer.class.php b/inc/formanswer.class.php index 80c8baaff..dfd9791f2 100644 --- a/inc/formanswer.class.php +++ b/inc/formanswer.class.php @@ -1418,8 +1418,6 @@ public function createIssue() { 'entities_id' => $this->fields['entities_id'], 'is_recursive' => $this->fields['is_recursive'], 'requester_id' => $this->fields['requester_id'], - 'users_id_validator' => $this->fields['users_id_validator'], - 'groups_id_validator'=> $this->fields['groups_id_validator'], 'comment' => '', ]); @@ -1522,8 +1520,6 @@ private function updateIssue() { 'entities_id' => $this->fields['entities_id'], 'is_recursive' => $this->fields['is_recursive'], 'requester_id' => $this->fields['requester_id'], - 'users_id_validator' => $this->fields['users_id_validator'], - 'groups_id_validator'=> $this->fields['groups_id_validator'], 'comment' => '', ]); diff --git a/inc/issue.class.php b/inc/issue.class.php index 28d6dfeec..040beefde 100644 --- a/inc/issue.class.php +++ b/inc/issue.class.php @@ -105,8 +105,6 @@ public static function getSyncIssuesRequest() : AbstractQuery { 'entities_id as entities_d', 'is_recursive as is_recursive', 'requester_id as requester_id', - 'users_id_validator as users_id_validator', - 'groups_id_validator as groups_id_validator', 'comment as comment', 'requester_id as users_id_recipient' ], @@ -145,16 +143,13 @@ public static function getSyncIssuesRequest() : AbstractQuery { new QueryExpression("CONCAT('t_', `$ticketTable`.`id`) as `display_id`"), "$ticketTable.id as items_id", new QueryExpression("'" . Ticket::getType() . "' as `itemtype`"), - new QueryExpression("IF(`$ticketValidationTable`.`status` IS NULL, + new QueryExpression("IF(`$ticketTable`.`global_validation` IN ('" . CommonITILValidation::NONE . "', '" . CommonITILValidation::ACCEPTED . "'), `$ticketTable`.`status`, - IF(`$ticketTable`.`global_validation` IN ('" . CommonITILValidation::NONE . "', '" . CommonITILValidation::ACCEPTED . "'), + IF(`$ticketTable`.`status` IN ('" . CommonITILObject::SOLVED . "', '" . CommonITILObject::CLOSED . "'), `$ticketTable`.`status`, - IF(`$ticketTable`.`status` IN ('" . CommonITILObject::SOLVED . "', '" . CommonITILObject::CLOSED . "'), - `$ticketTable`.`status`, - IF(`$ticketTable`.`global_validation` = '" . CommonITILValidation::WAITING . "', - '" . PluginFormcreatorFormAnswer::STATUS_WAITING . "', - '" . PluginFormcreatorFormAnswer::STATUS_REFUSED . "' - ) + IF(`$ticketTable`.`global_validation` = '" . CommonITILValidation::WAITING . "', + '" . PluginFormcreatorFormAnswer::STATUS_WAITING . "', + '" . PluginFormcreatorFormAnswer::STATUS_REFUSED . "' ) ) ) AS `status`"), @@ -165,8 +160,6 @@ public static function getSyncIssuesRequest() : AbstractQuery { ], new QueryExpression('0 as is_recursive'), new QueryExpression("COALESCE(`$ticketUserTable`.`users_id`, 0) as `requester_id`"), - new QueryExpression("COALESCE(`$ticketValidationTable`.`users_id_validate`, 0) as `users_id_validator`"), - new QueryExpression('0 as groups_id_validator'), "$ticketTable.content as comment", 'users_id_recipient as users_id_recipient' ], @@ -198,12 +191,6 @@ public static function getSyncIssuesRequest() : AbstractQuery { $ticketUserTable => $ticketFk, ], ], - $ticketValidationTable => [ - 'FKEY' => [ - $ticketTable => 'id', - $ticketValidationTable => $ticketFk, - ], - ], ], 'WHERE' => [ "$ticketTable.is_deleted" => 0, @@ -219,7 +206,7 @@ public static function getSyncIssuesRequest() : AbstractQuery { 'INNER JOIN' => [$itemTicketTable => $query2['LEFT JOIN'][$itemTicketTable]], 'LEFT JOIN' => [ $query2['LEFT JOIN'][0], // This is the TABLE => [...] subquery - $ticketValidationTable => $query2['LEFT JOIN'][$ticketValidationTable], + // $ticketValidationTable => $query2['LEFT JOIN'][$ticketValidationTable], ], 'WHERE' => $query2['WHERE'], 'GROUPBY' => ["$itemTicketTable.items_id"], @@ -521,7 +508,6 @@ public function rawSearchOptions() { 'name' => __('Name'), 'datatype' => 'itemlink', 'massiveaction' => false, - 'forcegroupby' => true, 'additionalfields' => [ '0' => 'display_id' ] @@ -608,14 +594,22 @@ public function rawSearchOptions() { } else { $newtab = [ 'id' => '9', - 'table' => 'glpi_users', + 'table' => User::getTable(), 'field' => 'name', 'linkfield' => 'users_id_validator', 'name' => __('Form approver', 'formcreator'), 'datatype' => 'dropdown', - 'massiveaction' => false + 'massiveaction' => false, + 'joinparams' => [ + 'beforejoin' => [ + 'table' => PluginFormcreatorFormAnswer::getTable(), + 'joinparams' => [ + 'jointype' => 'itemtype_item_revert', + 'specific_itemtype' => PluginFormcreatorFormAnswer::class, + ] + ], + ], ]; - } if (!Session::isCron() // no filter for cron && Session::getCurrentInterface() == 'helpdesk') { @@ -640,12 +634,12 @@ public function rawSearchOptions() { 'field' => 'name', 'linkfield' => 'users_id_validate', 'name' => __('Ticket approver', 'formcreator'), - 'datatype' => 'dropdown', + 'datatype' => 'itemlink', 'right' => [ '0' => 'validate_request', '1' => 'validate_incident' ], - 'forcegroupby' => false, + 'forcegroupby' => true, 'massiveaction' => false, 'joinparams' => [ 'beforejoin' => [ @@ -663,6 +657,12 @@ public function rawSearchOptions() { ], ] ]; + if (version_compare(GLPI_VERSION, '10.1') >= 0) { + $newtab['linkfield'] = 'items_id_target'; + $newtab['condition'] = [ + 'REFTABLE.itemtype_target' => User::class, + ]; + } if (!Session::isCron() // no filter for cron && Session::getCurrentInterface() == 'helpdesk') { $newtab['right'] = 'id'; @@ -675,7 +675,6 @@ public function rawSearchOptions() { 'field' => 'name', 'name' => __('Technician'), 'datatype' => 'dropdown', - 'forcegroupby' => true, 'massiveaction' => false, 'nodisplay' => $hide_technician, 'nosearch' => $hide_technician, @@ -716,7 +715,9 @@ public function rawSearchOptions() { 'beforejoin' => [ 'table' => Group_Ticket::getTable(), 'joinparams' => [ - 'condition' => "AND NEWTABLE.`type` = '2'", // Assign + 'condition' => [ + 'NEWTABLE.type' => CommonITILActor::ASSIGN, + ], 'jointype' => 'child', 'beforejoin' => [ 'table' => Ticket::getTable(), @@ -732,17 +733,63 @@ public function rawSearchOptions() { } if (Plugin::isPluginActive(PLUGIN_FORMCREATOR_ADVANCED_VALIDATION)) { - $tab[] = PluginAdvformIssue::rawSearchOptionFormApproverGroup(); + $newtab = PluginAdvformIssue::rawSearchOptionFormApproverGroup(); } else { - $tab[] = [ + $newtab = [ 'id' => '16', - 'table' => 'glpi_groups', + 'table' => Group::getTable(), 'field' => 'completename', + 'linkfield' => 'groups_id_validator', 'name' => __('Form approver group', 'formcreator'), 'datatype' => 'itemlink', 'massiveaction' => false, - 'linkfield' => 'groups_id_validator', + 'joinparams' => [ + 'beforejoin' => [ + 'table' => PluginFormcreatorFormAnswer::getTable(), + 'joinparams' => [ + 'jointype' => 'itemtype_item_revert', + 'specific_itemtype' => PluginFormcreatorFormAnswer::class, + ] + ], + ], + ]; + } + $tab[] = $newtab; + + if (version_compare(GLPI_VERSION, '10.1') >= 0) { + $newtab = [ + 'id' => '17', + 'table' => Group::getTable(), + 'field' => 'name', + 'linkfield' => 'items_id_target', + 'name' => __('Ticket approver group', 'formcreator'), + 'datatype' => 'itemlink', + 'condition' => [ + 'REFTABLE.itemtype_target' => User::class, + ], + 'right' => [ + '0' => 'validate_request', + '1' => 'validate_incident' + ], + 'forcegroupby' => true, + 'massiveaction' => false, + 'joinparams' => [ + 'beforejoin' => [ + 'table' => TicketValidation::getTable(), + 'joinparams' => [ + 'jointype' => 'child', + 'beforejoin' => [ + 'table' => Ticket::getTable(), + 'joinparams' => [ + 'jointype' => 'itemtype_item_revert', + 'specific_itemtype' => Ticket::class, + ] + ] + ] + ], + ] ]; + $tab[] = $newtab; } if (Plugin::isPluginActive(PLUGIN_FORMCREATOR_ADVANCED_VALIDATION)) { diff --git a/install/install.php b/install/install.php index 10fa0c6e0..1eb315b08 100644 --- a/install/install.php +++ b/install/install.php @@ -78,6 +78,7 @@ class PluginFormcreatorInstall { '2.12.1' => '2.12.5', '2.12.5' => '2.13', '2.13' => '2.13.1', + '2.13.1' => '2.13.3', ]; protected bool $resyncIssues = false; diff --git a/install/mysql/plugin_formcreator_2.13.3_empty.sql b/install/mysql/plugin_formcreator_2.13.3_empty.sql index a83d74bb6..90570138d 100644 --- a/install/mysql/plugin_formcreator_2.13.3_empty.sql +++ b/install/mysql/plugin_formcreator_2.13.3_empty.sql @@ -294,16 +294,12 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_issues` ( `entities_id` int unsigned NOT NULL DEFAULT '0', `is_recursive` tinyint(1) NOT NULL DEFAULT '0', `requester_id` int unsigned NOT NULL DEFAULT '0', - `users_id_validator` int unsigned NOT NULL DEFAULT '0', - `groups_id_validator` int unsigned NOT NULL DEFAULT '0', `comment` longtext, `users_id_recipient` int unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), INDEX `item` (`itemtype`, `items_id`), INDEX `entities_id` (`entities_id`), - INDEX `requester_id` (`requester_id`), - INDEX `users_id_validator` (`users_id_validator`), - INDEX `groups_id_validator` (`groups_id_validator`) + INDEX `requester_id` (`requester_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; CREATE TABLE IF NOT EXISTS `glpi_plugin_formcreator_items_targettickets` ( `id` int unsigned NOT NULL AUTO_INCREMENT, diff --git a/install/upgrade_to_2.13.3.php b/install/upgrade_to_2.13.3.php new file mode 100644 index 000000000..d29a0ea3d --- /dev/null +++ b/install/upgrade_to_2.13.3.php @@ -0,0 +1,54 @@ +. + * --------------------------------------------------------------------- + * @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 + * --------------------------------------------------------------------- + */ +class PluginFormcreatorUpgradeTo2_13_3 { + /** @var Migration */ + protected $migration; + + public function isResyncIssuesRequired() { + return true; + } + + /** + * @param Migration $migration + */ + public function upgrade(Migration $migration) { + $this->migration = $migration; + $this->dropIssuesValidators(); + } + + public function dropIssuesValidators() { + $table = 'glpi_plugin_formcreator_issues'; + $this->migration->dropKey($table, 'users_id_validator'); + $this->migration->dropKey($table, 'groups_id_validator'); + $this->migration->dropField($table, 'users_id_validator'); + $this->migration->dropField($table, 'groups_id_validator'); + } +} diff --git a/tests/3-unit/PluginFormcreatorIssue.php b/tests/3-unit/PluginFormcreatorIssue.php index 9b5ad7772..1e8f9c3bb 100644 --- a/tests/3-unit/PluginFormcreatorIssue.php +++ b/tests/3-unit/PluginFormcreatorIssue.php @@ -32,6 +32,12 @@ namespace tests\units; use GlpiPlugin\Formcreator\Tests\CommonTestCase; use Ticket; +use PluginFormcreatorFormAnswer; +use RuleAction; +use User; +use Rule; +use RuleCriteria; +use CommonITILObject; class PluginFormcreatorIssue extends CommonTestCase { public function beforeTestMethod($method) { @@ -109,8 +115,6 @@ public function providerGetsyncIssuesRequest_simpleTicket() { 'requester_id' => $ticket2->fields['users_id_recipient'], 'date_creation' => $ticket2->fields['date'], 'date_mod' => $ticket2->fields['date_mod'], - 'users_id_validator' => '0', - 'groups_id_validator' => '0', ] ] ]; @@ -145,6 +149,7 @@ public function providerGetsyncIssuesRequest_simpleFormanswers() { } public function providerGetSyncIssuesRequest_formAnswerWithOneTicket() { + // case 1 $form = $this->getForm(); $targetTicket1 = new \PluginFormcreatorTargetTicket(); $targetTicket1->add([ @@ -162,6 +167,46 @@ public function providerGetSyncIssuesRequest_formAnswerWithOneTicket() { $ticket = array_shift($formAnswer->targetList); $this->object($ticket)->isInstanceOf(\Ticket::getType()); + // case 2 + // Add business rules to add 2 validators + $rule = $this->getGlpiCoreItem(Rule::class, [ + 'sub_type' => RuleTicket::class, + 'name' => 'add validators', + 'match' => 'AND', + 'is_active' => 1, + 'condition' => 1, + ]); + $ruleCriteria = $this->getGlpiCoreItem(RuleCriteria::class, [ + $rule::getForeignKeyField() => $rule->getID(), + 'criteria' => '_groups_id_of_requester', + 'condition' => 1, + 'pattern' => User::getIdByName('normal'), + ]); + $ruleAction = $this->getGlpiCoreItem(RuleAction::class, [ + $rule::getForeignKeyField() => $rule->getID(), + 'action_type' => 'add_validation', + 'field' => 'users_id_validate', + 'value' => User::getIdByName('glpi'), + ]); + $ruleAction = $this->getGlpiCoreItem(RuleAction::class, [ + $rule::getForeignKeyField() => $rule->getID(), + 'action_type' => 'add_validation', + 'field' => 'users_id_validate', + 'value' => User::getIdByName('normal'), + ]); + $formAnswer = new \PluginFormcreatorFormAnswer(); + $formAnswer->add([ + 'plugin_formcreator_forms_id' => $form->getID(), + ]); + $rule->update([ + 'id' => $rule->getID(), + 'is_active' => 0, + ]); + $this->boolean($formAnswer->isNewItem())->isFalse(); + $formAnswer->getFromDB($formAnswer->getID()); + $ticket2 = array_shift($formAnswer->targetList); + $this->object($ticket)->isInstanceOf(\Ticket::getType()); + return [ 'formAnswerWithOneTicket' => [ 'item' => $ticket, @@ -176,6 +221,19 @@ public function providerGetSyncIssuesRequest_formAnswerWithOneTicket() { 'date_mod' => $ticket->fields['date_mod'], ], ], + 'formAnswer With One Ticket Having several validators' => [ + 'item' => $ticket2, + 'expected' => [ + 'itemtype' => \Ticket::getType(), + 'items_id' => $ticket2->getID(), + 'display_id' => 't_' . $ticket2->getID(), + 'name' => $ticket2->fields['name'], + 'status' => CommonITILObject::ASSIGNED, + 'requester_id' => $ticket2->fields['users_id_recipient'], + 'date_creation' => $ticket2->fields['date'], + 'date_mod' => $ticket2->fields['date_mod'], + ], + ], ]; } @@ -279,8 +337,6 @@ public function providerGetSyncIssuesRequest_formanswerUnderValidation() { 'requester_id' => $formAnswer->fields['requester_id'], 'date_creation' => $formAnswer->fields['request_date'], 'date_mod' => $formAnswer->fields['request_date'], - //'users_id_validator' => '4', // Tech - //'groups_id_validator' => '0', ], ], ]; @@ -319,8 +375,6 @@ public function providerGetsyncIssuesRequest_ticketUnderValidation() { 'requester_id' => $ticket->fields['users_id_recipient'], 'date_creation' => $ticket->fields['date'], 'date_mod' => $ticket->fields['date_mod'], - // 'users_id_validator' => '4', // Tech - // 'groups_id_validator' => '0', ], ], ]; @@ -358,6 +412,26 @@ public function providerGetsyncIssuesRequest_validatedTicket() { 'status' => \TicketValidation::ACCEPTED ]); + $ticket2 = $this->getGlpiCoreItem(Ticket::class, [ + 'name' => 'a ticket', + 'content' => 'foo', + 'status' => \Ticket::INCOMING, + '_add_validation' => '0', + 'validatortype' => User::class, + 'users_id_validate' => [User::getIdByName('tech'), User::getIdByName('normal')], // Tech + '_actors' => [ + 'requester' => [ + 0 => ['itemtype' => \User::class, + 'items_id' => User::getIdByName('glpi'), // glpi + 'use_notification' => 1, + 'alternative_email' => '', + ] + ] + ] + ]); + $this->boolean($ticket2->isNewItem())->isFalse(); + $ticket2->getFromDB($ticket2->getID()); + return [ 'validatedTicket' => [ 'item' => $ticket, @@ -370,10 +444,21 @@ public function providerGetsyncIssuesRequest_validatedTicket() { 'requester_id' => $ticket->fields['users_id_recipient'], 'date_creation' => $ticket->fields['date'], 'date_mod' => $ticket->fields['date_mod'], - // 'users_id_validator' => '4', // Tech - // 'groups_id_validator' => '0', ], ], + 'ticket with multiple validators' => [ + 'item' => $ticket2, + 'expected' => [ + 'itemtype' => \Ticket::getType(), + 'items_id' => $ticket2->getID(), + 'display_id' => 't_' . $ticket2->getID(), + 'name' => $ticket2->fields['name'], + 'status' => PluginFormcreatorFormAnswer::STATUS_WAITING, + 'requester_id' => $ticket2->fields['users_id_recipient'], + 'date_creation' => $ticket2->fields['date'], + 'date_mod' => $ticket2->fields['date_mod'], + ], + ] ]; } From 1c56d14e98230dc1117ca513ecd4dfed5e67eea7 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 27 Oct 2022 14:04:07 +0200 Subject: [PATCH 021/248] fix(textareadifield): error when deduplicating uploads --- inc/field/textareafield.class.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/inc/field/textareafield.class.php b/inc/field/textareafield.class.php index ed6a9bad8..967b355fe 100644 --- a/inc/field/textareafield.class.php +++ b/inc/field/textareafield.class.php @@ -191,6 +191,15 @@ public function serializeValue(PluginFormcreatorFormAnswer $formanswer): string $key = 'formcreator_field_' . $this->question->getID(); if (isset($this->uploads['_' . $key])) { $input = [$key => $this->value] + $this->uploads; + // for each uploaded document, check if it already exists in DB + foreach ($this->uploads['_tag_' . $key] as $docKey => $tag) { + $document = new Document(); + $newTag = $tag; + if ($document->getDuplicateOf($formanswer->fields['entities_id'], GLPI_TMP_DIR . '/' . $input['_' . $key][$docKey])) { + $newTag = $document->fields['tag']; + } + $this->uploads['dedup'][$tag] = $newTag; + } $input = $formanswer->addFiles( $input, [ @@ -203,11 +212,7 @@ public function serializeValue(PluginFormcreatorFormAnswer $formanswer): string // $this->value = $input[$key]; $this->value = Sanitizer::unsanitize($this->value); foreach ($input['_tag'] as $docKey => $tag) { - $document = new Document(); - $newTag = $tag; - if ($document->getDuplicateOf($formanswer->fields['entities_id'], GLPI_TMP_DIR . '/' . $input['_' . $key][$docKey])) { - $newTag = $document->fields['tag']; - } + $newTag = $this->uploads['dedup'][$tag]; $regex = '/]+' . preg_quote($tag, '/') . '[^<]+>/im'; $this->value = preg_replace($regex, "#$newTag#", $this->value); } From cc7028283e0989d9b247bca3752bcc7c87fb0d5a Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 27 Oct 2022 15:38:47 +0200 Subject: [PATCH 022/248] fix(abstractitiltarget): copy may generate unwanted ouput to navigator --- inc/abstractitiltarget.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/abstractitiltarget.class.php b/inc/abstractitiltarget.class.php index 50e91c11d..a1a5fbf75 100644 --- a/inc/abstractitiltarget.class.php +++ b/inc/abstractitiltarget.class.php @@ -2331,7 +2331,7 @@ protected function prepareUploadedFiles(array $data): array { foreach (PluginFormcreatorCommon::getDocumentsFromTag($data['content']) as $document) { $prefix = uniqid('', true); $filename = $prefix . 'image_paste.' . pathinfo($document['filename'], PATHINFO_EXTENSION); - if (!copy(GLPI_DOC_DIR . '/' . $document['filepath'], GLPI_TMP_DIR . '/' . $filename)) { + if (!@copy(GLPI_DOC_DIR . '/' . $document['filepath'], GLPI_TMP_DIR . '/' . $filename)) { continue; } From ebf0e020f66c3dd45336afb16d23b0b4f1db087b Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 27 Oct 2022 15:00:46 +0200 Subject: [PATCH 023/248] fix(filefield): do not assume index of files whern adding, removing then adding again a file, indexes are somewhat consumed. Indes 0 will not exist in uploads list. --- inc/field/filefield.class.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/inc/field/filefield.class.php b/inc/field/filefield.class.php index c43cfdfe8..baf97923a 100644 --- a/inc/field/filefield.class.php +++ b/inc/field/filefield.class.php @@ -129,8 +129,7 @@ public function moveUploads() { return; } $answer_value = []; - $index = 0; - foreach ($this->uploads["_$key"] as $document) { + foreach ($this->uploads["_$key"] as $index => $document) { $document = Toolbox::stripslashes_deep($document); if (is_file(GLPI_TMP_DIR . '/' . $document)) { $prefix = $this->uploads['_prefix_formcreator_field_' . $this->question->getID()][$index]; From 5d05170a79926d09c0e1231ad604d1c423cc5bd1 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Mon, 31 Oct 2022 14:00:13 +0100 Subject: [PATCH 024/248] fix(issue): default joint for issue missed in #2959 --- hook.php | 1 - 1 file changed, 1 deletion(-) diff --git a/hook.php b/hook.php index bb6cf4f2a..c69ab52ca 100644 --- a/hook.php +++ b/hook.php @@ -89,7 +89,6 @@ function plugin_formcreator_addDefaultJoin($itemtype, $ref_table, &$already_link case PluginFormcreatorIssue::class: // Get default joins for tickets $join = Search::addDefaultJoin(Ticket::getType(), Ticket::getTable(), $already_link_tables); - $join .= Search::addLeftJoin($itemtype, $ref_table, $already_link_tables, Group::getTable(), 'groups_id_validator'); // but we want to join in issues $join = str_replace('`glpi_tickets`.`id`', '`glpi_plugin_formcreator_issues`.`itemtype` = "Ticket" AND `glpi_plugin_formcreator_issues`.`items_id`', $join); $join = str_replace('`glpi_tickets`', '`glpi_plugin_formcreator_issues`', $join); From abebb55acb2a0503ab7cd2e1a5849d67619e544d Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Mon, 31 Oct 2022 14:33:33 +0100 Subject: [PATCH 025/248] fix(form): tab name must obey 'show count' setting --- inc/form.class.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/inc/form.class.php b/inc/form.class.php index 2f13de9f1..d1de6252f 100644 --- a/inc/form.class.php +++ b/inc/form.class.php @@ -556,9 +556,10 @@ class="far fa-trash-alt formcreator_delete_target" public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { if ($item instanceof PluginFormcreatorForm) { + $nb = $_SESSION['glpishow_count_on_tabs'] ? $item->countTargets() : 0; return [ 1 => self::createTabEntry( - _n('Target', 'Targets', Session::getPluralNumber(), 'formcreator'), + _n('Target', 'Targets', $nb, 'formcreator'), $item->countTargets() ), 2 => __('Preview'), From 5e3e1398eeafdb075f2ff0f37719016c96558bc5 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Mon, 31 Oct 2022 14:33:46 +0100 Subject: [PATCH 026/248] style: code cleanup --- setup.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.php b/setup.php index a1c1e7c57..a8254819b 100644 --- a/setup.php +++ b/setup.php @@ -163,7 +163,6 @@ function plugin_init_formcreator() { } } - // Html::requireJs('gridstack'); $CFG_GLPI['javascript']['admin'][strtolower(PluginFormcreatorForm::class)] = ['gridstack']; $CFG_GLPI['javascript']['helpdesk'][strtolower(PluginFormcreatorFormlist::class)] = ['gridstack']; $CFG_GLPI['javascript']['helpdesk'][strtolower(PluginFormcreatorIssue::class)] = ['photoswipe']; @@ -383,7 +382,6 @@ function plugin_formcreator_hook(): void { function plugin_formcreator_registerClasses() { // Load menu entries if user is logged in and if he has access to at least one form if (Session::getLoginUserID() !== false) { - Plugin::registerClass(PluginFormcreatorEntityconfig::class, ['addtabon' => Entity::class]); } Plugin::registerClass(PluginFormcreatorForm::class, ['addtabon' => Central::class]); From d7bdca15b4a3e33d0e2d11e2715596b13a0f664b Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Tue, 25 Oct 2022 13:13:03 +0200 Subject: [PATCH 027/248] fix(issue): access to saved searches from service catalog --- front/issue.php | 6 +++- inc/common.class.php | 9 +++++ setup.php | 3 +- tests/3-unit/PluginFormcreatorCommon.php | 46 ++++++++++++++++-------- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/front/issue.php b/front/issue.php index 3473d1016..6f4bf6f50 100644 --- a/front/issue.php +++ b/front/issue.php @@ -40,7 +40,11 @@ Html::displayRightError(); } if (Session::getCurrentInterface() == "helpdesk") { - Html::helpHeader(__('Service catalog', 'formcreator')); + Html::helpHeader( + __('Service catalog', 'formcreator'), + 'my_assistance_requests', + PluginFormcreatorIssue::class + ); } else { Html::header( __('Service catalog', 'formcreator'), diff --git a/inc/common.class.php b/inc/common.class.php index e9115537e..76d30ee99 100644 --- a/inc/common.class.php +++ b/inc/common.class.php @@ -783,6 +783,15 @@ public static function hookRedefineMenu($menus) { 'default' => PluginFormcreatorIssue::getSearchURL(false), 'title' => __('My requests for assistance', 'formcreator'), 'icon' => 'fa-fw ti ti-list', + 'content' => [ + PluginFormcreatorIssue::class => [ + 'title' => __('My requests for assistance', 'formcreator'), + 'icon' => 'fa-fw ti ti-list', + 'links' => [ + 'lists' => '', + ], + ], + ], ]; if (PluginFormcreatorEntityConfig::getUsedConfig('is_kb_separated', Session::getActiveEntity()) == PluginFormcreatorEntityConfig::CONFIG_KB_DISTINCT diff --git a/setup.php b/setup.php index a8254819b..3cfdcef01 100644 --- a/setup.php +++ b/setup.php @@ -349,7 +349,7 @@ function plugin_formcreator_hook(): void { ]; } if (strpos($_SERVER['REQUEST_URI'], 'issue.php') !== false) { - $CFG_GLPI['javascript']['self-service']['none'] = [ + $CFG_GLPI['javascript']['my_assistance_requests'][PluginFormcreatorIssue::class] = [ 'dashboard', 'gridstack' ]; @@ -364,6 +364,7 @@ function plugin_formcreator_hook(): void { } $PLUGIN_HOOKS['menu_toadd']['formcreator']['helpdesk'] = PluginFormcreatorFormlist::class; + $PLUGIN_HOOKS['menu_toadd']['formcreator']['my_assistance_requests'] = PluginFormcreatorIssue::class; // Massive Action definition $PLUGIN_HOOKS['use_massive_action']['formcreator'] = 1; diff --git a/tests/3-unit/PluginFormcreatorCommon.php b/tests/3-unit/PluginFormcreatorCommon.php index ab10ade3c..b6c806117 100644 --- a/tests/3-unit/PluginFormcreatorCommon.php +++ b/tests/3-unit/PluginFormcreatorCommon.php @@ -30,6 +30,7 @@ */ namespace tests\units; use GlpiPlugin\Formcreator\Tests\CommonTestCase; +use PluginFormcreatorIssue; use Ticket; use User; @@ -572,23 +573,29 @@ public function providerHookRedefineMenu() { yield [ 'input' => \Html::generateHelpMenu(), 'expected' => [ - 'seek_assistance' => - [ - 'default' => 'plugins/formcreator/front/wizard.php', - 'title' => 'Seek assistance', - 'icon' => 'fa-fw ti ti-headset', + 'seek_assistance' => [ + 'default' => 'plugins/formcreator/front/wizard.php', + 'title' => 'Seek assistance', + 'icon' => 'fa-fw ti ti-headset', ], - 'my_assistance_requests' => - [ - 'default' => '/plugins/formcreator/front/issue.php', - 'title' => 'My requests for assistance', - 'icon' => 'fa-fw ti ti-list', + 'my_assistance_requests' => [ + 'default' => '/plugins/formcreator/front/issue.php', + 'title' => 'My requests for assistance', + 'icon' => 'fa-fw ti ti-list', + 'content' => [ + PluginFormcreatorIssue::class => [ + 'title' => __('My requests for assistance', 'formcreator'), + 'icon' => 'fa-fw ti ti-list', + 'links' => [ + 'lists' => '', + ], + ], + ], ], - 'reservation' => - [ - 'default' => '/front/reservationitem.php', - 'title' => 'Reservations', - 'icon' => 'ti ti-calendar-event', + 'reservation' => [ + 'default' => '/front/reservationitem.php', + 'title' => 'Reservations', + 'icon' => 'ti ti-calendar-event', ], ] ]; @@ -619,6 +626,15 @@ public function providerHookRedefineMenu() { 'default' => '/plugins/formcreator/front/issue.php', 'title' => 'My requests for assistance', 'icon' => 'fa-fw ti ti-list', + 'content' => [ + PluginFormcreatorIssue::class => [ + 'title' => __('My requests for assistance', 'formcreator'), + 'icon' => 'fa-fw ti ti-list', + 'links' => [ + 'lists' => '', + ], + ], + ], ], 'reservation' => [ From 5cec2b7194d72422cebdd4ff703ffc3b00f83064 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Tue, 25 Oct 2022 13:58:46 +0200 Subject: [PATCH 028/248] fix(wizard): consistent breadcrumb on several pages --- front/formlist.php | 6 +++++- front/knowbaseitem.php | 6 +++++- front/wizard.php | 6 +++++- inc/common.class.php | 6 +++++- setup.php | 16 ++++++++++++++-- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/front/formlist.php b/front/formlist.php index 6a872c5f8..08993bd8b 100644 --- a/front/formlist.php +++ b/front/formlist.php @@ -42,7 +42,11 @@ if (plugin_formcreator_replaceHelpdesk()) { Html::redirect('issue.php'); } else { - Html::helpHeader(__('Form list', 'formcreator')); + Html::helpHeader( + __('Form list', 'formcreator'), + 'seek_assistance', + PluginFormcreatorForm::class + ); } } else { Html::header(__('Form list', 'formcreator')); diff --git a/front/knowbaseitem.php b/front/knowbaseitem.php index b9d1f1843..2dfe3e0c7 100644 --- a/front/knowbaseitem.php +++ b/front/knowbaseitem.php @@ -38,7 +38,11 @@ } if (Session::getCurrentInterface() == 'helpdesk') { - Html::helpHeader(__('Service catalog', 'formcreator')); + Html::helpHeader( + __('Service catalog', 'formcreator'), + 'faq', + PluginFormcreatorForm::class + ); } else { Html::header(__('Service catalog', 'formcreator')); } diff --git a/front/wizard.php b/front/wizard.php index 3c183cde6..c212e02a8 100644 --- a/front/wizard.php +++ b/front/wizard.php @@ -41,7 +41,11 @@ } if (Session::getCurrentInterface() == "helpdesk") { - Html::helpHeader(__('Service catalog', 'formcreator')); + Html::helpHeader( + __('Service catalog', 'formcreator'), + 'seek_assistance', + PluginFormcreatorForm::class + ); } else { Html::header(__('Service catalog', 'formcreator')); } diff --git a/inc/common.class.php b/inc/common.class.php index 76d30ee99..680fc87b4 100644 --- a/inc/common.class.php +++ b/inc/common.class.php @@ -682,7 +682,11 @@ public static function header() { switch (self::getInterface()) { case "servicecatalog": case "self-service": - return Html::helpHeader(__('Form list', 'formcreator'), $_SERVER['PHP_SELF']); + return Html::helpHeader( + __('Form list', 'formcreator'), + 'seek_assistance', + PluginFormcreatorForm::class + ); case "central": return Html::header( __('Form Creator', 'formcreator'), diff --git a/setup.php b/setup.php index 3cfdcef01..c8c3494f7 100644 --- a/setup.php +++ b/setup.php @@ -340,10 +340,9 @@ function plugin_formcreator_hook(): void { if (strpos($_SERVER['REQUEST_URI'], 'helpdesk') !== false || strpos($_SERVER['REQUEST_URI'], 'central.php') !== false || strpos($_SERVER['REQUEST_URI'], 'formcreator/front/formlist.php') !== false - || strpos($_SERVER['REQUEST_URI'], 'formcreator/front/knowbaseitem.php') !== false || strpos($_SERVER['REQUEST_URI'], 'formcreator/front/wizard.php') !== false) { $PLUGIN_HOOKS[Hooks::ADD_JAVASCRIPT]['formcreator'][] = 'lib/jquery-slinky/dist/slinky.min.js'; - $CFG_GLPI['javascript']['self-service']['none'] = [ + $CFG_GLPI['javascript']['seek_assistance'][PluginFormcreatorForm::class] = [ 'dashboard', 'gridstack' ]; @@ -354,6 +353,19 @@ function plugin_formcreator_hook(): void { 'gridstack' ]; } + if (strpos($_SERVER['REQUEST_URI'], 'formdisplay.php') !== false) { + $CFG_GLPI['javascript']['seek_assistance'][PluginFormcreatorForm::class] = [ + 'dashboard', + 'gridstack' + ]; + } + if (strpos($_SERVER['REQUEST_URI'], 'knowbaseitem.php') !== false) { + $PLUGIN_HOOKS[Hooks::ADD_JAVASCRIPT]['formcreator'][] = 'lib/jquery-slinky/dist/slinky.min.js'; + $CFG_GLPI['javascript']['faq'][PluginFormcreatorForm::class] = [ + 'dashboard', + 'gridstack' + ]; + } } } From 3ce969188f228552846d046b13fb923f10a0f9da Mon Sep 17 00:00:00 2001 From: stonebuzz Date: Wed, 2 Nov 2022 09:22:36 +0100 Subject: [PATCH 029/248] fix(dashboard): fix dashboard height --- inc/common.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/common.class.php b/inc/common.class.php index 680fc87b4..d9465e0a2 100644 --- a/inc/common.class.php +++ b/inc/common.class.php @@ -848,7 +848,7 @@ public static function showMiniDashboard(): void { Plugin::doHook(Hooks::DISPLAY_CENTRAL); if (PluginFormcreatorEntityconfig::getUsedConfig('is_dashboard_visible', Session::getActiveEntity()) == PluginFormcreatorEntityconfig::CONFIG_DASHBOARD_VISIBLE) { - $dashboard = new Glpi\Dashboard\Grid('plugin_formcreator_issue_counters', 33, 0, 'mini_core'); + $dashboard = new Glpi\Dashboard\Grid('plugin_formcreator_issue_counters', 33, 1, 'mini_core'); echo "
"; $dashboard->show(true); echo "
"; From 9b7743ddc389d34350dd1c0100272943aa7f868f Mon Sep 17 00:00:00 2001 From: Stanislas Date: Wed, 2 Nov 2022 09:43:34 +0100 Subject: [PATCH 030/248] Check GLPI version Co-authored-by: btry --- inc/common.class.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/inc/common.class.php b/inc/common.class.php index d9465e0a2..1c5b916aa 100644 --- a/inc/common.class.php +++ b/inc/common.class.php @@ -848,7 +848,11 @@ public static function showMiniDashboard(): void { Plugin::doHook(Hooks::DISPLAY_CENTRAL); if (PluginFormcreatorEntityconfig::getUsedConfig('is_dashboard_visible', Session::getActiveEntity()) == PluginFormcreatorEntityconfig::CONFIG_DASHBOARD_VISIBLE) { - $dashboard = new Glpi\Dashboard\Grid('plugin_formcreator_issue_counters', 33, 1, 'mini_core'); + if (version_compare(GLPI_VERSION, '10.0.3') > 0) { + $dashboard = new Glpi\Dashboard\Grid('plugin_formcreator_issue_counters', 33, 1, 'mini_core'); + } else { + $dashboard = new Glpi\Dashboard\Grid('plugin_formcreator_issue_counters', 33, 0, 'mini_core'); + } echo "
"; $dashboard->show(true); echo "
"; From b2afb7673a075380c0ff1785f759ca4f77706403 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 3 Nov 2022 12:09:58 +0100 Subject: [PATCH 031/248] fix(formanswer): page switching loose filter when changing page of form answer results of a form the filter on the form foreign key is lost. --- inc/formanswer.class.php | 97 ++++++++++++++++++----- templates/pages/form.formanswer.html.twig | 65 +++++++++++++++ 2 files changed, 140 insertions(+), 22 deletions(-) create mode 100644 templates/pages/form.formanswer.html.twig diff --git a/inc/formanswer.class.php b/inc/formanswer.class.php index dfd9791f2..94ebc9976 100644 --- a/inc/formanswer.class.php +++ b/inc/formanswer.class.php @@ -29,6 +29,7 @@ * --------------------------------------------------------------------- */ +use Glpi\Application\View\TemplateRenderer; use Glpi\Toolbox\Sanitizer; use GlpiPlugin\Formcreator\Field\DropdownField; @@ -207,7 +208,6 @@ public function rawSearchOptions() { 'table' => 'glpi_plugin_formcreator_forms', 'field' => 'name', 'name' => PluginFormcreatorForm::getTypeName(1), - 'searchtype' => 'contains', 'datatype' => 'string', 'massiveaction' => false ]; @@ -423,32 +423,85 @@ public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { } static function showForForm(PluginFormcreatorForm $form, $params = []) { - // set a session var to tweak search results - $_SESSION['formcreator']['form_search_answers'] = $form->getID(); - - // prepare params for search - $item = PluginFormcreatorCommon::getFormAnswer(); - $searchOptions = $item->rawSearchOptions(); - $filteredOptions = []; - foreach ($searchOptions as $value) { - if (is_numeric($value['id']) && $value['id'] <= 7) { - $filteredOptions[$value['id']] = $value; + global $DB; + + $table = self::getTable(); + $form_table = PluginFormcreatorForm::getTable(); + $form_fk = PluginFormcreatorForm::getForeignKeyField(); + $user_table = User::getTable(); + if (version_compare(GLPI_VERSION, '10.0.5') >= 0) { + $userQueryExpression = User::getFriendlyNameFields('requester_name'); + } else { + // Drop this alternative when the plugin requires GLPI 10.0.5+ + $alias = 'requester_name'; + $config = Config::getConfigurationValues('core'); + if ($config['names_format'] == User::FIRSTNAME_BEFORE) { + $first = "firstname"; + $second = "realname"; + } else { + $first = "realname"; + $second = "firstname"; } + + $first = DBmysql::quoteName("$user_table.$first"); + $second = DBmysql::quoteName("$user_table.$second"); + $alias = DBmysql::quoteName($alias); + $name = DBmysql::quoteName($user_table . '.' . self::getNameField()); + + $userQueryExpression = new QueryExpression("IF( + $first <> '' && $second <> '', + CONCAT($first, ' ', $second), + $name + ) AS $alias"); } - $searchOptions = $filteredOptions; - $sopt_keys = array_keys($searchOptions); - $forcedisplay = array_combine($sopt_keys, $sopt_keys); + $result = $DB->request([ + 'SELECT' => [ + $table => [ + 'id', + 'name', + 'requester_id', + 'users_id_validator', + 'request_date' + ], + $form_table => [ + 'name as form_name' + ], + $userQueryExpression + ], + 'FROM' => self::getTable(), + 'INNER JOIN' => [ + $form_table => [ + 'FKEY' => [ + $form_table => 'id', + $table => $form_fk, + ], + ], + ], + 'LEFT JOIN' => [ + $user_table => [ + 'FKEY' => [ + $user_table => 'id', + $table => 'requester_id', + ], + ], + ], + 'WHERE' => [ + $table . '.' . $form_fk => $form->getID(), + ], + 'LIMIT' => 20, + 'ORDER' => [ + 'request_date DESC', + ], + ]); - // do search - $params = Search::manageParams(__CLASS__, $params, false); - $data = Search::prepareDatasForSearch(__CLASS__, $params, $forcedisplay); - Search::constructSQL($data); - Search::constructData($data); - Search::displayData($data); + $total_count = count($result); - // remove previous session var (restore default view) - unset($_SESSION['formcreator']['form_search_answers']); + TemplateRenderer::getInstance()->display('@formcreator/pages/form.formanswer.html.twig', [ + 'form' => $form, + 'form_answers' => $result, + 'total_count' => $total_count, + ]); } /** diff --git a/templates/pages/form.formanswer.html.twig b/templates/pages/form.formanswer.html.twig new file mode 100644 index 000000000..616179b60 --- /dev/null +++ b/templates/pages/form.formanswer.html.twig @@ -0,0 +1,65 @@ +{# + # --------------------------------------------------------------------- + # 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 + # --------------------------------------------------------------------- + #} + +{% if total_count < 1 %} +
+ {{ __('No form answer yet', 'formcreator') }} +
+{% else %} + {% set criteria = '?criteria[0][field]=3&criteria[0][searchtype]=equals&criteria[0][value]=' ~ form.getID() ~ '' %} +
+ {{ __('%s latest items', 'formcreator')|format(total_count) }} + {{ __('See all', 'formcreator') }} +
+
+ + + + + + + + + + + + {% for row in form_answers %} + + + + + + + + {% endfor %} + +
{{ __('ID') }}{{ __('Name') }}{{ __('Form') }}{{ __('Requester', 'Requesters', 1) }}{{ __('Creation date') }}
{{ row.id }}{{ get_item_link('PluginFormcreatorFormAnswer', row.id) }}{{ row.form_name }}{{ row.requester_name }}{{ row.request_date }}
+
+{% endif %} From 4f628b167453acc36b24e34ebc164ebc388bc33e Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 3 Nov 2022 15:00:21 +0100 Subject: [PATCH 032/248] fix(category): don't activate plugin to access categories --- front/category.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/front/category.php b/front/category.php index c300d1a7f..2b0cca8dd 100644 --- a/front/category.php +++ b/front/category.php @@ -31,7 +31,10 @@ include ('../../../inc/includes.php'); -Plugin::load('formcreator', true); +// Check if plugin is activated... +if (!(new Plugin())->isActivated('formcreator')) { + Html::displayNotFoundError(); +} $dropdown = new PluginFormcreatorCategory(); include (GLPI_ROOT . "/front/dropdown.common.php"); From e1e9b0eab43464350af32cca760e66b09c2fcc7d Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Thu, 3 Nov 2022 15:01:38 +0100 Subject: [PATCH 033/248] fix(category): do not access page if the plugin is not active --- front/category.form.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/front/category.form.php b/front/category.form.php index 7f664e1eb..ef4fe9317 100644 --- a/front/category.form.php +++ b/front/category.form.php @@ -31,9 +31,12 @@ include ('../../../inc/includes.php'); -Session::checkRight("entity", UPDATE); +// Check if plugin is activated... +if (!(new Plugin())->isActivated('formcreator')) { + Html::displayNotFoundError(); +} -Plugin::load('formcreator', true); +Session::checkRight('entity', UPDATE); $dropdown = new PluginFormcreatorCategory(); From df7163e9983cb664c27ad4c5ffe6043bafb54357 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Sat, 5 Nov 2022 10:39:14 +0100 Subject: [PATCH 034/248] fix(datefield): change event and comparison --- inc/field/datefield.class.php | 4 ++++ js/scripts.js | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/inc/field/datefield.class.php b/inc/field/datefield.class.php index de9af5fd9..ae51843a8 100644 --- a/inc/field/datefield.class.php +++ b/inc/field/datefield.class.php @@ -147,7 +147,9 @@ public function equals($value): bool { $answer = $this->value; } $answerDatetime = DateTime::createFromFormat(self::DATE_FORMAT, $answer); + $answerDatetime->setTime(0,0,0,0); $compareDatetime = DateTime::createFromFormat(self::DATE_FORMAT, $value); + $compareDatetime->setTime(0,0,0,0); return $answerDatetime == $compareDatetime; } @@ -162,7 +164,9 @@ public function greaterThan($value): bool { $answer = $this->value; } $answerDatetime = DateTime::createFromFormat(self::DATE_FORMAT, $answer); + $answerDatetime->setTime(0,0,0,0); $compareDatetime = DateTime::createFromFormat(self::DATE_FORMAT, $value); + $compareDatetime->setTime(0,0,0,0); return $answerDatetime > $compareDatetime; } diff --git a/js/scripts.js b/js/scripts.js index 0655f4efc..070fd25a3 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -1673,13 +1673,10 @@ function pluginFormcreatorInitializeCheckboxes(fieldName, rand) { * Initialize a date field */ function pluginFormcreatorInitializeDate(fieldName, rand) { - var field = $('[name="_' + fieldName + '"]'); + var field = $('[name="' + fieldName + '"]'); field.on("change", function() { plugin_formcreator.showFields($(field[0].form)); }); - $('#resetdate' + rand).on("click", function() { - plugin_formcreator.showFields($(field[0].form)); - }); } /** From 469b70addd24d7519c984af3f9f0d5591f7bb758 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Sat, 5 Nov 2022 10:47:11 +0100 Subject: [PATCH 035/248] test(common): workaround HTTP request by GLPI --- tests/3-unit/PluginFormcreatorCommon.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/3-unit/PluginFormcreatorCommon.php b/tests/3-unit/PluginFormcreatorCommon.php index b6c806117..9521f329f 100644 --- a/tests/3-unit/PluginFormcreatorCommon.php +++ b/tests/3-unit/PluginFormcreatorCommon.php @@ -600,11 +600,15 @@ public function providerHookRedefineMenu() { ] ]; - $rssFeed = new \RSSFeed(); - $rssFeed->add([ + // Workaround HTTP request to the RSS url when using RSSFeed->add() + $DB->insert(RSSFeed::getTable(), [ + 'name' => 'RSS feed', 'url' => 'https://localhost/feed/', 'is_active' => 1, ]); + $rssFeed = new RSSFeed(); + $rssFeed->getFromDB($DB->insertId()); + $this->boolean($rssFeed->isNewItem())->isFalse(); $entityRssFeed = new \Entity_RSSFeed(); $entityRssFeed->add([ From cda7b2d57eaa796607536bc876ef7ee365afb329 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Sat, 5 Nov 2022 10:54:19 +0100 Subject: [PATCH 036/248] style(datefield): spaces after comma --- inc/field/datefield.class.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inc/field/datefield.class.php b/inc/field/datefield.class.php index ae51843a8..3c2c6dfd1 100644 --- a/inc/field/datefield.class.php +++ b/inc/field/datefield.class.php @@ -147,9 +147,9 @@ public function equals($value): bool { $answer = $this->value; } $answerDatetime = DateTime::createFromFormat(self::DATE_FORMAT, $answer); - $answerDatetime->setTime(0,0,0,0); + $answerDatetime->setTime(0, 0, 0, 0); $compareDatetime = DateTime::createFromFormat(self::DATE_FORMAT, $value); - $compareDatetime->setTime(0,0,0,0); + $compareDatetime->setTime(0, 0, 0, 0); return $answerDatetime == $compareDatetime; } @@ -164,9 +164,9 @@ public function greaterThan($value): bool { $answer = $this->value; } $answerDatetime = DateTime::createFromFormat(self::DATE_FORMAT, $answer); - $answerDatetime->setTime(0,0,0,0); + $answerDatetime->setTime(0, 0, 0, 0); $compareDatetime = DateTime::createFromFormat(self::DATE_FORMAT, $value); - $compareDatetime->setTime(0,0,0,0); + $compareDatetime->setTime(0, 0, 0, 0); return $answerDatetime > $compareDatetime; } From 05b5103a16c9a74801552c6bf146999b2fdfe564 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Sat, 5 Nov 2022 10:54:35 +0100 Subject: [PATCH 037/248] tests(datefield): missing use statement --- tests/3-unit/PluginFormcreatorCommon.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/3-unit/PluginFormcreatorCommon.php b/tests/3-unit/PluginFormcreatorCommon.php index 9521f329f..7ac5daeb5 100644 --- a/tests/3-unit/PluginFormcreatorCommon.php +++ b/tests/3-unit/PluginFormcreatorCommon.php @@ -31,6 +31,7 @@ namespace tests\units; use GlpiPlugin\Formcreator\Tests\CommonTestCase; use PluginFormcreatorIssue; +use RSSFeed; use Ticket; use User; From bf34ef4c9eb054b5cefd31ba5933ab16cf6c8173 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Mon, 7 Nov 2022 11:01:39 +0100 Subject: [PATCH 038/248] ci: explictly disable telemetry --- tests/script-functions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/script-functions.sh b/tests/script-functions.sh index 6129cd6f6..3613486a3 100644 --- a/tests/script-functions.sh +++ b/tests/script-functions.sh @@ -66,7 +66,7 @@ init_glpi() { echo Installing GLPI on database $1 mkdir -p ../../$TEST_GLPI_CONFIG_DIR mysql -u$2 -p$3 -h$DB_HOST --execute "CREATE DATABASE \`$1\`;" - php ../../bin/console glpi:database:install --db-host=$DB_HOST --db-user=$2 --db-password=$3 --db-name=$1 --config-dir=../../$TEST_GLPI_CONFIG_DIR --no-interaction --no-plugins --force + php ../../bin/console glpi:database:install --db-host=$DB_HOST --db-user=$2 --db-password=$3 --db-name=$1 --config-dir=../../$TEST_GLPI_CONFIG_DIR --no-interaction --no-plugins --force --no-telemetry # php ../../bin/console glpi:config:set --db-host=$DB_HOST --context=core url_base "http://localhost" # php ../../bin/console glpi:config:set --db-host=$DB_HOST --context=core url_base_api "http://localhost/api" mysql $1 -u$2 -p$3 -h$DB_HOST --execute "UPDATE \`glpi_configs\` SET \`value\`='http://localhost' WHERE \`context\`='core' AND \`name\`='url_base'" From e9626fc231b4e7ad049359e3c4d04c1a11d71e7a Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Wed, 9 Nov 2022 14:41:30 +0100 Subject: [PATCH 039/248] fix(form_language): call to undefined method --- inc/form_language.class.php | 2 +- locales/glpi.pot | 656 +++++++++++++++++++----------------- 2 files changed, 345 insertions(+), 313 deletions(-) diff --git a/inc/form_language.class.php b/inc/form_language.class.php index 01cf605cd..2ab688b2d 100644 --- a/inc/form_language.class.php +++ b/inc/form_language.class.php @@ -345,7 +345,7 @@ public function showTranslations($options = []) { $header = ''; $header.= '' . Html::getCheckAllAsCheckbox("translation_list$rand", $rand) . ''; $header.= '' . __('Original string', 'formcreator') . ''; - $header.= '' . PluginFormcreatorTranslation::getTypeName(1) . ''; + $header.= '' . __('Translation', 'Translations', 1, 'formcreator') . ''; $header.= ''; echo $header; echo ''; diff --git a/locales/glpi.pot b/locales/glpi.pot index 67c429571..830379d17 100644 --- a/locales/glpi.pot +++ b/locales/glpi.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-10-26 14:38+0200\n" +"POT-Creation-Date: 2022-11-09 14:40+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -64,8 +64,8 @@ msgid "No limit" msgstr "" #: ajax/homepage_link.php:36 inc/formlist.class.php:46 -#: inc/entityconfig.class.php:82 inc/form.class.php:111 inc/form.class.php:569 -#: inc/form.class.php:1951 +#: inc/entityconfig.class.php:84 inc/form.class.php:111 inc/form.class.php:570 +#: inc/form.class.php:1952 entrée standard:47 msgid "Form" msgid_plural "Forms" msgstr[0] "" @@ -104,9 +104,9 @@ msgstr "" msgid "Could not update the section" msgstr "" -#: front/knowbaseitem.php:41 front/knowbaseitem.php:43 front/wizard.php:44 -#: front/wizard.php:46 front/wizardfeeds.php:49 front/wizardfeeds.php:51 -#: front/issue.php:43 front/issue.php:46 front/formanswer.php:44 +#: front/knowbaseitem.php:42 front/knowbaseitem.php:47 front/wizard.php:45 +#: front/wizard.php:50 front/wizardfeeds.php:49 front/wizardfeeds.php:51 +#: front/issue.php:44 front/issue.php:50 front/formanswer.php:44 #: front/reservation.php:46 front/reservation.php:48 #: front/knowbaseitem.form.php:48 front/knowbaseitem.form.php:50 #: front/reservationitem.php:42 front/reservationitem.php:44 @@ -131,8 +131,8 @@ msgstr "" #: front/targetticket.form.php:95 front/targetchange.form.php:81 #: front/formanswer.php:47 front/targetproblem.form.php:81 -#: front/formanswer.form.php:78 front/form.php:44 inc/common.class.php:689 -#: inc/common.class.php:696 +#: front/formanswer.form.php:78 front/form.php:44 inc/common.class.php:692 +#: inc/common.class.php:699 msgid "Form Creator" msgstr "" @@ -142,12 +142,12 @@ msgstr "" msgid "%1$s = %2$s" msgstr "" -#: front/formlist.php:45 front/formlist.php:48 inc/common.class.php:686 +#: front/formlist.php:46 front/formlist.php:52 inc/common.class.php:686 msgid "Form list" msgstr "" -#: front/formdisplay.php:90 inc/formanswer.class.php:889 -#: inc/formanswer.class.php:1120 inc/formanswer.class.php:1170 +#: front/formdisplay.php:90 inc/formanswer.class.php:956 +#: inc/formanswer.class.php:1187 inc/formanswer.class.php:1237 msgid "The form has been successfully saved!" msgstr "" @@ -162,13 +162,13 @@ msgstr "" msgid "%1$s adds the reservation %2$s for item %3$s" msgstr "" -#: inc/knowbase.class.php:60 inc/form.class.php:646 +#: inc/knowbase.class.php:60 inc/form.class.php:647 msgid "Category" msgid_plural "Categories" msgstr[0] "" msgstr[1] "" -#: inc/knowbase.class.php:62 inc/form.class.php:649 +#: inc/knowbase.class.php:62 inc/form.class.php:650 entrée standard:39 msgid "See all" msgstr "" @@ -176,116 +176,125 @@ msgstr "" msgid "Please, describe your need here" msgstr "" -#: inc/field/textareafield.class.php:183 +#: inc/field/textareafield.class.php:184 msgid "Textarea" msgstr "" -#: inc/field/textareafield.class.php:242 inc/field/dropdownfield.class.php:440 -#: inc/field/ldapselectfield.class.php:114 inc/field/textfield.class.php:125 -#: inc/field/urgencyfield.class.php:165 inc/field/tagfield.class.php:163 -#: inc/field/timefield.class.php:113 inc/field/fieldsfield.class.php:488 -#: inc/field/requesttypefield.class.php:160 +#: inc/field/textareafield.class.php:248 inc/field/dropdownfield.class.php:585 +#: inc/field/ldapselectfield.class.php:115 inc/field/textfield.class.php:126 +#: inc/field/urgencyfield.class.php:166 inc/field/tagfield.class.php:165 +#: inc/field/timefield.class.php:114 inc/field/fieldsfield.class.php:489 +#: inc/field/requesttypefield.class.php:161 msgid "A required field is empty:" msgstr "" -#: inc/field/textareafield.class.php:260 inc/field/textfield.class.php:178 -#: inc/field/integerfield.class.php:112 inc/field/floatfield.class.php:193 +#: inc/field/textareafield.class.php:266 inc/field/textfield.class.php:179 +#: inc/field/integerfield.class.php:112 inc/field/floatfield.class.php:194 #: inc/conditionnabletrait.class.php:70 msgid "The regular expression is invalid" msgstr "" -#: inc/field/dropdownfield.class.php:73 inc/filter/entityfilter.class.php:48 +#: inc/field/dropdownfield.class.php:77 inc/filter/entityfilter.class.php:48 msgid "User and form" msgstr "" -#: inc/field/dropdownfield.class.php:431 entrée standard:38 +#: inc/field/dropdownfield.class.php:83 +msgid "Search filter" +msgstr "" + +#: inc/field/dropdownfield.class.php:576 entrée standard:38 msgid "Dropdown" msgid_plural "Dropdowns" msgstr[0] "" msgstr[1] "" -#: inc/field/dropdownfield.class.php:462 +#: inc/field/dropdownfield.class.php:607 msgid "Invalid value for " msgstr "" -#: inc/field/dropdownfield.class.php:474 +#: inc/field/dropdownfield.class.php:619 #, php-format msgid "The itemtype field is required: %s" msgstr "" -#: inc/field/dropdownfield.class.php:490 +#: inc/field/dropdownfield.class.php:635 #, php-format msgid "Invalid dropdown type: %s" msgstr "" -#: inc/field/dropdownfield.class.php:821 entrée standard:143 standard:126 +#: inc/field/dropdownfield.class.php:695 inc/questionfilter.class.php:62 +#: entrée standard:68 +msgid "Filter" +msgstr "" + +#: inc/field/dropdownfield.class.php:978 entrée standard:143 standard:126 msgid "Entity restriction" msgstr "" -#: inc/field/dropdownfield.class.php:830 +#: inc/field/dropdownfield.class.php:987 msgid "" "To respect the GLPI entity system, \"Form\" should be selected. Others " "settings will break the entity restrictions" msgstr "" -#: inc/field/ldapselectfield.class.php:95 +#: inc/field/ldapselectfield.class.php:96 msgid "LDAP Select" msgstr "" -#: inc/field/ldapselectfield.class.php:139 +#: inc/field/ldapselectfield.class.php:140 msgid "LDAP directory not defined!" msgstr "" -#: inc/field/ldapselectfield.class.php:146 +#: inc/field/ldapselectfield.class.php:147 msgid "LDAP directory not found!" msgstr "" -#: inc/field/textfield.class.php:146 inc/field/integerfield.class.php:71 -#: inc/field/floatfield.class.php:155 +#: inc/field/textfield.class.php:147 inc/field/integerfield.class.php:71 +#: inc/field/floatfield.class.php:156 #, php-format msgid "Specific format does not match: %s" msgstr "" -#: inc/field/textfield.class.php:155 +#: inc/field/textfield.class.php:156 #, php-format msgid "The text is too short (minimum %d characters): %s" msgstr "" -#: inc/field/textfield.class.php:160 +#: inc/field/textfield.class.php:161 #, php-format msgid "The text is too long (maximum %d characters): %s" msgstr "" -#: inc/field/textfield.class.php:168 +#: inc/field/textfield.class.php:169 msgid "Text" msgstr "" -#: inc/field/textfield.class.php:212 inc/field/floatfield.class.php:234 +#: inc/field/textfield.class.php:213 inc/field/floatfield.class.php:235 #: inc/questionregex.class.php:62 entrée standard:42 msgid "Regular expression" msgstr "" -#: inc/field/textfield.class.php:217 inc/field/floatfield.class.php:239 +#: inc/field/textfield.class.php:218 inc/field/floatfield.class.php:240 msgid "Range" msgstr "" -#: inc/field/textfield.class.php:223 inc/field/floatfield.class.php:245 +#: inc/field/textfield.class.php:224 inc/field/floatfield.class.php:246 msgid "Additional validation" msgstr "" -#: inc/field/radiosfield.class.php:109 +#: inc/field/radiosfield.class.php:110 msgid "Radios" msgstr "" -#: inc/field/radiosfield.class.php:115 inc/field/glpiselectfield.class.php:93 -#: inc/field/fieldsfield.class.php:512 inc/field/checkboxesfield.class.php:229 +#: inc/field/radiosfield.class.php:116 inc/field/glpiselectfield.class.php:108 +#: inc/field/fieldsfield.class.php:513 inc/field/checkboxesfield.class.php:230 msgid "The field value is required:" msgstr "" -#: inc/field/radiosfield.class.php:193 inc/field/selectfield.class.php:104 -#: inc/field/datetimefield.class.php:118 inc/field/actorfield.class.php:257 -#: inc/field/checkboxesfield.class.php:175 inc/field/floatfield.class.php:124 -#: inc/field/datefield.class.php:115 +#: inc/field/radiosfield.class.php:194 inc/field/selectfield.class.php:105 +#: inc/field/datetimefield.class.php:119 inc/field/actorfield.class.php:258 +#: inc/field/checkboxesfield.class.php:176 inc/field/floatfield.class.php:125 +#: inc/field/datefield.class.php:116 #, php-format msgid "A required field is empty: %s" msgstr "" @@ -295,12 +304,12 @@ msgstr "" msgid "This is not an integer: %s" msgstr "" -#: inc/field/integerfield.class.php:85 inc/field/floatfield.class.php:166 +#: inc/field/integerfield.class.php:85 inc/field/floatfield.class.php:167 #, php-format msgid "The following number must be greater than %d: %s" msgstr "" -#: inc/field/integerfield.class.php:91 inc/field/floatfield.class.php:172 +#: inc/field/integerfield.class.php:91 inc/field/floatfield.class.php:173 #, php-format msgid "The following number must be lower than %d: %s" msgstr "" @@ -313,37 +322,37 @@ msgstr "" msgid "Undefined" msgstr "" -#: inc/field/ipfield.class.php:122 +#: inc/field/ipfield.class.php:123 msgid "IP address" msgid_plural "IP addresses" msgstr[0] "" msgstr[1] "" -#: inc/field/urgencyfield.class.php:85 inc/abstractitiltarget.class.php:994 +#: inc/field/urgencyfield.class.php:86 inc/abstractitiltarget.class.php:994 msgid "Urgency" msgstr "" -#: inc/field/urgencyfield.class.php:119 +#: inc/field/urgencyfield.class.php:120 msgctxt "urgency" msgid "Very high" msgstr "" -#: inc/field/urgencyfield.class.php:120 +#: inc/field/urgencyfield.class.php:121 msgctxt "urgency" msgid "High" msgstr "" -#: inc/field/urgencyfield.class.php:121 +#: inc/field/urgencyfield.class.php:122 msgctxt "urgency" msgid "Medium" msgstr "" -#: inc/field/urgencyfield.class.php:122 +#: inc/field/urgencyfield.class.php:123 msgctxt "urgency" msgid "Low" msgstr "" -#: inc/field/urgencyfield.class.php:123 +#: inc/field/urgencyfield.class.php:124 msgctxt "urgency" msgid "Very low" msgstr "" @@ -352,68 +361,68 @@ msgstr "" msgid "Warning: Tag plugin is disabled or missing" msgstr "" -#: inc/field/tagfield.class.php:197 +#: inc/field/tagfield.class.php:199 msgid "Tag" msgid_plural "Tags" msgstr[0] "" msgstr[1] "" -#: inc/field/glpiselectfield.class.php:78 entrée standard:38 +#: inc/field/glpiselectfield.class.php:93 entrée standard:38 msgid "GLPI object" msgid_plural "GLPI objects" msgstr[0] "" msgstr[1] "" -#: inc/field/hostnamefield.class.php:115 +#: inc/field/hostnamefield.class.php:116 msgid "Hostname" msgid_plural "Hostnames" msgstr[0] "" msgstr[1] "" -#: inc/field/timefield.class.php:129 +#: inc/field/timefield.class.php:130 msgid "Time" msgstr "" -#: inc/field/emailfield.class.php:88 +#: inc/field/emailfield.class.php:89 #, php-format msgid "This is not a valid e-mail: %s" msgstr "" -#: inc/field/emailfield.class.php:99 +#: inc/field/emailfield.class.php:100 msgid "Email" msgid_plural "Emails" msgstr[0] "" msgstr[1] "" -#: inc/field/selectfield.class.php:97 +#: inc/field/selectfield.class.php:98 msgid "Select" msgstr "" -#: inc/field/datetimefield.class.php:139 +#: inc/field/datetimefield.class.php:140 msgid "Date & time" msgstr "" -#: inc/field/actorfield.class.php:83 +#: inc/field/actorfield.class.php:84 msgid "Actor" msgid_plural "Actors" msgstr[0] "" msgstr[1] "" -#: inc/field/actorfield.class.php:267 +#: inc/field/actorfield.class.php:268 #, php-format msgid "Invalid value: %s" msgstr "" -#: inc/field/actorfield.class.php:290 +#: inc/field/actorfield.class.php:291 #, php-format msgid "User not found or invalid email address: %s" msgstr "" -#: inc/field/filefield.class.php:105 +#: inc/field/filefield.class.php:106 msgid "No attached document" msgstr "" -#: inc/field/filefield.class.php:114 inc/field/filefield.class.php:252 +#: inc/field/filefield.class.php:115 inc/field/filefield.class.php:252 #: inc/field/filefield.class.php:259 msgid "Attached document" msgstr "" @@ -435,84 +444,84 @@ msgstr "" msgid "Warning: Additional Fields plugin is disabled or missing" msgstr "" -#: inc/field/fieldsfield.class.php:167 +#: inc/field/fieldsfield.class.php:168 msgid "Block" msgstr "" -#: inc/field/fieldsfield.class.php:171 inc/targetticket.class.php:339 +#: inc/field/fieldsfield.class.php:172 inc/targetticket.class.php:339 msgid "Field" msgstr "" -#: inc/field/fieldsfield.class.php:253 +#: inc/field/fieldsfield.class.php:254 msgid "show" msgstr "" -#: inc/field/fieldsfield.class.php:389 +#: inc/field/fieldsfield.class.php:390 #, php-format msgid "Field '%1$s' type not implemented yet!" msgstr "" -#: inc/field/fieldsfield.class.php:466 +#: inc/field/fieldsfield.class.php:467 msgid "Some numeric fields contains non numeric values" msgstr "" -#: inc/field/fieldsfield.class.php:471 +#: inc/field/fieldsfield.class.php:472 msgid "Some URL fields contains invalid links" msgstr "" -#: inc/field/fieldsfield.class.php:565 +#: inc/field/fieldsfield.class.php:566 msgid "Additionnal fields" msgstr "" -#: inc/field/checkboxesfield.class.php:117 +#: inc/field/checkboxesfield.class.php:118 msgid "Checkboxes" msgstr "" -#: inc/field/checkboxesfield.class.php:211 +#: inc/field/checkboxesfield.class.php:212 #, php-format msgid "The following question needs at least %d answers" msgstr "" -#: inc/field/checkboxesfield.class.php:217 +#: inc/field/checkboxesfield.class.php:218 #, php-format msgid "The following question does not accept more than %d answers" msgstr "" -#: inc/field/checkboxesfield.class.php:289 +#: inc/field/checkboxesfield.class.php:290 msgid "Range min" msgstr "" -#: inc/field/checkboxesfield.class.php:290 +#: inc/field/checkboxesfield.class.php:291 msgid "Range max" msgstr "" -#: inc/field/requesttypefield.class.php:86 inc/targetticket.class.php:1099 +#: inc/field/requesttypefield.class.php:87 inc/targetticket.class.php:1099 msgid "Request type" msgstr "" -#: inc/field/floatfield.class.php:141 +#: inc/field/floatfield.class.php:142 #, php-format msgid "This is not a number: %s" msgstr "" -#: inc/field/floatfield.class.php:182 +#: inc/field/floatfield.class.php:183 msgid "Float" msgstr "" -#: inc/field/datefield.class.php:136 +#: inc/field/datefield.class.php:137 msgid "Date" msgstr "" -#: inc/field/descriptionfield.class.php:105 inc/form.class.php:165 -#: inc/question.class.php:166 entrée standard:82 standard:52 standard:113 +#: inc/field/descriptionfield.class.php:106 inc/form.class.php:165 +#: inc/question.class.php:186 entrée standard:82 standard:52 standard:114 msgid "Description" msgstr "" -#: inc/field/descriptionfield.class.php:112 +#: inc/field/descriptionfield.class.php:113 msgid "A description field should have a description:" msgstr "" -#: inc/field/hiddenfield.class.php:95 +#: inc/field/hiddenfield.class.php:96 msgid "Hidden field" msgid_plural "Hidden fields" msgstr[0] "" @@ -551,11 +560,12 @@ msgstr "" #: inc/condition.class.php:179 inc/target_actor.class.php:215 #: inc/form_language.class.php:540 inc/form_validator.class.php:379 #: inc/targetproblem.class.php:540 inc/questionrange.class.php:196 -#: inc/form.class.php:1844 inc/targetchange.class.php:303 -#: inc/section.class.php:379 inc/restrictedformcriteria.class.php:200 -#: inc/question.class.php:828 inc/questionparameter/range.class.php:202 -#: inc/questionregex.class.php:185 inc/questiondependency.class.php:196 -#: inc/item_targetticket.class.php:154 inc/targetticket.class.php:1408 +#: inc/form.class.php:1845 inc/targetchange.class.php:303 +#: inc/questionfilter.class.php:217 inc/section.class.php:379 +#: inc/restrictedformcriteria.class.php:200 inc/question.class.php:844 +#: inc/questionparameter/range.class.php:202 inc/questionregex.class.php:185 +#: inc/questiondependency.class.php:196 inc/item_targetticket.class.php:154 +#: inc/targetticket.class.php:1408 #, php-format msgid "Failed to add or update the %1$s %2$s" msgstr "" @@ -563,11 +573,12 @@ msgstr "" #: inc/condition.class.php:201 inc/target_actor.class.php:236 #: inc/form_language.class.php:556 inc/form_validator.class.php:403 #: inc/targetproblem.class.php:375 inc/questionrange.class.php:131 -#: inc/form.class.php:1535 inc/targetchange.class.php:148 -#: inc/section.class.php:404 inc/restrictedformcriteria.class.php:226 -#: inc/question.class.php:877 inc/questionparameter/range.class.php:137 -#: inc/questionregex.class.php:120 inc/questiondependency.class.php:211 -#: inc/item_targetticket.class.php:64 inc/targetticket.class.php:1447 +#: inc/form.class.php:1536 inc/targetchange.class.php:148 +#: inc/questionfilter.class.php:152 inc/section.class.php:404 +#: inc/restrictedformcriteria.class.php:226 inc/question.class.php:871 +#: inc/questionparameter/range.class.php:137 inc/questionregex.class.php:120 +#: inc/questiondependency.class.php:211 inc/item_targetticket.class.php:64 +#: inc/targetticket.class.php:1447 #, php-format msgid "Cannot export an empty object: %s" msgstr "" @@ -594,68 +605,69 @@ msgstr[1] "" msgid "Update issue data from tickets and form answers" msgstr "" -#: inc/issue.class.php:392 +#: inc/issue.class.php:379 msgid "Satisfaction survey expired" msgstr "" -#: inc/issue.class.php:521 inc/form_language.class.php:213 -#: inc/form.class.php:156 inc/form.class.php:2148 entrée standard:52 -#: standard:49 standard:57 standard:38 standard:39 +#: inc/issue.class.php:508 inc/form_language.class.php:213 +#: inc/form.class.php:156 inc/form.class.php:2149 entrée standard:52 +#: standard:49 standard:57 standard:38 standard:39 standard:46 msgid "Name" msgstr "" -#: inc/issue.class.php:534 inc/formanswer.class.php:199 +#: inc/issue.class.php:520 inc/formanswer.class.php:200 #: inc/formanswer.class.php:273 inc/targetproblem.class.php:623 #: inc/form.class.php:147 inc/targetchange.class.php:334 -#: inc/section.class.php:95 inc/question.class.php:156 -#: inc/targetticket.class.php:178 +#: inc/section.class.php:95 inc/question.class.php:176 +#: inc/targetticket.class.php:178 entrée standard:45 msgid "ID" msgstr "" -#: inc/issue.class.php:543 inc/form.class.php:503 inc/form.class.php:2157 +#: inc/issue.class.php:529 inc/form.class.php:503 inc/form.class.php:2158 #: entrée standard:65 msgid "Type" msgid_plural "Types" msgstr[0] "" msgstr[1] "" -#: inc/issue.class.php:556 inc/formanswer.class.php:260 +#: inc/issue.class.php:542 inc/formanswer.class.php:260 msgid "Status" msgstr "" -#: inc/issue.class.php:568 +#: inc/issue.class.php:554 msgid "Opening date" msgstr "" -#: inc/issue.class.php:577 +#: inc/issue.class.php:563 msgid "Last update" msgstr "" -#: inc/issue.class.php:586 inc/abstracttarget.class.php:518 +#: inc/issue.class.php:572 inc/abstracttarget.class.php:518 msgid "Entity" msgid_plural "Entities" msgstr[0] "" msgstr[1] "" -#: inc/issue.class.php:596 inc/notificationtargetformanswer.class.php:79 +#: inc/issue.class.php:582 inc/notificationtargetformanswer.class.php:79 #: inc/target_actor.class.php:83 inc/formanswer.class.php:219 -#: inc/formanswer.class.php:618 inc/abstractitiltarget.class.php:1724 +#: inc/formanswer.class.php:671 inc/abstractitiltarget.class.php:1724 entrée +#: standard:48 msgid "Requester" msgid_plural "Requesters" msgstr[0] "" msgstr[1] "" -#: inc/issue.class.php:614 inc/formanswer.class.php:229 +#: inc/issue.class.php:600 inc/formanswer.class.php:229 msgid "Form approver" msgstr "" -#: inc/issue.class.php:630 inc/formanswer.class.php:544 -#: inc/formanswer.class.php:551 inc/formanswer.class.php:633 +#: inc/issue.class.php:624 inc/formanswer.class.php:597 +#: inc/formanswer.class.php:604 inc/formanswer.class.php:686 #: inc/form_language.class.php:227 msgid "Comment" msgstr "" -#: inc/issue.class.php:642 +#: inc/issue.class.php:636 msgid "Ticket approver" msgstr "" @@ -663,46 +675,50 @@ msgstr "" msgid "Technician" msgstr "" -#: inc/issue.class.php:708 +#: inc/issue.class.php:707 msgid "Technician group" msgstr "" -#: inc/issue.class.php:741 inc/formanswer.class.php:249 +#: inc/issue.class.php:743 inc/formanswer.class.php:249 msgid "Form approver group" msgstr "" -#: inc/issue.class.php:853 +#: inc/issue.class.php:765 +msgid "Ticket approver group" +msgstr "" + +#: inc/issue.class.php:900 #, php-format msgid "%1$s %2$s" msgstr "" -#: inc/issue.class.php:1115 inc/filter/itilcategoryfilter.class.php:56 -#: hook.php:629 +#: inc/issue.class.php:1162 inc/filter/itilcategoryfilter.class.php:56 +#: hook.php:644 msgid "All" msgstr "" -#: inc/issue.class.php:1121 hook.php:630 +#: inc/issue.class.php:1168 hook.php:645 msgid "New" msgstr "" -#: inc/issue.class.php:1127 hook.php:631 +#: inc/issue.class.php:1174 hook.php:646 msgid "Assigned" msgstr "" -#: inc/issue.class.php:1133 inc/formanswer.class.php:76 -#: inc/form_validator.class.php:69 hook.php:632 +#: inc/issue.class.php:1180 inc/formanswer.class.php:77 +#: inc/form_validator.class.php:69 hook.php:647 msgid "Waiting" msgstr "" -#: inc/issue.class.php:1139 hook.php:633 +#: inc/issue.class.php:1186 hook.php:648 msgid "To validate" msgstr "" -#: inc/issue.class.php:1145 hook.php:634 +#: inc/issue.class.php:1192 hook.php:649 msgid "Solved" msgstr "" -#: inc/issue.class.php:1151 hook.php:635 +#: inc/issue.class.php:1198 hook.php:650 msgid "Closed" msgstr "" @@ -724,19 +740,19 @@ msgstr "" msgid "The form as been saved" msgstr "" -#: inc/notificationtargetformanswer.class.php:44 install/install.php:408 +#: inc/notificationtargetformanswer.class.php:44 install/install.php:409 msgid "A form need to be validate" msgstr "" -#: inc/notificationtargetformanswer.class.php:45 install/install.php:414 +#: inc/notificationtargetformanswer.class.php:45 install/install.php:415 msgid "The form is refused" msgstr "" -#: inc/notificationtargetformanswer.class.php:46 install/install.php:420 +#: inc/notificationtargetformanswer.class.php:46 install/install.php:421 msgid "The form is accepted" msgstr "" -#: inc/notificationtargetformanswer.class.php:47 install/install.php:426 +#: inc/notificationtargetformanswer.class.php:47 install/install.php:427 msgid "The form is deleted" msgstr "" @@ -754,6 +770,7 @@ msgid "Validator" msgstr "" #: inc/notificationtargetformanswer.class.php:81 inc/formanswer.class.php:240 +#: entrée standard:49 msgid "Creation date" msgstr "" @@ -864,86 +881,86 @@ msgstr "" msgid "Failed to find a supplier: %1$s" msgstr "" -#: inc/formanswer.class.php:77 inc/form_validator.class.php:71 +#: inc/formanswer.class.php:78 inc/form_validator.class.php:71 msgid "Refused" msgstr "" -#: inc/formanswer.class.php:78 inc/form_validator.class.php:70 +#: inc/formanswer.class.php:79 inc/form_validator.class.php:70 msgid "Accepted" msgstr "" -#: inc/formanswer.class.php:184 +#: inc/formanswer.class.php:185 msgid "Form answer" msgid_plural "Form answers" msgstr[0] "" msgstr[1] "" -#: inc/formanswer.class.php:529 +#: inc/formanswer.class.php:582 msgid "Print this form" msgstr "" -#: inc/formanswer.class.php:554 +#: inc/formanswer.class.php:607 msgid "Form accepted by validator." msgstr "" -#: inc/formanswer.class.php:556 +#: inc/formanswer.class.php:609 msgid "Form successfully saved." msgstr "" -#: inc/formanswer.class.php:626 inc/formaccesstype.class.php:122 +#: inc/formanswer.class.php:679 inc/formaccesstype.class.php:122 msgid "Save" msgstr "" -#: inc/formanswer.class.php:638 +#: inc/formanswer.class.php:691 msgid "Required if refused" msgstr "" -#: inc/formanswer.class.php:644 +#: inc/formanswer.class.php:697 msgid "Refuse" msgstr "" -#: inc/formanswer.class.php:652 +#: inc/formanswer.class.php:705 msgid "Edit answers" msgstr "" -#: inc/formanswer.class.php:658 +#: inc/formanswer.class.php:711 msgid "Cancel edition" msgstr "" -#: inc/formanswer.class.php:665 +#: inc/formanswer.class.php:718 msgid "Accept" msgstr "" -#: inc/formanswer.class.php:688 +#: inc/formanswer.class.php:741 msgid "Refused comment is required!" msgstr "" -#: inc/formanswer.class.php:762 +#: inc/formanswer.class.php:823 msgid "You are not the validator of these answers" msgstr "" -#: inc/formanswer.class.php:895 +#: inc/formanswer.class.php:962 #, php-format msgid "Item sucessfully added: %1$s (%2$s: %3$s)" msgstr "" -#: inc/formanswer.class.php:977 inc/formanswer.class.php:979 +#: inc/formanswer.class.php:1044 inc/formanswer.class.php:1046 msgid "Form data" msgstr "" -#: inc/formanswer.class.php:1105 inc/formanswer.class.php:1157 +#: inc/formanswer.class.php:1172 inc/formanswer.class.php:1224 msgid "Cannot generate targets!" msgstr "" -#: inc/formanswer.class.php:1281 +#: inc/formanswer.class.php:1369 msgid "No turing test set" msgstr "" -#: inc/formanswer.class.php:1286 +#: inc/formanswer.class.php:1375 msgid "You failed the Turing test" msgstr "" -#: inc/formanswer.class.php:1326 +#: inc/formanswer.class.php:1399 msgid "You must select validator!" msgstr "" @@ -955,15 +972,15 @@ msgstr "" msgid "Failed to delete this issue. An internal error occured." msgstr "" -#: inc/common.class.php:780 +#: inc/common.class.php:783 msgid "Seek assistance" msgstr "" -#: inc/common.class.php:785 +#: inc/common.class.php:788 inc/common.class.php:792 msgid "My requests for assistance" msgstr "" -#: inc/common.class.php:815 +#: inc/common.class.php:827 msgid "Consult feeds" msgstr "" @@ -995,14 +1012,14 @@ msgid_plural "Form languages" msgstr[0] "" msgstr[1] "" -#: inc/form_language.class.php:72 +#: inc/form_language.class.php:72 inc/form_language.class.php:348 msgid "Translation" msgid_plural "Translations" msgstr[0] "" msgstr[1] "" -#: inc/form_language.class.php:111 inc/form.class.php:1076 -#: inc/form.class.php:1201 inc/abstracttarget.class.php:161 +#: inc/form_language.class.php:111 inc/form.class.php:1077 +#: inc/form.class.php:1202 inc/abstracttarget.class.php:161 #: inc/abstractitiltarget.class.php:1544 msgid "The name cannot be empty!" msgstr "" @@ -1015,7 +1032,7 @@ msgstr "" msgid "Add a translation" msgstr "" -#: inc/form_language.class.php:283 js/scripts.js:1156 +#: inc/form_language.class.php:283 js/scripts.js:1162 msgid "Update a translation" msgstr "" @@ -1073,7 +1090,7 @@ msgid "No" msgstr "" #: inc/form_validator.class.php:295 inc/targetproblem.class.php:146 -#: inc/entityconfig.class.php:411 inc/targetchange.class.php:483 +#: inc/entityconfig.class.php:437 inc/targetchange.class.php:483 #: inc/targetticket.class.php:316 inc/targetticket.class.php:351 #: inc/abstractitiltarget.class.php:2031 msgctxt "button" @@ -1134,122 +1151,134 @@ msgstr "" msgid "Symptom" msgstr "" -#: inc/entityconfig.class.php:90 inc/entityconfig.class.php:99 -#: inc/entityconfig.class.php:107 inc/entityconfig.class.php:115 -#: inc/entityconfig.class.php:123 inc/entityconfig.class.php:131 -#: inc/entityconfig.class.php:139 inc/entityconfig.class.php:147 -#: inc/entityconfig.class.php:155 +#: inc/entityconfig.class.php:92 inc/entityconfig.class.php:101 +#: inc/entityconfig.class.php:109 inc/entityconfig.class.php:117 +#: inc/entityconfig.class.php:125 inc/entityconfig.class.php:133 +#: inc/entityconfig.class.php:141 inc/entityconfig.class.php:149 +#: inc/entityconfig.class.php:157 inc/entityconfig.class.php:165 msgid "Inheritance of the parent entity" msgstr "" -#: inc/entityconfig.class.php:91 +#: inc/entityconfig.class.php:93 msgid "GLPi's helpdesk" msgstr "" -#: inc/entityconfig.class.php:92 +#: inc/entityconfig.class.php:94 msgid "Service catalog simplified" msgstr "" -#: inc/entityconfig.class.php:93 +#: inc/entityconfig.class.php:95 msgid "Service catalog extended" msgstr "" -#: inc/entityconfig.class.php:100 +#: inc/entityconfig.class.php:102 msgid "All available forms" msgstr "" -#: inc/entityconfig.class.php:101 +#: inc/entityconfig.class.php:103 msgid "Only default forms" msgstr "" -#: inc/entityconfig.class.php:108 +#: inc/entityconfig.class.php:110 msgid "Popularity sort" msgstr "" -#: inc/entityconfig.class.php:109 +#: inc/entityconfig.class.php:111 msgid "Alphabetic sort" msgstr "" -#: inc/entityconfig.class.php:116 +#: inc/entityconfig.class.php:118 msgid "Merged with Forms" msgstr "" -#: inc/entityconfig.class.php:117 +#: inc/entityconfig.class.php:119 msgid "Distinct menu entry" msgstr "" -#: inc/entityconfig.class.php:124 inc/entityconfig.class.php:132 -#: inc/entityconfig.class.php:140 inc/entityconfig.class.php:148 +#: inc/entityconfig.class.php:126 inc/entityconfig.class.php:134 +#: inc/entityconfig.class.php:142 inc/entityconfig.class.php:150 #: inc/form.class.php:285 entrée standard:100 msgid "Visible" msgstr "" -#: inc/entityconfig.class.php:125 inc/entityconfig.class.php:133 -#: inc/entityconfig.class.php:141 inc/entityconfig.class.php:149 +#: inc/entityconfig.class.php:127 inc/entityconfig.class.php:135 +#: inc/entityconfig.class.php:143 inc/entityconfig.class.php:151 msgid "Hidden" msgstr "" -#: inc/entityconfig.class.php:156 +#: inc/entityconfig.class.php:158 msgid "Variable height" msgstr "" -#: inc/entityconfig.class.php:157 +#: inc/entityconfig.class.php:159 msgid "Uniform height" msgstr "" -#: inc/entityconfig.class.php:253 +#: inc/entityconfig.class.php:166 +msgid "Folded" +msgstr "" + +#: inc/entityconfig.class.php:167 +msgid "Unfolded" +msgstr "" + +#: inc/entityconfig.class.php:263 msgid "Helpdesk" msgstr "" -#: inc/entityconfig.class.php:261 inc/entityconfig.class.php:425 entrée +#: inc/entityconfig.class.php:271 inc/entityconfig.class.php:451 entrée #: standard:44 msgid "Helpdesk mode" msgstr "" -#: inc/entityconfig.class.php:272 +#: inc/entityconfig.class.php:282 msgid "Default Form list mode" msgstr "" -#: inc/entityconfig.class.php:290 inc/entityconfig.class.php:435 +#: inc/entityconfig.class.php:300 inc/entityconfig.class.php:461 msgid "Sort order" msgstr "" -#: inc/entityconfig.class.php:306 inc/entityconfig.class.php:445 +#: inc/entityconfig.class.php:316 inc/entityconfig.class.php:471 msgid "Knowledge base" msgstr "" -#: inc/entityconfig.class.php:321 +#: inc/entityconfig.class.php:331 msgid "Search" msgstr "" -#: inc/entityconfig.class.php:337 +#: inc/entityconfig.class.php:347 msgid "Counters dashboard" msgstr "" -#: inc/entityconfig.class.php:353 +#: inc/entityconfig.class.php:363 msgid "Header message" msgstr "" -#: inc/entityconfig.class.php:369 +#: inc/entityconfig.class.php:379 msgid "Search issue" msgstr "" -#: inc/entityconfig.class.php:385 +#: inc/entityconfig.class.php:395 msgid "Tile design" msgstr "" -#: inc/entityconfig.class.php:397 inc/entityconfig.class.php:475 +#: inc/entityconfig.class.php:411 +msgid "Menu visibility (only for vertical menu)" +msgstr "" + +#: inc/entityconfig.class.php:423 inc/entityconfig.class.php:501 #: inc/form.class.php:239 entrée standard:84 msgid "Header" msgid_plural "Headers" msgstr[0] "" msgstr[1] "" -#: inc/entityconfig.class.php:455 +#: inc/entityconfig.class.php:481 msgid "Display search field" msgstr "" -#: inc/entityconfig.class.php:465 +#: inc/entityconfig.class.php:491 msgid "Display header" msgstr "" @@ -1283,7 +1312,7 @@ msgstr "" msgid "Answers waiting for validation" msgstr "" -#: inc/form.class.php:124 inc/form.class.php:1619 inc/form.class.php:1645 +#: inc/form.class.php:124 inc/form.class.php:1620 inc/form.class.php:1646 msgid "Import forms" msgstr "" @@ -1336,7 +1365,7 @@ msgstr "" msgid "All languages" msgstr "" -#: inc/form.class.php:502 inc/form.class.php:561 +#: inc/form.class.php:502 inc/form.class.php:562 msgid "Target" msgid_plural "Targets" msgstr[0] "" @@ -1346,197 +1375,197 @@ msgstr[1] "" msgid "Actions" msgstr "" -#: inc/form.class.php:544 inc/form.class.php:2145 +#: inc/form.class.php:544 inc/form.class.php:2146 msgid "Add a target" msgstr "" -#: inc/form.class.php:564 +#: inc/form.class.php:565 msgid "Preview" msgstr "" -#: inc/form.class.php:565 +#: inc/form.class.php:566 msgid "properties" msgstr "" -#: inc/form.class.php:884 +#: inc/form.class.php:885 msgid "What are you looking for?" msgstr "" -#: inc/form.class.php:893 +#: inc/form.class.php:894 #, php-format msgid "My %1$d last forms (requester)" msgstr "" -#: inc/form.class.php:920 +#: inc/form.class.php:921 msgid "No form posted yet" msgstr "" -#: inc/form.class.php:948 +#: inc/form.class.php:949 msgid "All my forms (requester)" msgstr "" -#: inc/form.class.php:961 +#: inc/form.class.php:962 #, php-format msgid "My %1$d last forms (validator)" msgstr "" -#: inc/form.class.php:988 +#: inc/form.class.php:989 msgid "No form waiting for validation" msgstr "" -#: inc/form.class.php:1016 +#: inc/form.class.php:1017 msgid "All my forms (validator)" msgstr "" -#: inc/form.class.php:1212 +#: inc/form.class.php:1213 msgid "Cannot use empty name for form answers. Keeping the previous value." msgstr "" -#: inc/form.class.php:1307 +#: inc/form.class.php:1308 #, php-format msgid "The question %s is not compatible with public forms" msgstr "" -#: inc/form.class.php:1387 +#: inc/form.class.php:1388 msgid "Errored duplicate" msgstr "" -#: inc/form.class.php:1398 +#: inc/form.class.php:1399 msgid "Duplicate" msgstr "" -#: inc/form.class.php:1445 +#: inc/form.class.php:1446 msgctxt "button" msgid "Post" msgstr "" -#: inc/form.class.php:1463 +#: inc/form.class.php:1464 #, php-format msgid "Form duplicated: %s" msgstr "" -#: inc/form.class.php:1474 +#: inc/form.class.php:1475 #, php-format msgid "Form Transfered: %s" msgstr "" -#: inc/form.class.php:1493 +#: inc/form.class.php:1494 msgid "Back" msgstr "" -#: inc/form.class.php:1592 +#: inc/form.class.php:1593 msgid "Upload of JSON files not allowed." msgstr "" -#: inc/form.class.php:1595 +#: inc/form.class.php:1596 msgid "You may allow JSON files right now." msgstr "" -#: inc/form.class.php:1596 +#: inc/form.class.php:1597 msgctxt "button" msgid "Create" msgstr "" -#: inc/form.class.php:1599 inc/form.class.php:1610 +#: inc/form.class.php:1600 inc/form.class.php:1611 msgid "Please contact your GLPI administrator." msgstr "" -#: inc/form.class.php:1600 inc/form.class.php:1611 +#: inc/form.class.php:1601 inc/form.class.php:1612 msgctxt "button" msgid "Back" msgstr "" -#: inc/form.class.php:1603 +#: inc/form.class.php:1604 msgid "Upload of JSON files not enabled." msgstr "" -#: inc/form.class.php:1606 inc/form.class.php:1609 +#: inc/form.class.php:1607 inc/form.class.php:1610 msgid "You may enable JSON files right now." msgstr "" -#: inc/form.class.php:1607 +#: inc/form.class.php:1608 msgctxt "button" msgid "Enable" msgstr "" -#: inc/form.class.php:1654 +#: inc/form.class.php:1655 msgctxt "button" msgid "Send" msgstr "" -#: inc/form.class.php:1673 +#: inc/form.class.php:1674 msgid "Forms import impossible, the file is empty" msgstr "" -#: inc/form.class.php:1677 inc/form.class.php:1681 +#: inc/form.class.php:1678 inc/form.class.php:1682 msgid "Forms import impossible, the file seems corrupt" msgstr "" -#: inc/form.class.php:1687 +#: inc/form.class.php:1688 msgid "Forms import impossible, the file was generated with another version" msgstr "" -#: inc/form.class.php:1694 +#: inc/form.class.php:1695 msgid "" "The file does not specifies the schema version. It was probably generated " "with a version older than 2.10. Giving up." msgstr "" -#: inc/form.class.php:1720 +#: inc/form.class.php:1721 #, php-format msgid "Failed to import %s" msgstr "" -#: inc/form.class.php:1725 +#: inc/form.class.php:1726 #, php-format msgid "Forms successfully imported from %s" msgstr "" -#: inc/form.class.php:1788 +#: inc/form.class.php:1789 #, php-format msgid "The form %1$s already exists and is in an unmodifiable entity." msgstr "" -#: inc/form.class.php:1796 +#: inc/form.class.php:1797 #, php-format msgid "You don't have right to update the entity %1$s." msgstr "" -#: inc/form.class.php:1806 +#: inc/form.class.php:1807 #, php-format msgid "The entity %1$s is required for the form %2$s." msgstr "" -#: inc/form.class.php:1888 +#: inc/form.class.php:1889 msgid "Failed to create JSON document type" msgstr "" -#: inc/form.class.php:1895 +#: inc/form.class.php:1896 msgid "JSON document type not found" msgstr "" -#: inc/form.class.php:1902 +#: inc/form.class.php:1903 msgid "Failed to update JSON document type" msgstr "" -#: inc/form.class.php:1922 +#: inc/form.class.php:1923 msgid "Forms without category" msgstr "" -#: inc/form.class.php:1943 +#: inc/form.class.php:1944 msgid "No form available" msgstr "" -#: inc/form.class.php:2176 inc/targetticket.class.php:391 +#: inc/form.class.php:2177 inc/targetticket.class.php:391 #: inc/abstractitiltarget.class.php:1748 inc/abstractitiltarget.class.php:2031 msgid "Add" msgstr "" -#: inc/form.class.php:2194 inc/form.class.php:2217 +#: inc/form.class.php:2195 inc/form.class.php:2218 msgid "Unsupported target type." msgstr "" -#: inc/form.class.php:2251 +#: inc/form.class.php:2252 msgid "plugin_formcreator_load_check" msgstr "" @@ -1566,6 +1595,12 @@ msgstr "" msgid "Check list" msgstr "" +#: inc/questionfilter.class.php:52 +msgid "Question filter" +msgid_plural "Question filters" +msgstr[0] "" +msgstr[1] "" + #: inc/section.class.php:71 entrée standard:53 standard:44 msgid "Section" msgid_plural "Sections" @@ -1573,7 +1608,7 @@ msgstr[0] "" msgstr[1] "" #: inc/section.class.php:123 inc/section.class.php:164 -#: inc/question.class.php:312 +#: inc/question.class.php:332 msgid "The title is required" msgstr "" @@ -1582,94 +1617,86 @@ msgstr "" msgid "Failed to find %1$s %2$s" msgstr "" -#: inc/question.class.php:70 inc/abstractitiltarget.class.php:1082 entrée +#: inc/question.class.php:72 inc/abstractitiltarget.class.php:1082 entrée #: standard:41 msgid "Question" msgid_plural "Questions" msgstr[0] "" msgstr[1] "" -#: inc/question.class.php:228 entrée standard:37 +#: inc/question.class.php:248 entrée standard:37 msgid "Count of conditions" msgstr "" -#: inc/question.class.php:320 -msgid "The field type is required" -msgstr "" - -#: inc/question.class.php:327 +#: inc/question.class.php:340 msgid "The section is required" msgstr "" -#: inc/question.class.php:339 +#: inc/question.class.php:352 #, php-format msgid "Field type %1$s is not available for question %2$s." msgstr "" -#: inc/question.class.php:353 +#: inc/question.class.php:366 msgid "This type of question is not compatible with public forms." msgstr "" -#: inc/question.class.php:362 -msgid "This type of question requires parameters" +#: inc/question.class.php:455 +msgid "The field type cannot be changed. Delete then recreate the question." msgstr "" -#: inc/question.class.php:368 -msgid "A parameter is missing for this question type" -msgstr "" - -#: inc/question.class.php:1178 +#: inc/question.class.php:1163 msgid "Service levels" msgstr "" -#: inc/question.class.php:1179 inc/abstractitiltarget.class.php:805 +#: inc/question.class.php:1164 inc/abstractitiltarget.class.php:805 msgid "SLA" msgstr "" -#: inc/question.class.php:1180 inc/abstractitiltarget.class.php:878 +#: inc/question.class.php:1165 inc/abstractitiltarget.class.php:878 msgid "OLA" msgstr "" -#: inc/question.class.php:1197 inc/question.class.php:1238 -#: inc/question.class.php:1241 +#: inc/question.class.php:1182 inc/question.class.php:1223 +#: inc/question.class.php:1226 msgid "Assets" msgstr "" -#: inc/question.class.php:1211 hook.php:643 hook.php:660 +#: inc/question.class.php:1196 hook.php:658 hook.php:675 msgid "Assistance" msgstr "" -#: inc/question.class.php:1217 +#: inc/question.class.php:1202 msgid "Management" msgstr "" -#: inc/question.class.php:1226 +#: inc/question.class.php:1211 msgid "Tools" msgstr "" -#: inc/question.class.php:1227 +#: inc/question.class.php:1212 msgid "Notes" msgstr "" -#: inc/question.class.php:1228 +#: inc/question.class.php:1213 msgid "RSS feed" msgstr "" -#: inc/question.class.php:1230 +#: inc/question.class.php:1215 msgid "Administration" msgstr "" -#: inc/question.class.php:1238 inc/question.class.php:1241 +#: inc/question.class.php:1223 inc/question.class.php:1226 msgid "Plugin" msgid_plural "Plugins" msgstr[0] "" msgstr[1] "" -#: inc/abstractquestionparameter.class.php:92 +#: inc/abstractquestionparameter.class.php:98 msgid "Parameter" msgstr "" -#: inc/abstractquestionparameter.class.php:99 +#: inc/abstractquestionparameter.class.php:105 msgid "Field name" msgstr "" @@ -1878,7 +1905,7 @@ msgstr "" msgid "Failed to link the item" msgstr "" -#: inc/targetticket.class.php:924 install/install.php:421 +#: inc/targetticket.class.php:924 install/install.php:422 msgid "Your form has been accepted by the validator" msgstr "" @@ -2101,72 +2128,72 @@ msgstr "" msgid "Tech group from the object" msgstr "" -#: install/install.php:130 +#: install/install.php:131 #, php-format msgid "Upgrade tables to innoDB; run %s" msgstr "" -#: install/install.php:160 +#: install/install.php:161 #, php-format msgid "" "The database schema is not consistent with the installed Formcreator %s. To " "see the logs run the command %s" msgstr "" -#: install/install.php:169 +#: install/install.php:170 #, php-format msgid "To ignore the inconsistencies and upgrade anyway run %s" msgstr "" -#: install/install.php:187 +#: install/install.php:188 msgid "" "Upgrade from version older than 2.5.0 is no longer supported. Please upgrade " "to GLPI 9.5.7, upgrade Formcreator to version 2.12.5, then upgrade again to " "GLPI 10 or later and Formcreator 2.13 or later." msgstr "" -#: install/install.php:239 +#: install/install.php:240 #, php-format msgid "" "The database schema is not consistent with the installed Formcreator %s. To " "see the logs enable the plugin and run the command %s" msgstr "" -#: install/install.php:250 +#: install/install.php:251 msgid "The tables of the plugin passed the schema integrity check." msgstr "" -#: install/install.php:402 +#: install/install.php:403 msgid "A form has been created" msgstr "" -#: install/install.php:403 +#: install/install.php:404 msgid "Your request has been saved" msgstr "" -#: install/install.php:404 +#: install/install.php:405 msgid "" "Hi,\\nYour request from GLPI has been successfully saved with number " "##formcreator.request_id## and transmitted to the helpdesk team.\\nYou can " "see your answers onto the following link:\\n##formcreator.validation_link##" msgstr "" -#: install/install.php:409 +#: install/install.php:410 msgid "A form from GLPI need to be validate" msgstr "" -#: install/install.php:410 +#: install/install.php:411 msgid "" "Hi,\\nA form from GLPI need to be validate and you have been choosen as the " "validator.\\nYou can access it by clicking onto this link:\\n##formcreator." "validation_link##" msgstr "" -#: install/install.php:415 +#: install/install.php:416 msgid "Your form has been refused by the validator" msgstr "" -#: install/install.php:416 +#: install/install.php:417 msgid "" "Hi,\\nWe are sorry to inform you that your form has been refused by the " "validator for the reason below:\\n##formcreator." @@ -2174,77 +2201,77 @@ msgid "" "onto this link:\\n##formcreator.validation_link##" msgstr "" -#: install/install.php:422 +#: install/install.php:423 msgid "" "Hi,\\nWe are pleased to inform you that your form has been accepted by the " "validator.\\nYour request will be considered soon." msgstr "" -#: install/install.php:427 +#: install/install.php:428 msgid "Your form has been deleted by an administrator" msgstr "" -#: install/install.php:428 +#: install/install.php:429 msgid "" "Hi,\\nWe are sorry to inform you that your request cannot be considered and " "has been deleted by an administrator." msgstr "" -#: install/install.php:654 +#: install/install.php:655 msgid "Formcreator - Sync service catalog issues" msgstr "" -#: install/install.php:842 +#: install/install.php:843 msgid "Failed to check the sanity of the tables!" msgstr "" -#: install/install.php:856 +#: install/install.php:857 #, php-format msgid "Table schema differs for table \"%s\"." msgstr "" -#: install/install.php:859 +#: install/install.php:860 #, php-format msgid "Table \"%s\" is missing." msgstr "" -#: install/install.php:862 +#: install/install.php:863 #, php-format msgid "Unknown table \"%s\" has been found in database." msgstr "" -#: hook.php:317 +#: hook.php:332 msgctxt "button" msgid "Duplicate" msgstr "" -#: hook.php:318 +#: hook.php:333 msgid "Transfer" msgstr "" -#: hook.php:319 +#: hook.php:334 msgctxt "button" msgid "Export" msgstr "" -#: hook.php:619 +#: hook.php:634 msgid "Cancel my ticket" msgstr "" -#: hook.php:637 +#: hook.php:652 msgid "Old" msgstr "" -#: hook.php:644 +#: hook.php:659 #, php-format msgid "Number of %s" msgstr "" -#: hook.php:661 +#: hook.php:676 msgid "Issues summary" msgstr "" -#: hook.php:699 +#: hook.php:714 msgid "" "Formcreator's mini dashboard not usable as default. This Setting has been " "ignored." @@ -2262,35 +2289,35 @@ msgstr "" msgid "No FAQ item found." msgstr "" -#: js/scripts.js:686 +#: js/scripts.js:687 msgid "Are you sure you want to delete this question?" msgstr "" -#: js/scripts.js:869 +#: js/scripts.js:871 msgid "Are you sure you want to delete this section?" msgstr "" -#: js/scripts.js:1109 +#: js/scripts.js:1115 msgid "Add translations" msgstr "" -#: js/scripts.js:1256 js/scripts.js:1280 +#: js/scripts.js:1262 js/scripts.js:1286 msgid "An error occured while querying forms" msgstr "" -#: js/scripts.js:1396 entrée standard:139 +#: js/scripts.js:1402 entrée standard:139 msgid "Send" msgstr "" -#: js/scripts.js:1405 +#: js/scripts.js:1411 msgid "An internal error occurred. Please report it to administrator." msgstr "" -#: js/scripts.js:1446 +#: js/scripts.js:1452 msgid "Are you sure you want to delete this target:" msgstr "" -#: entrée standard:43 standard:50 standard:44 standard:49 standard:78 +#: entrée standard:43 standard:50 standard:44 standard:49 standard:79 msgid "Required" msgstr "" @@ -2309,10 +2336,6 @@ msgid_plural "LDAP directories" msgstr[0] "" msgstr[1] "" -#: entrée standard:68 -msgid "Filter" -msgstr "" - #: entrée standard:78 msgid "Attribute" msgstr "" @@ -2349,7 +2372,7 @@ msgstr "" msgid "Condition to generate the target" msgstr "" -#: entrée standard:127 +#: entrée standard:128 msgid "Condition to show the question" msgstr "" @@ -2373,6 +2396,15 @@ msgstr "" msgid "Condition to show the submit button" msgstr "" +#: entrée standard:33 +msgid "No form answer yet" +msgstr "" + +#: entrée standard:38 +#, php-format +msgid "%s latest items" +msgstr "" + #: entrée standard:83 msgid "Add a question" msgstr "" From 9e362d667390857e1a53d301f915ceeee0527348 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Tue, 8 Nov 2022 15:24:18 +0100 Subject: [PATCH 040/248] fix(form): prevent sending two csrf tokens and force GLPI to answer JSON in case of error --- js/scripts.js | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/js/scripts.js b/js/scripts.js index 070fd25a3..272ca3e74 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -825,16 +825,19 @@ var plugin_formcreator = new function() { }; this.showFields = function (form) { + var data = form.serializeArray(); + data = this.serializeForAjax(form); + $.ajax({ url: formcreatorRootDoc + '/ajax/showfields.php', type: "POST", - data: form.serializeArray() + dataType: 'json', + data: data }).done(function(response){ try { - var itemToShow = JSON.parse(response); - var questionToShow = itemToShow['PluginFormcreatorQuestion']; - var sectionToShow = itemToShow['PluginFormcreatorSection']; - var submitButtonToShow = itemToShow['PluginFormcreatorForm']; + var questionToShow = response['PluginFormcreatorQuestion']; + var sectionToShow = response['PluginFormcreatorSection']; + var submitButtonToShow = response['PluginFormcreatorForm']; } catch (e) { // Do nothing for now } @@ -1430,6 +1433,18 @@ var plugin_formcreator = new function() { return true; }; + + /** + * Serialize a form without its csrf token + * @param {*} form + * @returns + */ + this.serializeForAjax = function (form) { + var serialized = form.serializeArray() + return serialized.filter( function( item ) { + return item.name != '_glpi_csrf_token'; + }); + } } // === TARGETS === From c9904180662b1cbc13080fe82e7a033871ad6972 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Mon, 14 Nov 2022 15:48:41 +0100 Subject: [PATCH 041/248] fix(abstracttarget): support for sla and ola from question --- inc/field/dropdownfield.class.php | 6 ++++-- templates/field/dropdownfield.html.twig | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/inc/field/dropdownfield.class.php b/inc/field/dropdownfield.class.php index 4728a48db..9ecead810 100644 --- a/inc/field/dropdownfield.class.php +++ b/inc/field/dropdownfield.class.php @@ -54,6 +54,7 @@ use Ticket_User; use Search; use SLA; +use SLM; use OLA; use QuerySubQuery; use QueryUnion; @@ -89,6 +90,7 @@ public function showForm(array $options): void { $this->question->fields['_tree_root_selectable'] = $decodedValues['selectable_tree_root'] ?? '0'; $this->question->fields['_tree_max_depth'] = $decodedValues['show_tree_depth'] ?? Dropdown::EMPTY_VALUE; $this->question->fields['_show_ticket_categories'] = isset($decodedValues['show_ticket_categories']) ? $decodedValues['show_ticket_categories'] : 'both'; + $this->question->fields['_show_service_level_types'] = isset($decodedValues['show_service_level_types']) ? $decodedValues['show_service_level_types'] : SLM::TTO; $this->question->fields['_entity_restrict'] = $decodedValues['entity_restrict'] ?? self::ENTITY_RESTRICT_FORM; $this->question->fields['_is_tree'] = '0'; $this->question->fields['_is_entity_restrict'] = '0'; @@ -514,8 +516,8 @@ public function prepareQuestionInputForSave($input) { } else if ($input['itemtype'] == SLA::getType() || $input['itemtype'] == OLA::getType() ) { - $input['values']['show_service_level_types'] = $input['show_service_level_types']; - unset($input['show_service_level_types']); + $input['values']['show_service_level_types'] = $input['_show_service_level_types']; + unset($input['_show_service_level_types']); } // Params for entity restrictables itemtypes diff --git a/templates/field/dropdownfield.html.twig b/templates/field/dropdownfield.html.twig index 13127e163..dc6c9eba7 100644 --- a/templates/field/dropdownfield.html.twig +++ b/templates/field/dropdownfield.html.twig @@ -99,6 +99,25 @@ }) }} {% endif %} + {% if item.fields['itemtype'] is defined and (item.fields['itemtype'] == 'SLA' or item.fields['itemtype'] == 'OLA' ) %} + {# keys are SLM:TTR, SLM::TTO #} + {{ fields.dropdownArrayField( + '_show_service_level_types', + item.fields['_show_service_level_types'], + { + 0: __('Time to resolve', 'formcreator'), + 1: __('Time to own', 'formcreator'), + }, + __('Type', 'formcreator') + ) }} + + {{ fields.nullField({ + label_class: 'col-xxl-4', + input_class: 'col-xxl-8', + }) }} + {% endif %} + + {# _is_tree is set in the showForm method, and helps to find if the itemtype is a tree #} {% if item.fields['_is_tree'] == '1' %} From 384d4628413e16aa0b6f4b301c0eafc3af9eb633 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Fri, 18 Nov 2022 17:31:42 +0100 Subject: [PATCH 042/248] fix(form): prevent SQL error when current user is not member of any group, MySQL may warn about truncated decimal (nonsens, but happens) because groups are tested against IN('') statement --- inc/form.class.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/inc/form.class.php b/inc/form.class.php index d1de6252f..f50538609 100644 --- a/inc/form.class.php +++ b/inc/form.class.php @@ -967,12 +967,6 @@ protected function showMyLastForms() : void { 'searchtype' => 'equals', 'value' => 'myself', ], - 1 => [ - 'link' => 'OR', - 'field' => 7, - 'searchtype' => 'equals', - 'value' => 'mygroups', - ], ], 'sort' => [ 0 => 6 @@ -981,6 +975,15 @@ protected function showMyLastForms() : void { 0 => 'DESC' ], ]; + if (count($_SESSION['glpigroups'] ?? []) > 0) { + // The user is member of some groups, then add criteria for those groups + $criteria['criteria'][] = [ + 'link' => 'OR', + 'field' => 7, + 'searchtype' => 'equals', + 'value' => 'mygroups', + ]; + } $backupListLimit = $_SESSION['glpilist_limit']; $_SESSION['glpilist_limit'] = 5; $search = Search::getDatas(PluginFormcreatorFormAnswer::class, $criteria, $showColumns); From 2ba4e765fd3d969444d4adfa5a8579c105fbe712 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Fri, 18 Nov 2022 17:51:54 +0100 Subject: [PATCH 043/248] fix(issue): useless criteria nesting --- inc/issue.class.php | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/inc/issue.class.php b/inc/issue.class.php index 040beefde..1aff22da2 100644 --- a/inc/issue.class.php +++ b/inc/issue.class.php @@ -993,17 +993,13 @@ static function getValidateCriteria() { } static function getSolvedCriteria() { - return ['criteria' => [ - [ - 'criteria' => [[ - 'link' => 'OR', - 'field' => 4, - 'searchtype' => 'equals', - 'value' => Ticket::SOLVED, // see Ticket::getAllStatusArray() - ], - ]], - ], - 'reset' => 'reset']; + return ['criteria' => [['link' => 'AND', + 'field' => 4, + 'searchtype' => 'equals', + 'value' => Ticket::SOLVED, // see Ticket::getAllStatusArray() + ], + ], + 'reset' => 'reset']; } static function getClosedCriteria() { From 5b7fa1af02fe15771954c686bc54e65b6be01f16 Mon Sep 17 00:00:00 2001 From: AdrienClairembault Date: Mon, 21 Nov 2022 16:12:15 +0100 Subject: [PATCH 044/248] Prevent warning on null value --- inc/formanswer.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/formanswer.class.php b/inc/formanswer.class.php index 94ebc9976..bced78bfa 100644 --- a/inc/formanswer.class.php +++ b/inc/formanswer.class.php @@ -1661,7 +1661,7 @@ public function deserializeAnswers() : bool { $this->loadAnswers(); $answers_values = $this->getAnswers(); - foreach (array_keys($this->questionFields) as $id) { + foreach (array_keys($this->questionFields ?? []) as $id) { if (!$this->questionFields[$id]->hasInput($answers_values)) { continue; } From e74c7299115c18713c6e134f380882e94d28cba3 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Fri, 18 Nov 2022 18:05:35 +0100 Subject: [PATCH 045/248] Revert "fix(issue): remove duplicate item in status dropdown" This reverts commit 27f9f313a08d1a7df7228d0f4dfbccff80ad72ec. --- inc/issue.class.php | 1 - 1 file changed, 1 deletion(-) diff --git a/inc/issue.class.php b/inc/issue.class.php index 1aff22da2..8518a13a9 100644 --- a/inc/issue.class.php +++ b/inc/issue.class.php @@ -825,7 +825,6 @@ public static function getSpecificValueToSelect($field, $name = '', $values = '' case 'status' : $ticket_opts = Ticket::getAllStatusArray(true); $ticket_opts = $ticket_opts + PluginFormcreatorFormAnswer::getStatuses(); - unset($ticket_opts[PluginFormcreatorFormAnswer::STATUS_WAITING]); return Dropdown::showFromArray($name, $ticket_opts, ['display' => false, 'value' => $values[$field]]); break; From b3dc16becb6f1eb07c46bdbf03148107c8b7b6cc Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Tue, 22 Nov 2022 09:04:02 +0100 Subject: [PATCH 046/248] test: fix bad class namespace --- tests/src/CommonTestCase.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/CommonTestCase.php b/tests/src/CommonTestCase.php index 98ae9a482..47e3d42a9 100644 --- a/tests/src/CommonTestCase.php +++ b/tests/src/CommonTestCase.php @@ -7,6 +7,7 @@ use Auth; use atoum; use Ticket; +use User; abstract class CommonTestCase extends atoum { From 9647c24c480b053ca585768b8a926316f4f45f7e Mon Sep 17 00:00:00 2001 From: btry Date: Tue, 22 Nov 2022 12:58:55 +0100 Subject: [PATCH 047/248] docs: update versions list version 2.12.6 for GLPI 9.5.10+ --- plugin.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugin.xml b/plugin.xml index ec02be4e8..3cf0505d7 100644 --- a/plugin.xml +++ b/plugin.xml @@ -78,6 +78,11 @@ Features ~9.5.5 https://github.com/pluginsGLPI/formcreator/releases/download/v2.12.6/glpi-formcreator-2.12.6.tar.bz2
+ + 2.12.6 + ~9.5.5 + https://github.com/pluginsGLPI/formcreator/releases/download/v2.12.6/glpi-formcreator-2.12.6.tar.bz2 + 2.12.5 ~9.5.5 From cfccf40c41ccb13133bf72b9d040f44ec663f46c Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Wed, 23 Nov 2022 21:41:58 +0100 Subject: [PATCH 048/248] ci: disable functoinal tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cf27642de..3aedd25c8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,7 +28,7 @@ env: SKIP_UPGRADE_TESTS: false SKIP_INSTALL_TESTS: false SKIP_TESTS: false - SKIP_FUNCTIONAL_TESTS: false + SKIP_FUNCTIONAL_TESTS: true SKIP_UNINSTALL_TESTS: false SKIP_LINT_TESTS: false SKIP_CS_TESTS: false From dfd014f913b4d0b906b2c1ac88539ba8df4a3139 Mon Sep 17 00:00:00 2001 From: Thierry Bugier Date: Wed, 23 Nov 2022 14:59:46 +0100 Subject: [PATCH 049/248] fix(issue): show save button for followup edit internal refs 24073, 25658 --- inc/issue.class.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/inc/issue.class.php b/inc/issue.class.php index 8518a13a9..a74b85087 100644 --- a/inc/issue.class.php +++ b/inc/issue.class.php @@ -388,7 +388,9 @@ public function displaySimplified(CommonDBTM $item, $options = []): void { echo '#itil-object-container .form-buttons span { display: none !important }'; echo '#itil-object-container .form-buttons { flex: inherit; width: auto}'; echo "#itil-object-container .timeline-buttons { flex: 1 1 auto }"; - echo "#itil-object-container button[type='submit'][name='update'] { display: none }"; + // The following line becomes useless with GLPI 10.0.5 as the save button of side panel does no longer show for extended service catalog + // To drop when GLPI 10.0.5 is the minimum version + echo "#itil-object-container .form-buttons button[type='submit'][name='update'] { display: none }"; echo ''; $item->showForm($item->getID()); echo ""; From 1017c8f2112acab0a61332941ed76b0f0095f0c8 Mon Sep 17 00:00:00 2001 From: Adrien Clairembault <42734840+AdrienClairembault@users.noreply.github.com> Date: Thu, 24 Nov 2022 10:36:34 +0100 Subject: [PATCH 050/248] feat(question): new hooks for other plugins interaction (#3093) --- inc/abstractfield.class.php | 8 +++ inc/abstractitiltarget.class.php | 99 ++++++++++++++++++++++++----- inc/plugintargetinterface.class.php | 47 ++++++++++++++ inc/target_actor.class.php | 18 +++++- js/scripts.js | 49 +------------- 5 files changed, 157 insertions(+), 64 deletions(-) create mode 100644 inc/plugintargetinterface.class.php diff --git a/inc/abstractfield.class.php b/inc/abstractfield.class.php index b91edce22..f388c33eb 100644 --- a/inc/abstractfield.class.php +++ b/inc/abstractfield.class.php @@ -96,9 +96,17 @@ public function show(string $domain, bool $canEdit = true): string { continue; } } + $description = Plugin::doHookFunction('formcreator_question_description', [ + 'description' => $description, + 'question' => $this->getQuestion() + ])['description']; $html .= '
' . html_entity_decode(__($description, $domain)) . '
'; } $html .= '
'; + $this->value = Plugin::doHookFunction('formcreator_question_default_value', [ + 'value' => $this->value, + 'question' => $this->getQuestion() + ])['value']; $html .= $this->getRenderedHtml($domain, $canEdit); $html .= '
'; diff --git a/inc/abstractitiltarget.class.php b/inc/abstractitiltarget.class.php index a1a5fbf75..3fc5cca1f 100644 --- a/inc/abstractitiltarget.class.php +++ b/inc/abstractitiltarget.class.php @@ -437,7 +437,7 @@ protected function setTargetUrgency($data, $formanswer) { * find all actors and prepare data for the ticket being created */ protected function prepareActors(PluginFormcreatorForm $form, PluginFormcreatorFormAnswer $formanswer) { - global $DB; + global $DB, $PLUGIN_HOOKS; $rows = $DB->request([ 'FROM' => PluginFormcreatorTarget_Actor::getTable(), @@ -586,6 +586,26 @@ protected function prepareActors(PluginFormcreatorForm $form, PluginFormcreatorF $this->addActor(PluginFormcreatorTarget_Actor::ACTOR_ROLE_SUPPLIER, $userId, $notify); } break; + default: + foreach (($PLUGIN_HOOKS['formcreator_actors_type'] ?? []) as $plugin => $classes) { + foreach ($classes as $plugin_target) { + if (!is_a($plugin_target, PluginFormcreatorPluginTargetInterface::class, true)) { + continue; + } + if ($actor['actor_type']== $plugin_target::getId()) { + $value = $plugin_target::getActorId($formanswer, $actor['actor_value']); + if ($value) { + if ($plugin_target::getActorType() == PluginFormcreatorPluginTargetInterface::ACTOR_TYPE_USER) { + $this->addActor($actor['actor_role'], $value, $notify); + } else if (PluginFormcreatorPluginTargetInterface::ACTOR_TYPE_GROUP) { + $this->addGroupActor($actor['actor_role'], $value); + } + } + break 2; + } + } + } + break; } } } @@ -1760,7 +1780,7 @@ protected function showActorSettingsHeader($type) { * @return void */ protected function showActorSettingsForType($actorType, array $actors) { - global $DB; + global $DB, $PLUGIN_HOOKS; $itemActor = new PluginFormcreatorTarget_Actor(); $dropdownItems = ['' => Dropdown::EMPTY_VALUE] + $itemActor::getEnumActorType(); @@ -1799,7 +1819,7 @@ protected function showActorSettingsForType($actorType, array $actors) { ] ); - echo '