diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41cc544a..d98d43e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Prevent the tag field from appearing in the satisfaction survey.
- Fix display translations
- Removed tag rights during plugin uninstall
+- Fix tag dropdown to allow tag creation directly from the field
## [2.14.3] - 2025-12-22
diff --git a/inc/destinationfield.class.php b/inc/destinationfield.class.php
index 9604144f..06edaffa 100644
--- a/inc/destinationfield.class.php
+++ b/inc/destinationfield.class.php
@@ -62,7 +62,7 @@ public function renderConfigForm(
throw new InvalidArgumentException("Unexpected config class");
}
- [$available_tags, $available_tags_color] = (new PluginTagQuestionType())->getAvailableTags();
+ [$available_tags_color, $condition] = (new PluginTagQuestionType())->getAvailableTags();
$twig = TemplateRenderer::getInstance();
return $twig->render('@tag/destinationfield.html.twig', [
@@ -77,8 +77,8 @@ public function renderConfigForm(
'specific_values_extra_field' => [
'input_name' => $input_name . "[" . PluginTagDestinationFieldConfig::SPECIFIC_TAG_IDS . "]",
'selected_tags' => $config->getSpecificTagIDs() ?? [],
- 'available_tags' => $available_tags,
'tags_color' => $available_tags_color,
+ 'condition' => $condition,
],
// Specific additional config for SPECIFIC_ANSWERS strategy
diff --git a/inc/questiontype.class.php b/inc/questiontype.class.php
index a288eb6e..8178de41 100644
--- a/inc/questiontype.class.php
+++ b/inc/questiontype.class.php
@@ -79,13 +79,13 @@ public function formatRawAnswer(mixed $answer, Question $question): string
#[Override]
public function renderAdministrationTemplate(?Question $question): string
{
- [$available_tags, $available_tags_color] = $this->getAvailableTags();
+ [$available_tags_color, $condition] = $this->getAvailableTags();
$twig = TemplateRenderer::getInstance();
return $twig->render('@tag/question_dropdown.html.twig', [
'input_name' => 'default_value',
'selected_tags' => empty($question?->fields['default_value']) ? [] : explode(',', (string) $question->fields['default_value']),
- 'available_tags' => $available_tags,
+ 'condition' => $condition,
'tags_color' => $available_tags_color,
'dropdown_params' => [
'no_label' => true,
@@ -97,13 +97,13 @@ public function renderAdministrationTemplate(?Question $question): string
#[Override]
public function renderEndUserTemplate(Question $question): string
{
- [$available_tags, $available_tags_color] = $this->getAvailableTags($question->getForm());
+ [$available_tags_color, $condition] = $this->getAvailableTags($question->getForm());
$twig = TemplateRenderer::getInstance();
return $twig->render('@tag/question_dropdown.html.twig', [
'input_name' => $question->getEndUserInputName(),
'selected_tags' => empty($question?->fields['default_value']) ? [] : explode(',', (string) $question->fields['default_value']),
- 'available_tags' => $available_tags,
+ 'condition' => $condition,
'tags_color' => $available_tags_color,
'show_search_tooltip' => false,
'dropdown_params' => [
@@ -142,9 +142,8 @@ public function getAvailableTags(?Form $form = null): array
}
$tag = new PluginTagTag();
- $available_tags = [];
$available_tags_color = [];
- $result = $tag->find([
+ $condition = [
'is_active' => 1,
'OR' => [
['type_menu' => ['LIKE', '%\"Ticket\"%']],
@@ -154,12 +153,13 @@ public function getAvailableTags(?Form $form = null): array
['type_menu' => ''],
['type_menu' => 'NULL'],
],
- ] + getEntitiesRestrictCriteria('', '', $active_entities_ids, true), 'name');
+ ] + getEntitiesRestrictCriteria('', '', $active_entities_ids, true);
+ $result = $tag->find($condition, 'name');
+
foreach ($result as $id => $data) {
- $available_tags[$id] = $data['name'];
$available_tags_color[$id] = $data['color'] ?: '#DDDDDD';
}
- return [$available_tags, $available_tags_color];
+ return [$available_tags_color, $condition];
}
}
diff --git a/inc/tag.class.php b/inc/tag.class.php
index 664b86d3..3c92cb28 100644
--- a/inc/tag.class.php
+++ b/inc/tag.class.php
@@ -660,7 +660,6 @@ public static function showTagDropdown($params = [])
$available_tags = $tag->find($where, 'name');
foreach ($available_tags as $tag_data) {
- $available_tags[$tag_data['id']] = $tag_data['name'];
$available_tags_color[$tag_data['id']] = $tag_data['color'] ?: '#DDDDDD';
}
@@ -705,7 +704,7 @@ public static function showTagDropdown($params = [])
TemplateRenderer::getInstance()->display('@tag/tag_dropdown.html.twig', [
'extra_class' => $extra_class ?? '',
'selected_tags' => $selected_tags,
- 'available_tags' => $available_tags,
+ 'condition' => $where,
'tags_color' => $available_tags_color ?? [],
'rand' => $rand,
'token_creation' => $token_creation,
diff --git a/public/js/common.js b/public/js/common.js
index 95ba833c..b36a48ed 100644
--- a/public/js/common.js
+++ b/public/js/common.js
@@ -52,37 +52,3 @@ var idealTextColor = function(hexTripletColor) {
var bgDelta = (components.R * 0.299) + (components.G * 0.587) + (components.B * 0.114);
return ((255 - bgDelta) < nThreshold) ? "#000000" : "#E6E6E6";
};
-
-var formatOptionSelection = function(option, container) {
- var color = (typeof option.color !== 'undefined' && option.color !== '')
- ? option.color
- : '#DDDDDD';
- var invertedcolor = idealTextColor(color);
-
- $(container)
- .css("background-color", color)
- .css("border-color", invertedcolor)
- .css("color", invertedcolor)
- .children('.select2-selection__choice__remove')
- .css("color", invertedcolor);
-
- var _elt = $('');
- _elt.html(escapeMarkupText(option.text));
- return _elt;
-};
-
-var formatOptionResult = function(option) {
-
- var color = (typeof option.color !== 'undefined' && option.color !== '')
- ? option.color
- : '#DDDDDD';
- var invertedcolor = idealTextColor(color);
-
- var template = `
-
- ${option.text}
-
- `;
-
- return $(template);
-};
diff --git a/public/js/modules/TagDropdownColorizer.js b/public/js/modules/TagDropdownColorizer.js
deleted file mode 100644
index 38aa03ca..00000000
--- a/public/js/modules/TagDropdownColorizer.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * -------------------------------------------------------------------------
- * Tag plugin for GLPI
- * -------------------------------------------------------------------------
- *
- * LICENSE
- *
- * This file is part of Tag.
- *
- * Tag 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.
- *
- * Tag 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 Tag. If not, see .
- * -------------------------------------------------------------------------
- * @copyright Copyright (C) 2014-2023 by Teclib'.
- * @license GPLv2 https://www.gnu.org/licenses/gpl-2.0.html
- * @link https://github.com/pluginsGLPI/tag
- * -------------------------------------------------------------------------
- */
-
-export class GlpiPluginTagTagDropdownColorizer {
- constructor(tagsColor, selector, $container) {
- this.tagsColor = tagsColor;
- this.selector = selector;
- this.$container = $container;
-
- this.init();
- }
-
- isDark(hexColor) {
- if (!hexColor) return false;
- hexColor = hexColor.replace('#', '');
- const r = parseInt(hexColor.substr(0, 2), 16);
- const g = parseInt(hexColor.substr(2, 2), 16);
- const b = parseInt(hexColor.substr(4, 2), 16);
- const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
- return luminance < 0.5;
- }
-
- applyTagColors($select) {
- const selectedIds = $select.find('option:selected').map(function() {
- return $(this).val();
- }).get();
-
- const $container = $select.nextAll('.select2').find('.select2-selection__rendered');
- $container.find('.select2-selection__choice').each((index, element) => {
- const id = selectedIds[index];
- const color = this.tagsColor[id];
- if (color) {
- $(element).css('background-color', color);
- $(element).css('color', this.isDark(color) ? '#eeeeee' : '');
-
- // Also style the remove button for better visibility
- $(element).find('.select2-selection__choice__remove').css('color', this.isDark(color) ? '#eeeeee' : '');
- }
- });
- }
-
- init() {
- const $select = this.$container.find(this.selector);
-
- $select.each((index, element) => {
- this.applyTagColors($(element));
- });
-
- $select.on('change select2:select select2:unselect', (event) => {
- this.applyTagColors($(event.target));
- });
-
- $select.on('select2:open', () => {
- setTimeout(() => {
- $('.select2-results__option').each((index, element) => {
- const matches = element.id.match(/result-[^-]+-(\d+)$/);
- if (matches && matches[1]) {
- const color = this.tagsColor[matches[1]];
- // Cible uniquement le span SANS la classe select2-rendered__match
- $(element).find('span:not(.select2-rendered__match)').css({
- 'background-color': color ? color : '',
- 'padding': color ? '2px' : '',
- 'color': (color && this.isDark(color)) ? '#fff' : '',
- 'border-radius': '2px'
- });
- }
- });
- }, 0);
- });
- }
-}
diff --git a/templates/destinationfield.html.twig b/templates/destinationfield.html.twig
index 98ac115f..ab5ad8bf 100644
--- a/templates/destinationfield.html.twig
+++ b/templates/destinationfield.html.twig
@@ -34,9 +34,9 @@
{% include "@tag/dropdown.html.twig" with {
'input_name' : specific_values_extra_field.input_name,
'selected_tags' : specific_values_extra_field.selected_tags,
- 'available_tags' : specific_values_extra_field.available_tags,
'tags_color' : specific_values_extra_field.tags_color,
'show_search_tooltip': show_search_tooltip|default(true),
+ 'condition' : condition,
'dropdown_params' : {
'no_label' : true,
'mb' : '',
diff --git a/templates/dropdown.html.twig b/templates/dropdown.html.twig
index 44220e5a..5c398806 100644
--- a/templates/dropdown.html.twig
+++ b/templates/dropdown.html.twig
@@ -39,18 +39,14 @@
'values' : selected_tags,
'multiple': true,
'mb' : '',
- 'add_data_attributes': {
- 'glpi-plugin-tag-dropdown-uuid': rand
+ 'condition': condition|default([]),
+ 'templateResult': 'formatOptionResult',
+ 'templateSelection': 'formatOptionSelection',
+ 'specific_tags': {
+ 'data-glpi-plugin-tag-dropdown-uuid': rand
}
}|merge(dropdown_params|default({})) %}
-{% set field %}
- {% do call('Dropdown::showFromArray', [input_name, available_tags, {
- 'value': null,
- 'rand': rand,
- }|merge(options)]) %}
-{% endset %}
-
{% set tooltip_parts = [] %}
{% if call('PluginTagTag::canCreate') and show_search_tooltip|default(true) %}
@@ -85,25 +81,57 @@
{% endif %}
{% endif %}
-{{ fields.field(
+
+
+{{ fields.dropdownField(
+ 'PluginTagTag',
input_name,
- field ~ tooltip,
+ selected_tags,
__('Tags', 'tag'),
- options|merge({'id': 'dropdown_' ~ input_name|replace({'[': '_', ']': '_'}) ~ rand})
+ options|merge({
+ 'rand': rand,
+ 'add_field_html': tooltip,
+ })
) }}
-
-
{% if show_save_button is defined and show_save_button %}
diff --git a/templates/tag_dropdown.html.twig b/templates/tag_dropdown.html.twig
index eb1260f6..b242c316 100644
--- a/templates/tag_dropdown.html.twig
+++ b/templates/tag_dropdown.html.twig
@@ -81,9 +81,9 @@
{% include "@tag/dropdown.html.twig" with {
'input_name' : '_plugin_tag_tag_values',
'selected_tags' : selected_tags,
- 'available_tags' : available_tags,
'tags_color' : tags_color,
'rand' : rand,
+ 'condition' : condition,
'dropdown_params': {
'id' : 'tag_select_',
'disabled' : readOnly,