Skip to content

Commit

Permalink
Fix attribute collector/enrollment flow configuration tabs associatio…
Browse files Browse the repository at this point in the history
…ns calculation (#359)
  • Loading branch information
Ioannis authored Dec 18, 2025
1 parent 3277adf commit ab58f2e
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 32 deletions.
8 changes: 2 additions & 6 deletions app/src/Controller/AppController.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,10 @@ public function getCOID(): ?int {
*/
public function getCurrentTable(): \Cake\ORM\Table
{
/** @var string $modelsName */
$modelsName = $this->getName();
$plugin = $this->getPlugin();

$alias = $this->getPlugin() !== null
? $this->getPlugin() . '.' . $modelsName
: $modelsName;

return $this->fetchTable($alias);
return $this->fetchTable(StringUtilities::getQualifiedName($plugin, $modelsName));
}


Expand Down
2 changes: 1 addition & 1 deletion app/src/Controller/StandardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ public function index() {
$this->set('vv_permission_set', $this->RegistryAuth->calculatePermissionsForResultSet($resultSet));

// Default index view title is model name
[$title, , ] = StringUtilities::entityAndActionToTitle($resultSet, $modelsName, 'index');
[$title, , ] = StringUtilities::entityAndActionToTitle(null, $modelsName, 'index');
$this->set('vv_title', $title);

// Let the view render
Expand Down
44 changes: 39 additions & 5 deletions app/src/Controller/StandardEnrollerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,45 @@ public function beforeRender(\Cake\Event\EventInterface $event) {

if(!empty($link->value)) {
$currentTable = $this->getCurrentTable();
$efsTable = $currentTable->getAssociation('EnrollmentFlowSteps')->getTarget();

$this->set('vv_bc_parent_obj', $efsTable->get($link->value));
$this->set('vv_bc_parent_displayfield', $efsTable->getDisplayField());
$this->set('vv_bc_parent_primarykey', $efsTable->getPrimaryKey());
// Not all enroller plugin tables have a direct EnrollmentFlowSteps association
if (method_exists($currentTable, 'hasAssociation')
&& $currentTable->hasAssociation('EnrollmentFlowSteps')) {

$efsTable = $currentTable->getAssociation('EnrollmentFlowSteps')->getTarget();

$this->set('vv_bc_parent_obj', $efsTable->get($link->value));
$this->set('vv_bc_parent_displayfield', $efsTable->getDisplayField());
$this->set('vv_bc_parent_primarykey', $efsTable->getPrimaryKey());
} else {
// Two-hop case: foreign key from $link (e.g. attribute_collector_id)
// -> AttributeCollectors
// -> EnrollmentFlowSteps
// Useful for deeply nested enrollment configuration objects.
if (!empty($link->attr)) {
// Derive the table class name from the foreign key name
$fkClassName = StringUtilities::foreignKeyToClassName($link->attr); // e.g. 'AttributeCollectors'

// Qualify with plugin if present
$tableAlias = !empty($link->plugin)
? $link->plugin . '.' . $fkClassName // e.g. 'CoreEnroller.AttributeCollectors'
: $fkClassName;

$attributeCollectorsTable = TableRegistry::getTableLocator()->get($tableAlias);

// Load the intermediate object (AttributeCollector, in your case)
$collector = $attributeCollectorsTable->get((int)$link->value);

// From the AttributeCollector, go up to the EnrollmentFlowStep
if (!empty($collector->enrollment_flow_step_id)) {
$efsTable = TableRegistry::getTableLocator()->get('EnrollmentFlowSteps');
$step = $efsTable->get((int)$collector->enrollment_flow_step_id);

$this->set('vv_bc_parent_obj', $step);
$this->set('vv_bc_parent_displayfield', $efsTable->getDisplayField());
$this->set('vv_bc_parent_primarykey', $efsTable->getPrimaryKey());
}
}
}
}

return parent::beforeRender($event);
Expand Down
64 changes: 44 additions & 20 deletions app/src/Lib/Util/StringUtilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@ public static function entityToClassName($entity): string {
// $classPath will be something like App\Model\Entity\Name, but we want to return "Names"
$classPath = get_class($entity);

return self::classPathClassName($classPath);
}

/**
* Extracts and pluralizes the class name from a fully qualified class path.
*
* @param string $classPath Fully qualified class path (eg: App\Model\Entity\Name)
* @return string Pluralized class name (eg: Names)
* @since COmanage Registry v5.2.0
*/
public static function classPathClassName(string $classPath): string {
return Inflector::pluralize(substr($classPath, strrpos($classPath, '\\')+1));
}

Expand Down Expand Up @@ -285,13 +296,15 @@ public static function entityAndActionToTitle($entity,
[$plugin, $modelsName] = explode('.', $modelPath, 2);
}

if($entity == null && !empty($plugin)) {
$count = $action == 'index' ? 99 : 1;
return [__d($domain, "controller.$modelsName", [$count]), '', ''];
} elseif($entity === null) {
$count = $action == 'index' ? 99 : 1;
return [__d($domain, "{$modelPath}.{$action}", [$count]), '', ''];
// Index view → use the controller plural form (token 99 convention)
if($action === 'index') {
if(!empty($plugin)) {
$domain = StringUtilities::pluginToTextDomain($plugin);
return [__d($domain, "controller.$modelsName", [99]), '', ''];
}
return [__d('controller', $modelsName, [99]), '', ''];
}

// Base table and default message IDs for translation
$linkTable = TableRegistry::getTableLocator()->get($modelPath);
$msgId = "{$action}.a"; // eg: "edit.a"
Expand All @@ -308,25 +321,20 @@ public static function entityAndActionToTitle($entity,
// If the entity actually belongs to a different model than the provided $modelsName,
// switch to that table and adjust the default message id pattern accordingly.
// This is necessary for TAB oriented views
if(Inflector::singularize(self::entityToClassName($entity)) !== Inflector::singularize($modelsName)) {
if(
$entity !== null
&& Inflector::singularize(self::entityToClassName($entity)) !== Inflector::singularize($modelsName)
) {
$linkTable = TableRegistry::getTableLocator()->get(self::entityToClassName($entity));
// If modelPath and action are equal, don’t concatenate (preserve legacy behavior)
$msgId = $modelPath === $action ? $modelPath : "{$modelPath}.{$action}";
}

// 2) No action → default to the controller label for the model (singular)
// No action → default to the controller label for the model (singular)
if($action === null) {
return [__d('controller', $modelsName), '', ''];
}

// 3) Index view → use the controller plural form (token 99 convention)
if($action === 'index') {
if(!empty($plugin)) {
return [__d($domain, "controller.$modelsName", [99]), '', ''];
}
return [__d('controller', $modelsName, [99]), '', ''];
}

// Add/Edit/View
// The MVEA Models have an entityId. The one from the parent model.
// We need to have a condition for this and exclude it.
Expand All @@ -338,7 +346,7 @@ public static function entityAndActionToTitle($entity,
$display = $entity->$field ?? null;
}

// 6) Edit/View-like case for an existing entity with a usable display
// Edit/View-like case for an existing entity with a usable display
// Title: translate with override key first; if not found, fall back to default key.
// Super/Sub titles: set to the display (needed for External IDs in UI).
if (
Expand All @@ -354,7 +362,7 @@ public static function entityAndActionToTitle($entity,
return [$title, $supertitle, $subtitle];
}

// 7) Fallbacks:
// Fallbacks:
// - New entities (no id),
// - Add/Delete actions,
// - Or we simply lack a display.
Expand All @@ -372,7 +380,7 @@ public static function entityAndActionToTitle($entity,
* @param string $domain Translation domain to use
* @param string $overrideKey Primary translation key to try first
* @param string $fallbackKey Fallback translation key if override not found
* @param string $value Value to substitute in translation
* @param string|int $value Value to substitute in translation
* @return string Translated string using either override or fallback key
* @since COmanage Registry v5.2.0
*/
Expand Down Expand Up @@ -415,6 +423,22 @@ public static function foreignKeyToController(string $s): string {
return Inflector::underscore(Inflector::pluralize(substr($s, 0, strlen($s)-3)));
}


/**
* Get the fully qualified name by combining plugin and name with a dot separator.
*
* @param string|null $plugin Plugin name, or null if no plugin
* @param string $name Base name to qualify
* @return string Qualified name in format "Plugin.Name" or just "Name" if no plugin
* @since COmanage Registry v5.2.0
*/
public static function getQualifiedName(?string $plugin, string $name): string
{
return $plugin !== null && $plugin !== ''
? $plugin . '.' . $name
: $name;
}

/**
* Localize a controller name, accounting for plugins.
*
Expand Down Expand Up @@ -452,7 +476,7 @@ public static function qualifyModelPath(string $modelPath, ?string $plugin): str
if (empty($plugin) || str_starts_with($modelPath, $plugin . '.')) {
return $modelPath;
}
return $plugin . '.' . $modelPath;
return self::getQualifiedName($plugin, $modelPath);;
}

/**
Expand Down

0 comments on commit ab58f2e

Please sign in to comment.