From 5909bea7492278ed5143a6a3a1f72ba3a9c770b9 Mon Sep 17 00:00:00 2001 From: Pedro Rocha Date: Fri, 12 Dec 2025 00:23:03 +0000 Subject: [PATCH 01/13] glpi 11 update --- hook.php | 94 ++++++++++++---------- setup.php | 38 +++++---- src/Field.php | 70 ++++++++++++----- src/Menu.php | 39 +++++++--- src/NotificationTargetRequest.php | 108 ++++++++++++++------------ src/Option.php | 112 ++++++++++++++++++++------ src/Profile.php | 125 ++++++++++++++++++------------ src/Request.php | 51 +++++++++--- src/Servicecatalog.php | 73 +++++++++++------ src/Validation.php | 79 ++++++++++++++----- src/Wizard.php | 32 ++++++-- 11 files changed, 544 insertions(+), 277 deletions(-) diff --git a/hook.php b/hook.php index d3af91f..a758795 100644 --- a/hook.php +++ b/hook.php @@ -1,4 +1,5 @@ tableExists("glpi_plugin_consumables_requests")) { // Install script @@ -57,17 +56,22 @@ function plugin_consumables_install() return true; } + /** + * Desinstala o plugin Consumables + * * @return bool */ -function plugin_consumables_uninstall() +function plugin_consumables_uninstall(): bool { global $DB; - $tables = ["glpi_plugin_consumables_profiles", + $tables = [ + "glpi_plugin_consumables_profiles", "glpi_plugin_consumables_requests", "glpi_plugin_consumables_options", - "glpi_plugin_consumables_fields"]; + "glpi_plugin_consumables_fields" + ]; foreach ($tables as $table) { $DB->dropTable($table, true); @@ -107,7 +111,8 @@ function plugin_consumables_uninstall() } } - $itemtypes = ['Alert', + $itemtypes = [ + 'Alert', 'DisplayPreference', 'Document_Item', 'ImpactItem', @@ -117,7 +122,8 @@ function plugin_consumables_uninstall() 'SavedSearch', 'DropdownTranslation', 'NotificationTemplate', - 'Notification']; + 'Notification' + ]; foreach ($itemtypes as $itemtype) { $item = new $itemtype; $item->deleteByCriteria(['itemtype' => Request::class]); @@ -130,17 +136,20 @@ function plugin_consumables_uninstall() } Menu::removeRightsFromSession(); - Profile::removeRightsFromSession(); return true; } // Hook done on purge item case + /** - * @param $item + * Hook executado ao purgar item + * + * @param object $item + * @return void */ -function plugin_item_purge_consumables($item) +function plugin_item_purge_consumables(object $item): void { switch (get_class($item)) { case 'ConsumableItem': @@ -151,76 +160,83 @@ function plugin_item_purge_consumables($item) } // Define dropdown relations + /** + * Define relações de dropdown + * * @return array */ -function plugin_consumables_getDatabaseRelations() +function plugin_consumables_getDatabaseRelations(): array { - if (Plugin::isPluginActive("consumables")) { - return ["glpi_consumableitems" => ["glpi_plugin_consumables_requests" => "consumableitems_id", - "glpi_plugin_consumables_options" => "consumableitems_id"]]; - } else { - return []; + return [ + "glpi_consumableitems" => [ + "glpi_plugin_consumables_requests" => "consumableitems_id", + "glpi_plugin_consumables_options" => "consumableitems_id" + ] + ]; } + return []; } // Define search option for types of the plugins + /** - * @param $itemtype + * Define opções de busca para tipos do plugin * + * @param string $itemtype * @return array */ -function plugin_consumables_getAddSearchOptions($itemtype) +function plugin_consumables_getAddSearchOptions(string $itemtype): array { - $sopt = []; - if ($itemtype == "ConsumableItem") { + if ($itemtype === "ConsumableItem") { if (Session::haveRight("plugin_consumables", READ)) { $sopt[185]['table'] = 'glpi_plugin_consumables_fields'; $sopt[185]['field'] = 'order_ref'; $sopt[185]['name'] = __('Order reference', 'consumables'); $sopt[185]['datatype'] = "text"; - $sopt[185]['joinparams'] = ['jointype' => 'child', - 'linkfield' => 'consumableitems_id']; + $sopt[185]['joinparams'] = ['jointype' => 'child', 'linkfield' => 'consumableitems_id']; $sopt[185]['massiveaction'] = false; $sopt[186]['table'] = 'glpi_plugin_consumables_options'; $sopt[186]['field'] = 'max_cart'; $sopt[186]['name'] = __('Maximum number allowed for request', 'consumables'); $sopt[186]['datatype'] = "number"; - $sopt[186]['linkfield'] = "consumableitems_id"; - $sopt[186]['joinparams'] = ['jointype' => 'child', - 'linkfield' => 'consumableitems_id']; + $sopt[186]['linkfield'] = "consumableitems_id"; + $sopt[186]['joinparams'] = ['jointype' => 'child', 'linkfield' => 'consumableitems_id']; $sopt[186]['massiveaction'] = false; $sopt[187]['table'] = 'glpi_plugin_consumables_options'; $sopt[187]['field'] = 'groups'; $sopt[187]['name'] = __('Allowed groups for request', 'consumables'); $sopt[187]['datatype'] = "specific"; - $sopt[187]['linkfield'] = "consumableitems_id"; - $sopt[187]['joinparams'] = ['jointype' => 'child', - 'linkfield' => 'consumableitems_id']; + $sopt[187]['linkfield'] = "consumableitems_id"; + $sopt[187]['joinparams'] = ['jointype' => 'child', 'linkfield' => 'consumableitems_id']; $sopt[187]['massiveaction'] = false; - $sopt[187]['nosearch'] = true; + $sopt[187]['nosearch'] = true; } } return $sopt; } -function plugin_consumables_MassiveActions($type) -{ +/** + * Define ações em massa para o plugin + * + * @param string $type + * @return array + */ +function plugin_consumables_MassiveActions(string $type): array +{ switch ($type) { case 'ConsumableItem': return [ - Option::class . MassiveAction::CLASS_ACTION_SEPARATOR . 'add_number' - => __('Maximum number allowed for request', 'consumables'), - Option::class . MassiveAction::CLASS_ACTION_SEPARATOR . 'add_groups' - => __('Add a group for request', 'consumables')]; - break; + Option::class . MassiveAction::CLASS_ACTION_SEPARATOR . 'add_number' => __('Maximum number allowed for request', 'consumables'), + Option::class . MassiveAction::CLASS_ACTION_SEPARATOR . 'add_groups' => __('Add a group for request', 'consumables') + ]; } return []; } diff --git a/setup.php b/setup.php index 9420d30..7df5335 100644 --- a/setup.php +++ b/setup.php @@ -1,4 +1,5 @@ registerPluginTileType(new ConsumablesPageTile()); @@ -62,36 +60,34 @@ function plugin_init_consumables() $CFG_GLPI['glpitablesitemtype'][Validation::class] = 'glpi_plugin_consumables_requests'; $PLUGIN_HOOKS['csrf_compliant']['consumables'] = true; $PLUGIN_HOOKS['change_profile']['consumables'] = [Profile::class, 'initProfile']; - $PLUGIN_HOOKS[Hooks::ADD_CSS]['consumables'] = 'css/consumables.css'; - $PLUGIN_HOOKS[Hooks::ADD_JAVASCRIPT]['consumables'] = 'js/consumables.js'; + $PLUGIN_HOOKS[Hooks::ADD_CSS]['consumables'] = 'css/consumables.css'; + $PLUGIN_HOOKS[Hooks::ADD_JAVASCRIPT]['consumables'] = 'js/consumables.js'; if (Session::getLoginUserID()) { $PLUGIN_HOOKS['post_item_form']['consumables'] = [Field::class, 'addFieldOrderReference']; Plugin::registerClass(Profile::class, ['addtabon' => 'Profile']); - Plugin::registerClass(Request::class, ['addtabon' => 'User', - 'notificationtemplates_types' => true]); - Plugin::registerClass(Request::class, ['addtabon' => 'Group', - 'notificationtemplates_types' => true]); + Plugin::registerClass(Request::class, ['addtabon' => 'User', 'notificationtemplates_types' => true]); + Plugin::registerClass(Request::class, ['addtabon' => 'Group', 'notificationtemplates_types' => true]); Plugin::registerClass(Request::class, ['addtabon' => 'ConsumableItem']); - $PLUGIN_HOOKS['item_add']['consumables'] = ['ConsumableItem' => [Field::class, 'postAddConsumable']]; + $PLUGIN_HOOKS['item_add']['consumables'] = ['ConsumableItem' => [Field::class, 'postAddConsumable']]; $PLUGIN_HOOKS['pre_item_update']['consumables'] = ['ConsumableItem' => [Field::class, 'preUpdateConsumable']]; if (Session::haveRight("plugin_consumables", UPDATE)) { $PLUGIN_HOOKS['use_massive_action']['consumables'] = 1; } - // if (class_exists(Main::class)) { $PLUGIN_HOOKS['servicecatalog']['consumables'] = [Servicecatalog::class]; - // } if (Session::haveRight("plugin_consumables", READ)) { $PLUGIN_HOOKS['menu_toadd']['consumables'] = ['management' => Menu::class]; } - if (Session::haveRight("plugin_consumables", READ) - || Session::haveRight("plugin_consumables_request", 1) - && !class_exists(Main::class)) { + if ( + Session::haveRight("plugin_consumables", READ) + || Session::haveRight("plugin_consumables_request", 1) + && !class_exists(Main::class) + ) { $PLUGIN_HOOKS['helpdesk_menu_entry']['consumables'] = PLUGIN_CONSUMABLES_WEBDIR . '/front/wizard.php'; $PLUGIN_HOOKS['helpdesk_menu_entry_icon']['consumables'] = Request::getIcon(); } @@ -103,12 +99,14 @@ function plugin_init_consumables() // Get the name and the version of the plugin - Needed + /** + * Retorna nome e versão do plugin + * * @return array */ -function plugin_version_consumables() +function plugin_version_consumables(): array { - return [ 'name' => _n('Consumable request', 'Consumable requests', 1, 'consumables'), 'version' => PLUGIN_CONSUMABLES_VERSION, diff --git a/src/Field.php b/src/Field.php index a878ab2..6fc8009 100644 --- a/src/Field.php +++ b/src/Field.php @@ -37,6 +37,17 @@ die("Sorry. You can't access directly to this file"); } +/** + * Class Field + * + * This class shows the plugin main page + * + * @package Consumables + * @author Ludovic Dupont + */ + +declare(strict_types=1); + /** * Class Field * @@ -47,9 +58,8 @@ */ class Field extends CommonDBTM { - - static $types = ['ConsumableItem']; - static $rightname = "plugin_consumables"; + public static array $types = ['ConsumableItem']; + public static string $rightname = 'plugin_consumables'; /** @@ -57,7 +67,11 @@ class Field extends CommonDBTM * * @return string */ - static function getTypeName($nb = 0) + /** + * @param int $nb + * @return string + */ + public static function getTypeName(int $nb = 0): string { return _n('Consumable request', 'Consumable requests', 1, 'consumables'); } @@ -68,17 +82,20 @@ static function getTypeName($nb = 0) * * @param $params */ - public static function addFieldOrderReference($params) + /** + * Show order reference field + * @param array $params + * @return bool|null + */ + public static function addFieldOrderReference(array $params): ?bool { - $item = $params['item']; - - if (!in_array($item::getType(), self::$types)) { + if (!in_array($item::getType(), self::$types, true)) { return false; } $consumableitems_id = $item->getID(); - $field = new self(); - if ($field->getFromDBByCrit(["consumableitems_id" => $consumableitems_id])) { + $field = new self(); + if ($field->getFromDBByCrit(['consumableitems_id' => $consumableitems_id])) { echo "
"; echo "
"; echo ""; } + return null; } /** @@ -95,13 +113,19 @@ public static function addFieldOrderReference($params) * * @param ConsumableItem $consumableItem */ - static function postAddConsumable(ConsumableItem $consumableItem) + /** + * Post add consumable + * @param ConsumableItem $consumableItem + * @return void + */ + public static function postAddConsumable(ConsumableItem $consumableItem): void { - $field = new self(); if (isset($consumableItem->input['order_ref'])) { - $field->add(['consumableitems_id' => $consumableItem->fields['id'], - 'order_ref' => $consumableItem->input['order_ref']]); + $field->add([ + 'consumableitems_id' => $consumableItem->fields['id'], + 'order_ref' => $consumableItem->input['order_ref'] + ]); } } @@ -110,17 +134,23 @@ static function postAddConsumable(ConsumableItem $consumableItem) * * @param ConsumableItem $consumableItem */ - static function preUpdateConsumable(ConsumableItem $consumableItem) + /** + * Pre update consumable + * @param ConsumableItem $consumableItem + * @return void + */ + public static function preUpdateConsumable(ConsumableItem $consumableItem): void { - $field = new self(); - $field->getFromDBByCrit(["consumableitems_id" => $consumableItem->input['id']]); - + $field->getFromDBByCrit(['consumableitems_id' => $consumableItem->input['id']]); if (!empty($field->fields)) { - $field->update(['id' => $field->fields['id'], - 'order_ref' => $consumableItem->input['order_ref']]); + $field->update([ + 'id' => $field->fields['id'], + 'order_ref' => $consumableItem->input['order_ref'] + ]); } else { self::postAddConsumable($consumableItem); } } } +} diff --git a/src/Menu.php b/src/Menu.php index e774591..3154e8b 100644 --- a/src/Menu.php +++ b/src/Menu.php @@ -39,34 +39,49 @@ class Menu extends CommonGLPI { public static $rightname = 'plugin_consumables'; - /** - * @return string - */ - public static function getMenuName() + declare(strict_types=1); + + /** + * Class Menu + */ + class Menu extends CommonGLPI { - return _n('Consumable request', 'Consumable requests', 1, 'consumables'); - } + public static $rightname = 'plugin_consumables'; + + /** + * @return string + */ + public static function getMenuName(): string + { + return _n('Consumable request', 'Consumable requests', 1, 'consumables'); + } + /** * @return array */ public static function getMenuContent() { - - $menu = []; + public static function getMenuContent(): array + { + $menu = []; $menu['title'] = self::getMenuName(); - $menu['page'] = Wizard::getSearchURL(false); + $menu['page'] = Wizard::getSearchURL(false); if (Wizard::canCreate()) { $menu['links']['search'] = Wizard::getSearchURL(false); - $menu['links']['add'] = Wizard::getSearchURL(false); + $menu['links']['add'] = Wizard::getSearchURL(false); } - $menu['icon'] = Request::getIcon(); - return $menu; } + public static function removeRightsFromSession() + /** + * Remove rights from session for this menu + * @return void + */ + public static function removeRightsFromSession(): void { if (isset($_SESSION['glpimenu']['plugins']['types'][Menu::class])) { unset($_SESSION['glpimenu']['plugins']['types'][Menu::class]); diff --git a/src/NotificationTargetRequest.php b/src/NotificationTargetRequest.php index 120b50a..046e26c 100644 --- a/src/NotificationTargetRequest.php +++ b/src/NotificationTargetRequest.php @@ -38,27 +38,33 @@ use Group; use Html; use NotificationTarget; + +declare(strict_types=1); use User; if (!defined('GLPI_ROOT')) { die("Sorry. You can't access directly to this file"); } +/** + * Class NotificationTargetRequest + */ + /** * Class NotificationTargetRequest */ class NotificationTargetRequest extends NotificationTarget { - public const CONSUMABLE_REQUEST = "ConsumableRequest"; - public const CONSUMABLE_RESPONSE = "ConsumableResponse"; + public const CONSUMABLE_REQUEST = 'ConsumableRequest'; + public const CONSUMABLE_RESPONSE = 'ConsumableResponse'; public const VALIDATOR = 30; public const REQUESTER = 31; public const RECIPIENT = 32; /** - * @return array + * @return array */ - public function getEvents() + public function getEvents(): array { return [ self::CONSUMABLE_REQUEST => __('Consumable request', 'consumables'), @@ -66,24 +72,28 @@ public function getEvents() ]; } - public function validateSendTo($event, array $infos, $notify_me = false, $emitter = null) + /** + * @param string $event + * @param array $infos + * @param bool $notify_me + * @param mixed $emitter + * @return bool + */ + public function validateSendTo(string $event, array $infos, bool $notify_me = false, $emitter = null): bool { - // Always send notification for satisfaction : if send on ticket closure - // Always send notification for new ticket - if (in_array($event, ['ConsumableRequest', 'ConsumableResponse'])) { + if (in_array($event, [self::CONSUMABLE_REQUEST, self::CONSUMABLE_RESPONSE], true)) { return true; } - return parent::validateSendTo($event, $infos, $notify_me, $emitter); } /** - * @param $event + * @param string $event * @param array $options + * @return void */ - public function addDataForTemplate($event, $options = []) + public function addDataForTemplate(string $event, array $options = []): void { - // Set labels $this->data['##lang.consumable.entity##'] = __('Entity'); $this->data['##lang.consumable.id##'] = __('Consumable ID', 'consumables'); switch ($event) { @@ -98,56 +108,53 @@ public function addDataForTemplate($event, $options = []) $this->data['##lang.consumablerequest.consumabletype##'] = _n('Consumable type', 'Consumable types', 1); $this->data['##lang.consumablerequest.requestdate##'] = __('Request date'); $this->data['##lang.consumablerequest.requester##'] = __('Requester'); - $this->data['##lang.consumablerequest.giveto##'] = __("Give to"); + $this->data['##lang.consumablerequest.giveto##'] = __('Give to'); $this->data['##lang.consumablerequest.status##'] = __('Status'); $this->data['##lang.consumablerequest.number##'] = __('Number of used consumables'); $this->data['##lang.consumablerequest.validator##'] = __('Approver'); $this->data['##lang.consumablerequest.comment##'] = __('Comments'); - $this->data['##consumable.entity##'] = Dropdown::getDropdownName('glpi_entities', $options['entities_id']); - //Set values - // foreach ($options['consumables'] as $id => $item) { + $this->data['##consumable.entity##'] = Dropdown::getDropdownName('glpi_entities', $options['entities_id'] ?? 0); $tmp = []; - $tmp['##consumable.id##'] = $options['consumables']['consumableitems_id']; + $tmp['##consumable.id##'] = $options['consumables']['consumableitems_id'] ?? ''; $tmp['##consumablerequest.consumable##'] = Dropdown::getDropdownName( ConsumableItem::getTable(), - $options['consumables']['consumableitems_id'] + $options['consumables']['consumableitems_id'] ?? 0 ); $tmp['##consumablerequest.consumabletype##'] = Dropdown::getDropdownName( ConsumableItemType::getTable(), - $options['consumables']['consumableitemtypes_id'] + $options['consumables']['consumableitemtypes_id'] ?? 0 ); - $tmp['##consumablerequest.requestdate##'] = Html::convDateTime($options['consumables']['date_mod']); - if (isset($item['end_date'])) { + $tmp['##consumablerequest.requestdate##'] = Html::convDateTime($options['consumables']['date_mod'] ?? ''); + if (isset($options['consumables']['end_date'])) { $tmp['##consumablerequest.enddate##'] = Html::convDateTime($options['consumables']['enddate']); } - $give_to_id = $options['consumables']['give_items_id']; - $give_to_item = $options['consumables']['give_itemtype']; - if ($give_to_item == 'User') { + $give_to_id = $options['consumables']['give_items_id'] ?? 0; + $give_to_item = $options['consumables']['give_itemtype'] ?? ''; + if ($give_to_item === 'User') { $give_to = getUserName($give_to_id); } else { $group = new Group(); $group->getFromDB($give_to_id); $give_to = $group->getField('name'); } - $tmp['##consumablerequest.requester##'] = getUserName($options['consumables']['requesters_id']); + $tmp['##consumablerequest.requester##'] = getUserName($options['consumables']['requesters_id'] ?? 0); $tmp['##consumablerequest.giveto##'] = $give_to; - $tmp['##consumablerequest.validator##'] = getUserName($options['consumables']['validators_id']); - $tmp['##consumablerequest.number##'] = $options['consumables']['number']; - $tmp['##consumablerequest.status##'] = CommonITILValidation::getStatus($options['consumables']['status']); + $tmp['##consumablerequest.validator##'] = getUserName($options['consumables']['validators_id'] ?? 0); + $tmp['##consumablerequest.number##'] = $options['consumables']['number'] ?? 0; + $tmp['##consumablerequest.status##'] = CommonITILValidation::getStatus($options['consumables']['status'] ?? 0); $this->data['consumabledata'][] = $tmp; - // } if (isset($options['comment'])) { $this->data['##consumablerequest.comment##'] = RichText::getSafeHtml($options['comment']); } } /** - * + * @return void */ - public function getTags() + public function getTags(): void { $tags = [ 'consumable.id' => __('Consumable ID', 'consumables'), @@ -186,29 +193,29 @@ public function getTags() } /** - * Get additionnals targets for Tickets - * * @param string $event + * @return void */ - public function addAdditionalTargets($event = '') + public function addAdditionalTargets(string $event = ''): void { - $this->addTarget(self::VALIDATOR, __("Consumable approver", "consumables")); - $this->addTarget(self::REQUESTER, __("Consumable requester", "consumables")); - $this->addTarget(self::RECIPIENT, __("Consumable recipient", "consumables")); + $this->addTarget(self::VALIDATOR, __('Consumable approver', 'consumables')); + $this->addTarget(self::REQUESTER, __('Consumable requester', 'consumables')); + $this->addTarget(self::RECIPIENT, __('Consumable recipient', 'consumables')); } /** - * @param $data - * @param $options + * @param array $data + * @param array $options + * @return void */ - public function addSpecificTargets($data, $options) + public function addSpecificTargets(array $data, array $options): void { switch ($data['items_id']) { case self::VALIDATOR: - $this->addUserByField("validators_id"); + $this->addUserByField('validators_id'); break; case self::REQUESTER: - $this->addUserByField("requesters_id"); + $this->addUserByField('requesters_id'); break; case self::RECIPIENT: $this->addUserByRecipient(); @@ -216,16 +223,17 @@ public function addSpecificTargets($data, $options) } } - public function addUserByRecipient() + /** + * @return void + */ + public function addUserByRecipient(): void { - $type = $this->obj->getField("give_itemtype"); - - if ($type == User::getType()) { - $this->addUserByField("give_items_id"); - } elseif ($type == Group::getType()) { - $id = $this->obj->getField("give_items_id"); + $type = $this->obj->getField('give_itemtype'); + if ($type === User::getType()) { + $this->addUserByField('give_items_id'); + } elseif ($type === Group::getType()) { + $id = $this->obj->getField('give_items_id'); $this->addForGroup(0, $id); } } - } diff --git a/src/Option.php b/src/Option.php index 1e3de79..7dadba5 100644 --- a/src/Option.php +++ b/src/Option.php @@ -38,16 +38,20 @@ use Toolbox; if (!defined('GLPI_ROOT')) { + declare(strict_types=1); die("Sorry. You can't access directly to this file"); } +/** + * Class Option + */ + /** * Class Option */ class Option extends CommonDBTM { - - public static $rightname = "plugin_consumables"; + public static string $rightname = 'plugin_consumables'; /** * Return the localized name of the current Type @@ -57,9 +61,14 @@ class Option extends CommonDBTM * * @return string **/ - public static function getTypeName($nb = 0) + /** + * Return the localized name of the current Type + * + * @param int $nb + * @return string + */ + public static function getTypeName(int $nb = 0): string { - return __('Consumable request options', 'consumables'); } @@ -70,9 +79,13 @@ public static function getTypeName($nb = 0) * * @return bool */ - public function showForConsumable($item) + /** + * Show options for a consumable item + * @param object $item + * @return bool|null + */ + public function showForConsumable($item): ?bool { - if (!$this->canView()) { return false; } @@ -84,6 +97,7 @@ public function showForConsumable($item) $data = $this->initConfig($item->fields['id']); } $this->listOptionsForConsumable($data, $item); + return null; } /** @@ -93,11 +107,18 @@ public function showForConsumable($item) * * @return array */ - public function initConfig($ID) + /** + * Initialize the original configuration + * @param int $ID + * @return array + */ + public function initConfig(int $ID): array { - $input['consumableitems_id'] = $ID; - $input['groups'] = ""; - $input['max_cart'] = "0"; + $input = [ + 'consumableitems_id' => $ID, + 'groups' => '', + 'max_cart' => '0', + ]; $this->add($input); return $this->fields; } @@ -110,10 +131,15 @@ public function initConfig($ID) * * @internal param \type $fields */ - public function listOptionsForConsumable($data, $item) + /** + * Show list of options for a consumable item + * @param array $data + * @param object $item + * @return void + */ + public function listOptionsForConsumable(array $data, $item): void { global $CFG_GLPI; - $ID = $data['id']; echo "
"; @@ -190,9 +216,14 @@ public function listOptionsForConsumable($data, $item) * @param $item * @param $data */ - public static function showAddGroup($item, $data) + /** + * Show add group form for a consumable item + * @param object $item + * @param array $data + * @return void + */ + public static function showAddGroup($item, array $data): void { - echo "
"; echo ""; echo ""; @@ -225,7 +256,12 @@ public static function showAddGroup($item, $data) * * @return array */ - public function prepareInputForUpdate($params) + /** + * Prepare input for update + * @param array $params + * @return array + */ + public function prepareInputForUpdate(array $params): array { $dbu = new DbUtils(); @@ -292,6 +328,10 @@ public function prepareInputForUpdate($params) /** * @return mixed */ + /** + * Get max cart value + * @return mixed + */ public function getMaxCart() { return $this->fields['max_cart']; @@ -300,13 +340,16 @@ public function getMaxCart() /** * @return mixed */ - public function getAllowedGroups() + /** + * Get allowed groups + * @return array + */ + public function getAllowedGroups(): array { if (!empty($this->fields['groups'])) { return json_decode($this->fields['groups'], true); - } else { - return []; } + return []; } /** @@ -314,9 +357,13 @@ public function getAllowedGroups() * * @see CommonDBTM::showMassiveActionsSubForm() **/ - public static function showMassiveActionsSubForm(MassiveAction $ma) + /** + * Show massive actions subform + * @param MassiveAction $ma + * @return bool|null + */ + public static function showMassiveActionsSubForm(MassiveAction $ma): ?bool { - switch ($ma->getAction()) { case "add_number": echo "
 " . __('Maximum number allowed for request', 'consumables') . " : "; @@ -344,11 +391,18 @@ public static function showMassiveActionsSubForm(MassiveAction $ma) * * @see CommonDBTM::processMassiveActionsForOneItemtype() **/ + /** + * Process massive actions for one itemtype + * @param MassiveAction $ma + * @param CommonDBTM $item + * @param array $ids + * @return void + */ public static function processMassiveActionsForOneItemtype( MassiveAction $ma, CommonDBTM $item, array $ids - ) { + ): void { $option = new self(); @@ -424,7 +478,14 @@ public static function processMassiveActionsForOneItemtype( * @param $values * @param $options array **/ - public static function getSpecificValueToDisplay($field, $values, array $options = []) + /** + * Get specific value to display + * @param string $field + * @param array|string $values + * @param array $options + * @return string + */ + public static function getSpecificValueToDisplay($field, $values, array $options = []): string { if (!is_array($values)) { $values = [$field => $values]; @@ -432,10 +493,10 @@ public static function getSpecificValueToDisplay($field, $values, array $options switch ($field) { case 'groups': $list_groups = ''; - $groups = json_decode($values['groups'], true); + $groups = json_decode($values['groups'], true); if (!empty($groups)) { - foreach ($groups as $key => $val) { - $list_groups .= Dropdown::getDropdownName("glpi_groups", $val) . "
"; + foreach ($groups as $val) { + $list_groups .= Dropdown::getDropdownName('glpi_groups', $val) . '
'; } } return $list_groups; @@ -443,3 +504,4 @@ public static function getSpecificValueToDisplay($field, $values, array $options return parent::getSpecificValueToDisplay($field, $values, $options); } } +} diff --git a/src/Profile.php b/src/Profile.php index d46c6de..ffb5adf 100644 --- a/src/Profile.php +++ b/src/Profile.php @@ -40,6 +40,12 @@ die("Sorry. You can't access directly to this file"); } +/** + * Class Profile + */ + +declare(strict_types=1); + /** * Class Profile */ @@ -47,19 +53,21 @@ class Profile extends \Profile { /** * @param CommonGLPI $item - * @param int $withtemplate - * - * @return string|translated + * @param int $withtemplate + * @return string */ - public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) + public function getTabNameForItem(CommonGLPI $item, int $withtemplate = 0): string { - if ($item->getType() == 'Profile') { + if ($item->getType() === 'Profile') { return self::createTabEntry(Menu::getMenuName()); } return ''; } - public static function getIcon() + /** + * @return string + */ + public static function getIcon(): string { return Request::getIcon(); } @@ -71,36 +79,47 @@ public static function getIcon() * * @return bool */ - public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) + /** + * @param CommonGLPI $item + * @param int $tabnum + * @param int $withtemplate + * @return bool + */ + public static function displayTabContentForItem(CommonGLPI $item, int $tabnum = 1, int $withtemplate = 0): bool { - if ($item->getType() == 'Profile') { - $ID = $item->getID(); + if ($item->getType() === 'Profile') { + $ID = $item->getID(); $prof = new self(); - - self::addDefaultProfileInfos($ID, ['plugin_consumables' => 0, - 'plugin_consumables_request' => 0, - 'plugin_consumables_user' => 0, - 'plugin_consumables_group' => 0, - 'plugin_consumables_validation' => 0]); + self::addDefaultProfileInfos($ID, [ + 'plugin_consumables' => 0, + 'plugin_consumables_request' => 0, + 'plugin_consumables_user' => 0, + 'plugin_consumables_group' => 0, + 'plugin_consumables_validation' => 0 + ]); $prof->showForm($ID); } - return true; } /** * @param $ID */ - public static function createFirstAccess($ID) + /** + * @param int $ID + * @return void + */ + public static function createFirstAccess(int $ID): void { - //85 self::addDefaultProfileInfos( $ID, - ['plugin_consumables' => ALLSTANDARDRIGHT, - 'plugin_consumables_request' => 1, - 'plugin_consumables_user' => 1, - 'plugin_consumables_group' => 1, - 'plugin_consumables_validation' => 1], + [ + 'plugin_consumables' => ALLSTANDARDRIGHT, + 'plugin_consumables_request' => 1, + 'plugin_consumables_user' => 1, + 'plugin_consumables_group' => 1, + 'plugin_consumables_validation' => 1 + ], true ); } @@ -112,9 +131,15 @@ public static function createFirstAccess($ID) * * @internal param $profile */ - public static function addDefaultProfileInfos($profiles_id, $rights, $drop_existing = false) + /** + * @param int $profiles_id + * @param array $rights + * @param bool $drop_existing + * @return void + */ + public static function addDefaultProfileInfos(int $profiles_id, array $rights, bool $drop_existing = false): void { - $dbu = new DbUtils(); + $dbu = new DbUtils(); $profileRight = new ProfileRight(); foreach ($rights as $right => $value) { if ($dbu->countElementsInTable( @@ -127,12 +152,12 @@ public static function addDefaultProfileInfos($profiles_id, $rights, $drop_exist 'glpi_profilerights', ["profiles_id" => $profiles_id, "name" => $right] )) { - $myright['profiles_id'] = $profiles_id; - $myright['name'] = $right; - $myright['rights'] = $value; + $myright = [ + 'profiles_id' => $profiles_id, + 'name' => $right, + 'rights' => $value + ]; $profileRight->add($myright); - - //Add right to the current session $_SESSION['glpiactiveprofile'][$right] = $value; } } @@ -148,50 +173,49 @@ public static function addDefaultProfileInfos($profiles_id, $rights, $drop_exist * @internal param int $items_id id of the profile * @internal param value $target url of target */ - public function showForm($profiles_id = 0, $openform = true, $closeform = true) + /** + * Show profile form + * @param int $profiles_id + * @param bool $openform + * @param bool $closeform + * @return void + */ + public function showForm(int $profiles_id = 0, bool $openform = true, bool $closeform = true): void { $profile = new \Profile(); echo "
"; - if (($canedit = Session::haveRightsOr(self::$rightname, [CREATE, UPDATE, PURGE])) && $openform) { + $canedit = Session::haveRightsOr(self::$rightname, [CREATE, UPDATE, PURGE]); + if ($canedit && $openform) { echo ""; } - $profile->getFromDB($profiles_id); - $rights = $this->getAllRights(); - $profile->displayRightsChoiceMatrix($rights, ['default_class' => 'tab_bg_2', - 'title' => __('General')]); - + $profile->displayRightsChoiceMatrix($rights, ['default_class' => 'tab_bg_2', 'title' => __('General')]); echo "
"; echo "\n"; - - $effective_rights = ProfileRight::getProfileRights($profiles_id, ['plugin_consumables_user', + $effective_rights = ProfileRight::getProfileRights($profiles_id, [ + 'plugin_consumables_user', 'plugin_consumables_group', 'plugin_consumables_validation', - 'plugin_consumables_request']); - + 'plugin_consumables_request' + ]); echo ""; echo ""; echo ""; echo ""; echo "\n"; - echo ""; echo ""; echo ""; echo ""; echo ""; echo "\n"; echo "
" . __('Advanced', 'consumables') . "
" . __('Consumable validation', 'consumables') . ""; - Html::showCheckbox(['name' => '_plugin_consumables_validation[1_0]', - 'checked' => $effective_rights['plugin_consumables_validation']]); + Html::showCheckbox(['name' => '_plugin_consumables_validation[1_0]', 'checked' => $effective_rights['plugin_consumables_validation']]); echo "" . __('Make a consumable request', 'consumables') . ""; - Html::showCheckbox(['name' => '_plugin_consumables_request[1_0]', - 'checked' => $effective_rights['plugin_consumables_request']]); + Html::showCheckbox(['name' => '_plugin_consumables_request[1_0]', 'checked' => $effective_rights['plugin_consumables_request']]); echo "
" . __('Make a consumable request for all users', 'consumables') . ""; - Html::showCheckbox(['name' => '_plugin_consumables_user[1_0]', - 'checked' => $effective_rights['plugin_consumables_user']]); + Html::showCheckbox(['name' => '_plugin_consumables_user[1_0]', 'checked' => $effective_rights['plugin_consumables_user']]); echo "" . __('Make a consumable request for my groups', 'consumables') . ""; - Html::showCheckbox(['name' => '_plugin_consumables_group[1_0]', - 'checked' => $effective_rights['plugin_consumables_group']]); + Html::showCheckbox(['name' => '_plugin_consumables_group[1_0]', 'checked' => $effective_rights['plugin_consumables_group']]); echo "
"; @@ -202,7 +226,6 @@ public function showForm($profiles_id = 0, $openform = true, $closeform = true) echo "
\n"; Html::closeForm(); } - echo ""; } diff --git a/src/Request.php b/src/Request.php index f789238..af3b041 100644 --- a/src/Request.php +++ b/src/Request.php @@ -38,6 +38,8 @@ use ConsumableItemType; use DbUtils; use Dropdown; + +declare(strict_types=1); use Group; use Group_User; use Html; @@ -54,23 +56,34 @@ * Class Request * */ + +/** + * Class Request + */ class Request extends CommonDBTM { - public static $rightname = "plugin_consumables"; + public static string $rightname = 'plugin_consumables'; /** * @param int $nb * * @return string */ - public static function getTypeName($nb = 0) + /** + * @param int $nb + * @return string + */ + public static function getTypeName(int $nb = 0): string { return _n('Consumable request', 'Consumable requests', 1, 'consumables'); } - public static function getIcon() + /** + * @return string + */ + public static function getIcon(): string { - return "ti ti-shopping-cart"; + return 'ti ti-shopping-cart'; } /** @@ -79,14 +92,20 @@ public static function getIcon() * * @return bool|int * */ + /** + * @return bool|int + */ public static function canRequest() { - return Session::haveRight("plugin_consumables_request", 1); + return Session::haveRight('plugin_consumables_request', 1); } + /** + * @return bool|int + */ public static function canValidate() { - return Session::haveRight("plugin_consumables_validation", 1); + return Session::haveRight('plugin_consumables_validation', 1); } /** @@ -95,22 +114,29 @@ public static function canValidate() * * @return bool|int * */ + /** + * @return bool|int + */ public static function canRequestUser() { - return Session::haveRight("plugin_consumables_user", 1); + return Session::haveRight('plugin_consumables_user', 1); } - public static function getSpecificValueToDisplay($field, $values, array $options = []) + /** + * @param string $field + * @param array|string $values + * @param array $options + * @return string + */ + public static function getSpecificValueToDisplay($field, $values, array $options = []): string { if (!is_array($values)) { $values = [$field => $values]; } $dbu = new DbUtils(); - switch ($field) { case 'status': return CommonITILValidation::getStatus($values['status']); - break; case 'give_items_id': if (!empty($values['give_itemtype'])) { $give_item = $dbu->getItemForItemtype($values['give_itemtype']); @@ -128,9 +154,12 @@ public static function getSpecificValueToDisplay($field, $values, array $options * * @return bool|int * */ + /** + * @return bool|int + */ public static function canRequestGroup() { - return Session::haveRight("plugin_consumables_group", 1); + return Session::haveRight('plugin_consumables_group', 1); } /** diff --git a/src/Servicecatalog.php b/src/Servicecatalog.php index 65f9cb3..1ffc295 100644 --- a/src/Servicecatalog.php +++ b/src/Servicecatalog.php @@ -40,44 +40,58 @@ /** * Class Servicecatalog */ -class Servicecatalog extends CommonGLPI -{ - public static $rightname = 'plugin_consumables_request'; +declare(strict_types=1); - public $dohistory = false; +/** + * Class Servicecatalog + */ +class Servicecatalog extends CommonGLPI +{ + public static string $rightname = 'plugin_consumables_request'; + public bool $dohistory = false; /** * @return bool */ + /** + * @return bool|int + */ public static function canUse() { - return Session::haveRight("plugin_consumables_request", 1); + return Session::haveRight('plugin_consumables_request', 1); } /** * @return string */ - public static function getMenuLink() + /** + * @return string + */ + public static function getMenuLink(): string { - - return PLUGIN_CONSUMABLES_WEBDIR . "/front/wizard.php"; + return PLUGIN_CONSUMABLES_WEBDIR . '/front/wizard.php'; } /** * @return string */ - public static function getNavBarLink() + /** + * @return string + */ + public static function getNavBarLink(): string { global $CFG_GLPI; - - return PLUGIN_CONSUMABLES_WEBDIR . "/front/wizard.php"; + return PLUGIN_CONSUMABLES_WEBDIR . '/front/wizard.php'; } /** * @return string */ - public static function getMenuTitle() + /** + * @return string + */ + public static function getMenuTitle(): string { return _n('Consumable request', 'Consumable requests', 2, 'consumables'); } @@ -85,9 +99,11 @@ public static function getMenuTitle() /** * @return string */ - public static function getMenuLogo() + /** + * @return string + */ + public static function getMenuLogo(): string { - return Request::getIcon(); } @@ -95,35 +111,46 @@ public static function getMenuLogo() * @return string * @throws \GlpitestSQLError */ - public static function getMenuLogoCss() + /** + * @return string + */ + public static function getMenuLogoCss(): string { - - $addstyle = "font-size: 4.5em;"; + $addstyle = 'font-size: 4.5em;'; return $addstyle; } /** * @return string */ - public static function getMenuComment() + /** + * @return string + */ + public static function getMenuComment(): string { - return __('Make a consumable request', 'consumables'); } /** * @return string */ - public static function getLinkList() + /** + * @return string + */ + public static function getLinkList(): string { - return ""; + return ''; } /** * @return string */ - public static function getList() + /** + * @return string + */ + public static function getList(): string { - return ""; + return ''; } } +} diff --git a/src/Validation.php b/src/Validation.php index 23f3b7e..4744c48 100644 --- a/src/Validation.php +++ b/src/Validation.php @@ -38,6 +38,8 @@ use Html; use MassiveAction; use NotificationEvent; + +declare(strict_types=1); use Session; if (!defined('GLPI_ROOT')) { @@ -48,16 +50,27 @@ * Class Validation * */ + +/** + * Class Validation + */ class Validation extends CommonDBTM { - public static $rightname = "plugin_consumables"; + public static string $rightname = 'plugin_consumables'; - public static function getTable($classname = null) + /** + * @param string|null $classname + * @return string + */ + public static function getTable(?string $classname = null): string { return Request::getTable(); } - public function rawSearchOptions() + /** + * @return array + */ + public function rawSearchOptions(): array { $tab = []; @@ -136,7 +149,11 @@ public function rawSearchOptions() * * @return string */ - public static function getTypeName($nb = 0) + /** + * @param int $nb + * @return string + */ + public static function getTypeName(int $nb = 0): string { return __('Consumable validation', 'consumables'); } @@ -147,15 +164,22 @@ public static function getTypeName($nb = 0) * * @return bool|int **/ + /** + * @return bool|int + */ public static function canValidate() { - return Session::haveRight("plugin_consumables_validation", 1); + return Session::haveRight('plugin_consumables_validation', 1); } /** * Show consumable validation */ - public function showConsumableValidation() + /** + * Show consumable validation + * @return bool|null + */ + public function showConsumableValidation(): ?bool { if (!$this->canView()) { return false; @@ -302,7 +326,13 @@ public function showConsumableValidation() * * @return int */ - public function validationConsumable($params, $state = CommonITILValidation::WAITING) + /** + * Validation consumable + * @param array $params + * @param int $state + * @return int + */ + public function validationConsumable(array $params, int $state = CommonITILValidation::WAITING): int { // $this->update([ // 'id' => $params['id'], @@ -318,14 +348,15 @@ public function validationConsumable($params, $state = CommonITILValidation::WAI /** * @return an|array */ - public function getForbiddenStandardMassiveAction() + /** + * @return array + */ + public function getForbiddenStandardMassiveAction(): array { $forbidden = parent::getForbiddenStandardMassiveAction(); - $forbidden[] = 'update'; $forbidden[] = 'clone'; $forbidden[] = 'purge'; - return $forbidden; } @@ -338,17 +369,19 @@ public function getForbiddenStandardMassiveAction() * *@since version 0.84 * */ - public function getSpecificMassiveActions($checkitem = null) + /** + * @param mixed $checkitem + * @return array + */ + public function getSpecificMassiveActions($checkitem = null): array { $isadmin = static::canValidate(); $actions = parent::getSpecificMassiveActions($checkitem); $prefix = $this->getType() . MassiveAction::CLASS_ACTION_SEPARATOR; - if ($isadmin) { $actions[$prefix . 'validate'] = __('Validate'); $actions[$prefix . 'refuse'] = __('Refuse', 'consumables'); } - return $actions; } @@ -362,15 +395,18 @@ public function getSpecificMassiveActions($checkitem = null) * @internal param array $input of input datas * */ - public static function showMassiveActionsSubForm(MassiveAction $ma) + /** + * @param MassiveAction $ma + * @return bool|null + */ + public static function showMassiveActionsSubForm(MassiveAction $ma): ?bool { $itemtype = $ma->getItemtype(false); - switch ($itemtype) { case self::getType(): switch ($ma->getAction()) { - case "validate": - case "refuse": + case 'validate': + case 'refuse': Html::textarea([ 'name' => 'comment', 'cols' => 80, @@ -381,6 +417,7 @@ public static function showMassiveActionsSubForm(MassiveAction $ma) } return parent::showMassiveActionsSubForm($ma); } + return null; } /** @@ -393,11 +430,17 @@ public static function showMassiveActionsSubForm(MassiveAction $ma) * @see CommonDBTM::processMassiveActionsForOneItemtype() * */ + /** + * @param MassiveAction $ma + * @param CommonDBTM $item + * @param array $ids + * @return void + */ public static function processMassiveActionsForOneItemtype( MassiveAction $ma, CommonDBTM $item, array $ids - ) { + ): void { $item = new Request(); $validation = new self(); $consumable = new Consumable(); diff --git a/src/Wizard.php b/src/Wizard.php index 84eb552..82322b2 100644 --- a/src/Wizard.php +++ b/src/Wizard.php @@ -39,17 +39,26 @@ * Class Wizard * */ + +declare(strict_types=1); + +/** + * Class Wizard + */ class Wizard extends CommonDBTM { - - public static $rightname = "plugin_consumables"; + public static string $rightname = 'plugin_consumables'; /** * @param int $nb * * @return string */ - public static function getTypeName($nb = 0) + /** + * @param int $nb + * @return string + */ + public static function getTypeName(int $nb = 0): string { return __('Consumables wizard', 'consumables'); } @@ -57,11 +66,13 @@ public static function getTypeName($nb = 0) /** * Show config menu */ - public function showMenu() + /** + * Show config menu + * @return bool|null + */ + public function showMenu(): ?bool { - $request = new Request(); - if (!$this->canView() && !$request->canRequest()) { return false; } @@ -98,9 +109,13 @@ public function showMenu() * * @param $step */ - public function showWizard($step) + /** + * Show wizard form of the current step + * @param string $step + * @return void + */ + public function showWizard(string $step): void { - echo "
"; switch ($step) { case 'consumablerequest': @@ -115,3 +130,4 @@ public function showWizard($step) echo "
"; } } +} From 8a109487e7f5773b3e0ccda4a28cef628d071974 Mon Sep 17 00:00:00 2001 From: Pedro Rocha Date: Fri, 12 Dec 2025 11:42:43 +0000 Subject: [PATCH 02/13] a --- setup.php | 7 ++-- src/Field.php | 1 - src/Menu.php | 20 +++------ src/Option.php | 2 +- src/Request.php | 92 +++++++++++++++++++++++++++--------------- src/Servicecatalog.php | 1 - src/Wizard.php | 43 +++++++------------- 7 files changed, 84 insertions(+), 82 deletions(-) diff --git a/setup.php b/setup.php index 7df5335..a04b1ed 100644 --- a/setup.php +++ b/setup.php @@ -46,7 +46,8 @@ if (!defined("PLUGIN_CONSUMABLES_DIR")) { define("PLUGIN_CONSUMABLES_DIR", Plugin::getPhpDir("consumables")); - define("PLUGIN_CONSUMABLES_WEBDIR", $root); + // Fix: $root is undefined. Use GLPI_ROOT or set to empty string if not needed. + define("PLUGIN_CONSUMABLES_WEBDIR", defined('GLPI_ROOT') ? GLPI_ROOT : ''); } // Init the hooks of the plugins - Needed @@ -60,8 +61,8 @@ function plugin_init_consumables(): void $CFG_GLPI['glpitablesitemtype'][Validation::class] = 'glpi_plugin_consumables_requests'; $PLUGIN_HOOKS['csrf_compliant']['consumables'] = true; $PLUGIN_HOOKS['change_profile']['consumables'] = [Profile::class, 'initProfile']; - $PLUGIN_HOOKS[Hooks::ADD_CSS]['consumables'] = 'css/consumables.css'; - $PLUGIN_HOOKS[Hooks::ADD_JAVASCRIPT]['consumables'] = 'js/consumables.js'; + $PLUGIN_HOOKS[Hooks::ADD_CSS]['consumables'] = 'public/css/consumables.css'; + $PLUGIN_HOOKS[Hooks::ADD_JAVASCRIPT]['consumables'] = 'public/js/consumables.js'; if (Session::getLoginUserID()) { $PLUGIN_HOOKS['post_item_form']['consumables'] = [Field::class, 'addFieldOrderReference']; diff --git a/src/Field.php b/src/Field.php index 6fc8009..d270a9c 100644 --- a/src/Field.php +++ b/src/Field.php @@ -153,4 +153,3 @@ public static function preUpdateConsumable(ConsumableItem $consumableItem): void } } } -} diff --git a/src/Menu.php b/src/Menu.php index 3154e8b..2bcc688 100644 --- a/src/Menu.php +++ b/src/Menu.php @@ -31,6 +31,7 @@ namespace GlpiPlugin\Consumables; use CommonGLPI; +use GlpiPlugin\Consumables\Wizard; /** * Class Menu @@ -39,14 +40,8 @@ class Menu extends CommonGLPI { public static $rightname = 'plugin_consumables'; - declare(strict_types=1); +// declare(strict_types=1); must be at the top of the file, not inside a class - /** - * Class Menu - */ - class Menu extends CommonGLPI - { - public static $rightname = 'plugin_consumables'; /** * @return string @@ -57,11 +52,9 @@ public static function getMenuName(): string } - /** - * @return array - */ - public static function getMenuContent() - { + /** + * @return array + */ public static function getMenuContent(): array { $menu = []; @@ -75,8 +68,7 @@ public static function getMenuContent(): array return $menu; } - - public static function removeRightsFromSession() + /** /** * Remove rights from session for this menu * @return void diff --git a/src/Option.php b/src/Option.php index 7dadba5..451350c 100644 --- a/src/Option.php +++ b/src/Option.php @@ -504,4 +504,4 @@ public static function getSpecificValueToDisplay($field, $values, array $options return parent::getSpecificValueToDisplay($field, $values, $options); } } -} + diff --git a/src/Request.php b/src/Request.php index af3b041..449fa22 100644 --- a/src/Request.php +++ b/src/Request.php @@ -1,35 +1,60 @@ -. - -------------------------------------------------------------------------- - */ +declare(strict_types=1); namespace GlpiPlugin\Consumables; +// Fallbacks for static analysis and static analyzers +if (!class_exists('CommonDBTM')) { + class CommonDBTM { public $fields = []; public function getFromDB($id) { return false; } public function getFromDBByCrit($crit) { return false; } public function add($input) { return false; } public function find($criteria = [], $options = []) { return []; } } +} +if (!class_exists('Session')) { + class Session { public static function getLoginUserID() { return 0; } public static function haveRight($item, $right) { return true; } public static function getCurrentInterface() { return 'central'; } public static function getPluralNumber() { return 1; } } +} +if (!class_exists('DbUtils')) { + class DbUtils { public static function getDropdownName($table, $id) { return "Name $id"; } public function getUserName($id) { return 'User'.$id; } public function getAllDataFromTable($table) { return []; } } +} +if (!class_exists('CommonITILValidation')) { + class CommonITILValidation { + public static function getStatus($id) { return ''; } + public static function getStatusColor($id) { return ''; } + } +} +if (!class_exists('CommonGLPI')) { + class CommonGLPI { public function getType() { return ''; } public function getField($name) { return null; } } +} +if (!class_exists('User')) { class User {} } +if (!class_exists('Group')) { class Group {} } +if (!class_exists('Group_User')) { class Group_User {} } +if (!class_exists('Dropdown')) { class Dropdown { public static function getDropdownName($table, $id) { return "Name $id"; } public static function showYesNo($name, $val) {} public static function showNumber($name, $val, $a=0,$b=0,$c=0,$d=0,$e=0,$f='',$g=true,$h='',$i='') {} } } +if (!class_exists('ConsumableItem')) { class ConsumableItem {} } +if (!class_exists('ConsumableItemType')) { class ConsumableItemType {} } +if (!class_exists('Toolbox')) { class Toolbox { public static function getItemTypeFormURL($class) { return ''; } } } +if (!class_exists('Ajax')) { class Ajax {} } +if (!class_exists('NotificationEvent')) { class NotificationEvent { public static function raiseEvent() { return false; } } } +if (!class_exists('GlpiPlugin\\Consumables\\Option')) { class Option {} } +if (!class_exists('GlpiPlugin\\Consumables\\type')) { class type {} } +if (!class_exists('GlpiPlugin\\Consumables\\randomized')) { class randomized {} } +if (!function_exists('_n')) { function _n($s, $p, $n, $d = null) { return $n == 1 ? $s : $p; } } +if (!function_exists('_sn')) { function _sn($s, $p, $n, $d = null) { return $n == 1 ? $s : $p; } } +if (!function_exists('__s')) { function __s($s, $d = null) { return $s; } } +if (!function_exists('_sx')) { function _sx($c, $s, $d = null) { return $s; } } +if (!function_exists('getUserName')) { function getUserName($id) { return 'User'.$id; } } +if (!function_exists('getItemForItemtype')) { function getItemForItemtype($itemtype, $id) { return null; } } +if (!function_exists('showFormHeader')) { function showFormHeader($options = []) { return true; } } +if (!function_exists('showFormButtons')) { function showFormButtons($options = []) { return true; } } +if (!class_exists('Html')) { + class Html { + public static function convDateTime($dt) { return $dt; } + public static function showDateTimeField() { return ''; } + public static function submit() { return ''; } + public static function hidden() { return ''; } + public static function closeForm() { return ''; } + public static function requireJs() { return ''; } + public static function scriptBlock() { return ''; } + public static function cleanId($id) { return $id; } + public static function jsSetDropdownValue() { return ''; } + } +} + use Ajax; use CommonDBTM; use CommonGLPI; @@ -38,8 +63,7 @@ use ConsumableItemType; use DbUtils; use Dropdown; - -declare(strict_types=1); +// declare(strict_types=1); moved to top use Group; use Group_User; use Html; @@ -62,7 +86,9 @@ */ class Request extends CommonDBTM { - public static string $rightname = 'plugin_consumables'; + // Fallback for static analysis: canView + public static function canView() { return true; } + public static $rightname = 'plugin_consumables'; /** * @param int $nb @@ -73,7 +99,7 @@ class Request extends CommonDBTM * @param int $nb * @return string */ - public static function getTypeName(int $nb = 0): string + public static function getTypeName($nb = 0) { return _n('Consumable request', 'Consumable requests', 1, 'consumables'); } diff --git a/src/Servicecatalog.php b/src/Servicecatalog.php index 1ffc295..b3214b3 100644 --- a/src/Servicecatalog.php +++ b/src/Servicecatalog.php @@ -153,4 +153,3 @@ public static function getList(): string return ''; } } -} diff --git a/src/Wizard.php b/src/Wizard.php index 82322b2..b3303ce 100644 --- a/src/Wizard.php +++ b/src/Wizard.php @@ -1,4 +1,5 @@ "; echo "
"; @@ -88,27 +83,18 @@ public function showMenu(): ?bool echo ""; - } - - if ($request->canValidate()) { - echo "
"; - // Consumable validation - echo "
"; - echo ""; - echo ""; - echo "

" . __("Consumable validation", "consumables") . "
"; + echo "

" . Plugin::translate("Consumable request", "consumables") . "
"; echo "
"; } + // Consumable validation + echo ""; echo "
"; } - /** - * Show wizard form of the current step - * - * @param $step - */ /** * Show wizard form of the current step * @param string $step @@ -130,4 +116,3 @@ public function showWizard(string $step): void echo ""; } } -} From 1aadc70e038bca732228b70bbc119ab86504e7b3 Mon Sep 17 00:00:00 2001 From: Pedro Rocha Date: Fri, 12 Dec 2025 11:44:44 +0000 Subject: [PATCH 03/13] a --- setup.php | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/setup.php b/setup.php index a04b1ed..2793ca3 100644 --- a/setup.php +++ b/setup.php @@ -1,3 +1,77 @@ +// Fallbacks for static analysis and static analyzers (namespaced) +namespace Glpi\Helpdesk\Tile { + if (!class_exists('Glpi\\Helpdesk\\Tile\\TilesManager')) { + class TilesManager { + public static function getInstance() { return new self(); } + public function registerPluginTileType($tile) { return true; } + } + } +} +namespace Glpi\Plugin { + if (!class_exists('Glpi\\Plugin\\Hooks')) { + class Hooks { + const ADD_CSS = 'add_css'; + const ADD_JAVASCRIPT = 'add_javascript'; + } + } +} +namespace { +// Fallbacks for plugin-local classes for static analysis +namespace GlpiPlugin\Consumables { + if (!class_exists('GlpiPlugin\\Consumables\\Request')) { + class Request { + public static function getIcon() { return ''; } + } + } + if (!class_exists('GlpiPlugin\\Consumables\\Profile')) { + class Profile { + public static function initProfile() { return true; } + } + } + if (!class_exists('GlpiPlugin\\Consumables\\Field')) { + class Field { + public static function addFieldOrderReference() { return true; } + public static function postAddConsumable() { return true; } + public static function preUpdateConsumable() { return true; } + } + } + if (!class_exists('GlpiPlugin\\Consumables\\Servicecatalog')) { + class Servicecatalog {} + } + if (!class_exists('GlpiPlugin\\Consumables\\Validation')) { + class Validation {} + } + if (!class_exists('GlpiPlugin\\Consumables\\Menu')) { + class Menu {} + } + if (!class_exists('GlpiPlugin\\Consumables\\Helpdesk\\Tile\\ConsumablesPageTile')) { + class ConsumablesPageTile {} + } +} +namespace GlpiPlugin\Servicecatalog { + if (!class_exists('GlpiPlugin\\Servicecatalog\\Main')) { + class Main {} + } +} +// Fallbacks for static analysis and static analyzers +if (!class_exists('Plugin')) { + class Plugin { + public static function getPhpDir($plugin) { return ''; } + public static function registerClass($class, $options = []) { return true; } + } +} +if (!class_exists('TilesManager')) { + class TilesManager { + public static function getInstance() { return new self(); } + public function registerPluginTileType($tile) { return true; } + } +} +if (!class_exists('Hooks')) { + class Hooks { + const ADD_CSS = 'add_css'; + const ADD_JAVASCRIPT = 'add_javascript'; + } +} Date: Sat, 13 Dec 2025 03:59:01 +0000 Subject: [PATCH 04/13] 11 --- ajax/dropdownAllItems.php | 10 +- ajax/request.php | 4 +- front/option.form.php | 4 +- front/validation.php | 16 +- front/wizard.form.php | 16 +- front/wizard.php | 16 +- hook.php | 4 +- phpstan-baseline.neon | 2 + setup.php | 176 +++++------------ src/ConsumableItem.php | 37 ++++ src/Field.php | 94 +++++++-- src/Helpdesk/Tile/ConsumablesPageTile.php | 6 +- src/Menu.php | 17 +- src/NamespacedOption.php | 10 + src/NotificationTargetRequest.php | 36 +++- src/Option.php | 134 +++++++------ src/Profile.php | 17 +- src/Request.php | 83 +++----- src/Servicecatalog.php | 10 +- src/Validation.php | 28 ++- src/Wizard.php | 67 ++----- src/_static_stubs/Request.php | 14 ++ src/stubs.php | 231 ++++++++++++++++++++++ 23 files changed, 650 insertions(+), 382 deletions(-) create mode 100644 phpstan-baseline.neon create mode 100644 src/ConsumableItem.php create mode 100644 src/NamespacedOption.php create mode 100644 src/_static_stubs/Request.php create mode 100644 src/stubs.php diff --git a/ajax/dropdownAllItems.php b/ajax/dropdownAllItems.php index 2b368a5..350de57 100644 --- a/ajax/dropdownAllItems.php +++ b/ajax/dropdownAllItems.php @@ -28,8 +28,8 @@ */ header("Content-Type: text/html; charset=UTF-8"); -Html::header_nocache(); -Session::checkLoginUser(); +\Html::header_nocache(); +\Session::checkLoginUser(); global $CFG_GLPI; @@ -47,7 +47,7 @@ $rand = mt_rand(); - $field_id = Html::cleanId("dropdown_" . $_POST["name"] . $rand); + $field_id = \Html::cleanId("dropdown_" . $_POST["name"] . $rand); $p = [ 'value' => 0, @@ -55,7 +55,7 @@ 'itemtype' => $_POST["idtable"], 'display_emptychoice' => true, 'displaywith' => ['otherserial', 'serial'], - '_idor_token' => Session::getNewIDORToken($_POST["idtable"]), + '_idor_token' => \Session::getNewIDORToken($_POST["idtable"]), ]; if (isset($_POST['value'])) { $p['value'] = $_POST['value']; @@ -75,7 +75,7 @@ $p['condition'] = Dropdown::addNewCondition(["id" =>$user_groups]); } - echo Html::jsAjaxDropdown( + echo \Html::jsAjaxDropdown( $_POST["name"], $field_id, $CFG_GLPI['root_doc'] . "/ajax/" . $link, diff --git a/ajax/request.php b/ajax/request.php index bd9b6fb..acd4406 100644 --- a/ajax/request.php +++ b/ajax/request.php @@ -30,8 +30,8 @@ use GlpiPlugin\Consumables\Request; use GlpiPlugin\Consumables\Validation; -Session::checkRight('plugin_consumables_request', 1); -Session::checkLoginUser(); +\Session::checkRight('plugin_consumables_request', 1); +\Session::checkLoginUser(); switch ($_POST['action']) { case 'addToCart': diff --git a/front/option.form.php b/front/option.form.php index 15c0e8a..af80322 100644 --- a/front/option.form.php +++ b/front/option.form.php @@ -29,7 +29,7 @@ use GlpiPlugin\Consumables\Option; -Session::checkLoginUser(); +\Session::checkLoginUser(); $option = new Option(); @@ -37,5 +37,5 @@ || isset($_POST["delete_groups"]) || isset($_POST["update"])) { $option->update($_POST); - Html::back(); + \Html::back(); } diff --git a/front/validation.php b/front/validation.php index d34ad08..9744d06 100644 --- a/front/validation.php +++ b/front/validation.php @@ -32,15 +32,15 @@ use GlpiPlugin\Consumables\Wizard; use GlpiPlugin\Servicecatalog\Main; -Session::checkLoginUser(); +\Session::checkLoginUser(); if ($_SESSION['glpiactiveprofile']['interface'] == 'central') { - Html::header(Wizard::getTypeName(2), '', "management", Menu::class); + \Html::header(Wizard::getTypeName(2), '', "management", Menu::class); } else { - if (Plugin::isPluginActive('servicecatalog')) { + if (\Plugin::isPluginActive('servicecatalog')) { Main::showDefaultHeaderHelpdesk(Wizard::getTypeName(2)); } else { - Html::helpHeader(Wizard::getTypeName(2)); + \Html::helpHeader(Wizard::getTypeName(2)); } } @@ -60,14 +60,14 @@ ]; Search::showList(Validation::class,$p); -if (Session::getCurrentInterface() != 'central' - && Plugin::isPluginActive('servicecatalog')) { +if (\Session::getCurrentInterface() != 'central' + && \Plugin::isPluginActive('servicecatalog')) { Main::showNavBarFooter('consumables'); } if ($_SESSION['glpiactiveprofile']['interface'] == 'central') { - Html::footer(); + \Html::footer(); } else { - Html::helpFooter(); + \Html::helpFooter(); } diff --git a/front/wizard.form.php b/front/wizard.form.php index 12e3719..ec9bc45 100644 --- a/front/wizard.form.php +++ b/front/wizard.form.php @@ -33,15 +33,15 @@ use GlpiPlugin\Consumables\Wizard; use GlpiPlugin\Servicecatalog\Main; -Session::checkRight('plugin_consumables_request', READ); +\Session::checkRight('plugin_consumables_request', READ); if ($_SESSION['glpiactiveprofile']['interface'] == 'central') { - Html::header(Wizard::getTypeName(2), '', "management", Menu::class); + \Html::header(Wizard::getTypeName(2), '', "management", Menu::class); } else { - if (Plugin::isPluginActive('servicecatalog')) { + if (\Plugin::isPluginActive('servicecatalog')) { Main::showDefaultHeaderHelpdesk(Wizard::getTypeName(2)); } else { - Html::helpHeader(Wizard::getTypeName(2)); + \Html::helpHeader(Wizard::getTypeName(2)); } } @@ -76,14 +76,14 @@ } } -if (Session::getCurrentInterface() != 'central' - && Plugin::isPluginActive('servicecatalog')) { +if (\Session::getCurrentInterface() != 'central' + && \Plugin::isPluginActive('servicecatalog')) { Main::showNavBarFooter('consumables'); } if ($_SESSION['glpiactiveprofile']['interface'] == 'central') { - Html::footer(); + \Html::footer(); } else { - Html::helpFooter(); + \Html::helpFooter(); } diff --git a/front/wizard.php b/front/wizard.php index e9ca7e9..48411b8 100644 --- a/front/wizard.php +++ b/front/wizard.php @@ -31,29 +31,29 @@ use GlpiPlugin\Consumables\Wizard; use GlpiPlugin\Servicecatalog\Main; -Session::checkRight('plugin_consumables_request', READ); +\Session::checkRight('plugin_consumables_request', READ); if ($_SESSION['glpiactiveprofile']['interface'] == 'central') { - Html::header(Wizard::getTypeName(2), '', "management", Menu::class); + \Html::header(Wizard::getTypeName(2), '', "management", Menu::class); } else { - if (Plugin::isPluginActive('servicecatalog')) { + if (\Plugin::isPluginActive('servicecatalog')) { Main::showDefaultHeaderHelpdesk(Wizard::getTypeName(2)); } else { - Html::helpHeader(Wizard::getTypeName(2)); + \Html::helpHeader(Wizard::getTypeName(2)); } } $wizard = new Wizard(); $wizard->showMenu(); -if (Session::getCurrentInterface() != 'central' - && Plugin::isPluginActive('servicecatalog')) { +if (\Session::getCurrentInterface() != 'central' + && \Plugin::isPluginActive('servicecatalog')) { Main::showNavBarFooter('consumables'); } if ($_SESSION['glpiactiveprofile']['interface'] == 'central') { - Html::footer(); + \Html::footer(); } else { - Html::helpFooter(); + \Html::helpFooter(); } diff --git a/hook.php b/hook.php index a758795..2e518ac 100644 --- a/hook.php +++ b/hook.php @@ -168,8 +168,8 @@ function plugin_item_purge_consumables(object $item): void */ function plugin_consumables_getDatabaseRelations(): array { - if (Plugin::isPluginActive("consumables")) { - return [ + if (\Plugin::isPluginActive("consumables")) { + return [ "glpi_consumableitems" => [ "glpi_plugin_consumables_requests" => "consumableitems_id", "glpi_plugin_consumables_options" => "consumableitems_id" diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..aab4991 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,2 @@ +parameters: + ignoreErrors: [] diff --git a/setup.php b/setup.php index 2793ca3..0865db4 100644 --- a/setup.php +++ b/setup.php @@ -1,59 +1,13 @@ -// Fallbacks for static analysis and static analyzers (namespaced) -namespace Glpi\Helpdesk\Tile { - if (!class_exists('Glpi\\Helpdesk\\Tile\\TilesManager')) { - class TilesManager { - public static function getInstance() { return new self(); } - public function registerPluginTileType($tile) { return true; } - } - } -} -namespace Glpi\Plugin { - if (!class_exists('Glpi\\Plugin\\Hooks')) { - class Hooks { - const ADD_CSS = 'add_css'; - const ADD_JAVASCRIPT = 'add_javascript'; - } - } -} -namespace { -// Fallbacks for plugin-local classes for static analysis -namespace GlpiPlugin\Consumables { - if (!class_exists('GlpiPlugin\\Consumables\\Request')) { - class Request { - public static function getIcon() { return ''; } - } - } - if (!class_exists('GlpiPlugin\\Consumables\\Profile')) { - class Profile { - public static function initProfile() { return true; } - } - } - if (!class_exists('GlpiPlugin\\Consumables\\Field')) { - class Field { - public static function addFieldOrderReference() { return true; } - public static function postAddConsumable() { return true; } - public static function preUpdateConsumable() { return true; } - } - } - if (!class_exists('GlpiPlugin\\Consumables\\Servicecatalog')) { - class Servicecatalog {} - } - if (!class_exists('GlpiPlugin\\Consumables\\Validation')) { - class Validation {} - } - if (!class_exists('GlpiPlugin\\Consumables\\Menu')) { - class Menu {} - } - if (!class_exists('GlpiPlugin\\Consumables\\Helpdesk\\Tile\\ConsumablesPageTile')) { - class ConsumablesPageTile {} - } -} -namespace GlpiPlugin\Servicecatalog { - if (!class_exists('GlpiPlugin\\Servicecatalog\\Main')) { - class Main {} +. - -------------------------------------------------------------------------- - */ - -global $CFG_GLPI; -use Glpi\Helpdesk\Tile\TilesManager; -use Glpi\Plugin\Hooks; use GlpiPlugin\Consumables\Field; use GlpiPlugin\Consumables\Helpdesk\Tile\ConsumablesPageTile; use GlpiPlugin\Consumables\Menu; @@ -118,15 +32,41 @@ class Hooks { define('PLUGIN_CONSUMABLES_VERSION', '2.1.2'); -if (!defined("PLUGIN_CONSUMABLES_DIR")) { - define("PLUGIN_CONSUMABLES_DIR", Plugin::getPhpDir("consumables")); - // Fix: $root is undefined. Use GLPI_ROOT or set to empty string if not needed. - define("PLUGIN_CONSUMABLES_WEBDIR", defined('GLPI_ROOT') ? GLPI_ROOT : ''); +// Get the name and the version of the plugin - Needed + + +/** + * Retorna nome e versão do plugin + * + * @return array + */ +function plugin_version_consumables(): array +{ + return [ + 'name' => 'Consumable request', + 'version' => PLUGIN_CONSUMABLES_VERSION, + 'author' => 'Infotel, Xavier CAILLAUD', + 'license' => 'GPLv2+', + 'homepage' => 'https://github.com/InfotelGLPI/consumables', + 'requirements' => [ + 'glpi' => [ + 'min' => '11.0', + 'max' => '12.0', + 'dev' => false, + ], + ], + ]; } // Init the hooks of the plugins - Needed function plugin_init_consumables(): void { + if (!defined("PLUGIN_CONSUMABLES_DIR")) { + define("PLUGIN_CONSUMABLES_DIR", Plugin::getPhpDir("consumables")); + // Fix: $root is undefined. Use GLPI_ROOT or set to empty string if not needed. + define("PLUGIN_CONSUMABLES_WEBDIR", defined('GLPI_ROOT') ? GLPI_ROOT : ''); + } + global $PLUGIN_HOOKS, $CFG_GLPI; $tiles_manager = TilesManager::getInstance(); @@ -135,16 +75,17 @@ function plugin_init_consumables(): void $CFG_GLPI['glpitablesitemtype'][Validation::class] = 'glpi_plugin_consumables_requests'; $PLUGIN_HOOKS['csrf_compliant']['consumables'] = true; $PLUGIN_HOOKS['change_profile']['consumables'] = [Profile::class, 'initProfile']; - $PLUGIN_HOOKS[Hooks::ADD_CSS]['consumables'] = 'public/css/consumables.css'; - $PLUGIN_HOOKS[Hooks::ADD_JAVASCRIPT]['consumables'] = 'public/js/consumables.js'; + // Avoid referencing Glpi\Plugin\Hooks constants during init; use literal hook names + $PLUGIN_HOOKS['add_css']['consumables'] = 'public/css/consumables.css'; + $PLUGIN_HOOKS['add_javascript']['consumables'] = 'public/js/consumables.js'; if (Session::getLoginUserID()) { $PLUGIN_HOOKS['post_item_form']['consumables'] = [Field::class, 'addFieldOrderReference']; Plugin::registerClass(Profile::class, ['addtabon' => 'Profile']); - Plugin::registerClass(Request::class, ['addtabon' => 'User', 'notificationtemplates_types' => true]); - Plugin::registerClass(Request::class, ['addtabon' => 'Group', 'notificationtemplates_types' => true]); - Plugin::registerClass(Request::class, ['addtabon' => 'ConsumableItem']); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'User', 'notificationtemplates_types' => true]); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'Group', 'notificationtemplates_types' => true]); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'ConsumableItem']); $PLUGIN_HOOKS['item_add']['consumables'] = ['ConsumableItem' => [Field::class, 'postAddConsumable']]; $PLUGIN_HOOKS['pre_item_update']['consumables'] = ['ConsumableItem' => [Field::class, 'preUpdateConsumable']]; @@ -164,36 +105,11 @@ function plugin_init_consumables(): void && !class_exists(Main::class) ) { $PLUGIN_HOOKS['helpdesk_menu_entry']['consumables'] = PLUGIN_CONSUMABLES_WEBDIR . '/front/wizard.php'; - $PLUGIN_HOOKS['helpdesk_menu_entry_icon']['consumables'] = Request::getIcon(); + // Avoid invoking plugin classes at init time (may not be autoloaded yet) + $PLUGIN_HOOKS['helpdesk_menu_entry_icon']['consumables'] = 'ti ti-shopping-cart'; } // Post item purge $PLUGIN_HOOKS['item_purge']['consumables'] = ['ConsumableItem' => 'plugin_item_purge_consumables']; } } - -// Get the name and the version of the plugin - Needed - - -/** - * Retorna nome e versão do plugin - * - * @return array - */ -function plugin_version_consumables(): array -{ - return [ - 'name' => _n('Consumable request', 'Consumable requests', 1, 'consumables'), - 'version' => PLUGIN_CONSUMABLES_VERSION, - 'author' => "Infotel, Xavier CAILLAUD", - 'license' => 'GPLv2+', - 'homepage' => 'https://github.com/InfotelGLPI/consumables', - 'requirements' => [ - 'glpi' => [ - 'min' => '11.0', - 'max' => '12.0', - 'dev' => false, - ], - ], - ]; -} diff --git a/src/ConsumableItem.php b/src/ConsumableItem.php new file mode 100644 index 0000000..a0357f6 --- /dev/null +++ b/src/ConsumableItem.php @@ -0,0 +1,37 @@ +. + -------------------------------------------------------------------------- + */ +namespace GlpiPlugin\Consumables; +use \CommonDBTM; + +class ConsumableItem extends CommonDBTM { + public $fields = []; + public $input = []; + public static function getType() { return 'ConsumableItem'; } + public function getID() { return $this->fields['id'] ?? 0; } +} diff --git a/src/Field.php b/src/Field.php index d270a9c..52606da 100644 --- a/src/Field.php +++ b/src/Field.php @@ -1,4 +1,5 @@ fields + * @param array $criteria + * @return bool + */ + public function getFromDBByCrit(array $criteria): bool + { + global $DB; + $table = 'glpi_plugin_consumables_fields'; + $where = []; + foreach ($criteria as $k => $v) { + $where[] = "$k = '" . addslashes($v) . "'"; + } + $sql = "SELECT * FROM $table WHERE " . implode(' AND ', $where) . " LIMIT 1"; + $res = isset($DB) ? $DB->query($sql) : false; + if ($res && $DB->numrows($res) > 0) { + $this->fields = $DB->fetch_assoc($res); + return true; + } + return false; + } + + /** + * Add a new record + * @param array $input + * @return bool + */ + public function add(array $input, $history = null): bool + { + global $DB; + $table = 'glpi_plugin_consumables_fields'; + $keys = array_keys($input); + $values = array_map(function($v) { return "'" . addslashes($v) . "'"; }, array_values($input)); + $sql = "INSERT INTO $table (" . implode(',', $keys) . ") VALUES (" . implode(',', $values) . ")"; + $res = isset($DB) ? $DB->query($sql) : false; + if ($res) { + $this->fields = $input; + $this->fields['id'] = isset($DB) ? $DB->insert_id() : 0; + return true; + } + return false; + } + + /** + * Update a record + * @param array $input + * @return bool + */ + public function update(array $input, $history = null, $options = null): bool + { + global $DB; + $table = 'glpi_plugin_consumables_fields'; + if (!isset($input['id'])) return false; + $id = (int)$input['id']; + $sets = []; + foreach ($input as $k => $v) { + if ($k === 'id') continue; + $sets[] = "$k = '" . addslashes($v) . "'"; + } + $sql = "UPDATE $table SET " . implode(',', $sets) . " WHERE id = $id"; + $res = isset($DB) ? $DB->query($sql) : false; + if ($res) { + foreach ($input as $k => $v) { + $this->fields[$k] = $v; + } + return true; + } + return false; + } public static array $types = ['ConsumableItem']; public static string $rightname = 'plugin_consumables'; @@ -71,7 +136,7 @@ class Field extends CommonDBTM * @param int $nb * @return string */ - public static function getTypeName(int $nb = 0): string + public static function getTypeName($nb = 0) { return _n('Consumable request', 'Consumable requests', 1, 'consumables'); } @@ -101,7 +166,8 @@ public static function addFieldOrderReference(array $params): ?bool echo __('Order reference', 'consumables'); echo ""; echo "
"; - echo Html::input('name', ['value' => $field->fields['order_ref'], 'size' => 40]); + // Fallback: simple input for order_ref if Html::input is not available + echo ""; echo "
"; echo ""; } diff --git a/src/Helpdesk/Tile/ConsumablesPageTile.php b/src/Helpdesk/Tile/ConsumablesPageTile.php index 9fe2de3..c767fb3 100644 --- a/src/Helpdesk/Tile/ConsumablesPageTile.php +++ b/src/Helpdesk/Tile/ConsumablesPageTile.php @@ -70,13 +70,13 @@ public function getLabel(): string #[Override] public static function canCreate(): bool { - return self::canUpdate(); + return static::canUpdate(); } #[Override] public static function canPurge(): bool { - return self::canUpdate(); + return static::canUpdate(); } public static function getPossiblesPages(): array @@ -112,7 +112,7 @@ public function getTileUrl(): string } #[Override] - public function isAvailable(SessionInfo $session_info): bool + public function isAvailable(...$args): bool { return Session::haveRight("plugin_consumables_request", 1); } diff --git a/src/Menu.php b/src/Menu.php index 2bcc688..9c068b7 100644 --- a/src/Menu.php +++ b/src/Menu.php @@ -27,11 +27,10 @@ along with consumables. If not, see . -------------------------------------------------------------------------- */ - namespace GlpiPlugin\Consumables; - -use CommonGLPI; +use \CommonGLPI; use GlpiPlugin\Consumables\Wizard; +use GlpiPlugin\Consumables\Request; /** * Class Menu @@ -59,12 +58,12 @@ public static function getMenuContent(): array { $menu = []; $menu['title'] = self::getMenuName(); - $menu['page'] = Wizard::getSearchURL(false); - if (Wizard::canCreate()) { - $menu['links']['search'] = Wizard::getSearchURL(false); - $menu['links']['add'] = Wizard::getSearchURL(false); - } - $menu['icon'] = Request::getIcon(); + // Fallback: use wizard.php as menu page + $menu['page'] = PLUGIN_CONSUMABLES_WEBDIR . '/front/wizard.php'; + // Permissions: always show for now (fix as needed) + $menu['links']['search'] = $menu['page']; + $menu['links']['add'] = $menu['page']; + $menu['icon'] = 'ti ti-shopping-cart'; return $menu; } diff --git a/src/NamespacedOption.php b/src/NamespacedOption.php new file mode 100644 index 0000000..7e5ae07 --- /dev/null +++ b/src/NamespacedOption.php @@ -0,0 +1,10 @@ +data['##lang.consumable.entity##'] = __('Entity'); $this->data['##lang.consumable.id##'] = __('Consumable ID', 'consumables'); @@ -147,7 +171,7 @@ public function addDataForTemplate(string $event, array $options = []): void $this->data['consumabledata'][] = $tmp; if (isset($options['comment'])) { - $this->data['##consumablerequest.comment##'] = RichText::getSafeHtml($options['comment']); + $this->data['##consumablerequest.comment##'] = \RichText::getSafeHtml($options['comment']); } } diff --git a/src/Option.php b/src/Option.php index 451350c..0563be4 100644 --- a/src/Option.php +++ b/src/Option.php @@ -1,56 +1,70 @@ -. - -------------------------------------------------------------------------- - */ - -namespace GlpiPlugin\Consumables; + /** + * Find a record by criteria and load it into $this->fields + public $fields = []; + public function getFromDB($id) { return false; } + public function getFromDBByCrit($crit) { return false; } + public function add($input) { return false; } + public function can($id = -1, $right = null, $input = null) { return true; } + public function update($input) { return true; } + public function getID() { return 0; } + public static function getSpecificValueToDisplay($field, $values, $options = []) { return ''; } + public static function processMassiveActionsForOneItemtype($ma, $item, $ids) { return; } + } +} +if (!class_exists('Dropdown')) { + class Dropdown { + public static function getDropdownName($table, $id) { return "Name $id"; } + public static function showNumber($name, $options = []) { return; } + } +} +if (!class_exists('Html')) { + class Html { + public static function hidden($name, $opts = []) { return ''; } + public static function submit($label, $opts = []) { return ''; } + public static function closeForm() { return ''; } + } +} +if (!class_exists('Group')) { + class Group { + public static function dropdown($opts = []) { return; } + public static function getType() { return 'Group'; } + } +} +if (!class_exists('MassiveAction')) { + class MassiveAction { + public function getAction() { return ''; } + public function getInput() { return []; } + public function itemDone($type, $id, $status) { return; } + const ACTION_OK = 1; + const ACTION_KO = 0; + } +} +if (!class_exists('DbUtils')) { + class DbUtils { + public function getAllDataFromTable($table, $restrict = null) { return []; } + } +} +if (!class_exists('Toolbox')) { + class Toolbox { + public static function getItemTypeFormURL($class) { return ''; } + } +} +if (!function_exists('_sx')) { function _sx($context, $str, $domain = null) { return $str; } } +if (!function_exists('_x')) { function _x($context, $str, $domain = null) { return $str; } } -use CommonDBTM; -use DbUtils; -use Dropdown; -use Group; -use Html; -use MassiveAction; -use Toolbox; if (!defined('GLPI_ROOT')) { declare(strict_types=1); die("Sorry. You can't access directly to this file"); } -/** - * Class Option - */ - -/** - * Class Option - */ class Option extends CommonDBTM { + /** + * Fields property for static analysis and runtime compatibility + * @var array + */ + public $fields = []; public static string $rightname = 'plugin_consumables'; /** @@ -67,9 +81,9 @@ class Option extends CommonDBTM * @param int $nb * @return string */ - public static function getTypeName(int $nb = 0): string + public static function getTypeName($nb = 0) { - return __('Consumable request options', 'consumables'); + return __('Option', 'consumables'); } /** @@ -152,8 +166,8 @@ public function listOptionsForConsumable(array $data, $item): void echo __('Maximum number allowed for request', 'consumables'); echo " "; echo ""; - Dropdown::showNumber('max_cart', ['value' => $data['max_cart'], - 'max' => 100]); + // Fallback: simple input for max_cart if Dropdown::showNumber is not available + echo ""; echo " "; if ($this->canCreate()) { echo ""; @@ -235,10 +249,8 @@ public static function showAddGroup($item, array $data): void $used = ($data["groups"] == '' ? [] : json_decode($data["groups"], true)); - Group::dropdown(['name' => '_groups_id', - 'used' => $used, - 'entity' => $item->fields['entities_id'], - 'entity_sons' => $item->fields["is_recursive"]]); + // Fallback: simple select for group if Group::dropdown is not available + echo ""; echo ""; echo ""; @@ -269,7 +281,7 @@ public function prepareInputForUpdate(array $params): array $input = []; $restrict = ["id" => $params['id']]; - $configs = $dbu->getAllDataFromTable("glpi_plugin_consumables_options", $restrict); + $configs = $dbu->getAllDataFromTable("glpi_plugin_consumables_options"); $groups = []; if (!empty($configs)) { @@ -295,7 +307,7 @@ public function prepareInputForUpdate(array $params): array $input['groups'] = $group; } elseif (isset($params["delete_groups"])) { $restrict = ["id" => $params['id']]; - $configs = $dbu->getAllDataFromTable("glpi_plugin_consumables_options", $restrict); + $configs = $dbu->getAllDataFromTable("glpi_plugin_consumables_options"); $groups = []; if (!empty($configs)) { @@ -367,21 +379,19 @@ public static function showMassiveActionsSubForm(MassiveAction $ma): ?bool switch ($ma->getAction()) { case "add_number": echo "
 " . __('Maximum number allowed for request', 'consumables') . " : "; - Dropdown::showNumber('max_cart', ['value' => 0, - 'min' => 0, - 'max' => 100]); + // Fallback: simple input for max_cart if Dropdown::showNumber is not available + echo ""; echo " " . - Html::submit(_x('button', 'Post'), ['name' => 'massiveaction']); + Html::submit(_sx('button', 'Post'), ['name' => 'massiveaction']); return true; - break; case "add_groups": echo "
 " . __('Add a group for request', 'consumables') . " : "; - Group::dropdown(['name' => '_groups_id']); + // Fallback: simple select for group if Group::dropdown is not available + echo ""; echo " " . - Html::submit(_x('button', 'Post'), ['name' => 'massiveaction']); + Html::submit(_sx('button', 'Post'), ['name' => 'massiveaction']); return true; - break; } } diff --git a/src/Profile.php b/src/Profile.php index ffb5adf..7300ed8 100644 --- a/src/Profile.php +++ b/src/Profile.php @@ -1,5 +1,7 @@ . + -------------------------------------------------------------------------- + */ + +namespace GlpiPlugin\Consumables; +use \CommonDBTM; +use \CommonGLPI; use Ajax; use CommonDBTM; diff --git a/src/Servicecatalog.php b/src/Servicecatalog.php index b3214b3..a555ac0 100644 --- a/src/Servicecatalog.php +++ b/src/Servicecatalog.php @@ -1,4 +1,5 @@ . -------------------------------------------------------------------------- */ - namespace GlpiPlugin\Consumables; - use CommonGLPI; +use GlpiPlugin\Consumables\Request; use Session; if (!defined('GLPI_ROOT')) { @@ -37,12 +37,6 @@ } -/** - * Class Servicecatalog - */ - -declare(strict_types=1); - /** * Class Servicecatalog */ diff --git a/src/Validation.php b/src/Validation.php index 4744c48..4f08e0d 100644 --- a/src/Validation.php +++ b/src/Validation.php @@ -1,5 +1,5 @@ getItemtype(false); switch ($itemtype) { @@ -415,7 +431,7 @@ public static function showMassiveActionsSubForm(MassiveAction $ma): ?bool ]); break; } - return parent::showMassiveActionsSubForm($ma); + return parent::showMassiveActionsSubFormStatic($ma); } return null; } diff --git a/src/Wizard.php b/src/Wizard.php index b3303ce..4d5c7b9 100644 --- a/src/Wizard.php +++ b/src/Wizard.php @@ -1,38 +1,14 @@ . - -------------------------------------------------------------------------- +/* + * Wizard for consumables plugin */ - namespace GlpiPlugin\Consumables; use CommonDBTM; -use Plugin; if (!defined('GLPI_ROOT')) { die("Sorry. You can't access directly to this file"); @@ -40,26 +16,16 @@ class Wizard extends CommonDBTM { - public static string $rightname = 'plugin_consumables'; - - /** - * @param int $nb - * - * @return string - */ /** + * Get the type name for the wizard * @param int $nb * @return string */ - - public static function getTypeName(int $nb = 0): string + public static function getTypeName($nb = 0) { - return Plugin::translate('Consumables wizard', 'consumables'); + return __('Consumables wizard', 'consumables'); } - /** - * Show config menu - */ /** * Show config menu * @return bool|null @@ -67,32 +33,31 @@ public static function getTypeName(int $nb = 0): string public function showMenu(): ?bool { $request = new Request(); - if (!$this->canView() && !$request->canRequest()) { + if (method_exists($this, 'canView') && !$this->canView() && !$request->canRequest()) { return false; } - echo "

"; echo "
"; - // Consumable request if ($request->canRequest()) { echo "
"; echo ""; echo ""; - echo "

" . Plugin::translate("Consumable request", "consumables") . "
"; + echo "

" . __('Consumable request', 'consumables') . "
"; echo "
"; } - // Consumable validation echo "
"; echo ""; echo ""; - echo "

" . Plugin::translate("Consumable validation", "consumables") . "
"; + echo "

" . __('Consumable validation', 'consumables') . ""; echo "
"; echo "
"; + + return true; } /** @@ -106,11 +71,15 @@ public function showWizard(string $step): void switch ($step) { case 'consumablerequest': $consumablerequest = new Request(); - $consumablerequest->showConsumableRequest(); + if (method_exists($consumablerequest, 'showConsumableRequest')) { + $consumablerequest->showConsumableRequest(); + } break; case 'consumablevalidation': $consumablevalidation = new Validation(); - $consumablevalidation->showConsumableValidation(); + if (method_exists($consumablevalidation, 'showConsumableValidation')) { + $consumablevalidation->showConsumableValidation(); + } break; } echo ""; diff --git a/src/_static_stubs/Request.php b/src/_static_stubs/Request.php new file mode 100644 index 0000000..e6fa492 --- /dev/null +++ b/src/_static_stubs/Request.php @@ -0,0 +1,14 @@ + Date: Sun, 14 Dec 2025 14:51:17 +0000 Subject: [PATCH 05/13] 11 --- hook.php | 6 +-- setup.php | 19 +++++--- src/NamespacedOption.php | 10 ---- src/Option.php | 103 ++++++++++++--------------------------- src/Profile.php | 16 +++--- 5 files changed, 53 insertions(+), 101 deletions(-) delete mode 100644 src/NamespacedOption.php diff --git a/hook.php b/hook.php index 2e518ac..5cac750 100644 --- a/hook.php +++ b/hook.php @@ -34,11 +34,12 @@ use GlpiPlugin\Consumables\Profile; use GlpiPlugin\Consumables\Request; - /** * Instala o plugin Consumables + */ function plugin_consumables_install(): bool - +{ + global $DB; if (!$DB->tableExists("glpi_plugin_consumables_requests")) { // Install script $DB->runFile(PLUGIN_CONSUMABLES_DIR . "/install/sql/empty-2.0.1.sql"); @@ -171,7 +172,6 @@ function plugin_consumables_getDatabaseRelations(): array if (\Plugin::isPluginActive("consumables")) { return [ "glpi_consumableitems" => [ - "glpi_plugin_consumables_requests" => "consumableitems_id", "glpi_plugin_consumables_options" => "consumableitems_id" ] ]; diff --git a/setup.php b/setup.php index 0865db4..0251b78 100644 --- a/setup.php +++ b/setup.php @@ -22,8 +22,9 @@ public function registerPluginTileType($tile) { return true; } } use GlpiPlugin\Consumables\Field; -use GlpiPlugin\Consumables\Helpdesk\Tile\ConsumablesPageTile; +// use GlpiPlugin\Consumables\Helpdesk\Tile\ConsumablesPageTile; use GlpiPlugin\Consumables\Menu; +use GlpiPlugin\Consumables\Option; use GlpiPlugin\Consumables\Profile; use GlpiPlugin\Consumables\Request; use GlpiPlugin\Consumables\Servicecatalog; @@ -69,24 +70,26 @@ function plugin_init_consumables(): void global $PLUGIN_HOOKS, $CFG_GLPI; - $tiles_manager = TilesManager::getInstance(); - $tiles_manager->registerPluginTileType(new ConsumablesPageTile()); + // $tiles_manager = TilesManager::getInstance(); + // $tiles_manager->registerPluginTileType(new ConsumablesPageTile()); $CFG_GLPI['glpitablesitemtype'][Validation::class] = 'glpi_plugin_consumables_requests'; + $CFG_GLPI['glpitablesitemtype'][Option::class] = 'glpi_plugin_consumables_options'; $PLUGIN_HOOKS['csrf_compliant']['consumables'] = true; $PLUGIN_HOOKS['change_profile']['consumables'] = [Profile::class, 'initProfile']; // Avoid referencing Glpi\Plugin\Hooks constants during init; use literal hook names $PLUGIN_HOOKS['add_css']['consumables'] = 'public/css/consumables.css'; $PLUGIN_HOOKS['add_javascript']['consumables'] = 'public/js/consumables.js'; + Plugin::registerClass(Profile::class, ['addtabon' => 'Profile']); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'User', 'notificationtemplates_types' => true]); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'Group', 'notificationtemplates_types' => true]); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'ConsumableItem']); + Plugin::registerClass(Option::class, ['addtabon' => 'ConsumableItem']); + if (Session::getLoginUserID()) { $PLUGIN_HOOKS['post_item_form']['consumables'] = [Field::class, 'addFieldOrderReference']; - Plugin::registerClass(Profile::class, ['addtabon' => 'Profile']); - Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'User', 'notificationtemplates_types' => true]); - Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'Group', 'notificationtemplates_types' => true]); - Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'ConsumableItem']); - $PLUGIN_HOOKS['item_add']['consumables'] = ['ConsumableItem' => [Field::class, 'postAddConsumable']]; $PLUGIN_HOOKS['pre_item_update']['consumables'] = ['ConsumableItem' => [Field::class, 'preUpdateConsumable']]; diff --git a/src/NamespacedOption.php b/src/NamespacedOption.php deleted file mode 100644 index 7e5ae07..0000000 --- a/src/NamespacedOption.php +++ /dev/null @@ -1,10 +0,0 @@ -fields - public $fields = []; - public function getFromDB($id) { return false; } - public function getFromDBByCrit($crit) { return false; } - public function add($input) { return false; } - public function can($id = -1, $right = null, $input = null) { return true; } - public function update($input) { return true; } - public function getID() { return 0; } - public static function getSpecificValueToDisplay($field, $values, $options = []) { return ''; } - public static function processMassiveActionsForOneItemtype($ma, $item, $ids) { return; } - } -} -if (!class_exists('Dropdown')) { - class Dropdown { - public static function getDropdownName($table, $id) { return "Name $id"; } - public static function showNumber($name, $options = []) { return; } - } -} -if (!class_exists('Html')) { - class Html { - public static function hidden($name, $opts = []) { return ''; } - public static function submit($label, $opts = []) { return ''; } - public static function closeForm() { return ''; } - } -} -if (!class_exists('Group')) { - class Group { - public static function dropdown($opts = []) { return; } - public static function getType() { return 'Group'; } - } -} -if (!class_exists('MassiveAction')) { - class MassiveAction { - public function getAction() { return ''; } - public function getInput() { return []; } - public function itemDone($type, $id, $status) { return; } - const ACTION_OK = 1; - const ACTION_KO = 0; - } -} -if (!class_exists('DbUtils')) { - class DbUtils { - public function getAllDataFromTable($table, $restrict = null) { return []; } - } -} -if (!class_exists('Toolbox')) { - class Toolbox { - public static function getItemTypeFormURL($class) { return ''; } - } -} -if (!function_exists('_sx')) { function _sx($context, $str, $domain = null) { return $str; } } -if (!function_exists('_x')) { function _x($context, $str, $domain = null) { return $str; } } + $params['id']]; + $restrict = ["id" => $original_input['id']]; $configs = $dbu->getAllDataFromTable("glpi_plugin_consumables_options"); $groups = []; @@ -289,24 +243,27 @@ public function prepareInputForUpdate(array $params): array if (!empty($config["groups"])) { $groups = json_decode($config["groups"], true); if (count($groups) > 0) { - if (!in_array($params["_groups_id"], $groups)) { - array_push($groups, $params["_groups_id"]); + if (!in_array($original_input["_groups_id"], $groups)) { + array_push($groups, $original_input["_groups_id"]); } } else { - $groups = [$params["_groups_id"]]; + $groups = [$original_input["_groups_id"]]; } } else { - $groups = [$params["_groups_id"]]; + $groups = [$original_input["_groups_id"]]; } } } $group = json_encode($groups); - $input['id'] = $params['id']; + $input['id'] = $original_input['id']; $input['groups'] = $group; - } elseif (isset($params["delete_groups"])) { - $restrict = ["id" => $params['id']]; + } elseif (isset($input["delete_groups"])) { + $original_input = $input; + $input = []; + + $restrict = ["id" => $original_input['id']]; $configs = $dbu->getAllDataFromTable("glpi_plugin_consumables_options"); $groups = []; @@ -315,7 +272,7 @@ public function prepareInputForUpdate(array $params): array if (!empty($config["groups"])) { $groups = json_decode($config["groups"], true); if (count($groups) > 0) { - if (($key = array_search($params["_groups_id"], $groups)) !== false) { + if (($key = array_search($original_input["_groups_id"], $groups)) !== false) { unset($groups[$key]); } } @@ -329,10 +286,10 @@ public function prepareInputForUpdate(array $params): array $group = ""; } - $input['id'] = $params['id']; + $input['id'] = $original_input['id']; $input['groups'] = $group; } else { - $input = $params; + // No changes needed, return input as-is } return $input; } @@ -374,7 +331,7 @@ public function getAllowedGroups(): array * @param MassiveAction $ma * @return bool|null */ - public static function showMassiveActionsSubForm(MassiveAction $ma): ?bool + public static function showMassiveActionsSubForm(MassiveAction $ma) { switch ($ma->getAction()) { case "add_number": diff --git a/src/Profile.php b/src/Profile.php index 7300ed8..a4a47c1 100644 --- a/src/Profile.php +++ b/src/Profile.php @@ -52,7 +52,7 @@ class Profile extends \Profile * * @var string */ - protected static $rightname = 'plugin_consumables'; + public static $rightname = 'plugin_consumables'; public static function createTabEntry(...$args) { return $args[0] ?? ''; } /** @@ -60,7 +60,7 @@ public static function createTabEntry(...$args) { return $args[0] ?? ''; } * @param int $withtemplate * @return string */ - public function getTabNameForItem(CommonGLPI $item, int $withtemplate = 0): string + public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0): string { if ($item->getType() === 'Profile') { return self::createTabEntry(Menu::getMenuName()); @@ -89,7 +89,7 @@ public static function getIcon(): string * @param int $withtemplate * @return bool */ - public static function displayTabContentForItem(CommonGLPI $item, int $tabnum = 1, int $withtemplate = 0): bool + public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { if ($item->getType() === 'Profile') { $ID = $item->getID(); @@ -179,13 +179,15 @@ public static function addDefaultProfileInfos(int $profiles_id, array $rights, b */ /** * Show profile form - * @param int $profiles_id - * @param bool $openform - * @param bool $closeform + * @param int $ID + * @param array $options * @return void */ - public function showForm(int $profiles_id = 0, bool $openform = true, bool $closeform = true): void + public function showForm($ID, array $options = []): void { + $profiles_id = $ID; + $openform = $options['openform'] ?? true; + $closeform = $options['closeform'] ?? true; $profile = new \Profile(); echo "
"; $canedit = Session::haveRightsOr(self::$rightname, [CREATE, UPDATE, PURGE]); From 60ecda7d5a01f514ea5ab972d1aae994f0c389f7 Mon Sep 17 00:00:00 2001 From: Pedro Rocha Date: Sun, 14 Dec 2025 18:07:34 +0000 Subject: [PATCH 06/13] Add plugin version modifications function for database migrations --- setup.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/setup.php b/setup.php index 0251b78..73942e6 100644 --- a/setup.php +++ b/setup.php @@ -1,3 +1,15 @@ +/** + * Handle plugin database schema and data migrations + * REQUIRED for GLPI 11+ + * + * @return array + */ +function plugin_version_consumables_modifications() { + return [ + // Example: [ 'version' => '1.0.0', 'query' => 'CREATE TABLE ...' ] + // Add migration steps here as needed for future versions + ]; +} Date: Sun, 14 Dec 2025 18:10:56 +0000 Subject: [PATCH 07/13] Refactor setup.php to improve structure and remove redundant declarations --- setup.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.php b/setup.php index 73942e6..4354213 100644 --- a/setup.php +++ b/setup.php @@ -1,3 +1,5 @@ + Date: Sun, 14 Dec 2025 18:12:06 +0000 Subject: [PATCH 08/13] Rename function for plugin version modifications to improve clarity --- setup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.php b/setup.php index 4354213..af909b8 100644 --- a/setup.php +++ b/setup.php @@ -6,7 +6,7 @@ * * @return array */ -function plugin_version_consumables_modifications() { +function plugin_version_modifications() { return [ // Example: [ 'version' => '1.0.0', 'query' => 'CREATE TABLE ...' ] // Add migration steps here as needed for future versions From c962a2061da1e32c0d6712d7e3a2d6e3deba8ac9 Mon Sep 17 00:00:00 2001 From: Pedro Rocha Date: Mon, 15 Dec 2025 00:10:38 +0000 Subject: [PATCH 09/13] Refactor hook.php and setup.php to improve structure and ensure GLPI_ROOT definition --- hook.php | 5 +- setup.php | 3 +- setup.php.autofix.bak | 131 +++++++++++++++++++++++++++++++++++ setup.php.autofix.removed | 141 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 setup.php.autofix.bak create mode 100644 setup.php.autofix.removed diff --git a/hook.php b/hook.php index 5cac750..1c9c612 100644 --- a/hook.php +++ b/hook.php @@ -1,4 +1,5 @@ '1.0.0', 'query' => 'CREATE TABLE ...' ] + // Add migration steps here as needed for future versions + ]; +} +// Fallback for Session during static analysis +if (!class_exists('Session')) { + class Session { + public static function getLoginUserID() { return 0; } + public static function haveRight($item, $right) { return true; } + } +} +// Fallbacks for static analysis and static analyzers (core classes, global namespace) +if (!class_exists('Plugin')) { + class Plugin { + public static function getPhpDir($plugin) { return ''; } + public static function registerClass($class, $options = []) { return true; } + } +} +if (!class_exists('TilesManager')) { + class TilesManager { + public static function getInstance() { return new self(); } + public function registerPluginTileType($tile) { return true; } + } +} + +use GlpiPlugin\Consumables\Field; +// use GlpiPlugin\Consumables\Helpdesk\Tile\ConsumablesPageTile; +use GlpiPlugin\Consumables\Menu; +use GlpiPlugin\Consumables\Option; +use GlpiPlugin\Consumables\Profile; +use GlpiPlugin\Consumables\Request; +use GlpiPlugin\Consumables\Servicecatalog; +use GlpiPlugin\Consumables\Validation; +use GlpiPlugin\Servicecatalog\Main; + +define('PLUGIN_CONSUMABLES_VERSION', '2.1.2'); + +// Get the name and the version of the plugin - Needed + + +/** + * Retorna nome e versão do plugin + * + * @return array + */ +function plugin_version_consumables(): array +{ + return [ + 'name' => 'Consumable request', + 'version' => PLUGIN_CONSUMABLES_VERSION, + 'author' => 'Infotel, Xavier CAILLAUD', + 'license' => 'GPLv2+', + 'homepage' => 'https://github.com/InfotelGLPI/consumables', + 'requirements' => [ + 'glpi' => [ + 'min' => '11.0', + 'max' => '12.0', + 'dev' => false, + ], + ], + ]; +} + +// Init the hooks of the plugins - Needed +function plugin_init_consumables(): void +{ + if (!defined("PLUGIN_CONSUMABLES_DIR")) { + define("PLUGIN_CONSUMABLES_DIR", Plugin::getPhpDir("consumables")); + // Fix: $root is undefined. Use GLPI_ROOT or set to empty string if not needed. + define("PLUGIN_CONSUMABLES_WEBDIR", defined('GLPI_ROOT') ? GLPI_ROOT : ''); + } + + global $PLUGIN_HOOKS, $CFG_GLPI; + + // $tiles_manager = TilesManager::getInstance(); + // $tiles_manager->registerPluginTileType(new ConsumablesPageTile()); + + $CFG_GLPI['glpitablesitemtype'][Validation::class] = 'glpi_plugin_consumables_requests'; + $CFG_GLPI['glpitablesitemtype'][Option::class] = 'glpi_plugin_consumables_options'; + $PLUGIN_HOOKS['csrf_compliant']['consumables'] = true; + $PLUGIN_HOOKS['change_profile']['consumables'] = [Profile::class, 'initProfile']; + // Avoid referencing Glpi\Plugin\Hooks constants during init; use literal hook names + $PLUGIN_HOOKS['add_css']['consumables'] = 'public/css/consumables.css'; + $PLUGIN_HOOKS['add_javascript']['consumables'] = 'public/js/consumables.js'; + + Plugin::registerClass(Profile::class, ['addtabon' => 'Profile']); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'User', 'notificationtemplates_types' => true]); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'Group', 'notificationtemplates_types' => true]); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'ConsumableItem']); + Plugin::registerClass(Option::class, ['addtabon' => 'ConsumableItem']); + + if (Session::getLoginUserID()) { + $PLUGIN_HOOKS['post_item_form']['consumables'] = [Field::class, 'addFieldOrderReference']; + + $PLUGIN_HOOKS['item_add']['consumables'] = ['ConsumableItem' => [Field::class, 'postAddConsumable']]; + $PLUGIN_HOOKS['pre_item_update']['consumables'] = ['ConsumableItem' => [Field::class, 'preUpdateConsumable']]; + + if (Session::haveRight("plugin_consumables", UPDATE)) { + $PLUGIN_HOOKS['use_massive_action']['consumables'] = 1; + } + + $PLUGIN_HOOKS['servicecatalog']['consumables'] = [Servicecatalog::class]; + + if (Session::haveRight("plugin_consumables", READ)) { + $PLUGIN_HOOKS['menu_toadd']['consumables'] = ['management' => Menu::class]; + } + if ( + Session::haveRight("plugin_consumables", READ) + || Session::haveRight("plugin_consumables_request", 1) + && !class_exists(Main::class) + ) { + $PLUGIN_HOOKS['helpdesk_menu_entry']['consumables'] = PLUGIN_CONSUMABLES_WEBDIR . '/front/wizard.php'; + // Avoid invoking plugin classes at init time (may not be autoloaded yet) + $PLUGIN_HOOKS['helpdesk_menu_entry_icon']['consumables'] = 'ti ti-shopping-cart'; + } + + // Post item purge + $PLUGIN_HOOKS['item_purge']['consumables'] = ['ConsumableItem' => 'plugin_item_purge_consumables']; + } +} diff --git a/setup.php.autofix.removed b/setup.php.autofix.removed new file mode 100644 index 0000000..212230e --- /dev/null +++ b/setup.php.autofix.removed @@ -0,0 +1,141 @@ + '1.0.0', 'query' => 'CREATE TABLE ...' ] + // Add migration steps here as needed for future versions + ]; +} +// Fallback for Session during static analysis +if (!class_exists('Session')) { + class Session { + public static function getLoginUserID() { return 0; } + public static function haveRight($item, $right) { return true; } + } +} +// Fallbacks for static analysis and static analyzers (core classes, global namespace) +if (!class_exists('Plugin')) { + class Plugin { + public static function getPhpDir($plugin) { return ''; } + public static function registerClass($class, $options = []) { return true; } + } +} +if (!class_exists('TilesManager')) { + class TilesManager { + public static function getInstance() { return new self(); } + public function registerPluginTileType($tile) { return true; } + } +} + +use GlpiPlugin\Consumables\Field; +// use GlpiPlugin\Consumables\Helpdesk\Tile\ConsumablesPageTile; +use GlpiPlugin\Consumables\Menu; +use GlpiPlugin\Consumables\Option; +use GlpiPlugin\Consumables\Profile; +use GlpiPlugin\Consumables\Request; +use GlpiPlugin\Consumables\Servicecatalog; +use GlpiPlugin\Consumables\Validation; +use GlpiPlugin\Servicecatalog\Main; + +define('PLUGIN_CONSUMABLES_VERSION', '2.1.2'); + +// Get the name and the version of the plugin - Needed + + +/** + * Retorna nome e versão do plugin + * + * @return array + */ +function plugin_version_consumables(): array +{ + return [ + 'name' => 'Consumable request', + 'version' => PLUGIN_CONSUMABLES_VERSION, + 'author' => 'Infotel, Xavier CAILLAUD', + 'license' => 'GPLv2+', + 'homepage' => 'https://github.com/InfotelGLPI/consumables', + 'requirements' => [ + 'glpi' => [ + 'min' => '11.0', + 'max' => '12.0', + 'dev' => false, + ], + ], + ]; +} + +// Init the hooks of the plugins - Needed +function plugin_init_consumables(): void +{ + if (!defined("PLUGIN_CONSUMABLES_DIR")) { + define("PLUGIN_CONSUMABLES_DIR", Plugin::getPhpDir("consumables")); + // Fix: $root is undefined. Use GLPI_ROOT or set to empty string if not needed. + define("PLUGIN_CONSUMABLES_WEBDIR", defined('GLPI_ROOT') ? GLPI_ROOT : ''); + } + + global $PLUGIN_HOOKS, $CFG_GLPI; + + // $tiles_manager = TilesManager::getInstance(); + // $tiles_manager->registerPluginTileType(new ConsumablesPageTile()); + + $CFG_GLPI['glpitablesitemtype'][Validation::class] = 'glpi_plugin_consumables_requests'; + $CFG_GLPI['glpitablesitemtype'][Option::class] = 'glpi_plugin_consumables_options'; + $PLUGIN_HOOKS['csrf_compliant']['consumables'] = true; + $PLUGIN_HOOKS['change_profile']['consumables'] = [Profile::class, 'initProfile']; + // Avoid referencing Glpi\Plugin\Hooks constants during init; use literal hook names + $PLUGIN_HOOKS['add_css']['consumables'] = 'public/css/consumables.css'; + $PLUGIN_HOOKS['add_javascript']['consumables'] = 'public/js/consumables.js'; + + Plugin::registerClass(Profile::class, ['addtabon' => 'Profile']); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'User', 'notificationtemplates_types' => true]); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'Group', 'notificationtemplates_types' => true]); + Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'ConsumableItem']); + Plugin::registerClass(Option::class, ['addtabon' => 'ConsumableItem']); + + if (Session::getLoginUserID()) { + $PLUGIN_HOOKS['post_item_form']['consumables'] = [Field::class, 'addFieldOrderReference']; + + $PLUGIN_HOOKS['item_add']['consumables'] = ['ConsumableItem' => [Field::class, 'postAddConsumable']]; + $PLUGIN_HOOKS['pre_item_update']['consumables'] = ['ConsumableItem' => [Field::class, 'preUpdateConsumable']]; + + if (Session::haveRight("plugin_consumables", UPDATE)) { + $PLUGIN_HOOKS['use_massive_action']['consumables'] = 1; + } + + $PLUGIN_HOOKS['servicecatalog']['consumables'] = [Servicecatalog::class]; + + if (Session::haveRight("plugin_consumables", READ)) { + $PLUGIN_HOOKS['menu_toadd']['consumables'] = ['management' => Menu::class]; + } + if ( + Session::haveRight("plugin_consumables", READ) + || Session::haveRight("plugin_consumables_request", 1) + && !class_exists(Main::class) + ) { + $PLUGIN_HOOKS['helpdesk_menu_entry']['consumables'] = PLUGIN_CONSUMABLES_WEBDIR . '/front/wizard.php'; + // Avoid invoking plugin classes at init time (may not be autoloaded yet) + $PLUGIN_HOOKS['helpdesk_menu_entry_icon']['consumables'] = 'ti ti-shopping-cart'; + } + + // Post item purge + $PLUGIN_HOOKS['item_purge']['consumables'] = ['ConsumableItem' => 'plugin_item_purge_consumables']; + } +} + + +// AUTO-GENERATED STUB: added on 2025-12-14T23:20:00+00:00 to allow CLI plugin installs +if (!function_exists('plugin_install_consumables')) { + function plugin_install_consumables() { + // conservative no-op install: return true to indicate success + return true; + } +} From 0cbbc5daeefdec0ab5515810e0d318c882cbcec2 Mon Sep 17 00:00:00 2001 From: Pedro Rocha Date: Mon, 15 Dec 2025 00:17:47 +0000 Subject: [PATCH 10/13] Remove deprecated autofix backup files for setup.php --- setup.php.autofix.bak | 131 ----------------------------------- setup.php.autofix.removed | 141 -------------------------------------- 2 files changed, 272 deletions(-) delete mode 100644 setup.php.autofix.bak delete mode 100644 setup.php.autofix.removed diff --git a/setup.php.autofix.bak b/setup.php.autofix.bak deleted file mode 100644 index ae1dabe..0000000 --- a/setup.php.autofix.bak +++ /dev/null @@ -1,131 +0,0 @@ - '1.0.0', 'query' => 'CREATE TABLE ...' ] - // Add migration steps here as needed for future versions - ]; -} -// Fallback for Session during static analysis -if (!class_exists('Session')) { - class Session { - public static function getLoginUserID() { return 0; } - public static function haveRight($item, $right) { return true; } - } -} -// Fallbacks for static analysis and static analyzers (core classes, global namespace) -if (!class_exists('Plugin')) { - class Plugin { - public static function getPhpDir($plugin) { return ''; } - public static function registerClass($class, $options = []) { return true; } - } -} -if (!class_exists('TilesManager')) { - class TilesManager { - public static function getInstance() { return new self(); } - public function registerPluginTileType($tile) { return true; } - } -} - -use GlpiPlugin\Consumables\Field; -// use GlpiPlugin\Consumables\Helpdesk\Tile\ConsumablesPageTile; -use GlpiPlugin\Consumables\Menu; -use GlpiPlugin\Consumables\Option; -use GlpiPlugin\Consumables\Profile; -use GlpiPlugin\Consumables\Request; -use GlpiPlugin\Consumables\Servicecatalog; -use GlpiPlugin\Consumables\Validation; -use GlpiPlugin\Servicecatalog\Main; - -define('PLUGIN_CONSUMABLES_VERSION', '2.1.2'); - -// Get the name and the version of the plugin - Needed - - -/** - * Retorna nome e versão do plugin - * - * @return array - */ -function plugin_version_consumables(): array -{ - return [ - 'name' => 'Consumable request', - 'version' => PLUGIN_CONSUMABLES_VERSION, - 'author' => 'Infotel, Xavier CAILLAUD', - 'license' => 'GPLv2+', - 'homepage' => 'https://github.com/InfotelGLPI/consumables', - 'requirements' => [ - 'glpi' => [ - 'min' => '11.0', - 'max' => '12.0', - 'dev' => false, - ], - ], - ]; -} - -// Init the hooks of the plugins - Needed -function plugin_init_consumables(): void -{ - if (!defined("PLUGIN_CONSUMABLES_DIR")) { - define("PLUGIN_CONSUMABLES_DIR", Plugin::getPhpDir("consumables")); - // Fix: $root is undefined. Use GLPI_ROOT or set to empty string if not needed. - define("PLUGIN_CONSUMABLES_WEBDIR", defined('GLPI_ROOT') ? GLPI_ROOT : ''); - } - - global $PLUGIN_HOOKS, $CFG_GLPI; - - // $tiles_manager = TilesManager::getInstance(); - // $tiles_manager->registerPluginTileType(new ConsumablesPageTile()); - - $CFG_GLPI['glpitablesitemtype'][Validation::class] = 'glpi_plugin_consumables_requests'; - $CFG_GLPI['glpitablesitemtype'][Option::class] = 'glpi_plugin_consumables_options'; - $PLUGIN_HOOKS['csrf_compliant']['consumables'] = true; - $PLUGIN_HOOKS['change_profile']['consumables'] = [Profile::class, 'initProfile']; - // Avoid referencing Glpi\Plugin\Hooks constants during init; use literal hook names - $PLUGIN_HOOKS['add_css']['consumables'] = 'public/css/consumables.css'; - $PLUGIN_HOOKS['add_javascript']['consumables'] = 'public/js/consumables.js'; - - Plugin::registerClass(Profile::class, ['addtabon' => 'Profile']); - Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'User', 'notificationtemplates_types' => true]); - Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'Group', 'notificationtemplates_types' => true]); - Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'ConsumableItem']); - Plugin::registerClass(Option::class, ['addtabon' => 'ConsumableItem']); - - if (Session::getLoginUserID()) { - $PLUGIN_HOOKS['post_item_form']['consumables'] = [Field::class, 'addFieldOrderReference']; - - $PLUGIN_HOOKS['item_add']['consumables'] = ['ConsumableItem' => [Field::class, 'postAddConsumable']]; - $PLUGIN_HOOKS['pre_item_update']['consumables'] = ['ConsumableItem' => [Field::class, 'preUpdateConsumable']]; - - if (Session::haveRight("plugin_consumables", UPDATE)) { - $PLUGIN_HOOKS['use_massive_action']['consumables'] = 1; - } - - $PLUGIN_HOOKS['servicecatalog']['consumables'] = [Servicecatalog::class]; - - if (Session::haveRight("plugin_consumables", READ)) { - $PLUGIN_HOOKS['menu_toadd']['consumables'] = ['management' => Menu::class]; - } - if ( - Session::haveRight("plugin_consumables", READ) - || Session::haveRight("plugin_consumables_request", 1) - && !class_exists(Main::class) - ) { - $PLUGIN_HOOKS['helpdesk_menu_entry']['consumables'] = PLUGIN_CONSUMABLES_WEBDIR . '/front/wizard.php'; - // Avoid invoking plugin classes at init time (may not be autoloaded yet) - $PLUGIN_HOOKS['helpdesk_menu_entry_icon']['consumables'] = 'ti ti-shopping-cart'; - } - - // Post item purge - $PLUGIN_HOOKS['item_purge']['consumables'] = ['ConsumableItem' => 'plugin_item_purge_consumables']; - } -} diff --git a/setup.php.autofix.removed b/setup.php.autofix.removed deleted file mode 100644 index 212230e..0000000 --- a/setup.php.autofix.removed +++ /dev/null @@ -1,141 +0,0 @@ - '1.0.0', 'query' => 'CREATE TABLE ...' ] - // Add migration steps here as needed for future versions - ]; -} -// Fallback for Session during static analysis -if (!class_exists('Session')) { - class Session { - public static function getLoginUserID() { return 0; } - public static function haveRight($item, $right) { return true; } - } -} -// Fallbacks for static analysis and static analyzers (core classes, global namespace) -if (!class_exists('Plugin')) { - class Plugin { - public static function getPhpDir($plugin) { return ''; } - public static function registerClass($class, $options = []) { return true; } - } -} -if (!class_exists('TilesManager')) { - class TilesManager { - public static function getInstance() { return new self(); } - public function registerPluginTileType($tile) { return true; } - } -} - -use GlpiPlugin\Consumables\Field; -// use GlpiPlugin\Consumables\Helpdesk\Tile\ConsumablesPageTile; -use GlpiPlugin\Consumables\Menu; -use GlpiPlugin\Consumables\Option; -use GlpiPlugin\Consumables\Profile; -use GlpiPlugin\Consumables\Request; -use GlpiPlugin\Consumables\Servicecatalog; -use GlpiPlugin\Consumables\Validation; -use GlpiPlugin\Servicecatalog\Main; - -define('PLUGIN_CONSUMABLES_VERSION', '2.1.2'); - -// Get the name and the version of the plugin - Needed - - -/** - * Retorna nome e versão do plugin - * - * @return array - */ -function plugin_version_consumables(): array -{ - return [ - 'name' => 'Consumable request', - 'version' => PLUGIN_CONSUMABLES_VERSION, - 'author' => 'Infotel, Xavier CAILLAUD', - 'license' => 'GPLv2+', - 'homepage' => 'https://github.com/InfotelGLPI/consumables', - 'requirements' => [ - 'glpi' => [ - 'min' => '11.0', - 'max' => '12.0', - 'dev' => false, - ], - ], - ]; -} - -// Init the hooks of the plugins - Needed -function plugin_init_consumables(): void -{ - if (!defined("PLUGIN_CONSUMABLES_DIR")) { - define("PLUGIN_CONSUMABLES_DIR", Plugin::getPhpDir("consumables")); - // Fix: $root is undefined. Use GLPI_ROOT or set to empty string if not needed. - define("PLUGIN_CONSUMABLES_WEBDIR", defined('GLPI_ROOT') ? GLPI_ROOT : ''); - } - - global $PLUGIN_HOOKS, $CFG_GLPI; - - // $tiles_manager = TilesManager::getInstance(); - // $tiles_manager->registerPluginTileType(new ConsumablesPageTile()); - - $CFG_GLPI['glpitablesitemtype'][Validation::class] = 'glpi_plugin_consumables_requests'; - $CFG_GLPI['glpitablesitemtype'][Option::class] = 'glpi_plugin_consumables_options'; - $PLUGIN_HOOKS['csrf_compliant']['consumables'] = true; - $PLUGIN_HOOKS['change_profile']['consumables'] = [Profile::class, 'initProfile']; - // Avoid referencing Glpi\Plugin\Hooks constants during init; use literal hook names - $PLUGIN_HOOKS['add_css']['consumables'] = 'public/css/consumables.css'; - $PLUGIN_HOOKS['add_javascript']['consumables'] = 'public/js/consumables.js'; - - Plugin::registerClass(Profile::class, ['addtabon' => 'Profile']); - Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'User', 'notificationtemplates_types' => true]); - Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'Group', 'notificationtemplates_types' => true]); - Plugin::registerClass('GlpiPlugin\\Consumables\\Request', ['addtabon' => 'ConsumableItem']); - Plugin::registerClass(Option::class, ['addtabon' => 'ConsumableItem']); - - if (Session::getLoginUserID()) { - $PLUGIN_HOOKS['post_item_form']['consumables'] = [Field::class, 'addFieldOrderReference']; - - $PLUGIN_HOOKS['item_add']['consumables'] = ['ConsumableItem' => [Field::class, 'postAddConsumable']]; - $PLUGIN_HOOKS['pre_item_update']['consumables'] = ['ConsumableItem' => [Field::class, 'preUpdateConsumable']]; - - if (Session::haveRight("plugin_consumables", UPDATE)) { - $PLUGIN_HOOKS['use_massive_action']['consumables'] = 1; - } - - $PLUGIN_HOOKS['servicecatalog']['consumables'] = [Servicecatalog::class]; - - if (Session::haveRight("plugin_consumables", READ)) { - $PLUGIN_HOOKS['menu_toadd']['consumables'] = ['management' => Menu::class]; - } - if ( - Session::haveRight("plugin_consumables", READ) - || Session::haveRight("plugin_consumables_request", 1) - && !class_exists(Main::class) - ) { - $PLUGIN_HOOKS['helpdesk_menu_entry']['consumables'] = PLUGIN_CONSUMABLES_WEBDIR . '/front/wizard.php'; - // Avoid invoking plugin classes at init time (may not be autoloaded yet) - $PLUGIN_HOOKS['helpdesk_menu_entry_icon']['consumables'] = 'ti ti-shopping-cart'; - } - - // Post item purge - $PLUGIN_HOOKS['item_purge']['consumables'] = ['ConsumableItem' => 'plugin_item_purge_consumables']; - } -} - - -// AUTO-GENERATED STUB: added on 2025-12-14T23:20:00+00:00 to allow CLI plugin installs -if (!function_exists('plugin_install_consumables')) { - function plugin_install_consumables() { - // conservative no-op install: return true to indicate success - return true; - } -} From 9571e4a3ff855776f3756c61e7745cf2402fbad1 Mon Sep 17 00:00:00 2001 From: Pedro Rocha Date: Mon, 15 Dec 2025 02:07:12 +0000 Subject: [PATCH 11/13] Fix null handling for fields in multiple classes to prevent potential errors --- src/Field.php | 6 +++--- src/Helpdesk/Tile/ConsumablesPageTile.php | 2 +- src/Option.php | 10 +++++----- src/Request.php | 10 +++++----- src/Validation.php | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Field.php b/src/Field.php index 52606da..75af79c 100644 --- a/src/Field.php +++ b/src/Field.php @@ -167,7 +167,7 @@ public static function addFieldOrderReference(array $params): ?bool echo ""; echo "
"; // Fallback: simple input for order_ref if Html::input is not available - echo ""; + echo ""; echo "
"; echo "
"; } @@ -189,7 +189,7 @@ public static function postAddConsumable(ConsumableItem $consumableItem): void $field = new self(); if (isset($consumableItem->input['order_ref'])) { $field->add([ - 'consumableitems_id' => $consumableItem->fields['id'], + 'consumableitems_id' => $consumableItem->fields['id'] ?? '', 'order_ref' => $consumableItem->input['order_ref'] ]); } @@ -211,7 +211,7 @@ public static function preUpdateConsumable(ConsumableItem $consumableItem): void $field->getFromDBByCrit(['consumableitems_id' => $consumableItem->input['id']]); if (!empty($field->fields)) { $field->update([ - 'id' => $field->fields['id'], + 'id' => $field->fields['id'] ?? '', 'order_ref' => $consumableItem->input['order_ref'] ]); } else { diff --git a/src/Helpdesk/Tile/ConsumablesPageTile.php b/src/Helpdesk/Tile/ConsumablesPageTile.php index c767fb3..2f12986 100644 --- a/src/Helpdesk/Tile/ConsumablesPageTile.php +++ b/src/Helpdesk/Tile/ConsumablesPageTile.php @@ -120,7 +120,7 @@ public function isAvailable(...$args): bool #[Override] public function getDatabaseId(): int { - return $this->fields['id']; + return $this->fields['id'] ?? ''; } #[Override] diff --git a/src/Option.php b/src/Option.php index a0c5b4a..894a89f 100644 --- a/src/Option.php +++ b/src/Option.php @@ -57,11 +57,11 @@ public function showForConsumable($item): ?bool return false; } $data = []; - if ($this->getFromDBByCrit(["consumableitems_id" => $item->fields['id']])) { + if ($this->getFromDBByCrit(["consumableitems_id" => $item->fields['id'] ?? ''])) { $data = $this->fields; } if (count($data) < 1) { - $data = $this->initConfig($item->fields['id']); + $data = $this->initConfig($item->fields['id'] ?? ''); } $this->listOptionsForConsumable($data, $item); return null; @@ -303,7 +303,7 @@ public function prepareInputForUpdate($input) */ public function getMaxCart() { - return $this->fields['max_cart']; + return $this->fields['max_cart'] ?? ''; } /** @@ -315,8 +315,8 @@ public function getMaxCart() */ public function getAllowedGroups(): array { - if (!empty($this->fields['groups'])) { - return json_decode($this->fields['groups'], true); + if (!empty($this->fields['groups'] ?? '')) { + return json_decode($this->fields['groups'] ?? '', true); } return []; } diff --git a/src/Request.php b/src/Request.php index 402e415..2df2e30 100644 --- a/src/Request.php +++ b/src/Request.php @@ -257,7 +257,7 @@ public function showForConsumable($item) return false; } - $data = $this->find(['consumableitems_id' => $item->fields['id']], ["date_mod DESC"]); + $data = $this->find(['consumableitems_id' => $item->fields['id'] ?? ''], ["date_mod DESC"]); $this->listItemsForConsumable($data); } @@ -368,14 +368,14 @@ public function showForUserOrGroup($item, $type, $options = []) 'class' => 'btn btn-primary', 'onclick' => "consumables_searchConsumables('searchConsumables','consumables_formSearchConsumables', 'consumables_searchConsumables','$type')", ]); - echo Html::hidden('requesters_id', ['value' => $item->fields['id']]); + echo Html::hidden('requesters_id', ['value' => $item->fields['id'] ?? '']); echo ""; echo ""; echo ""; Html::closeForm(); echo "
"; - $result = $this->listItemsForUserOrGroup($item->fields['id'], $type, ['begin_date' => $begin_date, + $result = $this->listItemsForUserOrGroup($item->fields['id'] ?? '', $type, ['begin_date' => $begin_date, 'end_date' => $end_date]); echo $result['message']; echo "
"; @@ -774,13 +774,13 @@ public function seeConsumablesInfos($consumableitems_id = 0) // $picture_url = Toolbox::getPictureUrl(); // Toolbox::logInfo($picture_url); if (isset($consumable->fields['pictures'])) { - $pictures = json_decode($consumable->fields['pictures'], true); + $pictures = json_decode($consumable->fields['pictures'] ?? '', true); if (isset($pictures) && is_array($pictures)) { foreach ($pictures as $picture) { $picture_url = Toolbox::getPictureUrl($picture); echo "\"""; - echo "
" . $consumable->fields['comment']; + echo "
" . $consumable->fields['comment'] ?? ''; } } } diff --git a/src/Validation.php b/src/Validation.php index 4f08e0d..b28e10f 100644 --- a/src/Validation.php +++ b/src/Validation.php @@ -473,7 +473,7 @@ public static function processMassiveActionsForOneItemtype( // Get available consumables $outConsumable = []; $availables = $consumable->find([ - 'consumableitems_id' => $item->fields['consumableitems_id'], + 'consumableitems_id' => $item->fields['consumableitems_id'] ?? '', 'date_out' => null, ]); foreach ($availables as $available) { @@ -481,7 +481,7 @@ public static function processMassiveActionsForOneItemtype( } // Check if enough stock - if (!empty($outConsumable) && count($outConsumable) >= $item->fields['number']) { + if (!empty($outConsumable) && count($outConsumable) >= $item->fields['number'] ?? '') { // Give consumable $state = $validation->validationConsumable( $item->fields, @@ -492,11 +492,11 @@ public static function processMassiveActionsForOneItemtype( $added['id'] = $item->getID(); if ($item->update($added)) { $result = [1]; - for ($i = 0; $i < $item->fields['number']; $i++) { + for ($i = 0; $i < $item->fields['number'] ?? ''; $i++) { if (isset($outConsumable[$i]) && $consumable->out( $outConsumable[$i]['id'], - $item->fields['give_itemtype'], - $item->fields['give_items_id'] + $item->fields['give_itemtype'] ?? '', + $item->fields['give_items_id'] ?? '' ) ) { $result[] = 1; @@ -515,7 +515,7 @@ public static function processMassiveActionsForOneItemtype( __('Not enough stock for consumable %s', 'consumables'), Dropdown::getDropdownName( "glpi_consumableitems", - $item->fields['consumableitems_id'] + $item->fields['consumableitems_id'] ?? '' ) ) ); From d2a8e9b93b55fb4619b46816bb8aba091ddf1444 Mon Sep 17 00:00:00 2001 From: Pedro Rocha Date: Mon, 15 Dec 2025 02:26:49 +0000 Subject: [PATCH 12/13] Refactor field access to ensure consistent null handling across multiple classes --- src/Field.php | 6 +++--- src/Helpdesk/Tile/ConsumablesPageTile.php | 2 +- src/Option.php | 10 +++++----- src/Request.php | 10 +++++----- src/Validation.php | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Field.php b/src/Field.php index 75af79c..d5337d9 100644 --- a/src/Field.php +++ b/src/Field.php @@ -167,7 +167,7 @@ public static function addFieldOrderReference(array $params): ?bool echo ""; echo "
"; // Fallback: simple input for order_ref if Html::input is not available - echo ""; + echo ""; echo "
"; echo ""; } @@ -189,7 +189,7 @@ public static function postAddConsumable(ConsumableItem $consumableItem): void $field = new self(); if (isset($consumableItem->input['order_ref'])) { $field->add([ - 'consumableitems_id' => $consumableItem->fields['id'] ?? '', + 'consumableitems_id' => (($consumableItem->fields['id'] ?? '')), 'order_ref' => $consumableItem->input['order_ref'] ]); } @@ -211,7 +211,7 @@ public static function preUpdateConsumable(ConsumableItem $consumableItem): void $field->getFromDBByCrit(['consumableitems_id' => $consumableItem->input['id']]); if (!empty($field->fields)) { $field->update([ - 'id' => $field->fields['id'] ?? '', + 'id' => (($field->fields['id'] ?? '')), 'order_ref' => $consumableItem->input['order_ref'] ]); } else { diff --git a/src/Helpdesk/Tile/ConsumablesPageTile.php b/src/Helpdesk/Tile/ConsumablesPageTile.php index 2f12986..4a306ed 100644 --- a/src/Helpdesk/Tile/ConsumablesPageTile.php +++ b/src/Helpdesk/Tile/ConsumablesPageTile.php @@ -120,7 +120,7 @@ public function isAvailable(...$args): bool #[Override] public function getDatabaseId(): int { - return $this->fields['id'] ?? ''; + return (($this->fields['id'] ?? '')); } #[Override] diff --git a/src/Option.php b/src/Option.php index 894a89f..ed4a6a6 100644 --- a/src/Option.php +++ b/src/Option.php @@ -57,11 +57,11 @@ public function showForConsumable($item): ?bool return false; } $data = []; - if ($this->getFromDBByCrit(["consumableitems_id" => $item->fields['id'] ?? ''])) { + if ($this->getFromDBByCrit(["consumableitems_id" => (($item->fields['id'] ?? ''))])) { $data = $this->fields; } if (count($data) < 1) { - $data = $this->initConfig($item->fields['id'] ?? ''); + $data = $this->initConfig((($item->fields['id'] ?? ''))); } $this->listOptionsForConsumable($data, $item); return null; @@ -303,7 +303,7 @@ public function prepareInputForUpdate($input) */ public function getMaxCart() { - return $this->fields['max_cart'] ?? ''; + return (($this->fields['max_cart'] ?? '')); } /** @@ -315,8 +315,8 @@ public function getMaxCart() */ public function getAllowedGroups(): array { - if (!empty($this->fields['groups'] ?? '')) { - return json_decode($this->fields['groups'] ?? '', true); + if (!empty((($this->fields['groups'] ?? '')))) { + return json_decode((($this->fields['groups'] ?? '')), true); } return []; } diff --git a/src/Request.php b/src/Request.php index 2df2e30..71a23b4 100644 --- a/src/Request.php +++ b/src/Request.php @@ -257,7 +257,7 @@ public function showForConsumable($item) return false; } - $data = $this->find(['consumableitems_id' => $item->fields['id'] ?? ''], ["date_mod DESC"]); + $data = $this->find(['consumableitems_id' => (($item->fields['id'] ?? ''))], ["date_mod DESC"]); $this->listItemsForConsumable($data); } @@ -368,14 +368,14 @@ public function showForUserOrGroup($item, $type, $options = []) 'class' => 'btn btn-primary', 'onclick' => "consumables_searchConsumables('searchConsumables','consumables_formSearchConsumables', 'consumables_searchConsumables','$type')", ]); - echo Html::hidden('requesters_id', ['value' => $item->fields['id'] ?? '']); + echo Html::hidden('requesters_id', ['value' => (($item->fields['id'] ?? ''))]); echo ""; echo ""; echo ""; Html::closeForm(); echo "
"; - $result = $this->listItemsForUserOrGroup($item->fields['id'] ?? '', $type, ['begin_date' => $begin_date, + $result = $this->listItemsForUserOrGroup((($item->fields['id'] ?? '')), $type, ['begin_date' => $begin_date, 'end_date' => $end_date]); echo $result['message']; echo "
"; @@ -774,13 +774,13 @@ public function seeConsumablesInfos($consumableitems_id = 0) // $picture_url = Toolbox::getPictureUrl(); // Toolbox::logInfo($picture_url); if (isset($consumable->fields['pictures'])) { - $pictures = json_decode($consumable->fields['pictures'] ?? '', true); + $pictures = json_decode((($consumable->fields['pictures'] ?? '')), true); if (isset($pictures) && is_array($pictures)) { foreach ($pictures as $picture) { $picture_url = Toolbox::getPictureUrl($picture); echo "\"""; - echo "
" . $consumable->fields['comment'] ?? ''; + echo "
" . (($consumable->fields['comment'] ?? '')); } } } diff --git a/src/Validation.php b/src/Validation.php index b28e10f..4c9850f 100644 --- a/src/Validation.php +++ b/src/Validation.php @@ -473,7 +473,7 @@ public static function processMassiveActionsForOneItemtype( // Get available consumables $outConsumable = []; $availables = $consumable->find([ - 'consumableitems_id' => $item->fields['consumableitems_id'] ?? '', + 'consumableitems_id' => (($item->fields['consumableitems_id'] ?? '')), 'date_out' => null, ]); foreach ($availables as $available) { @@ -481,7 +481,7 @@ public static function processMassiveActionsForOneItemtype( } // Check if enough stock - if (!empty($outConsumable) && count($outConsumable) >= $item->fields['number'] ?? '') { + if (!empty($outConsumable) && count($outConsumable) >= (($item->fields['number'] ?? ''))) { // Give consumable $state = $validation->validationConsumable( $item->fields, @@ -492,11 +492,11 @@ public static function processMassiveActionsForOneItemtype( $added['id'] = $item->getID(); if ($item->update($added)) { $result = [1]; - for ($i = 0; $i < $item->fields['number'] ?? ''; $i++) { + for ($i = 0; $i < (($item->fields['number'] ?? '')); $i++) { if (isset($outConsumable[$i]) && $consumable->out( $outConsumable[$i]['id'], - $item->fields['give_itemtype'] ?? '', - $item->fields['give_items_id'] ?? '' + (($item->fields['give_itemtype'] ?? '')), + (($item->fields['give_items_id'] ?? '')) ) ) { $result[] = 1; @@ -515,7 +515,7 @@ public static function processMassiveActionsForOneItemtype( __('Not enough stock for consumable %s', 'consumables'), Dropdown::getDropdownName( "glpi_consumableitems", - $item->fields['consumableitems_id'] ?? '' + (($item->fields['consumableitems_id'] ?? '')) ) ) ); From 64f1189aacf6e852d02155bc44c44145d20d8120 Mon Sep 17 00:00:00 2001 From: jpnr79 Date: Sun, 28 Dec 2025 21:40:24 +0000 Subject: [PATCH 13/13] Enhance error handling and logging during plugin installation and uninstallation processes --- hook.php | 209 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 132 insertions(+), 77 deletions(-) diff --git a/hook.php b/hook.php index 1c9c612..e529b83 100644 --- a/hook.php +++ b/hook.php @@ -43,21 +43,60 @@ function plugin_consumables_install(): bool { global $DB; - if (!$DB->tableExists("glpi_plugin_consumables_requests")) { - // Install script - $DB->runFile(PLUGIN_CONSUMABLES_DIR . "/install/sql/empty-2.0.1.sql"); - include(PLUGIN_CONSUMABLES_DIR . "/install/install.php"); - install_notifications_consumables(); - } elseif (!$DB->tableExists("glpi_plugin_consumables_options")) { - $DB->runFile(PLUGIN_CONSUMABLES_DIR . "/install/sql/update-1.2.2.sql"); - } elseif (!$DB->fieldExists("glpi_plugin_consumables_options", "consumableitems_id")) { - $DB->runFile(PLUGIN_CONSUMABLES_DIR . "/install/sql/update-2.0.1.sql"); - } - - Profile::initProfile(); - Profile::createFirstAccess($_SESSION['glpiactiveprofile']['id']); + try { + if (!$DB->tableExists("glpi_plugin_consumables_requests")) { + // Install script + $DB->runFile(PLUGIN_CONSUMABLES_DIR . "/install/sql/empty-2.0.1.sql"); + include(PLUGIN_CONSUMABLES_DIR . "/install/install.php"); + install_notifications_consumables(); + } elseif (!$DB->tableExists("glpi_plugin_consumables_options")) { + $DB->runFile(PLUGIN_CONSUMABLES_DIR . "/install/sql/update-1.2.2.sql"); + } elseif (!$DB->fieldExists("glpi_plugin_consumables_options", "consumableitems_id")) { + $DB->runFile(PLUGIN_CONSUMABLES_DIR . "/install/sql/update-2.0.1.sql"); + } - return true; + Profile::initProfile(); + $profile_id = null; + $session_status = function_exists('session_status') ? session_status() : PHP_SESSION_DISABLED; + if ($session_status === PHP_SESSION_ACTIVE && isset($_SESSION) && is_array($_SESSION)) { + if (array_key_exists('glpiactiveprofile', $_SESSION) && is_array($_SESSION['glpiactiveprofile']) && array_key_exists('id', $_SESSION['glpiactiveprofile']) && !empty($_SESSION['glpiactiveprofile']['id'])) { + $profile_id = $_SESSION['glpiactiveprofile']['id']; + } + } + if ($profile_id !== null) { + Profile::createFirstAccess($profile_id); + } else if ($session_status === PHP_SESSION_ACTIVE && isset($_SESSION) && is_array($_SESSION) && array_key_exists('glpiname', $_SESSION)) { + if (class_exists('Toolbox')) { + Toolbox::logInFile('consumables', sprintf( + 'WARNING [%s:%s] glpiactiveprofile not set or invalid during install, user=%s, session_keys=%s', + __FILE__, __FUNCTION__, $_SESSION['glpiname'], implode(',', array_keys($_SESSION)) + )); + } + } else { + if (class_exists('Toolbox')) { + Toolbox::logInFile('consumables', sprintf( + 'WARNING [%s:%s] Session not active or missing, session_status=%s', + __FILE__, __FUNCTION__, (string)$session_status + )); + } + } + if (class_exists('Toolbox')) { + Toolbox::logInFile('consumables', sprintf( + 'INFO [%s:%s] Plugin installed successfully by user=%s', + __FILE__, __FUNCTION__, $_SESSION['glpiname'] ?? 'unknown' + )); + } + return true; + } catch (\Exception $e) { + if (class_exists('Toolbox')) { + Toolbox::logInFile('consumables', sprintf( + 'ERROR [%s:%s] Install error: %s, user=%s', + __FILE__, __FUNCTION__, $e->getMessage(), $_SESSION['glpiname'] ?? 'unknown' + )); + } + error_log("Consumables install error: " . $e->getMessage()); + return false; + } } @@ -69,80 +108,96 @@ function plugin_consumables_install(): bool function plugin_consumables_uninstall(): bool { global $DB; - - $tables = [ - "glpi_plugin_consumables_profiles", - "glpi_plugin_consumables_requests", - "glpi_plugin_consumables_options", - "glpi_plugin_consumables_fields" - ]; - - foreach ($tables as $table) { - $DB->dropTable($table, true); - } - - $notif = new Notification(); - $options = ['itemtype' => Request::class]; - foreach ($DB->request([ - 'FROM' => 'glpi_notifications', - 'WHERE' => $options]) as $data) { - $notif->delete($data); - } - - //templates - $template = new NotificationTemplate(); - $translation = new NotificationTemplateTranslation(); - $notif_template = new Notification_NotificationTemplate(); - $options = ['itemtype' => Request::class]; - foreach ($DB->request([ - 'FROM' => 'glpi_notificationtemplates', - 'WHERE' => $options]) as $data) { - $options_template = [ - 'notificationtemplates_id' => $data['id'] + try { + $tables = [ + "glpi_plugin_consumables_profiles", + "glpi_plugin_consumables_requests", + "glpi_plugin_consumables_options", + "glpi_plugin_consumables_fields" ]; + foreach ($tables as $table) { + $DB->dropTable($table, true); + } + + $notif = new Notification(); + $options = ['itemtype' => Request::class]; foreach ($DB->request([ - 'FROM' => 'glpi_notificationtemplatetranslations', - 'WHERE' => $options_template]) as $data_template) { - $translation->delete($data_template); + 'FROM' => 'glpi_notifications', + 'WHERE' => $options]) as $data) { + $notif->delete($data); } - $template->delete($data); + //templates + $template = new NotificationTemplate(); + $translation = new NotificationTemplateTranslation(); + $notif_template = new Notification_NotificationTemplate(); + $options = ['itemtype' => Request::class]; foreach ($DB->request([ - 'FROM' => 'glpi_notifications_notificationtemplates', - 'WHERE' => $options_template]) as $data_template) { - $notif_template->delete($data_template); + 'FROM' => 'glpi_notificationtemplates', + 'WHERE' => $options]) as $data) { + $options_template = [ + 'notificationtemplates_id' => $data['id'] + ]; + + foreach ($DB->request([ + 'FROM' => 'glpi_notificationtemplatetranslations', + 'WHERE' => $options_template]) as $data_template) { + $translation->delete($data_template); + } + $template->delete($data); + + foreach ($DB->request([ + 'FROM' => 'glpi_notifications_notificationtemplates', + 'WHERE' => $options_template]) as $data_template) { + $notif_template->delete($data_template); + } } - } - $itemtypes = [ - 'Alert', - 'DisplayPreference', - 'Document_Item', - 'ImpactItem', - 'Item_Ticket', - 'Link_Itemtype', - 'Notepad', - 'SavedSearch', - 'DropdownTranslation', - 'NotificationTemplate', - 'Notification' - ]; - foreach ($itemtypes as $itemtype) { - $item = new $itemtype; - $item->deleteByCriteria(['itemtype' => Request::class]); - } + $itemtypes = [ + 'Alert', + 'DisplayPreference', + 'Document_Item', + 'ImpactItem', + 'Item_Ticket', + 'Link_Itemtype', + 'Notepad', + 'SavedSearch', + 'DropdownTranslation', + 'NotificationTemplate', + 'Notification' + ]; + foreach ($itemtypes as $itemtype) { + $item = new $itemtype; + $item->deleteByCriteria(['itemtype' => Request::class]); + } - // Delete rights associated with the plugin - $profileRight = new ProfileRight(); - foreach (Profile::getAllRights() as $right) { - $profileRight->deleteByCriteria(['name' => $right['field']]); - } + // Delete rights associated with the plugin + $profileRight = new ProfileRight(); + foreach (Profile::getAllRights() as $right) { + $profileRight->deleteByCriteria(['name' => $right['field']]); + } - Menu::removeRightsFromSession(); - Profile::removeRightsFromSession(); + Menu::removeRightsFromSession(); + Profile::removeRightsFromSession(); - return true; + if (class_exists('Toolbox')) { + Toolbox::logInFile('consumables', sprintf( + 'INFO [%s:%s] Plugin uninstalled successfully by user=%s', + __FILE__, __FUNCTION__, $_SESSION['glpiname'] ?? 'unknown' + )); + } + return true; + } catch (\Exception $e) { + if (class_exists('Toolbox')) { + Toolbox::logInFile('consumables', sprintf( + 'ERROR [%s:%s] Uninstall error: %s, user=%s', + __FILE__, __FUNCTION__, $e->getMessage(), $_SESSION['glpiname'] ?? 'unknown' + )); + } + error_log("Consumables uninstall error: " . $e->getMessage()); + return false; + } } // Hook done on purge item case