diff --git a/CHANGELOG.md b/CHANGELOG.md index 93358c1..cdcf090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### Fixed + +- Avoid a per-tag database lookup when rendering the tag column in item lists + ## [2.14.6] - 2026-08-04 ### Fixed diff --git a/hook.php b/hook.php index 1640651..e4600c7 100644 --- a/hook.php +++ b/hook.php @@ -99,6 +99,9 @@ function plugin_tag_getAddSearchOptionsNew($itemtype) 'searchtype' => ['equals','notequals','contains'], 'massiveaction' => false, 'forcegroupby' => true, + // Fetch the color alongside the name in the same grouped query, + // avoiding a getFromDB() call per tag per row in plugin_tag_giveItem(). + 'additionalfields' => ['color'], 'joinparams' => [ 'beforejoin' => [ 'table' => 'glpi_plugin_tag_tagitems', @@ -133,6 +136,7 @@ function plugin_tag_getAddSearchOptionsNew($itemtype) 'massiveaction' => false, 'forcegroupby' => true, 'usehaving' => true, + 'additionalfields' => ['color'], 'joinparams' => [ 'condition' => "AND 1=1", // to force distinct complex id than the previous option 'beforejoin' => [ @@ -163,7 +167,15 @@ function plugin_tag_giveItem($type, $field, $data, $num, $linkfield = "") $separator = ''; foreach ($data[$num] as $tag) { if (isset($tag['id']) && isset($tag['name'])) { - $out .= PluginTagTag::getSingleTag($tag['id'], $separator); + $color = $tag['color'] ?: '#DDDDDD'; + $textcolor = idealTextColor($color); + $out .= sprintf( + "%s%s", + htmlentities((string) $color, ENT_QUOTES, "UTF-8"), + $textcolor, + $separator, + htmlentities((string) $tag['name'], ENT_QUOTES, "UTF-8"), + ); //For export (CSV, PDF) of GLPI core $separator = ', '; } diff --git a/tests/Units/TagTest.php b/tests/Units/TagTest.php index 25ea623..e055009 100644 --- a/tests/Units/TagTest.php +++ b/tests/Units/TagTest.php @@ -96,6 +96,35 @@ public function testAddHavingEscapesValue(): void ); } + public function testGiveItemRendersColorFromSearchDataWithoutDbLookup(): void + { + // Tag ids intentionally do not exist in DB: if plugin_tag_giveItem() + // ever falls back to a getFromDB() call per tag, this fails because + // the lookup returns nothing and the fallback color/empty name show up instead. + $data = [ + 0 => [ + 'count' => 2, + 0 => [ + 'id' => 999998, + 'name' => 'First tag', + 'color' => '#123456', + ], + 1 => [ + 'id' => 999999, + 'name' => 'Second tag', + 'color' => '#abcdef', + ], + ], + ]; + + $out = plugin_tag_giveItem('Ticket', PluginTagTag::S_OPTION, $data, 0); + + $this->assertStringContainsString('#123456', $out); + $this->assertStringContainsString('First tag', $out); + $this->assertStringContainsString('#abcdef', $out); + $this->assertStringContainsString('Second tag', $out); + } + public function testUpdateAcceptsScalarTypeMenu(): void { $tag = $this->createItem(