From e091a269019fb68100226425120d987667840225 Mon Sep 17 00:00:00 2001 From: Benn Oshrin Date: Wed, 3 Sep 2025 20:25:17 -0400 Subject: [PATCH] Patch BreadcrumbsComponent (CFM-274) --- .../Component/BreadcrumbComponent.php | 171 +++++++++--------- 1 file changed, 89 insertions(+), 82 deletions(-) diff --git a/app/src/Controller/Component/BreadcrumbComponent.php b/app/src/Controller/Component/BreadcrumbComponent.php index 349d16c7e..44e24920c 100644 --- a/app/src/Controller/Component/BreadcrumbComponent.php +++ b/app/src/Controller/Component/BreadcrumbComponent.php @@ -35,8 +35,9 @@ use \Cake\Utility\Inflector; use \App\Lib\Util\StringUtilities; -class BreadcrumbComponent extends Component -{ +class BreadcrumbComponent extends Component { + use \App\Lib\Traits\LabeledLogTrait; + /* * Breadcrump example * COmanage Registry > Alfa Community > Configuration > External Identity Sources > Test Filesource Plugin > Configure Test Filesource Plugin @@ -176,95 +177,101 @@ public function beforeRender(EventInterface $event) { public function injectPrimaryLink(object $link, bool $index=true, string $linkLabel=null): void { - // eg: "People" - $modelsName = StringUtilities::foreignKeyToClassName($link->attr); - if(!empty($this->getController()->viewBuilder()->getVar('vv_primary_link_model'))) { - // $link doesn't seem to handle table aliases (eg "Groups" instead of "RecipientGroups" for - // Notifications)). This may also return a plugin qualified path (eg SshKeyAuthenticator.SshKeyAuthenticators). - $modelsName = $this->getController()->viewBuilder()->getVar('vv_primary_link_model'); - } - $modelPath = $modelsName; + try { + // eg: "People" + $modelsName = StringUtilities::foreignKeyToClassName($link->attr); + if(!empty($this->getController()->viewBuilder()->getVar('vv_primary_link_model'))) { + // $link doesn't seem to handle table aliases (eg "Groups" instead of "RecipientGroups" for + // Notifications)). This may also return a plugin qualified path (eg SshKeyAuthenticator.SshKeyAuthenticators). + $modelsName = $this->getController()->viewBuilder()->getVar('vv_primary_link_model'); + } + $modelPath = $modelsName; - if(!empty($link->plugin) && !str_starts_with($modelsName, $link->plugin . '.')) { - // eg: "CoreEnroller.AttributeCollectors", however check first since we may have the - // path from vv_primary_link_model. - $modelPath = $link->plugin . '.' . $modelsName; - } + if(!empty($link->plugin) && !str_starts_with($modelsName, $link->plugin . '.')) { + // eg: "CoreEnroller.AttributeCollectors", however check first since we may have the + // path from vv_primary_link_model. + $modelPath = $link->plugin . '.' . $modelsName; + } - // Construct the getContains function name - $requestAction = $this->getController()->getRequest()->getParam('action'); - $mappedRequestAction = $requestAction; - // In the case we are dealing with non-standard actions we need to fallback to a standard one - // in order to get access to the contain array. We will use the permissions to decide which - // action to fall back to - if(!\in_array($requestAction, [ - 'index', 'view', 'delete', 'add', 'edit' - ])) { - $permissionsArray = $this->getController()->RegistryAuth->calculatePermissionsForView($requestAction); - $id = $this->getController()->getRequest()->getParam('pass')[0] ?? null; - if (isset($id)) { - $mappedRequestAction = ( isset($permissionsArray['edit']) && $permissionsArray['edit'] ) ? 'edit' : 'view'; - } else { - $mappedRequestAction = 'index'; + // Construct the getContains function name + $requestAction = $this->getController()->getRequest()->getParam('action'); + $mappedRequestAction = $requestAction; + // In the case we are dealing with non-standard actions we need to fallback to a standard one + // in order to get access to the contain array. We will use the permissions to decide which + // action to fall back to + if(!\in_array($requestAction, [ + 'index', 'view', 'delete', 'add', 'edit' + ])) { + $permissionsArray = $this->getController()->RegistryAuth->calculatePermissionsForView($requestAction); + $id = $this->getController()->getRequest()->getParam('pass')[0] ?? null; + if (isset($id)) { + $mappedRequestAction = ( isset($permissionsArray['edit']) && $permissionsArray['edit'] ) ? 'edit' : 'view'; + } else { + $mappedRequestAction = 'index'; + } } - } - $containsList = 'get' . ucfirst($mappedRequestAction) . 'Contains'; - - $linkTable = TableRegistry::getTableLocator()->get($modelPath); - $contain = method_exists($linkTable, $containsList) ? $linkTable->$containsList() : []; - - $linkObj = $linkTable->get($link->value, ['contain' => $contain]); - - if($index) { - // We need to determine the primary link of the parent, which might or might - // not be co_id - - if(method_exists($linkTable, "findPrimaryLink")) { - // If findPrimaryLink doesn't exist, we're probably working with CosTable - - $parentLink = $linkTable->findPrimaryLink($linkObj->id); - - $this->injectParents[ $modelPath . $parentLink->value] = [ - 'target' => [ - 'plugin' => $parentLink->plugin ?? null, - 'controller' => $modelsName, - 'action' => 'index', - '?' => [ - $parentLink->attr => $parentLink->value - ] - ], - 'label' => StringUtilities::localizeController( - controllerName: $modelsName, - pluginName: $link->plugin ?? null, - plural: true - ) - ]; + $containsList = 'get' . ucfirst($mappedRequestAction) . 'Contains'; + + $linkTable = TableRegistry::getTableLocator()->get($modelPath); + $contain = method_exists($linkTable, $containsList) ? $linkTable->$containsList() : []; + + $linkObj = $linkTable->get($link->value, ['contain' => $contain]); + + if($index) { + // We need to determine the primary link of the parent, which might or might + // not be co_id + + if(method_exists($linkTable, "findPrimaryLink")) { + // If findPrimaryLink doesn't exist, we're probably working with CosTable + + $parentLink = $linkTable->findPrimaryLink($linkObj->id); + + $this->injectParents[ $modelPath . $parentLink->value] = [ + 'target' => [ + 'plugin' => $parentLink->plugin ?? null, + 'controller' => $modelsName, + 'action' => 'index', + '?' => [ + $parentLink->attr => $parentLink->value + ] + ], + 'label' => StringUtilities::localizeController( + controllerName: $modelsName, + pluginName: $link->plugin ?? null, + plural: true + ) + ]; + } } - } - // Find the allowed action - $breadcrumbAction = method_exists($linkObj, 'isReadOnly') ? - ($linkObj->isReadOnly() ? 'view' : 'edit') : - $mappedRequestAction; + // Find the allowed action + $breadcrumbAction = method_exists($linkObj, 'isReadOnly') ? + ($linkObj->isReadOnly() ? 'view' : 'edit') : + $mappedRequestAction; - // We specifically need to check for the add action - if($mappedRequestAction == 'add' || $mappedRequestAction == 'delete') { - $breadcrumbAction = $mappedRequestAction; - } + // We specifically need to check for the add action + if($mappedRequestAction == 'add' || $mappedRequestAction == 'delete') { + $breadcrumbAction = $mappedRequestAction; + } - // The action in the following injectParents dictates the action here - [$title,,] = StringUtilities::entityAndActionToTitle($linkObj, $modelPath, $breadcrumbAction); + // The action in the following injectParents dictates the action here + [$title,,] = StringUtilities::entityAndActionToTitle($linkObj, $modelPath, $breadcrumbAction); - $this->injectParents[ $linkTable->getTable() . $linkObj->id ] = [ - 'target' => [ - 'plugin' => $link->plugin ?? null, - 'controller' => $modelsName, - 'action' => $breadcrumbAction, - $linkObj->id - ], - 'label' => $linkLabel ?? $title - ]; + $this->injectParents[ $linkTable->getTable() . $linkObj->id ] = [ + 'target' => [ + 'plugin' => $link->plugin ?? null, + 'controller' => $modelsName, + 'action' => $breadcrumbAction, + $linkObj->id + ], + 'label' => $linkLabel ?? $title + ]; + } + catch(\Exception $e) { + // If anything goes wrong we don't want to crash the entire page + $this->llog('error', "Breadcrumbs failed: " . $e->getMessage()); + } } /**