diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d6c1105d..f5ad37b5 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -20,7 +20,7 @@ jobs: name: "Generate CI matrix" uses: "glpi-project/plugin-ci-workflows/.github/workflows/generate-ci-matrix.yml@v1" with: - glpi-version: "11.0.x" + glpi-version: "12.0.x" ci: name: "GLPI ${{ matrix.glpi-version }} - php:${{ matrix.php-version }} - ${{ matrix.db-image }}" needs: "generate-ci-matrix" diff --git a/CHANGELOG.md b/CHANGELOG.md index c579fcf3..b9ee66f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- GLPI 12 compatibility + ### Fixed - Fix flaky escalation test on solve/reject-solution data sets - CI: fix Psalm cache directory, drop the stray `glpi-project/tools` composer dependency, declare a unique composer autoloader suffix +- Simplify group reassignment to a single actor diff, relying on GLPI core's status preservation on simultaneous actor removal/addition + ## [2.10.8] 2026-09-11 diff --git a/front/config.form.php b/front/config.form.php index e65a7bbb..cabd1e72 100644 --- a/front/config.form.php +++ b/front/config.form.php @@ -30,9 +30,7 @@ Session::checkLoginUser(); -if (! isset($_GET["id"])) { - $_GET["id"] = 0; -} +$_GET["id"] ??= 0; if (!Plugin::isPluginActive('escalade')) { echo "Plugin not installed or activated"; @@ -42,15 +40,15 @@ $config = new PluginEscaladeConfig(); if (isset($_POST["add"])) { - Session::checkRight("config", CREATE); + Session::checkRight(Config::$rightname, CREATE); $newID = $config->add($_POST); Html::back(); } elseif (isset($_POST["update"])) { - Session::checkRight("config", UPDATE); + Session::checkRight(Config::$rightname, UPDATE); $config->update($_POST); Html::back(); } elseif (isset($_POST["delete"])) { - Session::checkRight("config", DELETE); + Session::checkRight(Config::$rightname, DELETE); $config->delete($_POST, true); Html::redirect("./config.form.php"); } else { diff --git a/front/group_group.form.php b/front/group_group.form.php index 37899cf6..ca06a5c1 100644 --- a/front/group_group.form.php +++ b/front/group_group.form.php @@ -34,7 +34,7 @@ Html::header("escalade", $_SERVER["PHP_SELF"], "plugins", "escalade", "group_group"); -if (Session::haveRight('group', UPDATE)) { +if (Session::haveRight(Group::$rightname, UPDATE)) { if (isset($_POST['addgroup'])) { $group_source = new Group(); $group_destination = new Group(); diff --git a/hook.php b/hook.php index 752848fe..6b759f7b 100644 --- a/hook.php +++ b/hook.php @@ -329,9 +329,7 @@ function plugin_escalade_install() $tickets_id = $data['tickets_id']; unset($data['tickets_id']); - if (!isset($histories[$tickets_id])) { - $histories[$tickets_id] = []; - } + $histories[$tickets_id] ??= []; $histories[$tickets_id][] = $data; } @@ -525,9 +523,7 @@ function plugin_escalade_pre_item_add_group_ticket($item) && $item->input['type'] == CommonITILActor::ASSIGN && empty($item->input['_plugin_escalade_rules_only']) ) { - if (!isset($_SESSION['plugin_escalade']['current_group_assignment'])) { - $_SESSION['plugin_escalade']['current_group_assignment'] = []; - } + $_SESSION['plugin_escalade']['current_group_assignment'] ??= []; $_SESSION['plugin_escalade']['current_group_assignment'][$item->input['tickets_id']] = [ 'group_id' => $item->input['groups_id'], diff --git a/inc/config.class.php b/inc/config.class.php index e0b84cdb..01f7d34d 100644 --- a/inc/config.class.php +++ b/inc/config.class.php @@ -32,7 +32,7 @@ class PluginEscaladeConfig extends CommonDBTM { - public static $rightname = 'config'; + public static string $rightname = 'config'; public static function getMenuName() { diff --git a/inc/group_group.class.php b/inc/group_group.class.php index 0228c363..709cef31 100644 --- a/inc/group_group.class.php +++ b/inc/group_group.class.php @@ -38,13 +38,13 @@ class PluginEscaladeGroup_Group extends CommonDBRelation { // From CommonDBRelation - public static $itemtype_1 = 'Group'; + public static ?string $itemtype_1 = 'Group'; - public static $items_id_1 = 'groups_id_source'; + public static ?string $items_id_1 = 'groups_id_source'; - public static $itemtype_2 = 'Group'; + public static ?string $itemtype_2 = 'Group'; - public static $items_id_2 = 'groups_id_destination'; + public static ?string $items_id_2 = 'groups_id_destination'; public function getForbiddenStandardMassiveAction() { @@ -96,7 +96,7 @@ public function manageGroup($groups_id) $gg_found = $this->find(['groups_id_source' => $groups_id]); $nb = count($gg_found); - if (Session::haveRight('group', UPDATE)) { + if (Session::haveRight(Group::$rightname, UPDATE)) { $groups_id_used = []; foreach ($gg_found as $gg) { $groups_id_used[] = $gg['groups_id_destination']; @@ -123,7 +123,7 @@ public function manageGroup($groups_id) } TemplateRenderer::getInstance()->display('@escalade/group_group.html.twig', [ - 'canedit' => Session::haveRight('group', UPDATE), + 'canedit' => Session::haveRight(Group::$rightname, UPDATE), 'group_id' => $groups_id, 'groups' => $groups, 'massiveactionparams' => $massiveactionparams ?? [], diff --git a/inc/history.class.php b/inc/history.class.php index 53600d7b..a8c570eb 100644 --- a/inc/history.class.php +++ b/inc/history.class.php @@ -41,9 +41,9 @@ public static function getFirstLineForTicket($tickets_id) $found = self::getFullHistory($tickets_id); if (count($found) === 0) { return false; - } else { - return array_pop($found); } + + return array_pop($found); } public static function getlastLineForTicket($tickets_id) @@ -51,9 +51,9 @@ public static function getlastLineForTicket($tickets_id) $found = self::getFullHistory($tickets_id); if (count($found) === 0) { return false; - } else { - return array_shift($found); } + + return array_shift($found); } public static function getLastHistoryForTicketAndGroup($tickets_id, $groups_id, $previous_groups_id) @@ -272,7 +272,7 @@ public static function getHistory($tickets_id, $full_history = false) public static function showGroupLink($group, $full_history = false) { - if (!$group->can($group->fields['id'], READ)) { + if (!Session::haveRight(Group::$rightname, READ)) { return ''; } @@ -287,7 +287,7 @@ public static function showGroupLink($group, $full_history = false) echo sprintf(" onclick='self.opener.location.href=\"%s\"; self.close();'", $link); } - echo ">" . $group->getNameID(true) . ""; + echo ">" . $group->getNameID() . ""; return null; } @@ -304,9 +304,9 @@ public static function showCentralSpecificList($type) global $CFG_GLPI, $DB; if ( - ! Session::haveRight("ticket", Ticket::READALL) - && ! Session::haveRight("ticket", Ticket::READASSIGN) - && ! Session::haveRight("ticket", CREATE) + ! Session::haveRight(Ticket::$rightname, Ticket::READALL) + && ! Session::haveRight(Ticket::$rightname, Ticket::READASSIGN) + && ! Session::haveRight(Ticket::$rightname, CREATE) && ! Session::haveRight("ticketvalidation", TicketValidation::VALIDATEREQUEST & TicketValidation::VALIDATEINCIDENT) ) { diff --git a/inc/notification.class.php b/inc/notification.class.php index 6eb15768..aff04891 100644 --- a/inc/notification.class.php +++ b/inc/notification.class.php @@ -169,34 +169,21 @@ public static function getActionTargets(NotificationTarget $target) case self::NTRGT_TICKET_REQUESTER_GROUP: // phpcs:ignore $group_type = CommonITILActor::REQUESTER; // no break - case self::NTRGT_TICKET_WATCH_GROUP: // phpcs:ignore - if (!isset($group_type)) { - $group_type = CommonITILActor::OBSERVER; - } + case self::NTRGT_TICKET_WATCH_GROUP: $group_type ??= CommonITILActor::OBSERVER; // no break case self::NTRGT_TICKET_TECH_GROUP: $manager = 0; // manager of group's users // no break - case self::NTRGT_TICKET_REQUESTER_GROUP_MANAGER: // phpcs:ignore - if (!isset($group_type)) { - $group_type = CommonITILActor::REQUESTER; - } + case self::NTRGT_TICKET_REQUESTER_GROUP_MANAGER: $group_type ??= CommonITILActor::REQUESTER; // no break - case self::NTRGT_TICKET_WATCH_GROUP_MANAGER: // phpcs:ignore - if (!isset($group_type)) { - $group_type = CommonITILActor::OBSERVER; - } + case self::NTRGT_TICKET_WATCH_GROUP_MANAGER: $group_type ??= CommonITILActor::OBSERVER; // no break case self::NTRGT_TICKET_TECH_GROUP_MANAGER: - if (!isset($manager)) { - $manager = 1; - } + $manager ??= 1; - if (!isset($group_type)) { - $group_type = CommonITILActor::ASSIGN; - } + $group_type ??= CommonITILActor::ASSIGN; self::addGroupsOfTicket($target, $ticket->getID(), $manager, $group_type); break; @@ -205,15 +192,10 @@ public static function getActionTargets(NotificationTarget $target) case self::NTRGT_TICKET_REQUESTER_USER: // phpcs:ignore $user_type = CommonITILActor::REQUESTER; // no break - case self::NTRGT_TICKET_WATCH_USER: // phpcs:ignore - if (!isset($user_type)) { - $user_type = CommonITILActor::OBSERVER; - } + case self::NTRGT_TICKET_WATCH_USER: $user_type ??= CommonITILActor::OBSERVER; // no break case self::NTRGT_TICKET_TECH_USER: - if (!isset($user_type)) { - $user_type = CommonITILActor::ASSIGN; - } + $user_type ??= CommonITILActor::ASSIGN; self::addUsersOfTicket($target, $ticket->getID(), $user_type); break; @@ -228,9 +210,7 @@ public static function getActionTargets(NotificationTarget $target) $manager = 0; // no break case self::NTRGT_TICKET_ESCALADE_GROUP_MANAGER: - if (!isset($manager)) { - $manager = 1; - } + $manager ??= 1; $history = new PluginEscaladeHistory(); foreach ($history->find(['tickets_id' => $ticket->getID()]) as $found_history) { @@ -249,9 +229,7 @@ public static function getActionTargets(NotificationTarget $target) $manager = 0; // no break case self::NTRGT_TICKET_LAST_ESCALADE_GROUP_MANAGER: - if (!isset($manager)) { - $manager = 1; - } + $manager ??= 1; $group_to_notify = null; diff --git a/inc/taskmanager.class.php b/inc/taskmanager.class.php index aba8ce0e..70d7e818 100644 --- a/inc/taskmanager.class.php +++ b/inc/taskmanager.class.php @@ -42,7 +42,7 @@ public static function setTicketTask(array $input): void return; } - if (!empty(self::$ticket_task->input)) { + if (self::$ticket_task->input !== [] && self::$ticket_task->input !== false) { return; } diff --git a/inc/ticket.class.php b/inc/ticket.class.php index 31ab26a8..a52b3fe9 100644 --- a/inc/ticket.class.php +++ b/inc/ticket.class.php @@ -484,7 +484,7 @@ public static function addHistoryOnAddGroup(CommonDBTM $item) $previous_groups_id = 0; $counter = 0; - if (count($group_ticket->fields) > 0) { + if ($group_ticket->fields !== []) { $previous_groups_id = $group_ticket->fields['groups_id']; $last_history_groups = PluginEscaladeHistory::getLastHistoryForTicketAndGroup($tickets_id, $groups_id, $previous_groups_id); @@ -551,24 +551,6 @@ public static function processAfterAddGroup(Group_Ticket $item) if ($_SESSION['glpi_plugins']['escalade']['config']['remove_group'] == true) { $all_actors = self::getTicketFieldsWithActors($tickets_id, $groups_id); - // Keep only the new group in the assign list (drop old ones). - $seen_new_group = false; - $all_actors['assign'] = array_values(array_filter( - $all_actors['assign'], - function (array $actor) use ($groups_id, &$seen_new_group): bool { - if ($actor['itemtype'] !== 'Group') { - return true; - } - - if ($actor['items_id'] == $groups_id && !$seen_new_group) { - $seen_new_group = true; - return true; - } - - return false; - }, - )); - $ticket_for_rules = new Ticket(); $ticket_for_rules->getFromDB($tickets_id); $ticket_for_rules->update([ @@ -1279,6 +1261,11 @@ public static function getTicketFieldsWithActors($tickets_id, $group_id) } } + // Diff-only when remove_group: lets core delete the old group(s) and add the new one in a single call. + if ($_SESSION['glpi_plugins']['escalade']['config']['remove_group'] == true) { + $ticket_actors['Group']['assign'] = []; + } + $ticket_actors['Group']['assign'][] = [ 'itemtype' => 'Group', 'items_id' => $group_id, diff --git a/inc/user.class.php b/inc/user.class.php index b2a5c9fb..e2837eb1 100644 --- a/inc/user.class.php +++ b/inc/user.class.php @@ -64,9 +64,9 @@ public static function processMassiveActionsForOneItemtype(MassiveAction $ma, Co if ($escalade_user->getFromDBByCrit(['users_id' => $id])) { $escalade_user->fields['bypass_filter_assign_group'] = $input['bypass_filter_assign_group']; if ($escalade_user->update($escalade_user->fields)) { - $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK); + $ma->itemDone($item::class, $id, MassiveAction::ACTION_OK); } else { - $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO); + $ma->itemDone($item::class, $id, MassiveAction::ACTION_KO); } } } diff --git a/public/js/central.js.php b/public/js/central.js.php index fbd5ae0a..58476b66 100644 --- a/public/js/central.js.php +++ b/public/js/central.js.php @@ -34,8 +34,8 @@ //not executed in self-service interface & right verification if ( $_SESSION['glpiactiveprofile']['interface'] == "central" - && (Session::haveRight("ticket", CREATE) - || Session::haveRight("ticket", UPDATE)) + && (Session::haveRight(Ticket::$rightname, CREATE) + || Session::haveRight(Ticket::$rightname, UPDATE)) ) { $locale_group_view = __s('Group View'); diff --git a/public/js/cloneandlink_ticket.js.php b/public/js/cloneandlink_ticket.js.php index bf397970..79454d26 100644 --- a/public/js/cloneandlink_ticket.js.php +++ b/public/js/cloneandlink_ticket.js.php @@ -34,8 +34,8 @@ //not executed in self-service interface & right verification if ( $_SESSION['glpiactiveprofile']['interface'] == "central" - && Session::haveRight("ticket", CREATE) - && Session::haveRight("ticket", UPDATE) + && Session::haveRight(Ticket::$rightname, CREATE) + && Session::haveRight(Ticket::$rightname, UPDATE) ) { $locale_cloneandlink = __s("Clone and link", "escalade"); $locale_clone = __s("Clone"); diff --git a/public/js/escalade.js.php b/public/js/escalade.js.php index 29e68e4a..648d779f 100644 --- a/public/js/escalade.js.php +++ b/public/js/escalade.js.php @@ -34,8 +34,8 @@ //not executed in self-service interface & right verification if ( $_SESSION['glpiactiveprofile']['interface'] == "central" - && (Session::haveRight("ticket", CREATE) - || Session::haveRight("ticket", UPDATE)) + && (Session::haveRight(Ticket::$rightname, CREATE) + || Session::haveRight(Ticket::$rightname, UPDATE)) ) { $locale_actor = __s('Actor'); diff --git a/setup.php b/setup.php index 80313473..6c5523a5 100644 --- a/setup.php +++ b/setup.php @@ -32,9 +32,9 @@ define('PLUGIN_ESCALADE_VERSION', '2.10.8'); // Minimal GLPI version, inclusive -define("PLUGIN_ESCALADE_MIN_GLPI", "11.0.0"); +define("PLUGIN_ESCALADE_MIN_GLPI", "12.0.0"); // Maximum GLPI version, exclusive -define("PLUGIN_ESCALADE_MAX_GLPI", "11.0.99"); +define("PLUGIN_ESCALADE_MAX_GLPI", "12.0.99"); if (!defined("PLUGIN_ESCALADE_DIR")) { define("PLUGIN_ESCALADE_DIR", Plugin::getPhpDir("escalade")); @@ -134,7 +134,7 @@ function plugin_init_escalade() } // == Interface links == - if (Session::haveRight('config', UPDATE)) { + if (Session::haveRight(Config::$rightname, UPDATE)) { $PLUGIN_HOOKS['config_page']['escalade'] = 'front/config.form.php'; $PLUGIN_HOOKS['menu_toadd']['escalade'] = [ @@ -172,7 +172,7 @@ function plugin_init_escalade() function plugin_version_escalade() { return [ - 'name' => __s("Escalation", "escalade"), + 'name' => "Escalation", 'version' => PLUGIN_ESCALADE_VERSION, 'author' => "Teclib'", 'homepage' => "https://github.com/pluginsGLPI/escalade", diff --git a/templates/config.html.twig b/templates/config.html.twig index 52f8b921..d4848d80 100644 --- a/templates/config.html.twig +++ b/templates/config.html.twig @@ -34,7 +34,6 @@
-
@@ -61,7 +60,6 @@ {% set display_remove_group_options = not config["remove_group"] ? 'd-none' : '' %} {% set display_tech_group_options = not config["use_assign_user_group"] ? 'd-none' : '' %} - {% set display_hidden_field = (not config["remove_group"] and config["use_assign_user_group"]) or (config["remove_group"] and not config["use_assign_user_group"]) ? '' : 'd-none' %}
@@ -79,47 +77,37 @@
- {{ fields.sliderField( - 'show_history', - config["show_history"], - __("Show group assign history visually", "escalade"), - field_options|merge({'add_field_class': display_remove_group_options}) - ) }} - - {{ fields.hiddenField( - 'splitfield', - '', - '', - field_options|merge({'add_field_class': display_hidden_field}) - ) }} - - {{ fields.sliderField( - 'use_assign_user_group_creation', - config["use_assign_user_group_creation"], - __('at creation time', 'escalade'), - field_options|merge({'add_field_class': display_tech_group_options}) - ) }} - - {{ fields.sliderField( - 'solve_return_group', - config["solve_return_group"], - __("Assign ticket to initial group on solve ticket", "escalade"), - field_options|merge({'add_field_class': display_remove_group_options}) - ) }} - - {{ fields.hiddenField( - 'splitfield', - '', - '', - field_options|merge({'add_field_class': display_hidden_field}) - ) }} +
+ {{ fields.sliderField( + 'show_history', + config["show_history"], + __("Show group assign history visually", "escalade"), + field_options|merge({'field_class': 'col-12', 'add_field_class': display_remove_group_options}) + ) }} + + {{ fields.sliderField( + 'solve_return_group', + config["solve_return_group"], + __("Assign ticket to initial group on solve ticket", "escalade"), + field_options|merge({'field_class': 'col-12', 'add_field_class': display_remove_group_options}) + ) }} +
- {{ fields.sliderField( - 'use_assign_user_group_modification', - config["use_assign_user_group_modification"], - __('at modification time', 'escalade'), - field_options|merge({'add_field_class': display_tech_group_options}) - ) }} +
+ {{ fields.sliderField( + 'use_assign_user_group_creation', + config["use_assign_user_group_creation"], + __('at creation time', 'escalade'), + field_options|merge({'field_class': 'col-12', 'add_field_class': display_tech_group_options}) + ) }} + + {{ fields.sliderField( + 'use_assign_user_group_modification', + config["use_assign_user_group_modification"], + __('at modification time', 'escalade'), + field_options|merge({'field_class': 'col-12', 'add_field_class': display_tech_group_options}) + ) }} +
@@ -342,10 +330,8 @@ \ No newline at end of file diff --git a/templates/escalade_form.html.twig b/templates/escalade_form.html.twig index b22e62a7..2f88130e 100644 --- a/templates/escalade_form.html.twig +++ b/templates/escalade_form.html.twig @@ -37,7 +37,6 @@ data-track-changes="true" data-submit-once> -

{{ __('Escalate ticket', 'escalade') }}

diff --git a/templates/group_group.html.twig b/templates/group_group.html.twig index 9768cb34..2d3db6f4 100644 --- a/templates/group_group.html.twig +++ b/templates/group_group.html.twig @@ -41,7 +41,6 @@ } ) }} -