From c962f40afbedf82802d19ac6e5b235d0757fc8ec Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 6 Apr 2026 14:40:37 +0300 Subject: [PATCH] Fix incorrect tab navigation link ID for plugin model index views --- app/src/Lib/Util/StringUtilities.php | 37 ++++++++++++++++++++++++++++ app/src/Lib/Util/TableUtilities.php | 5 ++-- app/src/View/Helper/TabHelper.php | 14 ++++++++--- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/app/src/Lib/Util/StringUtilities.php b/app/src/Lib/Util/StringUtilities.php index c7cce8012..bbed926cf 100644 --- a/app/src/Lib/Util/StringUtilities.php +++ b/app/src/Lib/Util/StringUtilities.php @@ -29,6 +29,7 @@ namespace App\Lib\Util; +use Cake\Core\Plugin; use Cake\ORM\TableRegistry; use \Cake\Utility\Inflector; @@ -495,6 +496,42 @@ public static function pluginModel(string $s): string { return $s; } + + /** + * Convert a foreign key into the full qualified (plugin) model name (eg: Reports.Report). + * + * @param string $foreignKey Foreign key name (eg: report_id) + * @return string Pluralized plugin model name. + * @since COmanage Registry v5.2.0 + */ + public static function foreignKeyToQualifiedModelName(string $foreignKey): string { + $primaryLinkModelName = StringUtilities::foreignKeyToClassName($foreignKey); + + return self::modelNameToQualifiedModelName($primaryLinkModelName); + } + + + /** + * Determine the qualified model name for a given model, taking into account plugins. + * + * @param string $modelName Model name to qualify with plugin if applicable. + * @return string Fully qualified plugin name or base model name if no plugin. + * @since COmanage Registry v5.2.0 + */ + public static function modelNameToQualifiedModelName(string $modelName): string { + $plugin = null; + if (!class_exists('App\\Model\\Table\\' . $modelName . 'Table')) { + foreach (Plugin::loaded() as $loadedPlugin) { + if (class_exists($loadedPlugin . '\\Model\\Table\\' . $modelName . 'Table')) { + $plugin = $loadedPlugin; + break; + } + } + } + + return StringUtilities::qualifyModelPath($modelName, $plugin); + } + /** * Determine the plugin component of a Plugin path. * diff --git a/app/src/Lib/Util/TableUtilities.php b/app/src/Lib/Util/TableUtilities.php index 66165888c..e1b9be4e0 100644 --- a/app/src/Lib/Util/TableUtilities.php +++ b/app/src/Lib/Util/TableUtilities.php @@ -230,7 +230,8 @@ public static function treeTraversalFromPrimaryLink( $collection = $db->getSchemaCollection(); $listOfTables = $collection->listTables(); - $primaryLinkModelName = StringUtilities::foreignKeyToClassName(($primaryLinkKey)); + $primaryLinkModelName = StringUtilities::foreignKeyToQualifiedModelName($primaryLinkKey); + // Check if the table exists. // We can not handle @@ -259,7 +260,7 @@ public static function treeTraversalFromPrimaryLink( && $col !== $primaryLinkKey && str_ends_with($col, '_id') ) { - $fkModel = StringUtilities::foreignKeyToClassName(($col)); + $fkModel = StringUtilities::foreignKeyToQualifiedModelName(($col)); $fk_table = Inflector::underscore($fkModel); if (\in_array($fk_table, $listOfTables, true)) { self::treeTraversalFromPrimaryLink($col, $val, $results); diff --git a/app/src/View/Helper/TabHelper.php b/app/src/View/Helper/TabHelper.php index 9ba4d85de..5bef2078f 100644 --- a/app/src/View/Helper/TabHelper.php +++ b/app/src/View/Helper/TabHelper.php @@ -119,7 +119,8 @@ public function constructLinkUrl(string $tab, string|int $curId, bool $isNested } // I will get the id from the associated ids table - $url[] = $vv_associated_ids[$controller]; + $modelPath = StringUtilities::qualifyModelPath($controller, $plugin); + $url[] = $vv_associated_ids[$modelPath]; } else { $url[] = $curId; } @@ -145,7 +146,12 @@ public function getDeepNestedId(array $linkFilter): ?int // Generate the ModelName and instantiate the linked Table $modelName = StringUtilities::foreignKeyToClassName($linkFilterForeignKey); $table = TableRegistry::getTableLocator()->get($modelName); - $linkFilterId = $vv_associated_ids[Inflector::pluralize($modelName)] ?? null; + + $pluralModelName = Inflector::pluralize($modelName); + $linkFilterId = $vv_associated_ids[$pluralModelName] + ?? current(array_filter($vv_associated_ids, fn($k) => str_ends_with($k, '.' . $pluralModelName), ARRAY_FILTER_USE_KEY)) + ?: null; + if($linkFilterId !== null) { return (int)$linkFilterId; } @@ -253,7 +259,7 @@ public function getCurrentId(string $tabName = null, bool $isNested = false): in $vv_primary_link = $this->getView()->get('vv_primary_link'); $vv_bc_title_links = $this->getView()->get('vv_bc_title_links'); $request = $this->getView()->getRequest(); - $curController = $request->getParam('controller'); + $curController = StringUtilities::getQualifiedName($request->getParam('plugin'), $request->getParam('controller')); $vv_sub_nav_attributes = $this->getView()->get('vv_sub_nav_attributes'); $tab_actions = !$isNested ? $vv_sub_nav_attributes['action'] : $vv_sub_nav_attributes['nested']['action']; $tabs = !$isNested ? $vv_sub_nav_attributes['tabs'] : $vv_sub_nav_attributes['nested']['tabs']; @@ -265,7 +271,7 @@ public function getCurrentId(string $tabName = null, bool $isNested = false): in // Get the ids of all the associated Model records $results = []; if ($request->getQuery($vv_primary_link) !== null) { - TableUtilities::treeTraversalFromPrimaryLink($vv_primary_link, (int)$tid, $results, ); + TableUtilities::treeTraversalFromPrimaryLink($vv_primary_link, (int)$tid, $results); } else { TableUtilities::treeTraversalFromId($curController, (int)$tid, $results); }