From 72c94fd6b97ac6cc052f14655f4101686251ac12 Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Mon, 8 Jan 2024 19:57:22 +0200 Subject: [PATCH] Calculate injectPrimaryLink only once for Plugins, inside StandardPluginController remove configuration crumb from search page --- .../Component/BreadcrumbComponent.php | 217 +++++++++--------- app/src/Controller/DashboardsController.php | 3 +- .../StandardPluggableController.php | 2 +- app/src/Model/Table/PeopleTable.php | 3 +- 4 files changed, 119 insertions(+), 106 deletions(-) diff --git a/app/src/Controller/Component/BreadcrumbComponent.php b/app/src/Controller/Component/BreadcrumbComponent.php index 2b01f506e..7bd0399e5 100644 --- a/app/src/Controller/Component/BreadcrumbComponent.php +++ b/app/src/Controller/Component/BreadcrumbComponent.php @@ -37,6 +37,18 @@ class BreadcrumbComponent extends Component { + /* + * Breadcrump example + * COmanage Registry > Alfa Community > Configuration > External Identity Sources > Test Filesource Plugin > Configure Test Filesource Plugin + * + * Root link(prepend): COmanage Registry // Root node (link) + * CO Level Link: Alfa Community // if Current CO is defined (link) + * configuration: Configuration // Configuration breadcrumb (link) + * path: Parent // Render the path from dashboard (link) + * Title Links: + * Page Title: Title // e.g. Configure Test Filesource Plugin (string) + */ + // Configuration provided by the controller // Don't render any breadcrumbs protected $skipAllPaths = []; @@ -45,11 +57,14 @@ class BreadcrumbComponent extends Component // Don't render the parent links protected $skipParentPaths = []; // Inject parent links (these render before the index link, if set) + // The parent links are constructed as part of the injectPrimaryLink function. This in the StandardController as well + // as in the StandardPluginController, MVEAController, ProvisioningHistoryRecordController, etc. These controllers are + // a descendant from the StandardController we will calculate the Parents twice. In order to avoid duplicates the + // injectParents table has to be an associative array. The uniqueness of the key will preserve the uniqueness of the parent + // path while the order of firing will create the correct breadcumb path order protected $injectParents = []; // Inject title links (immediately before the title breadcrumb) protected $injectTitleLinks = []; - // Whether parent links are frozen - protected $parentsFrozen = false; /** * Callback run prior to rendering the view. @@ -61,115 +76,105 @@ class BreadcrumbComponent extends Component public function beforeRender(EventInterface $event) { $controller = $event->getSubject(); $request = $controller->getRequest(); + + if($request->is('restful') || $request->is('ajax')) { + return; + } + $modelsName = $controller->getName(); - if(!$request->is('restful')) { - // Determine the request target, but strip off query params - $requestTarget = $request->getRequestTarget(false); + // Determine the request target, but strip off query params + $requestTarget = $request->getRequestTarget(false); - $skipAll = false; - $skipConfig = false; + $skipAll = false; + $skipConfig = false; - foreach($this->skipAllPaths as $p) { - if(preg_match($p, $requestTarget)) { - $skipAll = true; - break; - } + foreach($this->skipAllPaths as $p) { + if(preg_match($p, $requestTarget)) { + $skipAll = true; + break; } + } - foreach($this->skipConfigPaths as $p) { - if(preg_match($p, $requestTarget)) { - $skipConfig = true; - break; - } + foreach($this->skipConfigPaths as $p) { + if(preg_match($p, $requestTarget)) { + $skipConfig = true; + break; } + } - // Determine if the current request maps to a path where - // breadcrumb rendering should be skipped in whole or in part - $controller->set('vv_bc_skip', $skipAll); + // Determine if the current request maps to a path where + // breadcrumb rendering should be skipped in whole or in part + $controller->set('vv_bc_skip', $skipAll); - $controller->set('vv_bc_skip_config', $skipConfig); + $controller->set('vv_bc_skip_config', $skipConfig); - // Do we have a target model, and if so is it a configuration - // model (eg: ApiUsers) or an object model (eg: CoPeople)? - if(isset($controller->$modelsName) // May not be set under certain error conditions - && method_exists($controller->$modelsName, "isConfigurationTable")) { - $controller->set('vv_bc_configuration_link', $controller->$modelsName->isConfigurationTable()); - } else { - $controller->set('vv_bc_configuration_link', false); - } + // Do we have a target model, and if so is it a configuration + // model (eg: ApiUsers) or an object model (eg: CoPeople)? + if(isset($controller->$modelsName) // May not be set under certain error conditions + && method_exists($controller->$modelsName, "isConfigurationTable")) { + $controller->set('vv_bc_configuration_link', $controller->$modelsName->isConfigurationTable()); + } else { + $controller->set('vv_bc_configuration_link', false); + } - // Build a list of intermediate parent links, starting with any - // injected parents. This overrides $skipParentPaths. - $parents = $this->injectParents; + // Build a list of intermediate parent links, starting with any + // injected parents. This overrides $skipParentPaths. + $parents = $this->injectParents; - $skipParent = false; + $skipParent = false; - foreach($this->skipParentPaths as $p) { - if(preg_match($p, $requestTarget)) { - $skipParent = true; - break; - } + foreach($this->skipParentPaths as $p) { + if(preg_match($p, $requestTarget)) { + $skipParent = true; + break; } + } + + if(!$skipParent) { + // For non-index views, insert a link back to the index. + $action = $request->getParam('action'); + $primaryLink = $controller->getPrimaryLink(true); + + if($action != 'index') { + $target = [ + 'plugin' => $primaryLink->plugin ?? null, + 'controller' => $modelsName, + 'action' => 'index' + ]; - if(!$skipParent) { - // For non-index views, insert a link back to the index. - $action = $request->getParam('action'); - $primaryLink = $controller->getPrimaryLink(true); - - if($action != 'index') { - $target = [ - 'plugin' => $primaryLink->plugin ?? null, - 'controller' => $modelsName, - 'action' => 'index' - ]; - - if(!empty($primaryLink->attr)) { - $target['?'] = [$primaryLink->attr => $primaryLink->value]; - } - - $label = (!empty($primaryLink->plugin) - ? __d(Inflector::underscore($primaryLink->plugin), 'controller.'.$modelsName, [99]) - : __d('controller', $modelsName, [99])); - - $parents[] = [ - 'label' => $label, - 'target' => $target - ]; + if(!empty($primaryLink->attr)) { + $target['?'] = [$primaryLink->attr => $primaryLink->value]; } - } - $controller->set('vv_bc_parents', $parents); + $label = (!empty($primaryLink->plugin) + ? __d(Inflector::underscore($primaryLink->plugin), 'controller.'.$modelsName, [99]) + : __d('controller', $modelsName, [99])); - $controller->set('vv_bc_title_links', $this->injectTitleLinks); + $parents[] = [ + 'label' => $label, + 'target' => $target + ]; + } } - } - /** - * Prevent any additional parent links from being added. Intended primarily for - * Controllers that extend StandardController but do not want the standard behavior. - * - * @since COmanage Registry v5.0.0 - */ + $controller->set('vv_bc_parents', $parents); - public function freezeParents() { - $this->parentsFrozen = true; + $controller->set('vv_bc_title_links', $this->injectTitleLinks); } /** * Inject the primary link into the breadcrumb path. * - * @since COmanage Registry v5.0.0 - * @param object $link Primary Link (as returned by getPrimaryLink()) - * @param bool $index Include link to parent index - * @param string $linkLabel Label to use for Primary Link instead of displayField + * @param object $link Primary Link (as returned by getPrimaryLink()) + * @param bool $index Include link to parent index + * @param string|null $linkLabel Override the constructed label + * + *@since COmanage Registry v5.0.0 */ - public function injectPrimaryLink(object $link, bool $index=true, $linkLabel=null) { - if($this->parentsFrozen) { - return; - } - + public function injectPrimaryLink(object $link, bool $index=true, string $linkLabel=null): void + { // eg: "People" $modelsName = StringUtilities::foreignKeyToClassName($link->attr); $modelPath = $modelsName; @@ -179,12 +184,14 @@ public function injectPrimaryLink(object $link, bool $index=true, $linkLabel=nul $modelPath = $link->plugin . "." . $modelsName; } - $contain = []; - $primaryName = null; + // Construct the getContains function name + $requestAction = $this->getController()->getRequest()->getParam('action'); + $containsList = "get" . ucfirst($requestAction) . "Contains"; $linkTable = TableRegistry::getTableLocator()->get($modelPath); + $contain = method_exists($linkTable, $containsList) ? $linkTable->$containsList() : []; + $linkObj = $linkTable->get($link->value, ['contain' => $contain]); - $displayField = $linkTable->getDisplayField(); if($index) { // We need to determine the primary link of the parent, which might or might @@ -195,7 +202,7 @@ public function injectPrimaryLink(object $link, bool $index=true, $linkLabel=nul $parentLink = $linkTable->findPrimaryLink($linkObj->id); - $this->injectParents[] = [ + $this->injectParents[ $modelPath . $parentLink->value] = [ 'target' => [ 'plugin' => $parentLink->plugin ?? null, 'controller' => $modelsName, @@ -213,32 +220,36 @@ public function injectPrimaryLink(object $link, bool $index=true, $linkLabel=nul } } - $label = $linkLabel ?? $linkObj->$displayField; + // Find the allowed action + $breadcrumbAction = method_exists($linkObj, 'isReadOnly') ? + ($linkObj->isReadOnly() ? 'view' : 'edit') : + 'edit'; - if($modelsName == 'People' || $modelsName == 'ExternalIdentities') { - // We need the Primary Name (or first name found) to render it + // The action in the following injectParents dictates the action here + // XXX This is a duplicate from StandardController. + if(method_exists($linkTable, 'generateDisplayField')) { + // We don't use a trait for this since each table will implement different logic - $Names = TableRegistry::getTableLocator()->get('Names'); + $label = __d('operation', "{$breadcrumbAction}.ai", $linkTable->generateDisplayField($linkObj)); + } else { + // Default view title is edit object display field + $field = $linkTable->getDisplayField(); - // This will throw an error on failure - $primaryName = $Names->primaryName($linkObj->id, Inflector::underscore(Inflector::singularize($modelsName))); - - $label = $primaryName->full_name; - } - - // If we don't have a visible label use the record ID - if(empty($label)) { - $label = $linkObj->id; + if(!empty($obj->$field)) { + $label = __d('operation', "{$breadcrumbAction}.ai", $obj->$field); + } else { + $label = __d('operation', "{$breadcrumbAction}.ai", __d('controller', $modelsName, [1])); + } } - $this->injectParents[] = [ + $this->injectParents[ $linkTable->getTable() . $linkObj->id ] = [ 'target' => [ 'plugin' => $link->plugin ?? null, 'controller' => $modelsName, - 'action' => 'edit', + 'action' => $breadcrumbAction, $linkObj->id ], - 'label' => $label + 'label' => $linkLabel ?? $label ]; } diff --git a/app/src/Controller/DashboardsController.php b/app/src/Controller/DashboardsController.php index 89ec5c8f7..5c66ae07e 100644 --- a/app/src/Controller/DashboardsController.php +++ b/app/src/Controller/DashboardsController.php @@ -50,7 +50,8 @@ public function initialize(): void { $this->Breadcrumb->skipConfig([ '/^\/dashboards\/artifacts/', '/^\/dashboards\/dashboard/', - '/^\/dashboards\/registries/' + '/^\/dashboards\/registries/', + '/^\/dashboards\/search/' ]); // There is currently no inventory of dashboards, so we skip parents // for configuration, dashboard, and registries actions diff --git a/app/src/Controller/StandardPluggableController.php b/app/src/Controller/StandardPluggableController.php index 0fb8234b7..e8f33c287 100644 --- a/app/src/Controller/StandardPluggableController.php +++ b/app/src/Controller/StandardPluggableController.php @@ -42,7 +42,7 @@ class StandardPluggableController extends StandardController { */ public function configure(string $id) { - // We basically implement a redirect here to faciliate view rendering. + // We basically implement a redirect here to facilitate view rendering. // (We only need to map into the plugin on actual link click, instead of // potentially many times on an index view for links that may not be used.) diff --git a/app/src/Model/Table/PeopleTable.php b/app/src/Model/Table/PeopleTable.php index 98b44a90a..b4a6527fd 100644 --- a/app/src/Model/Table/PeopleTable.php +++ b/app/src/Model/Table/PeopleTable.php @@ -145,7 +145,8 @@ public function initialize(array $config): void { 'Urls' ]); $this->setIndexContains(['PrimaryName']); - + $this->setViewContains(['PrimaryName']); + $this->setAutoViewVars([ 'statuses' => [ 'type' => 'enum',