From 615cae195a3ae7bfe02fcfde648143ac1d0e8fff Mon Sep 17 00:00:00 2001 From: adelaunay Date: Mon, 22 Apr 2024 16:56:41 +0200 Subject: [PATCH 1/5] reorder fields for an asset definition --- inc/define.php | 1 + .../update_10.0.x_to_11.0.0/assets.php | 3 +- install/mysql/glpi-empty.sql | 1 + src/Asset/AssetDefinition.php | 105 +++++++++++++++++- .../assetdefinition/fields_display.html.twig | 99 +++++++++++++++++ 5 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 templates/pages/admin/assetdefinition/fields_display.html.twig diff --git a/inc/define.php b/inc/define.php index 746b30e532b..31dfa9fc377 100644 --- a/inc/define.php +++ b/inc/define.php @@ -594,6 +594,7 @@ ], ], 'config' => [ + 'assetdefinition' => ['sortable'], 'commondropdown' => [ 'ITILFollowupTemplate' => ['tinymce'], 'ProjectTaskTemplate' => ['tinymce'], diff --git a/install/migrations/update_10.0.x_to_11.0.0/assets.php b/install/migrations/update_10.0.x_to_11.0.0/assets.php index 38e1d41d268..13841b217e3 100644 --- a/install/migrations/update_10.0.x_to_11.0.0/assets.php +++ b/install/migrations/update_10.0.x_to_11.0.0/assets.php @@ -54,6 +54,7 @@ `capacities` JSON NOT NULL, `profiles` JSON NOT NULL, `translations` JSON NOT NULL, + `fields_display ` JSON NOT NULL, `date_creation` timestamp NULL DEFAULT NULL, `date_mod` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), @@ -65,7 +66,7 @@ SQL; $DB->doQueryOrDie($query); } else { - foreach (['profiles', 'translations'] as $field) { + foreach (['profiles', 'translations', 'fields_display'] as $field) { $migration->addField('glpi_assets_assetdefinitions', $field, 'JSON NOT NULL', ['update' => "'[]'"]); } } diff --git a/install/mysql/glpi-empty.sql b/install/mysql/glpi-empty.sql index fc90be34888..7c06ed02957 100644 --- a/install/mysql/glpi-empty.sql +++ b/install/mysql/glpi-empty.sql @@ -9827,6 +9827,7 @@ CREATE TABLE `glpi_assets_assetdefinitions` ( `capacities` JSON NOT NULL, `profiles` JSON NOT NULL, `translations` JSON NOT NULL, + `fields_display` JSON NOT NULL, `date_creation` timestamp NULL DEFAULT NULL, `date_mod` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), diff --git a/src/Asset/AssetDefinition.php b/src/Asset/AssetDefinition.php index 2217e6b9b48..067e7e61030 100644 --- a/src/Asset/AssetDefinition.php +++ b/src/Asset/AssetDefinition.php @@ -46,9 +46,14 @@ use Glpi\DBAL\QueryExpression; use Glpi\DBAL\QueryFunction; use Glpi\Search\SearchOption; +use Group; +use Location; +use Manufacturer; use Profile; use ProfileRight; use Session; +use State; +use User; final class AssetDefinition extends CommonDBTM { @@ -110,6 +115,7 @@ public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) $translations_count = 0; if ($_SESSION['glpishow_count_on_tabs']) { $capacities_count = count($item->getDecodedCapacitiesField()); + $fields_count = count($item->getDecodedFieldsField()); $profiles_count = count(array_filter($item->getDecodedProfilesField())); $translations_count = count($item->getDecodedTranslationsField()); } @@ -120,6 +126,12 @@ public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) self::class, 'ti ti-adjustments' ), + 2 => self::createTabEntry( + __('Fields'), + $fields_count, + self::class, + 'ti ti-forms' + ), // 2 is reserved for "Fields" 3 => self::createTabEntry( _n('Profile', 'Profiles', Session::getPluralNumber()), @@ -147,7 +159,7 @@ public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $ $item->showCapacitiesForm(); break; case 2: - // 2 is reserved for "Fields" form + $item->showFieldsForm(); break; case 3: $item->showProfilesForm(); @@ -223,6 +235,29 @@ private function showCapacitiesForm(): void ); } + + /** + * Display capacities form. + * + * @return void + */ + private function showFieldsForm(): void + { + $all_fields = $this->getAllFields(); + $fields_display = $this->getDecodedFieldsField(); + + TemplateRenderer::getInstance()->display( + 'pages/admin/assetdefinition/fields_display.html.twig', + [ + 'item' => $this, + 'classname' => $this->getAssetClassName(), + 'all_fields' => $all_fields, + 'fields_display' => $fields_display, + 'used' => array_column($fields_display, 'key'), + ] + ); + } + /** * Display profiles form. * @@ -473,6 +508,17 @@ private function prepareInput(array $input): array|bool } } + if (array_key_exists('fields_display', $input)) { + $formatted_fields_display = []; + foreach ($input['fields_display'] as $field_order => $field_key) { + $formatted_fields_display[] = [ + 'order' => $field_order, + 'key' => $field_key, + ]; + } + $input['fields_display'] = json_encode($formatted_fields_display); + } + return $has_errors ? false : $input; } @@ -951,6 +997,63 @@ private function getDecodedCapacitiesField(): array return $capacities; } + + private function getAllFields(): array + { + $fields = [ + 'name' => __('Name'), + 'comment' => __('Comment'), + 'serial' => __('Serial'), + 'otherserial' => __('Inventory number'), + 'contact' => __('Alternate username'), + 'contact_num' => __('Alternate username number'), + 'users_id' => User::getTypeName(), + 'groups_id' => Group::getTypeName(), + 'users_id_tech' => __('Technician in charge'), + 'groups_id_tech' => __('Group in charge'), + `locations_id` => Location::getTypeName(), + `manufacturers_id` => Manufacturer::getTypeName(), + `states_id` => State::getTypeName(), + ]; + + // TODO add assets_assetmodels_id and assets_assettypes_id + // TODO add custom fields + + return $fields; + } + + private function getDefaultFieldsDisplay(): array + { + $all_fields = $this->getAllFields(); + + $default = []; + $order = 0; + foreach ($all_fields as $key => $label) { + $default[] = [ + 'key' => $key, + 'order' => $order, + ]; + $order++; + } + + return $default; + } + + + /** + * Return the decoded value of the `capacities` field. + * + * @return array + */ + private function getDecodedFieldsField(): array + { + $fields_display = @json_decode($this->fields['fields_display'], associative: true); + if (!is_array($fields_display) || count($fields_display) === 0) { + return $this->getDefaultFieldsDisplay(); + } + return $fields_display; + } + /** * Validate that the given capacities array contains valid values. * diff --git a/templates/pages/admin/assetdefinition/fields_display.html.twig b/templates/pages/admin/assetdefinition/fields_display.html.twig new file mode 100644 index 00000000000..cfdac698e07 --- /dev/null +++ b/templates/pages/admin/assetdefinition/fields_display.html.twig @@ -0,0 +1,99 @@ +{# + # --------------------------------------------------------------------- + # + # GLPI - Gestionnaire Libre de Parc Informatique + # + # http://glpi-project.org + # + # @copyright 2015-2024 Teclib' and contributors. + # @licence https://www.gnu.org/licenses/gpl-3.0.html + # + # --------------------------------------------------------------------- + # + # LICENSE + # + # This file is part of GLPI. + # + # This program is free software: you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by + # the Free Software Foundation, either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU General Public License + # along with this program. If not, see . + # + # --------------------------------------------------------------------- + #} + +{% extends "generic_show_form.html.twig" %} +{% import 'components/form/fields_macros.html.twig' as fields %} + +{% set params = {} %} +{# do not display delete button #} +{% set params = params|merge({'candel': false}) %} +{# do not display footer with dates #} +{% set params = params|merge({'formfooter': false}) %} + +{% block form_fields %} + + +
+ + {#
#} + {% for field in fields_display %} +
+ + + {{ all_fields[field['key']] }} +
+ {% endfor %} +
+ + + +
+
+ {% do call('Dropdown::showFromArray', ['new_field', all_fields, { + 'display_emptychoice': true, + 'rand': rand, + 'used': used, + }]) %} + +
+
+ + +{% endblock %} From aa212b256ee5ffd99bee4f66340baf4d4f78baf5 Mon Sep 17 00:00:00 2001 From: adelaunay Date: Tue, 23 Apr 2024 19:33:36 +0200 Subject: [PATCH 2/5] re-use twig template and implement remove field action --- .../assetdefinition/fields_display.html.twig | 65 +++++++++++-------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/templates/pages/admin/assetdefinition/fields_display.html.twig b/templates/pages/admin/assetdefinition/fields_display.html.twig index cfdac698e07..4c182cdbfc1 100644 --- a/templates/pages/admin/assetdefinition/fields_display.html.twig +++ b/templates/pages/admin/assetdefinition/fields_display.html.twig @@ -33,6 +33,22 @@ {% extends "generic_show_form.html.twig" %} {% import 'components/form/fields_macros.html.twig' as fields %} +{% set all_fields = all_fields ?? {} %} + +{% macro draggable_field() %} +
+ +
+ + %field_name% +
+ +
+{% endmacro %} +{% import _self as my_draggable_macros %} + {% set params = {} %} {# do not display delete button #} {% set params = params|merge({'candel': false}) %} @@ -43,26 +59,14 @@
- - {#
#} {% for field in fields_display %} -
- - - {{ all_fields[field['key']] }} -
+ {{ my_draggable_macros.draggable_field()|replace({ + '%field_key%': field['key'], + '%field_name%': all_fields[field['key']] + })|raw }} {% endfor %}
- -
{% do call('Dropdown::showFromArray', ['new_field', all_fields, { @@ -79,21 +83,30 @@ {% endblock %} From c2364509b65813b83db65d9fee8e12eef7ba9f2a Mon Sep 17 00:00:00 2001 From: adelaunay Date: Tue, 23 Apr 2024 21:03:12 +0200 Subject: [PATCH 3/5] finish editor logic --- src/Asset/AssetDefinition.php | 13 ++++++----- .../assetdefinition/fields_display.html.twig | 22 ++++++++++++++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/Asset/AssetDefinition.php b/src/Asset/AssetDefinition.php index 067e7e61030..4d178a4d35e 100644 --- a/src/Asset/AssetDefinition.php +++ b/src/Asset/AssetDefinition.php @@ -243,17 +243,18 @@ private function showCapacitiesForm(): void */ private function showFieldsForm(): void { - $all_fields = $this->getAllFields(); $fields_display = $this->getDecodedFieldsField(); + $used = array_column($fields_display, 'key'); + $used = array_combine($used, $used); TemplateRenderer::getInstance()->display( 'pages/admin/assetdefinition/fields_display.html.twig', [ 'item' => $this, 'classname' => $this->getAssetClassName(), - 'all_fields' => $all_fields, + 'all_fields' => $this->getAllFields(), 'fields_display' => $fields_display, - 'used' => array_column($fields_display, 'key'), + 'used' => $used, ] ); } @@ -1011,9 +1012,9 @@ private function getAllFields(): array 'groups_id' => Group::getTypeName(), 'users_id_tech' => __('Technician in charge'), 'groups_id_tech' => __('Group in charge'), - `locations_id` => Location::getTypeName(), - `manufacturers_id` => Manufacturer::getTypeName(), - `states_id` => State::getTypeName(), + 'locations_id' => Location::getTypeName(), + 'manufacturers_id' => Manufacturer::getTypeName(), + 'states_id' => State::getTypeName(), ]; // TODO add assets_assetmodels_id and assets_assettypes_id diff --git a/templates/pages/admin/assetdefinition/fields_display.html.twig b/templates/pages/admin/assetdefinition/fields_display.html.twig index 4c182cdbfc1..f0b699e5d74 100644 --- a/templates/pages/admin/assetdefinition/fields_display.html.twig +++ b/templates/pages/admin/assetdefinition/fields_display.html.twig @@ -40,7 +40,7 @@
- %field_name% + %field_label%
@@ -89,22 +89,38 @@ placeholderClass: 'sortable-placeholder g-col-6', }); + const all_fields = {{ all_fields|json_encode()|raw }}; + // add field action $('#add-field').on('click', function() { //get select2 value const field_key = $('#dropdown_new_field{{ rand }}').val(); - if (field_key) { + if (field_key && field_key != 0 ) { // re-use twig macro to generate the field let field_html = `{{ my_draggable_macros.draggable_field() }}`; field_html = field_html.replace('%field_key%', field_key); + field_html = field_html.replace('%field_label%', all_fields[field_key]); //add field to the list $('#sortable-fields').append(field_html); + + // remove the selected option in select2 + $('#dropdown_new_field{{ rand }} option[value=' + field_key + ']').remove() + $('#dropdown_new_field{{ rand }}').val(0).trigger('change'); } }); // remove field action $('#sortable-fields').on('click', '.remove-field', function() { + const field_key = $(this).closest('.sortable-field').find('input').val(); + + // re-add the option in select2 + $('#dropdown_new_field{{ rand }}').append($('