From f00a7faafdbae67fc31c6737f2aeb5c0f7ae22fb Mon Sep 17 00:00:00 2001 From: Herafia Date: Wed, 7 Oct 2026 14:06:48 +0200 Subject: [PATCH] fix tag associated item main --- CHANGELOG.md | 6 ++ composer.json | 1 + inc/tagitem.class.php | 4 +- src/Controller/TagItemController.php | 92 ++++++++++++++++++++++++++++ tests/TagTestCase.php | 20 +++--- tests/Units/TagItemTest.php | 47 ++++++-------- 6 files changed, 129 insertions(+), 41 deletions(-) create mode 100644 src/Controller/TagItemController.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f963e5b..2e7f1b2 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 + +- Fix tag associate item + ## [2.15.0] - 2026-10-06 ### Added diff --git a/composer.json b/composer.json index f1e2ad1..7627874 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ }, "autoload": { "psr-4": { + "GlpiPlugin\\Tag\\": "src/", "GlpiPlugin\\Tag\\Tests\\": "tests" } }, diff --git a/inc/tagitem.class.php b/inc/tagitem.class.php index dbb924a..8934df1 100644 --- a/inc/tagitem.class.php +++ b/inc/tagitem.class.php @@ -197,7 +197,7 @@ public static function showForTag(PluginTagTag $tag) if ($canedit) { echo "
"; echo "
"; + action='" . Toolbox::getItemTypeFormURL('PluginTagTagItem') . "'>"; echo ""; echo ""; @@ -223,7 +223,7 @@ public static function showForTag(PluginTagTag $tag) ]); echo ""; echo "
" . __s('Add an item') . "
"; echo sprintf("", $instID); - echo ""; + echo ""; echo "
"; diff --git a/src/Controller/TagItemController.php b/src/Controller/TagItemController.php new file mode 100644 index 0000000..6a7a571 --- /dev/null +++ b/src/Controller/TagItemController.php @@ -0,0 +1,92 @@ +. + * ------------------------------------------------------------------------- + * @copyright Copyright (C) 2014-2026 by Teclib'. + * @license GPLv2 https://www.gnu.org/licenses/gpl-2.0.html + * @link https://github.com/pluginsGLPI/tag + * ------------------------------------------------------------------------- + */ + +namespace GlpiPlugin\Tag\Controller; + +use CommonDBTM; +use Glpi\Controller\GenericFormController; +use Glpi\Exception\Http\AccessDeniedHttpException; +use Glpi\Exception\Http\BadRequestHttpException; +use Glpi\Http\RedirectResponse; +use Html; +use PluginTagTag; +use PluginTagTagItem; +use Session; +use Glpi\Routing\Attribute\ItemtypeFormRoute; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +final class TagItemController extends GenericFormController +{ + #[ItemtypeFormRoute(PluginTagTagItem::class)] + public function associate(Request $request): Response + { + Session::checkLoginUser(); + + $tag_id = $request->request->getInt('plugin_tag_tags_id'); + $itemtype = $request->request->get('itemtype'); + $item_id = $request->request->getInt('items_id'); + + if (!$tag_id || !$itemtype || !$item_id) { + throw new BadRequestHttpException(__s('Missing parameters', 'tag')); + } + + $tag = new PluginTagTag(); + if (!$tag->getFromDB($tag_id) || !$tag->can($tag_id, UPDATE)) { + throw new AccessDeniedHttpException(__s('You do not have permission to update this tag', 'tag')); + } + + if (!is_a($itemtype, CommonDBTM::class, true) || !PluginTagTag::canItemtype($itemtype)) { + throw new BadRequestHttpException(__s('Invalid item type', 'tag')); + } + + $item = new $itemtype(); + if (!$item->getFromDB($item_id) || !$item->canUpdateItem()) { + throw new AccessDeniedHttpException(__s('You do not have permission to update this item', 'tag')); + } + + $tag_item = new PluginTagTagItem(); + $found = $tag_item->find([ + 'plugin_tag_tags_id' => $tag_id, + 'items_id' => $item_id, + 'itemtype' => $itemtype, + ]); + + if (count($found) === 0) { + $tag_item->add([ + 'plugin_tag_tags_id' => $tag_id, + 'items_id' => $item_id, + 'itemtype' => $itemtype, + ]); + } + + return new RedirectResponse(Html::getBackUrl()); + } +} diff --git a/tests/TagTestCase.php b/tests/TagTestCase.php index 8018c05..34b520f 100644 --- a/tests/TagTestCase.php +++ b/tests/TagTestCase.php @@ -46,7 +46,7 @@ protected function logOut() $_SESSION['glpi_currenttime'] = $ctime; } - public function loginAs(array $credentials): int + public function loginAs(array $credentials, int $rights = READ | CREATE | UPDATE | PURGE): int { global $DB; @@ -59,7 +59,7 @@ public function loginAs(array $credentials): int $DB->update( 'glpi_profilerights', [ - 'rights' => READ | CREATE | UPDATE | PURGE, + 'rights' => $rights, ], [ 'profiles_id' => $user_profile, @@ -72,17 +72,13 @@ public function loginAs(array $credentials): int return $user->getID(); } - public function createTag(string $tagName): int + public function createTag(string $tagName, array $typeMenu = ['Ticket']): int { - $tag = new PluginTagTag(); - $tag->add( - [ - 'name' => $tagName, - 'is_active' => 1, - 'type_menu' => ['Ticket'], - ], - ); - $this->assertGreaterThan(0, $tag->getID()); + $tag = $this->createItem(PluginTagTag::class, [ + 'name' => $tagName, + 'is_active' => 1, + 'type_menu' => $typeMenu, + ], ['type_menu']); return $tag->getID(); } diff --git a/tests/Units/TagItemTest.php b/tests/Units/TagItemTest.php index f700cbe..34bfeba 100644 --- a/tests/Units/TagItemTest.php +++ b/tests/Units/TagItemTest.php @@ -30,15 +30,19 @@ namespace GlpiPlugin\Tag\Tests\Units; +use Computer; +use GlpiPlugin\Tag\Controller\TagItemController; use GlpiPlugin\Tag\Tests\TagTestCase; -use PluginTagTag; +use Symfony\Component\HttpFoundation\Request; use Ticket; final class TagItemTest extends TagTestCase { + private const TECH_USER = ['login' => 'tech', 'pass' => 'tech']; + public function testTagsFromTicket(): void { - $this->login(); + $this->loginAs(self::TECH_USER); $tagID1 = $this->createTag('TicketTag1'); $tagID2 = $this->createTag('TicketTag2'); @@ -58,36 +62,25 @@ public function testTagsFromTicket(): void $this->isItemTagged($ticket, $tagID2); } - public function testTagOutOfEntityScopeIsNotLinked(): void + public function testTagAssociationCreatesLink(): void { - $this->login(); + $this->loginAs(self::TECH_USER); - $out_of_scope_entity = getItemByTypeName('Entity', '_test_child_2', true); - $tag = new PluginTagTag(); - $tag->add([ - 'name' => 'OutOfScopeTag', - 'is_active' => 1, - 'type_menu' => ['Ticket'], - 'entities_id' => $out_of_scope_entity, - 'is_recursive' => 0, + $tag = $this->createTag('MyTag', ['Computer']); + $computer = $this->createItem(Computer::class, [ + 'name' => 'Computer to tag', + 'entities_id' => 0, ]); - $tagID = $tag->getID(); - $this->assertGreaterThan(0, $tagID); - - $this->setEntity('_test_child_1', false); - $ticket = new Ticket(); - $ticket->add([ - 'name' => 'Ticket out of scope tag', - 'content' => 'Ticket out of scope tag', - 'entities_id' => getItemByTypeName('Entity', '_test_child_1', true), - '_plugin_tag_tag_process_form' => 1, - '_plugin_tag_tag_values' => [ - $tagID, - ], + $controller = new TagItemController(); + $request = Request::create('/plugins/tag/associate', 'POST', [ + 'plugin_tag_tags_id' => $tag, + 'itemtype' => Computer::class, + 'items_id' => $computer->getID(), ]); - $this->assertGreaterThan(0, $ticket->getID()); - $this->isItemNotTagged($ticket, $tagID); + $controller->associate($request); + + $this->isItemTagged($computer, $tag); } }