'groupMods', 'be_users' => 'userMods', ]; /** * @var array an array with the old module identifier as key and the new one as value */ protected array $moduleRenaming = [ 'web_list' => 'records', 'web_info' => 'content_status', 'workspaces_admin' => 'workspaces_publish', 'site_redirects' => 'redirects', 'web_linkvalidator' => 'linkvalidator_checklinks', ]; /** * Modules that require a parent module to be accessible. * * @var array Key: the new module identifier, Value: required parent module */ protected array $requiredParentModules = [ 'redirects' => 'link_management', 'linkvalidator_checklinks' => 'link_management', ]; public function getTitle(): string { return 'Migrate module permissions'; } public function getDescription(): string { return 'Migrate permissions for renamed modules in user and group module permissions. ' . 'Also adds required parent modules when a module has been moved to a new location in the module hierarchy.'; } public function getPrerequisites(): array { return [ DatabaseUpdatedPrerequisite::class, ]; } public function updateNecessary(): bool { return $this->migrate(true); } public function executeUpdate(): bool { return $this->migrate(false); } private function migrate(bool $dryRun): bool { $migrated = false; foreach ($this->tables as $table => $field) { $queryBuilder = $this->getConnectionPool()->getQueryBuilderForTable($table); $connection = $this->getConnectionPool()->getConnectionForTable($table); $queryBuilder->select('uid', $field) ->from($table) ->executeQuery(); foreach ($queryBuilder->fetchAllAssociative() as $record) { $originalModules = (string)($record[$field] ?? ''); $modules = explode(',', $originalModules); $parentModulesToAdd = []; $updatedModules = array_map(function ($module) use (&$parentModulesToAdd) { $trimmedModule = trim($module); if (isset($this->moduleRenaming[$trimmedModule])) { $newModule = $this->moduleRenaming[$trimmedModule]; if (isset($this->requiredParentModules[$newModule])) { $parentModulesToAdd[] = $this->requiredParentModules[$newModule]; } return $newModule; } return $module; }, $modules); $trimmedModules = array_filter(array_map('trim', $updatedModules)); $deduplicatedModules = array_unique($trimmedModules); // Check if existing modules (already renamed in a previous migration) need parent modules // This handles the case where the wizard ran in v14.0 before $requiredParentModules existed foreach ($deduplicatedModules as $module) { if (isset($this->requiredParentModules[$module])) { $parentModulesToAdd[] = $this->requiredParentModules[$module]; } } // Add parent modules if not already present foreach (array_unique($parentModulesToAdd) as $parentModule) { if (!in_array($parentModule, $deduplicatedModules, true)) { $deduplicatedModules[] = $parentModule; } } $newModules = implode(',', $deduplicatedModules); if ($originalModules !== $newModules) { if ($dryRun) { return true; } $migrated = $connection->update( $table, [ $field => $newModules, ], ['uid' => (int)$record['uid']] ) > 0 || $migrated; } } } return $migrated; } protected function getConnectionPool(): ConnectionPool { return GeneralUtility::makeInstance(ConnectionPool::class); } }