getAttribute('site'); if (!$site instanceof SiteInterface) { throw new SiteNotFoundException('No site found in request', 1731234567); } // Check page access $pageRecord = BackendUtility::readPageAccess($pageId, $backendUser->getPagePermsClause(Permission::PAGE_SHOW)) ?: null; if ($pageId === 0 || !$pageRecord) { // Either root page (pid=0) which has no real page record or no access. // Return context with preserved pageId. // pageRecord might be ['path' => '/'] for admins or NULL if no access or non-admin // Still calculate permissions (admins have access to pid=0, editors don't). return new PageContext( pageId: $pageId, pageRecord: $pageRecord, site: $site, rootLine: [], pageTsConfig: GeneralUtility::removeDotsFromTS(BackendUtility::getPagesTSconfig(0)), selectedLanguageIds: [0], languageInformation: $this->languageService->getLanguageInformationForPage(0, $site, $backendUser), pagePermissions: new Permission($backendUser->calcPerms($pageRecord ?: ['uid' => 0])), ); } // Get language information FIRST (needed for validation) $languageInformation = $this->languageService->getLanguageInformationForPage($pageId, $site, $backendUser); // Resolve languages with fallback chain $languagesFromRequest = $request->getQueryParams()['languages'] ?? $request->getParsedBody()['languages'] ?? null; // Extract ModuleData languages (with backward compat for old 'language' parameter) $moduleData = $request->getAttribute('moduleData'); $moduleDataLanguages = null; if ($moduleData instanceof ModuleData) { $moduleDataLanguages = $moduleData->get('languages'); // Backward compatibility: convert old 'language' (single int) to 'languages' (array) if ($moduleDataLanguages === null) { $oldLanguage = $moduleData->get('language'); if ($oldLanguage !== null) { $moduleDataLanguages = [(int)$oldLanguage]; } } } // Use SharedUserPreferences fallback chain (page-specific > ModuleData > default) // This ensures page-specific preferences are shared across modules $resolvedLanguages = $this->sharedPreferences->resolveLanguages( $backendUser, $languagesFromRequest, $pageId, $moduleDataLanguages ); // Validate against existing translations on this page (ensures getPrimaryLanguageId() is valid) // Preference is preserved across navigation (only stored when explicitly changed via request) $existingLanguageIds = $languageInformation->getAllExistingLanguageIds(); $validLanguages = array_intersect($resolvedLanguages, $existingLanguageIds); // Ensure at least default language if none are valid if (empty($validLanguages)) { $validLanguages = [0]; } $validLanguages = array_values($validLanguages); // Store preference in SharedUserPreferences when explicitly changed via request if ($languagesFromRequest !== null) { $this->sharedPreferences->setPageLanguages($backendUser, $pageId, $validLanguages); } // Also update ModuleData if present (for backward compatibility and UI state) if ($moduleData instanceof ModuleData) { $moduleData->set('languages', $validLanguages); } // Create full PageContext for resolved page record return new PageContext( pageId: $pageId, pageRecord: $pageRecord, site: $site, rootLine: BackendUtility::BEgetRootLine($pageId), pageTsConfig: GeneralUtility::removeDotsFromTS(BackendUtility::getPagesTSconfig($pageId)), selectedLanguageIds: $validLanguages, languageInformation: $languageInformation, pagePermissions: new Permission($backendUser->calcPerms($pageRecord)), ); } /** * Create PageContext with specific languages (no fallback resolution). * * This is useful for testing or to explicitly set languages * without going through the fallback chain. * * Access Handling: * If the user has no access to the requested page or pid=0, a PageContext is still returned, * while pageRecord mit be null if no access. Controllers should check isAccessible(). */ public function createWithLanguages( ServerRequestInterface $request, int $pageId, array $languageIds, BackendUserAuthentication $backendUser ): PageContext { $site = $request->getAttribute('site'); if (!$site instanceof SiteInterface) { throw new SiteNotFoundException('No site found in request', 1731234569); } $pageRecord = BackendUtility::readPageAccess($pageId, $backendUser->getPagePermsClause(Permission::PAGE_SHOW)) ?: null; if ($pageId === 0 || !$pageRecord) { // Either root page (pid=0) which has no real page record or no access. // Return context with preserved pageId. // pageRecord might be ['path' => '/'] for admins or NULL if no access or non-admin // Still calculate permissions (admins have access to pid=0, editors don't). return new PageContext( pageId: $pageId, pageRecord: $pageRecord, site: $site, rootLine: [], pageTsConfig: GeneralUtility::removeDotsFromTS(BackendUtility::getPagesTSconfig(0)), selectedLanguageIds: array_map('intval', $languageIds), languageInformation: $this->languageService->getLanguageInformationForPage(0, $site, $backendUser), pagePermissions: new Permission($backendUser->calcPerms($pageRecord ?: ['uid' => 0])), ); } // Create full PageContext for resolved page record return new PageContext( pageId: $pageId, pageRecord: $pageRecord, site: $site, rootLine: BackendUtility::BEgetRootLine($pageId), pageTsConfig: GeneralUtility::removeDotsFromTS(BackendUtility::getPagesTSconfig($pageId)), selectedLanguageIds: array_map('intval', $languageIds), languageInformation: $this->languageService->getLanguageInformationForPage($pageId, $site, $backendUser), pagePermissions: new Permission($backendUser->calcPerms($pageRecord)), ); } }