diff --git a/app/src/Controller/AppController.php b/app/src/Controller/AppController.php
index 4e49f846f..f2128d9d2 100644
--- a/app/src/Controller/AppController.php
+++ b/app/src/Controller/AppController.php
@@ -219,20 +219,21 @@ public function getPrimaryLink(bool $lookup=false) {
if($lookup) {
foreach($availablePrimaryLinks as $potentialPrimaryLink) {
+ // $potentialPrimaryLink will be something like 'attribute_collector_id'
+ // $potentialPrimaryLinkTable will be something like 'CoreEnroller.AttributeCollectors'
+ $potentialPrimaryLinkTable = $this->$modelsName->getPrimaryLinkTableName($potentialPrimaryLink);
+ $potentialPlugin = null;
+
// Try to find a value
- if(strstr($potentialPrimaryLink, '.')) {
+ if(strstr($potentialPrimaryLinkTable, '.')) {
// For looking up values in records here, we want only the attribute
// itself and not the plugin name (used for hacky notation by
// PrimaryLinkTrait::setPrimaryLink(). Note this is a field and not
// a model, but pluginModel() gets us the bit we need.
// Store the plugin for possible later reference.
- $potentialPlugin = StringUtilities::pluginPlugin($potentialPrimaryLink);
-
- // We clobber $potentialPrimaryLink to avoid rewriting a bunch of code,
- // but probably we should rewrite it.
- $potentialPrimaryLink = StringUtilities::pluginModel($potentialPrimaryLink);
+ $potentialPlugin = StringUtilities::pluginPlugin($potentialPrimaryLinkTable);
}
if($this->request->is('get')) {
@@ -321,6 +322,9 @@ public function getPrimaryLink(bool $lookup=false) {
if(!empty($this->cur_pl->value)) {
// We found a populated primary link. Store the attribute and break the loop.
$this->cur_pl->attr = $potentialPrimaryLink;
+ if($potentialPlugin) {
+ $this->cur_pl->plugin = $potentialPlugin;
+ }
$this->set('vv_primary_link', $this->cur_pl->attr);
break;
}
diff --git a/app/src/Controller/Component/BreadcrumbComponent.php b/app/src/Controller/Component/BreadcrumbComponent.php
index d78e27d22..2b01f506e 100644
--- a/app/src/Controller/Component/BreadcrumbComponent.php
+++ b/app/src/Controller/Component/BreadcrumbComponent.php
@@ -48,6 +48,8 @@ class BreadcrumbComponent extends Component
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.
@@ -117,7 +119,7 @@ public function beforeRender(EventInterface $event) {
if($action != 'index') {
$target = [
- 'plugin' => null,
+ 'plugin' => $primaryLink->plugin ?? null,
'controller' => $modelsName,
'action' => 'index'
];
@@ -126,8 +128,12 @@ public function beforeRender(EventInterface $event) {
$target['?'] = [$primaryLink->attr => $primaryLink->value];
}
+ $label = (!empty($primaryLink->plugin)
+ ? __d(Inflector::underscore($primaryLink->plugin), 'controller.'.$modelsName, [99])
+ : __d('controller', $modelsName, [99]));
+
$parents[] = [
- 'label' => __d('controller', $modelsName, [99]),
+ 'label' => $label,
'target' => $target
];
}
@@ -140,65 +146,74 @@ public function beforeRender(EventInterface $event) {
}
/**
- * Inject a title link based on the display field of an entity into the breadcrumb set.
+ * 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
- * @param Table $table Table for $entity
- * @param Entity $entity Entity to generate title link for
- * @param string $action Action to link to
- * @param string $label If set, use this label instead of the entity's displayField
*/
-
- public function injectTitleLink(
- $table,
- $entity,
- string $action='edit',
- ?string $label=null
- ) {
- $displayField = $table->getDisplayField();
- $this->injectTitleLinks[] = [
- 'target' => [
- 'plugin' => null,
- 'controller' => $table->getTable(),
- 'action' => $action,
- $entity->id
- ],
- 'label' => $label ?: $entity->$displayField
- ];
+ public function freezeParents() {
+ $this->parentsFrozen = true;
}
/**
* Inject the primary link into the breadcrumb path.
*
* @since COmanage Registry v5.0.0
- * @param object link Primary Link (as returned by getPrimaryLink())
+ * @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
*/
- public function injectPrimaryLink(object $link) {
+ public function injectPrimaryLink(object $link, bool $index=true, $linkLabel=null) {
+ if($this->parentsFrozen) {
+ return;
+ }
+
// eg: "People"
$modelsName = StringUtilities::foreignKeyToClassName($link->attr);
+ $modelPath = $modelsName;
+
+ if(!empty($link->plugin)) {
+ // eg: "CoreEnroller.AttributeCollectors"
+ $modelPath = $link->plugin . "." . $modelsName;
+ }
$contain = [];
$primaryName = null;
- $linkTable = TableRegistry::getTableLocator()->get($modelsName);
+ $linkTable = TableRegistry::getTableLocator()->get($modelPath);
$linkObj = $linkTable->get($link->value, ['contain' => $contain]);
$displayField = $linkTable->getDisplayField();
- $this->injectParents[] = [
- 'target' => [
- 'plugin' => null,
- 'controller' => $modelsName,
- 'action' => 'index',
- '?' => [
- 'co_id' => $link->co_id
- ]
- ],
- 'label' => __d('controller', $modelsName, [99])
- ];
+ 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[] = [
+ 'target' => [
+ 'plugin' => $parentLink->plugin ?? null,
+ 'controller' => $modelsName,
+ 'action' => 'index',
+ '?' => [
+ $parentLink->attr => $parentLink->value
+ ]
+ ],
+ 'label' => StringUtilities::localizeController(
+ controllerName: $modelsName,
+ pluginName: $link->plugin ?? null,
+ plural: true
+ )
+ ];
+ }
+ }
- $label = $linkObj->$displayField;
+ $label = $linkLabel ?? $linkObj->$displayField;
if($modelsName == 'People' || $modelsName == 'ExternalIdentities') {
// We need the Primary Name (or first name found) to render it
@@ -218,7 +233,7 @@ public function injectPrimaryLink(object $link) {
$this->injectParents[] = [
'target' => [
- 'plugin' => null,
+ 'plugin' => $link->plugin ?? null,
'controller' => $modelsName,
'action' => 'edit',
$linkObj->id
@@ -227,6 +242,35 @@ public function injectPrimaryLink(object $link) {
];
}
+ /**
+ * Inject a title link based on the display field of an entity into the breadcrumb set.
+ *
+ * @since COmanage Registry v5.0.0
+ * @param Table $table Table for $entity
+ * @param Entity $entity Entity to generate title link for
+ * @param string $action Action to link to
+ * @param string $label If set, use this label instead of the entity's displayField
+ */
+
+ public function injectTitleLink(
+ $table,
+ $entity,
+ string $action='edit',
+ ?string $label=null
+ ) {
+ $displayField = $table->getDisplayField();
+
+ $this->injectTitleLinks[] = [
+ 'target' => [
+ 'plugin' => null,
+ 'controller' => $table->getTable(),
+ 'action' => $action,
+ $entity->id
+ ],
+ 'label' => $label ?: $entity->$displayField
+ ];
+ }
+
/**
* Set the set of paths that should be skipped when rendering breadcrumbs.
* Paths are specified as regular expressions, eg: '/^\/cos\/select/'
diff --git a/app/src/Controller/StandardController.php b/app/src/Controller/StandardController.php
index a6bc19b6d..c2ec6813a 100644
--- a/app/src/Controller/StandardController.php
+++ b/app/src/Controller/StandardController.php
@@ -506,8 +506,12 @@ public function generateRedirect($entity) {
if(!empty($link->attr) && !empty($link->value)) {
$redirect['?'] = [$link->attr => $link->value];
}
+
+ if(!empty($this->getPlugin())) {
+ $redirect['plugin'] = $this->getPlugin();
+ }
}
-
+
return $this->redirect($redirect);
}
@@ -671,7 +675,7 @@ protected function populateAutoViewVars(object $obj=null) {
case 'array':
// Use the provided array of values. By default, we use the values
// for the keys as well, to generate HTML along the lines of
- //
+ // . (See also 'hash'.)
$this->set($vvar, array_combine($avv['array'], $avv['array']));
break;
case 'enum':
@@ -684,6 +688,10 @@ protected function populateAutoViewVars(object $obj=null) {
}
$this->set($vvar, $class::getLocalizedConsts());
break;
+ case 'hash':
+ // Like 'array' but we assume we are passed key/value pairs
+ $this->set($vvar, $avv['hash']);
+ break;
// "auxiliary" and "select" do basically the same thing, but the former
// returns the full object and the latter just returns a hash suitable
// for a select. "type" is a shorthand for "select" for type_id.
diff --git a/app/src/Lib/Traits/PrimaryLinkTrait.php b/app/src/Lib/Traits/PrimaryLinkTrait.php
index 7c2ba6f68..42e4b92e1 100644
--- a/app/src/Lib/Traits/PrimaryLinkTrait.php
+++ b/app/src/Lib/Traits/PrimaryLinkTrait.php
@@ -180,11 +180,11 @@ public function findFilterPrimaryLink(\Cake\ORM\Query $query, array $options) {
* @since COmanage Registry v5.0.0
* @param int $id Object ID
* @param bool $archived Whether to retrieve archived (deleted) records
- * @return Entity Primary Link (as an Entity)
+ * @return object Primary Link information (as an object)
* @throws \InvalidArgumentException
*/
- public function findPrimaryLink(int $id, bool $archived=false) {
+ public function findPrimaryLink(int $id, bool $archived=false): object {
$obj = $this->get($id, ['archived' => $archived]); //->firstOrFail();
// We might have multiple primary link keys (eg for MVEAs), but only one