diff --git a/app/src/Controller/Component/RegistryAuthComponent.php b/app/src/Controller/Component/RegistryAuthComponent.php index 656f27689..603457ae2 100644 --- a/app/src/Controller/Component/RegistryAuthComponent.php +++ b/app/src/Controller/Component/RegistryAuthComponent.php @@ -411,7 +411,8 @@ protected function calculatePermissions(?int $id=null): array { ->applyOptions(['archived' => true]); // QueryModificationTrait - $getActionMethod = "get{$reqAction}Contains"; + $reqActionCapitlize = ucfirst($reqAction) ; + $getActionMethod = "get{$reqActionCapitlize}Contains"; if(method_exists($table, $getActionMethod) && $table->$getActionMethod()) { $query = $query->contain($table->$getActionMethod()); } diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php index d4f332a8c..8cbd50898 100644 --- a/app/src/View/Helper/FieldHelper.php +++ b/app/src/View/Helper/FieldHelper.php @@ -428,7 +428,7 @@ public function formField(string $fieldName, string $fieldType = null, array $fieldSelectOptions = null, string $fieldNameAlias = null, - bool $labelIsTextOnly = null): string + bool $labelIsTextOnly = null): string { $fieldArgs = $fieldOptions ?? []; $fieldArgs['label'] = $fieldOptions['label'] ?? false; diff --git a/app/src/View/Helper/TabHelper.php b/app/src/View/Helper/TabHelper.php index c3f8db031..b692b8004 100644 --- a/app/src/View/Helper/TabHelper.php +++ b/app/src/View/Helper/TabHelper.php @@ -78,6 +78,9 @@ public function constructLinkUrl(string $tab, string|int $curId, bool $isNested if(str_ends_with($tab, '.Plugin')) { // This is always the second tab of the plugin and it is configuration $controller = $curController; + $controllerQualifiedName = StringUtilities::modelNameToQualifiedModelName($controller); + $plugin = StringUtilities::pluginPlugin($controllerQualifiedName); + $plugin = !empty($plugin) ? $plugin : null; $action = 'configure'; } else if (str_ends_with($tab, '.Hierarchy')) { $modelName = $this->retrievePluginName($tab, (int)$curId); @@ -236,7 +239,7 @@ public function getLinkClass(string $tab, bool $isNested = false, array $nesting // Always mark active the parent Tab !$isNested && $parentModelForNested !== null && $tab === $parentModelForNested && in_array($fullModelName, $nestings), // Match Configuration and Hierarchy tabs - isset($plugin) && str_contains($tab, '.Plugin') && $curAction === 'edit', + isset($plugin) && str_contains($tab, '.Plugin') && $curAction === 'configure', isset($plugin) && str_contains($tab, '.Hierarchy') && $curAction === 'index', // Matches the action tab links, e.g. FileSource/search $tab === "{$curController}@action.{$curAction}" => 'nav-link active', @@ -547,14 +550,37 @@ public function retrievePluginName(string $tab, int $curId): string { // Get the name of the Core Model $coreModel = substr($tab, 0, strrpos($tab, '.')); - $ModelTable = TableRegistry::getTableLocator()->get($coreModel); - $response = $ModelTable - ->find() - ->select(['plugin']) - ->where(['id' => $curId]) - ->first(); - return $response?->plugin; + // Ensure we resolve plugin tables correctly + $qualifiedCoreModel = StringUtilities::modelNameToQualifiedModelName($coreModel); + $ModelTable = TableRegistry::getTableLocator()->get($qualifiedCoreModel); + + // If this table actually has a "plugin" column, fetch the plugin model path from the record. + // Example (pluggable wrapper tables): + // ProvisioningTargets.plugin = "LdapConnector.LdapProvisioners" + if ($ModelTable->getSchema()->hasColumn('plugin')) { + $response = $ModelTable + ->find() + ->select(['plugin']) + ->where(['id' => $curId]) + ->first(); + + return (string)($response?->plugin ?? ''); + } + + // Otherwise, this isn't a pluggable wrapper record (no plugin column). + // Fall back to the table's registry alias, which is already in Plugin.Model form + // for plugin tables. + // + // Example: + // Table registry alias: "LdapConnector.PersonSchemas" + // This avoids querying a non-existent "plugin" column (eg person_schemas.plugin). + if (method_exists($ModelTable, 'getRegistryAlias')) { + return (string)$ModelTable->getRegistryAlias(); + } + + // Last resort: return the qualified name we used to locate the table. + return (string)$qualifiedCoreModel; } /** diff --git a/app/templates/element/form/listItem.php b/app/templates/element/form/listItem.php index 9357d3a46..50d7aa60d 100644 --- a/app/templates/element/form/listItem.php +++ b/app/templates/element/form/listItem.php @@ -34,18 +34,24 @@ // - add a prefix and create a namespace // - wrap them in an array. // We choose the latter. +use App\Lib\Util\StringUtilities; + $this->set('fieldName', $arguments['fieldName']); $fieldName = $arguments['fieldName']; $this->set('vv_field_arguments', $arguments); +$qualifiedModelName = StringUtilities::entityToPluginClassName($this->Field->getEntity()); +$plugin = StringUtilities::pluginPlugin($qualifiedModelName); +$controller = StringUtilities::pluginModel($qualifiedModelName); + // If an attribute is frozen, inject a special link to unfreeze it, since // the attribute is read-only and the admin can't simply uncheck the setting if($fieldName == 'frozen' && $this->Field->getEntity()->frozen) { $url = [ 'label' => __d('operation', 'unfreeze'), 'url' => [ - 'plugin' => null, - 'controller' => \App\Lib\Util\StringUtilities::entityToClassname($this->Field->getEntity()), + 'plugin' => !empty($plugin) ? $plugin : null, + 'controller' => $controller, 'action' => 'unfreeze', $this->Field->getEntity()->id ] @@ -63,8 +69,8 @@ $url = [ 'label' => __d('operation', 'configure.plugin'), 'url' => [ - 'plugin' => null, - 'controller' => \App\Lib\Util\StringUtilities::entityToClassname($this->Field->getEntity()), + 'plugin' => !empty($plugin) ? $plugin : null, + 'controller' => $controller, 'action' => 'configure', $this->Field->getEntity()->id ] diff --git a/app/templates/element/subnavigation/supertitle.php b/app/templates/element/subnavigation/supertitle.php index 71d6a4205..f08d75591 100644 --- a/app/templates/element/subnavigation/supertitle.php +++ b/app/templates/element/subnavigation/supertitle.php @@ -62,7 +62,7 @@ if ( (!empty($vv_obj) || !empty($$objectName)) && !empty($this->getPlugin()) - && $vv_subnavigation_tabs[0] !== StringUtilities::entityToClassName($vv_bc_parent_obj) + && $vv_subnavigation_tabs[0] !== $vv_bc_parent_obj->getSource() ) { $object = $vv_obj ?? $$objectName?->items()?->first(); if ($object === null) { @@ -73,7 +73,7 @@ // If we get here, it means that neither the request object nor its parent can give us a supertitle. // We need to fetch all the ids and get the supertitle from the root tab/node $results = []; - TableUtilities::treeTraversalFromId(StringUtilities::entityToClassName($object), (int)$object->id, $results); + TableUtilities::treeTraversalFromId($object->getSource(), (int)$object->id, $results); $superTitleModelReference = $this->Tab->getModelTableReference($vv_subnavigation_tabs[0]); $superTitleModelDisplayField = $superTitleModelReference->getDisplayField(); $superTitleModelId = $results[$vv_subnavigation_tabs[0]]; diff --git a/app/templates/element/subnavigation/tabTitle.php b/app/templates/element/subnavigation/tabTitle.php index f17c4b894..540a499ff 100644 --- a/app/templates/element/subnavigation/tabTitle.php +++ b/app/templates/element/subnavigation/tabTitle.php @@ -57,7 +57,11 @@ $tabToTableName = Inflector::tableize(Inflector::singularize($tab)); // Plugin Configuration Tab -if (str_contains($tab, '.') && in_array('edit', $navigation_action[$tab], true)) { +if ( + str_contains($tab, '.') + && in_array('edit', $navigation_action[$tab], true) + && array_search($tab, $vv_subnavigation_tabs, true) !== 0 +) { $title = __d('operation','configure.plugin'); } else if (str_contains($tab, '@action.')) { // Top Links/Actions [$modelName, ] = explode('@', $tab);