diff --git a/app/plugins/Transmogrify/config/schema/tables.json b/app/plugins/Transmogrify/config/schema/tables.json
index 294f4ffe..d4103fba 100644
--- a/app/plugins/Transmogrify/config/schema/tables.json
+++ b/app/plugins/Transmogrify/config/schema/tables.json
@@ -574,5 +574,22 @@
"co_enrollment_flow_id": null,
"co_pipeline_id": "pipeline_id"
}
+ },
+ "external_identity_sources": {
+ "source": "cm_org_identity_sources",
+ "displayField": "description",
+ "booleans": ["hash_source_record"],
+ "cache": ["co_id"],
+ "fieldMap": {
+ "plugin": "&mapExternalIdentitySourcePlugin",
+ "co_pipeline_id": "pipeline_id",
+ "sync_mode": null,
+ "sync_query_mismatch_mode": null,
+ "sync_query_skip_known": null,
+ "sync_on_user_login": null,
+ "eppn_identifier_type": null,
+ "eppn_suffix": null,
+ "org_identity_source_id": "external_identity_source_id"
+ }
}
}
diff --git a/app/plugins/Transmogrify/src/Command/TransmogrifyCommand.php b/app/plugins/Transmogrify/src/Command/TransmogrifyCommand.php
index 35e9965d..acc1cfc4 100644
--- a/app/plugins/Transmogrify/src/Command/TransmogrifyCommand.php
+++ b/app/plugins/Transmogrify/src/Command/TransmogrifyCommand.php
@@ -451,6 +451,9 @@ public function execute(Arguments $args, ConsoleIo $io): int
}
catch(\Exception $e) {
$this->cache['error'] += 1;
+ if (isset($row['id'])) {
+ $this->cache['rejected'][$outboundQualifiedTableName][$row['id']] = $row;
+ }
$rowIdLabel = $row['id'] ?? ($this->tables[$t]['displayField'] ?? 'n/a');
$this->cmdPrinter->error("$t record " . (string)$rowIdLabel . ": " . $e->getMessage());
$this->cmdPrinter->pause();
diff --git a/app/plugins/Transmogrify/src/Lib/Traits/TypeMapperTrait.php b/app/plugins/Transmogrify/src/Lib/Traits/TypeMapperTrait.php
index a65a7ded..47b15b49 100644
--- a/app/plugins/Transmogrify/src/Lib/Traits/TypeMapperTrait.php
+++ b/app/plugins/Transmogrify/src/Lib/Traits/TypeMapperTrait.php
@@ -30,6 +30,7 @@
namespace Transmogrify\Lib\Traits;
use App\Lib\Enum\PetitionStatusEnum;
+use Cake\ORM\TableRegistry;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Transmogrify\Lib\Util\RawSqlQueries;
@@ -38,7 +39,7 @@
/**
* Encapsulates all type mapping logic and helpers (map_type + specific wrappers).
*
- * @since COmanage Registry v5.0.0
+ * @since COmanage Registry v5.2.0
*/
trait TypeMapperTrait
{
@@ -143,6 +144,68 @@ protected function mapAffiliationType(array $row, ?int $coId = null): ?int
}
/**
+ * Map v4 External Identity Source plugin name to v5 plugin model path.
+ *
+ * Generic logic:
+ * - If a plugin exists with the same name as the v4 Source (ends with "Source"),
+ * return "Plugin.PluralTable" (eg: EnvSource -> EnvSource.EnvSources).
+ * - Else, if a "Connector" plugin exists for a "BaseSource", return
+ * "BaseConnector.PluralTable" (eg: FileSource -> FileConnector.FileSources).
+ *
+ * @param array $row A row from cm_org_identity_sources containing 'plugin'
+ * @return string|null The v5 Plugin.Table string or null if unmapped/empty
+ * @since COmanage Registry v5.2.0
+ */
+ protected function mapExternalIdentitySourcePlugin(array $row): ?string
+ {
+ $v4 = $row['plugin'] ?? null;
+ if (!$v4) {
+ return null;
+ }
+
+ $pluginsTable = TableRegistry::getTableLocator()->get('Plugins');
+
+ $pluginRows = $pluginsTable
+ ->find()
+ ->select(['plugin'])
+ ->where(fn($exp) => $exp->isNotNull('plugin'))
+ ->distinct()
+ ->disableHydration()
+ ->all();
+
+ $available = array_map(static fn(array $r) => (string)$r['plugin'], $pluginRows->toList());
+
+ // Only process names that end with "Source"
+ if (!str_ends_with($v4, 'Source')) {
+ return null;
+ }
+
+ // Pluralized Table name for the model side (eg, EnvSource -> EnvSources)
+ $pluralTable = Inflector::pluralize($v4);
+
+ // 1) Direct plugin exists with same name as the Source
+ // Example: EnvSource -> EnvSource.EnvSources
+ if (in_array($v4, $available, true)) {
+ return $v4 . '.' . $pluralTable;
+ }
+
+ // 2) If it's a "Source", check for "Connector"
+ // Example: FileSource -> FileConnector.FileSources
+ $base = substr($v4, 0, -strlen('Source'));
+ $connector = $base . 'Connector';
+
+ if (in_array($connector, $available, true)) {
+ return $connector . '.' . $pluralTable;
+ }
+
+ // No mapping found
+// return null;
+ throw new \InvalidArgumentException("Unable to map External Identity Source plugin: $v4. Plugin not implemented in COmanage Registry v5.");
+ }
+
+
+
+ /**
* Map email type to corresponding type ID
*
* @param array $row Row data containing email type