From 6f94427f6a0e5cc03cd15287ef6dbe6aca82dcce Mon Sep 17 00:00:00 2001 From: Yoan Bozhilov Date: Tue, 28 Apr 2026 11:14:58 +0300 Subject: [PATCH 1/4] Add Orderable capacity and per-user permission filtering for GLPI 11 custom assets Following review feedback on pluginsGLPI#574, replace the unconditional auto-registration of all active custom asset definitions with an explicit, admin-controlled opt-in via a new Orderable capacity: - Introduce PluginOrderOrderableCapacity (extends \Glpi\Asset\Capacity\AbstractCapacity). Admins enable/disable it per asset definition under Setup -> Asset definitions -> {asset} -> Capacities. Provides label, icon, description, and a usage count based on existing PluginOrderReference rows. - In plugin_init_order(), register the capacity with AssetDefinitionManager, then iterate active definitions and append the generated asset class to $ORDER_TYPES only when: 1. the Orderable capacity is enabled on that definition, AND 2. the current user passes $class::canView() (which delegates to the asset's AssignableItem READ check + is_active). - The class_exists() guard preserves backward compatibility on GLPI <= 10.x. End-to-end tested on GLPI 11.0.6, PHP 8.3, Order plugin 2.12.6: - Capacity appears in the Capacities tab with proper label/icon/description. - With capacity disabled: custom asset is absent from the Item type dropdown. - With capacity enabled: custom asset appears, references can be created, Generate item massive action renders and submits, items reach Taken delivery state, and generated assets appear in the Assets section. - Native itemtypes remain unaffected. Addresses review comments from @stonebuzz on pluginsGLPI#574. --- CHANGELOG.md | 3 +++ inc/orderablecapacity.class.php | 45 +++++++++++++++++++++++++++++++++ setup.php | 23 +++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 inc/orderablecapacity.class.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 233952fe40..4b324348fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] +### Added +- Add an `Orderable` capacity for GLPI 11 custom asset definitions, allowing each definition to opt-in to being used as a Product reference itemtype. Asset classes are filtered by per-user read permission. + ### Fixed - Fix generate associated item massive action diff --git a/inc/orderablecapacity.class.php b/inc/orderablecapacity.class.php new file mode 100644 index 0000000000..951637c48a --- /dev/null +++ b/inc/orderablecapacity.class.php @@ -0,0 +1,45 @@ + + * Asset definitions -> {your asset} -> Capacities), the corresponding + * generated asset class is appended to $ORDER_TYPES and becomes selectable + * as an Item type when creating a Product reference. + */ +class PluginOrderOrderableCapacity extends \Glpi\Asset\Capacity\AbstractCapacity +{ + public function getLabel(): string + { + return __('Orderable', 'order'); + } + + public function getIcon(): string + { + return 'ti ti-shopping-cart'; + } + + public function getDescription(): string + { + return __( + 'Allow this asset to be referenced as a Product reference and ' + . 'generated from the Generate item massive action.', + 'order' + ); + } + + public function getCapacityUsageDescription(string $classname): string + { + $count = 0; + if (class_exists('PluginOrderReference')) { + $count = countElementsInTable( + \PluginOrderReference::getTable(), + ['itemtype' => $classname] + ); + } + return sprintf( + _n('Used by %d order reference', 'Used by %d order references', $count, 'order'), + $count + ); + } +} diff --git a/setup.php b/setup.php index 7cd7cfd6b0..d59fdfcc4f 100644 --- a/setup.php +++ b/setup.php @@ -125,6 +125,29 @@ function plugin_init_order() 'Pdu', ]; + + // Register the Orderable capacity for GLPI 11 custom assets and append + // any custom asset class that has it enabled to $ORDER_TYPES, provided + // the current user is allowed to view it. + if (class_exists(\Glpi\Asset\AssetDefinitionManager::class)) { + $asset_manager = \Glpi\Asset\AssetDefinitionManager::getInstance(); + $orderable_capacity = new PluginOrderOrderableCapacity(); + $asset_manager->registerCapacity($orderable_capacity); + $asset_manager->bootDefinitions(); + foreach ($asset_manager->getDefinitions(true) as $definition) { + if (!$definition->hasCapacityEnabled($orderable_capacity)) { + continue; + } + $custom_asset_class = $definition->getAssetClassName(); + if ( + !in_array($custom_asset_class, $ORDER_TYPES, true) + && $custom_asset_class::canView() + ) { + $ORDER_TYPES[] = $custom_asset_class; + } + } + } + $CFG_GLPI['plugin_order_types'] = $ORDER_TYPES; $PLUGIN_HOOKS['pre_item_purge']['order'] = [ From 5fd09757a11bfe1962385648d42c224f9ac0139d Mon Sep 17 00:00:00 2001 From: Yoan Bozhilov Date: Tue, 28 Apr 2026 17:22:12 +0300 Subject: [PATCH 2/4] Clean up linked plugin data when Orderable capacity is disabled Following further review feedback on pluginsGLPI#574, implement the onCapacityDisabled() callback on PluginOrderOrderableCapacity so that disabling the Orderable capacity on a custom asset definition removes the plugin data that becomes meaningless once the asset class is no longer orderable. The cascade order matters: PluginOrderReference::pre_deleteItem() refuses deletion while a reference is still in use by orders_items rows, so child records (order line items) are removed before parent records (product references). Both passes target only rows whose itemtype matches the disabled custom asset class, leaving native itemtypes and other custom assets untouched. Free-form references (PluginOrderReferenceFree) are intentionally not touched, as they are standalone records that do not store any itemtype. End-to-end tested on GLPI 11.0.6, PHP 8.3 on two independent installations: - Disabling the capacity on a definition with linked references and order line items removes both, with native itemtypes and other custom assets untouched. - Re-enabling the capacity restores the asset class in the Item type dropdown; new references and order items can be created normally. - The capacity usage description (visible in the Capacities tab and in the GLPI confirmation dialog) shows the correct count of order references that will be removed if the admin proceeds. Addresses follow-up review comment from @stonebuzz on pluginsGLPI#574. --- CHANGELOG.md | 2 +- inc/orderablecapacity.class.php | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b324348fe..279f830d69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] ### Added -- Add an `Orderable` capacity for GLPI 11 custom asset definitions, allowing each definition to opt-in to being used as a Product reference itemtype. Asset classes are filtered by per-user read permission. +- Add an `Orderable` capacity for GLPI 11 custom asset definitions, allowing each definition to opt-in to being used as a Product reference itemtype. Asset classes are filtered by per-user read permission. Disabling the capacity on a definition cleans up its order line items and product references. ### Fixed diff --git a/inc/orderablecapacity.class.php b/inc/orderablecapacity.class.php index 951637c48a..47c4578da1 100644 --- a/inc/orderablecapacity.class.php +++ b/inc/orderablecapacity.class.php @@ -42,4 +42,32 @@ public function getCapacityUsageDescription(string $classname): string $count ); } + + /** + * Clean up plugin data linked to the asset class when the capacity is + * disabled on its definition: remove Product references targeting the + * class and order line items that link orders to instances of the class. + * Free-form references are intentionally left untouched, as they are + * standalone records that do not reference any itemtype. + */ + public function onCapacityDisabled(string $classname, \Glpi\Asset\CapacityConfig $config): void + { + // Delete order line items first: PluginOrderReference::pre_deleteItem() + // refuses deletion while references are still in use by orders_items, + // so child records must be removed before parent references. + if (class_exists('PluginOrderOrder_Item')) { + (new \PluginOrderOrder_Item())->deleteByCriteria( + ['itemtype' => $classname], + force: true, + history: false + ); + } + if (class_exists('PluginOrderReference')) { + (new \PluginOrderReference())->deleteByCriteria( + ['itemtype' => $classname], + force: true, + history: false + ); + } + } } From 13c246c5267599cfdf2b5693b28798be3387dcfe Mon Sep 17 00:00:00 2001 From: bygadd <40861440+bygadd@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:28:24 +0300 Subject: [PATCH 3/4] Update CHANGELOG.md Co-authored-by: Stanislas --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 279f830d69..9b01d2050d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] ### Added -- Add an `Orderable` capacity for GLPI 11 custom asset definitions, allowing each definition to opt-in to being used as a Product reference itemtype. Asset classes are filtered by per-user read permission. Disabling the capacity on a definition cleans up its order line items and product references. +- Add an `Orderable` capacity for GLPI 11 custom asset definitions. ### Fixed From edcc339b29d614b74e4f936bb662885248b5a33e Mon Sep 17 00:00:00 2001 From: Yoan Bozhilov Date: Thu, 30 Apr 2026 10:42:22 +0300 Subject: [PATCH 4/4] Fix code style: licence header, namespace imports, statement spacing Apply the code style fixes flagged by the PHP-CS-Fixer (PER-CS), Rector, and licence-headers-check tools that run as part of the GLPI plugin CI pipeline. inc/orderablecapacity.class.php: - Add the canonical Order plugin licence header. - Import Glpi\Asset\Capacity\AbstractCapacity and Glpi\Asset\CapacityConfig via use statements instead of fully-qualified names. - Drop leading backslashes on PluginOrderReference and PluginOrderOrder_Item references that resolve to the global namespace. - Add the blank lines between methods, after closing if blocks before return statements, and between consecutive if blocks expected by NewlineAfterStatementRector. - Add the trailing commas on multi-line argument lists expected by PER-CS. setup.php: - Import Glpi\Asset\AssetDefinitionManager via a use statement and use the short name in the class_exists() check and the getInstance() call. - Add the blank line after the `if (!hasCapacityEnabled) { continue; }` guard inside the foreach. - Separate the `use` import group from `use function Safe\define` with a blank line as required by PER-CS. No functional changes. End-to-end re-tested on GLPI 11.0.6 / PHP 8.3: the Orderable capacity still appears, custom asset classes are still filtered into $ORDER_TYPES based on capacity + read permission, and the onCapacityDisabled cleanup still removes order line items and product references for the disabled class while leaving native itemtypes and other custom assets untouched. --- inc/orderablecapacity.class.php | 61 +++++++++++++++++++++++++-------- setup.php | 7 ++-- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/inc/orderablecapacity.class.php b/inc/orderablecapacity.class.php index 47c4578da1..4cadf05037 100644 --- a/inc/orderablecapacity.class.php +++ b/inc/orderablecapacity.class.php @@ -1,5 +1,36 @@ . + * ------------------------------------------------------------------------- + * @copyright Copyright (C) 2009-2023 by Order plugin team. + * @license GPLv3 https://www.gnu.org/licenses/gpl-3.0.html + * @link https://github.com/pluginsGLPI/order + * ------------------------------------------------------------------------- + */ + +use Glpi\Asset\Capacity\AbstractCapacity; +use Glpi\Asset\CapacityConfig; + /** * Capacity that flags a GLPI 11 custom asset definition as orderable * through the Order plugin. When enabled on an asset definition (Setup -> @@ -7,7 +38,7 @@ * generated asset class is appended to $ORDER_TYPES and becomes selectable * as an Item type when creating a Product reference. */ -class PluginOrderOrderableCapacity extends \Glpi\Asset\Capacity\AbstractCapacity +class PluginOrderOrderableCapacity extends AbstractCapacity { public function getLabel(): string { @@ -24,7 +55,7 @@ public function getDescription(): string return __( 'Allow this asset to be referenced as a Product reference and ' . 'generated from the Generate item massive action.', - 'order' + 'order', ); } @@ -33,40 +64,40 @@ public function getCapacityUsageDescription(string $classname): string $count = 0; if (class_exists('PluginOrderReference')) { $count = countElementsInTable( - \PluginOrderReference::getTable(), - ['itemtype' => $classname] + PluginOrderReference::getTable(), + ['itemtype' => $classname], ); } + return sprintf( _n('Used by %d order reference', 'Used by %d order references', $count, 'order'), - $count + $count, ); } /** * Clean up plugin data linked to the asset class when the capacity is - * disabled on its definition: remove Product references targeting the - * class and order line items that link orders to instances of the class. + * disabled on its definition: remove order line items first (parent + * Product references refuse deletion via pre_deleteItem() while still + * referenced by orders_items), then remove the references themselves. * Free-form references are intentionally left untouched, as they are * standalone records that do not reference any itemtype. */ - public function onCapacityDisabled(string $classname, \Glpi\Asset\CapacityConfig $config): void + public function onCapacityDisabled(string $classname, CapacityConfig $config): void { - // Delete order line items first: PluginOrderReference::pre_deleteItem() - // refuses deletion while references are still in use by orders_items, - // so child records must be removed before parent references. if (class_exists('PluginOrderOrder_Item')) { - (new \PluginOrderOrder_Item())->deleteByCriteria( + (new PluginOrderOrder_Item())->deleteByCriteria( ['itemtype' => $classname], force: true, - history: false + history: false, ); } + if (class_exists('PluginOrderReference')) { - (new \PluginOrderReference())->deleteByCriteria( + (new PluginOrderReference())->deleteByCriteria( ['itemtype' => $classname], force: true, - history: false + history: false, ); } } diff --git a/setup.php b/setup.php index d59fdfcc4f..a918351bd4 100644 --- a/setup.php +++ b/setup.php @@ -28,6 +28,8 @@ * ------------------------------------------------------------------------- */ +use Glpi\Asset\AssetDefinitionManager; + use function Safe\define; define('PLUGIN_ORDER_VERSION', '2.12.6'); @@ -129,8 +131,8 @@ function plugin_init_order() // Register the Orderable capacity for GLPI 11 custom assets and append // any custom asset class that has it enabled to $ORDER_TYPES, provided // the current user is allowed to view it. - if (class_exists(\Glpi\Asset\AssetDefinitionManager::class)) { - $asset_manager = \Glpi\Asset\AssetDefinitionManager::getInstance(); + if (class_exists(AssetDefinitionManager::class)) { + $asset_manager = AssetDefinitionManager::getInstance(); $orderable_capacity = new PluginOrderOrderableCapacity(); $asset_manager->registerCapacity($orderable_capacity); $asset_manager->bootDefinitions(); @@ -138,6 +140,7 @@ function plugin_init_order() if (!$definition->hasCapacityEnabled($orderable_capacity)) { continue; } + $custom_asset_class = $definition->getAssetClassName(); if ( !in_array($custom_asset_class, $ORDER_TYPES, true)