From 510f9556738e6eea624cb7a19ca7f99ff3cbf94e Mon Sep 17 00:00:00 2001 From: Lainow Date: Mon, 23 Mar 2026 15:05:01 +0100 Subject: [PATCH 1/5] Fixed a solution added even though the required fields were not filled in --- hook.php | 35 --------- setup.php | 7 ++ src/Controller.php | 154 +++++++++++++++++++++++++------------ tests/Units/ConfigTest.php | 88 +++++++++++++++++++++ 4 files changed, 200 insertions(+), 84 deletions(-) diff --git a/hook.php b/hook.php index 3105ab5..966de5f 100644 --- a/hook.php +++ b/hook.php @@ -31,43 +31,8 @@ * ------------------------------------------------------------------------- */ -use Glpi\Inventory\Conf; use GlpiPlugin\Moreoptions\Config; -/** - * ------------------------------------------------------------------------- - * MoreOptions plugin for GLPI - * ------------------------------------------------------------------------- - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * ------------------------------------------------------------------------- - * @copyright Copyright (C) 2025 by the MoreOptions plugin team. - * @copyright Copyright (C) 2022-2024 by More Options plugin team. - * @license MIT https://opensource.org/licenses/mit-license.php - * @license GPLv3 https://www.gnu.org/licenses/gpl-3.0.html - * @link https://github.com/pluginsGLPI/moreoptions - * @link https://gitlab.teclib.com/glpi-network/cancelsend/ - * ------------------------------------------------------------------------- - */ - function plugin_moreoptions_install(): bool { $migration = new Migration(PLUGIN_MOREOPTIONS_VERSION); diff --git a/setup.php b/setup.php index 7763483..b0dea80 100644 --- a/setup.php +++ b/setup.php @@ -96,6 +96,13 @@ function plugin_init_moreoptions(): void Controller::class, 'beforeCloseITILObject', ]; + $PLUGIN_HOOKS[Hooks::PRE_ITEM_ADD]['moreoptions'][ITILSolution::class] = [ + Controller::class, 'beforeCloseITILObject', + ]; + $PLUGIN_HOOKS[Hooks::PRE_ITEM_UPDATE]['moreoptions'][ITILSolution::class] = [ + Controller::class, 'beforeCloseITILObject', + ]; + $PLUGIN_HOOKS[Hooks::PRE_ITEM_UPDATE]['moreoptions'][Config::class] = [ Config::class, 'preItemUpdate', ]; diff --git a/src/Controller.php b/src/Controller.php index 709f880..d4ff83d 100644 --- a/src/Controller.php +++ b/src/Controller.php @@ -48,6 +48,7 @@ use CommonITILObject; use CommonITILValidation; use Glpi\Form\Category; +use GlpiPlugin\Behaviors\Common; use GlpiPlugin\Moreoptions\Config; use Group_Item; use Group_Problem; @@ -56,6 +57,7 @@ use Item_Ticket; use ITILCategory; use ITILSolution; +use phpDocumentor\Reflection\Types\Boolean; use Planning; use Problem; use Problem_User; @@ -271,16 +273,23 @@ private static function addGroupsForActorType(CommonDBTM $item, Config $moconfig } } - public static function beforeCloseITILObject(CommonITILObject $item): void + public static function beforeCloseITILObject(CommonDBTM $item): void { if (!is_array($item->input)) { return; } + if ($item instanceof ITILSolution) { + self::requireFieldsAddSolution($item); + } + if ( - (isset($item->input['status']) && ($item->input['status'] == CommonITILObject::CLOSED || $item->input['status'] == CommonITILObject::SOLVED)) - || $item->fields['status'] == CommonITILObject::CLOSED - || $item->fields['status'] == CommonITILObject::SOLVED + $item instanceof CommonITILObject + && ( + (isset($item->input['status']) && ($item->input['status'] == CommonITILObject::CLOSED || $item->input['status'] == CommonITILObject::SOLVED)) + || $item->fields['status'] == CommonITILObject::CLOSED + || $item->fields['status'] == CommonITILObject::SOLVED + ) ) { self::requireFieldsToClose($item); self::preventClosure($item); @@ -324,84 +333,97 @@ public static function preventClosure(CommonDBTM $item): void } } - public static function requireFieldsToClose(CommonDBTM $item): void + /** + * Check required fields to close a ticket/change/problem and return a message listing missing fields + * + * @param false|array $input The input data of the item being closed + * @param string $configSuffix The suffix to use for configuration fields (e.g. '_ticket', '_change', '_problem') + * @param string $userClass The user link class of the item (e.g. Ticket_User, Change_User, Problem_User) + * @param string $groupClass The group link class of the item (e.g. Group_Ticket, Group_Change, Group_Problem) + */ + public static function checkFieldsToClose(false|array $input, string $configSuffix, string $userClass, string $groupClass, string $itemIdField): string { $conf = Config::getConfig(); - if ($conf->fields['is_active'] != 1) { - return; + if ( + $conf->fields['is_active'] != 1 + || !is_array($input) + ) { + return ''; } $message = ''; - $itemtype = get_class($item); + $id = $input['id'] ?? 0; - // Determine the configuration suffix and actor classes based on item type - $configSuffix = '_' . strtolower($itemtype); - $userClass = $item->userlinkclass ?? ''; - $groupClass = $item->grouplinkclass ?? ''; - $itemIdField = $item->getForeignKeyField(); + if (!$id) { + return ''; + } // Check for required technician - if ($conf->fields['require_technician_to_close' . $configSuffix] == 1) { - if (is_a($userClass, CommonDBTM::class, true)) { - $tech = new $userClass(); - } else { - // If the user class is not valid, skip this check - return; - } - $techs = $tech->find([ - $itemIdField => $item->fields['id'], - 'type' => CommonITILActor::ASSIGN, - ]); - if (count($techs) == 0) { + if ($conf->fields['require_technician_to_close' . $configSuffix] == 1 && is_a($userClass, CommonDBTM::class, true)) { + /** @var CommonDBTM $tech */ + $tech = new $userClass(); + if (count($tech->find([$itemIdField => $id, 'type' => CommonITILActor::ASSIGN])) == 0) { $message .= '- ' . __s('Technician') . '
'; } } // Check for required technician group - if ($conf->fields['require_technicians_group_to_close' . $configSuffix] == 1) { - if (is_a($groupClass, CommonDBTM::class, true)) { - $group = new $groupClass(); - } else { - // If the group class is not valid, skip this check - return; - } - $groups = $group->find([ - $itemIdField => $item->fields['id'], - 'type' => CommonITILActor::ASSIGN, - ]); - if (count($groups) == 0) { + if ($conf->fields['require_technicians_group_to_close' . $configSuffix] == 1 && is_a($groupClass, CommonDBTM::class, true)) { + /** @var CommonDBTM $group */ + $group = new $groupClass(); + if (count($group->find([$itemIdField => $id, 'type' => CommonITILActor::ASSIGN])) == 0) { $message .= '- ' . __s('Technician group') . '
'; } } // Check for required category if ($conf->fields['require_category_to_close' . $configSuffix] == 1) { - if ((!isset($item->input['itilcategories_id']) || empty($item->input['itilcategories_id']))) { + if (empty($input['itilcategories_id'] ?? 0)) { $message .= '- ' . __s('Category') . '
'; } } // Check for required location if ($conf->fields['require_location_to_close' . $configSuffix] == 1) { - if ((!isset($item->input['locations_id']) || empty($item->input['locations_id']))) { + if (empty($input['locations_id'] ?? 0)) { $message .= '- ' . __s('Location') . '
'; } } + return $message; + } + + public static function requireFieldsToClose(CommonDBTM $item): void + { + $conf = Config::getConfig(); + if ($conf->fields['is_active'] != 1) { + return; + } + + $itemtype = get_class($item); + $configSuffix = '_' . strtolower($itemtype); + + $message = self::checkFieldsToClose( + is_array($item->input) ? $item->input : [], + $configSuffix, + $item->userlinkclass ?? '', + $item->grouplinkclass ?? '', + $item->getForeignKeyField() + ); + // Check if solution exists before closing if ($conf->fields['require_solution_to_close' . $configSuffix] == 1 - && is_array($item->input) && isset($item->input['status']) - && $item->input['status'] == CommonITILObject::CLOSED) { + && $item->input['status'] == CommonITILObject::CLOSED + ) { $solution = new ITILSolution(); - $solutions = $solution->find([ - 'itemtype' => $itemtype, - 'items_id' => $item->fields['id'], - 'NOT' => [ - 'status' => CommonITILValidation::REFUSED, - ], - ]); - if (count($solutions) == 0) { + if (count( + $solution->find([ + 'itemtype' => $itemtype, + 'items_id' => $item->fields['id'] ?? 0, + 'NOT' => ['status' => CommonITILValidation::REFUSED], + ]) + ) == 0) { $message .= '- ' . __s('Solution') . '
'; } } @@ -412,8 +434,42 @@ public static function requireFieldsToClose(CommonDBTM $item): void $message = sprintf(__s('To close this %s, you must fill in the following fields:', 'moreoptions'), $itemTypeLabel) . '
' . $message; Session::addMessageAfterRedirect($message, false, ERROR); $item->input = false; + } + } + + public static function requireFieldsAddSolution(ITILSolution $item): void + { + $conf = Config::getConfig(); + if ($conf->fields['is_active'] != 1 || !is_array($item->input)) { return; } + + $itemtype = $item->fields['itemtype'] ?? $item->input['itemtype'] ?? ''; + $items_id = $item->fields['items_id'] ?? $item->input['items_id'] ?? 0; + + if (empty($itemtype) || empty($items_id) || !class_exists($itemtype) || !is_a($itemtype, CommonITILObject::class, true)) { + return; + } + + /** @var CommonITILObject $parentItem */ + $parentItem = new $itemtype(); + if (!$parentItem->getFromDB($items_id)) { + return; + } + + $message = self::checkFieldsToClose( + is_array($parentItem->fields) ? $parentItem->fields : [], + '_' . strtolower($itemtype), + $parentItem->userlinkclass, + $parentItem->grouplinkclass, + $parentItem->getForeignKeyField() + ); + + if (!empty($message)) { + $message = __s('To add a solution to this item, you must fill in the following fields:', 'moreoptions') . '
' . $message; + Session::addMessageAfterRedirect($message, false, ERROR); + $item->input = false; + } } public static function checkTaskRequirements(CommonDBTM $item): CommonDBTM diff --git a/tests/Units/ConfigTest.php b/tests/Units/ConfigTest.php index 7b29121..9fec1b5 100644 --- a/tests/Units/ConfigTest.php +++ b/tests/Units/ConfigTest.php @@ -306,6 +306,94 @@ public function testTicketMandatoryFieldsBeforeCloseTicket(): void $this->assertTrue($resetResult); } + /** + * Test mandatory fields before adding a solution + */ + public function testCannotAddSolutionWhenMissingMandatoryFields(): void + { + $this->login(); + + $conf = $this->getCurrentConfig(); + + // Configure mandatory fields before closing (which impacts solutions too) + $result = $this->updateTestConfig($conf, [ + 'is_active' => 1, + 'entities_id' => 0, + 'require_technician_to_close_ticket' => 1, + 'require_category_to_close_ticket' => 1, + ]); + $this->assertTrue($result); + + // Create a ticket without mandatory fields + $ticket = $this->createItem( + \Ticket::class, + [ + 'name' => 'Test ticket solution', + 'content' => 'Test content', + ], + ); + $tid = $ticket->getID(); + + // Attempt to add a solution (Expected to fail because missing tech and category) + $solution = new \ITILSolution(); + $resultFields = $solution->add([ + 'itemtype' => \Ticket::class, + 'items_id' => $tid, + 'content' => 'My test solution', + 'status' => \CommonITILObject::SOLVED, + ]); + + $this->assertFalse($resultFields); + $this->clearSessionMessages(); + + // Add technician to the ticket + $user = new \User(); + $this->assertTrue($user->getFromDBByCrit(['name' => 'glpi'])); + + $this->createItem( + \Ticket_User::class, + [ + 'tickets_id' => $tid, + 'users_id' => $user->getID(), + 'type' => \Ticket_User::ASSIGN, + ], + ); + + // Create category and update ticket + $category = $this->createItem( + \ITILCategory::class, + [ + 'name' => 'Test category for solution test', + ], + ); + $this->updateItem( + \Ticket::class, + $tid, + [ + 'itilcategories_id' => $category->getID(), + ], + ); + + // Attempt to add solution with all mandatory fields present (Expected to succeed) + $solution2 = new \ITILSolution(); + $resultOk = $solution2->add([ + 'itemtype' => \Ticket::class, + 'items_id' => $tid, + 'solutiontypes_id' => 0, + 'content' => 'My test solution with fields ok', + 'status' => \CommonITILObject::SOLVED, + ]); + $this->assertIsInt($resultOk); + $this->clearSessionMessages(); + + // Reset config + $resetResult = $this->updateTestConfig($conf, [ + 'require_technician_to_close_ticket' => 0, + 'require_category_to_close_ticket' => 0, + ]); + $this->assertTrue($resetResult); + } + /** * Test mandatory fields before closing a change */ From cf79f68315481dbff31c4ab83f2bbf4bb2badbf1 Mon Sep 17 00:00:00 2001 From: Lainow Date: Mon, 23 Mar 2026 15:15:46 +0100 Subject: [PATCH 2/5] Fix PHP CS --- src/Controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Controller.php b/src/Controller.php index d4ff83d..6f3807b 100644 --- a/src/Controller.php +++ b/src/Controller.php @@ -408,7 +408,7 @@ public static function requireFieldsToClose(CommonDBTM $item): void $configSuffix, $item->userlinkclass ?? '', $item->grouplinkclass ?? '', - $item->getForeignKeyField() + $item->getForeignKeyField(), ); // Check if solution exists before closing @@ -422,7 +422,7 @@ public static function requireFieldsToClose(CommonDBTM $item): void 'itemtype' => $itemtype, 'items_id' => $item->fields['id'] ?? 0, 'NOT' => ['status' => CommonITILValidation::REFUSED], - ]) + ]), ) == 0) { $message .= '- ' . __s('Solution') . '
'; } @@ -462,7 +462,7 @@ public static function requireFieldsAddSolution(ITILSolution $item): void '_' . strtolower($itemtype), $parentItem->userlinkclass, $parentItem->grouplinkclass, - $parentItem->getForeignKeyField() + $parentItem->getForeignKeyField(), ); if (!empty($message)) { From a092f8be1783d039f6cae4fde2d72bd2a0f86575 Mon Sep 17 00:00:00 2001 From: Lainow Date: Mon, 23 Mar 2026 16:24:40 +0100 Subject: [PATCH 3/5] Fixed a solution added even though the required fields were not filled in --- src/Controller.php | 173 +++++++++++++++---------------------- tests/Units/ConfigTest.php | 2 +- 2 files changed, 72 insertions(+), 103 deletions(-) diff --git a/src/Controller.php b/src/Controller.php index 6f3807b..5bace84 100644 --- a/src/Controller.php +++ b/src/Controller.php @@ -57,7 +57,6 @@ use Item_Ticket; use ITILCategory; use ITILSolution; -use phpDocumentor\Reflection\Types\Boolean; use Planning; use Problem; use Problem_User; @@ -279,8 +278,15 @@ public static function beforeCloseITILObject(CommonDBTM $item): void return; } + $closed = true; + if ($item instanceof ITILSolution) { - self::requireFieldsAddSolution($item); + $parent_item = new $item->input['itemtype'](); + if (!$parent_item->getFromDB($item->input['items_id'])) { + return; + } + $closed = self::requireFieldsToClose($parent_item); + $closed = self::preventClosure($parent_item) && $closed; } if ( @@ -291,16 +297,20 @@ public static function beforeCloseITILObject(CommonDBTM $item): void || $item->fields['status'] == CommonITILObject::SOLVED ) ) { - self::requireFieldsToClose($item); - self::preventClosure($item); + $closed = self::requireFieldsToClose($item); + $closed = self::preventClosure($item) && $closed; + } + + if (!$closed) { + $item->input = false; } } - public static function preventClosure(CommonDBTM $item): void + public static function preventClosure(CommonDBTM $item): bool { $conf = Config::getConfig(); if ($conf->fields['is_active'] != 1) { - return; + return true; } $tasks = []; @@ -328,102 +338,95 @@ public static function preventClosure(CommonDBTM $item): void if (is_array($t) && isset($t['state']) && $t['state'] == Planning::TODO) { Session::addMessageAfterRedirect(__s('The ticket you wish to close has tasks that need to be completed.', 'moreoptions'), false, ERROR); $item->input = false; - return; + return false; } } + return true; } - /** - * Check required fields to close a ticket/change/problem and return a message listing missing fields - * - * @param false|array $input The input data of the item being closed - * @param string $configSuffix The suffix to use for configuration fields (e.g. '_ticket', '_change', '_problem') - * @param string $userClass The user link class of the item (e.g. Ticket_User, Change_User, Problem_User) - * @param string $groupClass The group link class of the item (e.g. Group_Ticket, Group_Change, Group_Problem) - */ - public static function checkFieldsToClose(false|array $input, string $configSuffix, string $userClass, string $groupClass, string $itemIdField): string + public static function requireFieldsToClose(CommonDBTM $item, bool $is_solution = false): bool { $conf = Config::getConfig(); - if ( - $conf->fields['is_active'] != 1 - || !is_array($input) - ) { - return ''; + if ($conf->fields['is_active'] != 1) { + return true; } $message = ''; - $id = $input['id'] ?? 0; + $itemtype = get_class($item); - if (!$id) { - return ''; - } + $data = empty($item->input) ? $item->fields : $item->input; + + // Determine the configuration suffix and actor classes based on item type + $configSuffix = '_' . strtolower($itemtype); + $userClass = $item->userlinkclass ?? ''; + $groupClass = $item->grouplinkclass ?? ''; + $itemIdField = $item->getForeignKeyField(); // Check for required technician - if ($conf->fields['require_technician_to_close' . $configSuffix] == 1 && is_a($userClass, CommonDBTM::class, true)) { - /** @var CommonDBTM $tech */ - $tech = new $userClass(); - if (count($tech->find([$itemIdField => $id, 'type' => CommonITILActor::ASSIGN])) == 0) { + if ($conf->fields['require_technician_to_close' . $configSuffix] == 1) { + if (is_a($userClass, CommonDBTM::class, true)) { + $tech = new $userClass(); + } else { + // If the user class is not valid, skip this check + return false; + } + $techs = $tech->find([ + $itemIdField => $data['id'], + 'type' => CommonITILActor::ASSIGN, + ]); + if (count($techs) == 0) { $message .= '- ' . __s('Technician') . '
'; } } // Check for required technician group - if ($conf->fields['require_technicians_group_to_close' . $configSuffix] == 1 && is_a($groupClass, CommonDBTM::class, true)) { - /** @var CommonDBTM $group */ - $group = new $groupClass(); - if (count($group->find([$itemIdField => $id, 'type' => CommonITILActor::ASSIGN])) == 0) { + if ($conf->fields['require_technicians_group_to_close' . $configSuffix] == 1) { + if (is_a($groupClass, CommonDBTM::class, true)) { + $group = new $groupClass(); + } else { + // If the group class is not valid, skip this check + return false; + } + $groups = $group->find([ + $itemIdField => $data['id'], + 'type' => CommonITILActor::ASSIGN, + ]); + if (count($groups) == 0) { $message .= '- ' . __s('Technician group') . '
'; } } // Check for required category if ($conf->fields['require_category_to_close' . $configSuffix] == 1) { - if (empty($input['itilcategories_id'] ?? 0)) { + if ((!isset($data['itilcategories_id']) || empty($data['itilcategories_id']))) { $message .= '- ' . __s('Category') . '
'; } } // Check for required location if ($conf->fields['require_location_to_close' . $configSuffix] == 1) { - if (empty($input['locations_id'] ?? 0)) { + if ((!isset($data['locations_id']) || empty($data['locations_id']))) { $message .= '- ' . __s('Location') . '
'; } } - return $message; - } - - public static function requireFieldsToClose(CommonDBTM $item): void - { - $conf = Config::getConfig(); - if ($conf->fields['is_active'] != 1) { - return; - } - - $itemtype = get_class($item); - $configSuffix = '_' . strtolower($itemtype); - - $message = self::checkFieldsToClose( - is_array($item->input) ? $item->input : [], - $configSuffix, - $item->userlinkclass ?? '', - $item->grouplinkclass ?? '', - $item->getForeignKeyField(), - ); - // Check if solution exists before closing - if ($conf->fields['require_solution_to_close' . $configSuffix] == 1 - && isset($item->input['status']) - && $item->input['status'] == CommonITILObject::CLOSED + if ( + !$is_solution + && $conf->fields['require_solution_to_close' . $configSuffix] == 1 + && is_array($data) + && isset($data['status']) + && $data['status'] == CommonITILObject::CLOSED ) { $solution = new ITILSolution(); - if (count( - $solution->find([ - 'itemtype' => $itemtype, - 'items_id' => $item->fields['id'] ?? 0, - 'NOT' => ['status' => CommonITILValidation::REFUSED], - ]), - ) == 0) { + $solutions = $solution->find([ + 'itemtype' => $itemtype, + 'items_id' => $data['id'], + 'NOT' => [ + 'status' => CommonITILValidation::REFUSED, + ], + ]); + if (count($solutions) == 0) { $message .= '- ' . __s('Solution') . '
'; } } @@ -433,43 +436,9 @@ public static function requireFieldsToClose(CommonDBTM $item): void $message = sprintf(__s('To close this %s, you must fill in the following fields:', 'moreoptions'), $itemTypeLabel) . '
' . $message; Session::addMessageAfterRedirect($message, false, ERROR); - $item->input = false; - } - } - - public static function requireFieldsAddSolution(ITILSolution $item): void - { - $conf = Config::getConfig(); - if ($conf->fields['is_active'] != 1 || !is_array($item->input)) { - return; - } - - $itemtype = $item->fields['itemtype'] ?? $item->input['itemtype'] ?? ''; - $items_id = $item->fields['items_id'] ?? $item->input['items_id'] ?? 0; - - if (empty($itemtype) || empty($items_id) || !class_exists($itemtype) || !is_a($itemtype, CommonITILObject::class, true)) { - return; - } - - /** @var CommonITILObject $parentItem */ - $parentItem = new $itemtype(); - if (!$parentItem->getFromDB($items_id)) { - return; - } - - $message = self::checkFieldsToClose( - is_array($parentItem->fields) ? $parentItem->fields : [], - '_' . strtolower($itemtype), - $parentItem->userlinkclass, - $parentItem->grouplinkclass, - $parentItem->getForeignKeyField(), - ); - - if (!empty($message)) { - $message = __s('To add a solution to this item, you must fill in the following fields:', 'moreoptions') . '
' . $message; - Session::addMessageAfterRedirect($message, false, ERROR); - $item->input = false; + return false; } + return true; } public static function checkTaskRequirements(CommonDBTM $item): CommonDBTM diff --git a/tests/Units/ConfigTest.php b/tests/Units/ConfigTest.php index 9fec1b5..824d39b 100644 --- a/tests/Units/ConfigTest.php +++ b/tests/Units/ConfigTest.php @@ -343,8 +343,8 @@ public function testCannotAddSolutionWhenMissingMandatoryFields(): void 'status' => \CommonITILObject::SOLVED, ]); - $this->assertFalse($resultFields); $this->clearSessionMessages(); + $this->assertFalse($resultFields); // Add technician to the ticket $user = new \User(); From 0896b1ffb1602b0a49dd6d34342f14c50d45cbe9 Mon Sep 17 00:00:00 2001 From: Lainow Date: Mon, 23 Mar 2026 16:58:07 +0100 Subject: [PATCH 4/5] Fix PHPStan --- src/Controller.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Controller.php b/src/Controller.php index 5bace84..b21f07f 100644 --- a/src/Controller.php +++ b/src/Controller.php @@ -281,7 +281,22 @@ public static function beforeCloseITILObject(CommonDBTM $item): void $closed = true; if ($item instanceof ITILSolution) { - $parent_item = new $item->input['itemtype'](); + $itemtype = $item->input['itemtype'] ?? null; + + switch ($itemtype) { + case 'Ticket': + $parent_item = new Ticket(); + break; + case 'Change': + $parent_item = new Change(); + break; + case 'Problem': + $parent_item = new Problem(); + break; + default: + return; + } + if (!$parent_item->getFromDB($item->input['items_id'])) { return; } From a1cf3e8c7b543adc19ac69053dff082f4517bb56 Mon Sep 17 00:00:00 2001 From: Lainow Date: Tue, 24 Mar 2026 17:18:11 +0100 Subject: [PATCH 5/5] Fix is solution --- src/Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller.php b/src/Controller.php index b21f07f..a52c93d 100644 --- a/src/Controller.php +++ b/src/Controller.php @@ -300,7 +300,7 @@ public static function beforeCloseITILObject(CommonDBTM $item): void if (!$parent_item->getFromDB($item->input['items_id'])) { return; } - $closed = self::requireFieldsToClose($parent_item); + $closed = self::requireFieldsToClose($parent_item, true); $closed = self::preventClosure($parent_item) && $closed; }