From 28d89b1c0e4abd8be9790b74e240ad21dc4f0903 Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Fri, 10 Dec 2021 12:23:09 +0100
Subject: [PATCH] [BUGFIX] Properly support translations only page module

This fixes several issues for the case that an editor only
has access to translated pages and not to the default
language (L=0).

Resolves: #95795
Releases: main, 11.5, 10.4
Change-Id: Ied31c7aa5229d3b8686741dd97005f9b5f16ef16
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/72616
Tested-by: core-ci <typo3@b13.com>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Jochen <rothjochen@gmail.com>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Jochen <rothjochen@gmail.com>
Reviewed-by: Benni Mack <benni@typo3.org>
---
 .../Controller/EditDocumentController.php     |   7 +-
 .../Controller/PageLayoutController.php       | 108 +++++++++---------
 .../Form/FieldWizard/OtherLanguageContent.php |   2 +-
 .../BackendLayout/Grid/LanguageColumn.php     |   3 +-
 .../Classes/View/PageLayoutContext.php        |  11 +-
 5 files changed, 74 insertions(+), 57 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Controller/EditDocumentController.php b/typo3/sysext/backend/Classes/Controller/EditDocumentController.php
index e4f89f914a15..263db21d9159 100644
--- a/typo3/sysext/backend/Classes/Controller/EditDocumentController.php
+++ b/typo3/sysext/backend/Classes/Controller/EditDocumentController.php
@@ -1291,7 +1291,7 @@ class EditDocumentController
     {
         $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
 
-        if ($this->firstEl !== null) {
+        if (!empty($this->firstEl)) {
             $record = BackendUtility::getRecord($this->firstEl['table'], $this->firstEl['uid']);
             $TCActrl = $GLOBALS['TCA'][$this->firstEl['table']]['ctrl'];
 
@@ -2118,6 +2118,11 @@ class EditDocumentController
             } else {
                 $availableLanguages = $this->getLanguages((int)$pid, $table);
             }
+            // Remove default language, if user does not have access. This is necessary, since
+            // the default language is always added when fetching the system languages (#88504).
+            if (isset($availableLanguages[0]) && !$this->getBackendUser()->checkLanguageAccess(0)) {
+                unset($availableLanguages[0]);
+            }
             // Page available in other languages than default language?
             if (count($availableLanguages) > 1) {
                 $rowsByLang = [];
diff --git a/typo3/sysext/backend/Classes/Controller/PageLayoutController.php b/typo3/sysext/backend/Classes/Controller/PageLayoutController.php
index 2e3f5c9b68dd..7a4605953110 100644
--- a/typo3/sysext/backend/Classes/Controller/PageLayoutController.php
+++ b/typo3/sysext/backend/Classes/Controller/PageLayoutController.php
@@ -312,7 +312,8 @@ class PageLayoutController
             1 => $this->getLanguageService()->getLL('m_function_1'),
         ];
         // Find if there are ANY languages at all (and if not, do not show the language option from function menu).
-        if (count($this->availableLanguages) > 1) {
+        // The second check is for an edge case: Only two languages in the site and the default is not allowed.
+        if (count($this->availableLanguages) > 1 || (int)array_key_first($this->availableLanguages) > 0) {
             $actions[2] = $this->getLanguageService()->getLL('m_function_2');
         }
         // Page / user TSconfig blinding of menu-items
@@ -823,69 +824,70 @@ class PageLayoutController
             ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
         $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
 
-        // Edit page properties and page language overlay icons
+        // Edit page properties
         if ($this->isPageEditable(0)) {
-            /** @var \TYPO3\CMS\Core\Http\NormalizedParams */
-            $normalizedParams = $request->getAttribute('normalizedParams');
-            // Edit localized pages only when one specific language is selected
-            if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
-                $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
-                $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
-                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                    ->getQueryBuilderForTable('pages');
-                $queryBuilder->getRestrictions()
-                    ->removeAll()
-                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
-                    ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUser()->workspace));
-                $overlayRecord = $queryBuilder
-                    ->select('uid')
-                    ->from('pages')
-                    ->where(
-                        $queryBuilder->expr()->eq(
-                            $localizationParentField,
-                            $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
-                        ),
-                        $queryBuilder->expr()->eq(
-                            $languageField,
-                            $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
-                        )
-                    )
-                    ->setMaxResults(1)
-                    ->execute()
-                    ->fetchAssociative();
-                BackendUtility::workspaceOL('pages', $overlayRecord, (int)$this->getBackendUser()->workspace);
-                // Edit button
-                $urlParameters = [
+            $url = (string)$this->uriBuilder->buildUriFromRoute(
+                'record_edit',
+                [
                     'edit' => [
                         'pages' => [
-                            $overlayRecord['uid'] => 'edit',
+                            $this->id => 'edit',
                         ],
                     ],
-                    'returnUrl' => $normalizedParams->getRequestUri(),
-                ];
-
-                $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
-                $editLanguageButton = $this->buttonBar->makeLinkButton()
-                    ->setHref($url)
-                    ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
-                    ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
-                $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
-            }
-            $urlParameters = [
-                'edit' => [
-                    'pages' => [
-                        $this->id => 'edit',
-                    ],
-                ],
-                'returnUrl' => $normalizedParams->getRequestUri(),
-            ];
-            $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
+                    'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri(),
+                ]
+            );
             $editPageButton = $this->buttonBar->makeLinkButton()
                 ->setHref($url)
                 ->setTitle($lang->getLL('editPageProperties'))
                 ->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
             $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
         }
+
+        // Edit page properties of page language overlay (Only when one specific language is selected)
+        if ((int)$this->MOD_SETTINGS['function'] === 1
+            && $this->current_sys_language > 0
+            && $this->isPageEditable($this->current_sys_language)
+        ) {
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
+            $queryBuilder->getRestrictions()
+                ->removeAll()
+                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
+                ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getBackendUser()->workspace));
+            $overlayRecord = $queryBuilder
+                ->select('uid')
+                ->from('pages')
+                ->where(
+                    $queryBuilder->expr()->eq(
+                        $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
+                        $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
+                    ),
+                    $queryBuilder->expr()->eq(
+                        $GLOBALS['TCA']['pages']['ctrl']['languageField'],
+                        $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
+                    )
+                )
+                ->setMaxResults(1)
+                ->execute()
+                ->fetchAssociative();
+            BackendUtility::workspaceOL('pages', $overlayRecord, $this->getBackendUser()->workspace);
+            $url = (string)$this->uriBuilder->buildUriFromRoute(
+                'record_edit',
+                [
+                    'edit' => [
+                        'pages' => [
+                            $overlayRecord['uid'] => 'edit',
+                        ],
+                    ],
+                    'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri(),
+                ]
+            );
+            $editLanguageButton = $this->buttonBar->makeLinkButton()
+                ->setHref($url)
+                ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
+                ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
+            $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
+        }
     }
 
     /*******************************
diff --git a/typo3/sysext/backend/Classes/Form/FieldWizard/OtherLanguageContent.php b/typo3/sysext/backend/Classes/Form/FieldWizard/OtherLanguageContent.php
index ffa29adc77e9..ed26b35203c9 100644
--- a/typo3/sysext/backend/Classes/Form/FieldWizard/OtherLanguageContent.php
+++ b/typo3/sysext/backend/Classes/Form/FieldWizard/OtherLanguageContent.php
@@ -70,7 +70,7 @@ class OtherLanguageContent extends AbstractNode
             $defaultLanguageRow['pid']
         ) ?? '';
         if ($defaultLanguageValue !== '') {
-            $iconIdentifier = $this->data['systemLanguageRows'][0]['flagIconIdentifier'] ?: 'flags-multiple';
+            $iconIdentifier = ($this->data['systemLanguageRows'][0]['flagIconIdentifier'] ?? false) ?: 'flags-multiple';
             $html[] = '<div class="t3-form-original-language">';
             $html[] =   $iconFactory->getIcon($iconIdentifier, Icon::SIZE_SMALL)->render();
             $html[] =   $this->previewFieldValue($defaultLanguageValue);
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/Grid/LanguageColumn.php b/typo3/sysext/backend/Classes/View/BackendLayout/Grid/LanguageColumn.php
index 590bcd18e2ab..8bb20fbddf72 100644
--- a/typo3/sysext/backend/Classes/View/BackendLayout/Grid/LanguageColumn.php
+++ b/typo3/sysext/backend/Classes/View/BackendLayout/Grid/LanguageColumn.php
@@ -108,7 +108,8 @@ class LanguageColumn extends AbstractGridObject
 
     public function getAllowEditPage(): bool
     {
-        return $this->getBackendUser()->check('tables_modify', 'pages');
+        return $this->getBackendUser()->check('tables_modify', 'pages')
+            && $this->getBackendUser()->checkLanguageAccess($this->context->getSiteLanguage()->getLanguageId());
     }
 
     public function getPageEditTitle(): string
diff --git a/typo3/sysext/backend/Classes/View/PageLayoutContext.php b/typo3/sysext/backend/Classes/View/PageLayoutContext.php
index 5d69bdbac057..e226f2846efb 100644
--- a/typo3/sysext/backend/Classes/View/PageLayoutContext.php
+++ b/typo3/sysext/backend/Classes/View/PageLayoutContext.php
@@ -191,7 +191,16 @@ class PageLayoutContext
     {
         $selectedLanguageId = $this->drawingConfiguration->getSelectedLanguageId();
         if ($selectedLanguageId === -1) {
-            return $this->getSiteLanguages();
+            $languages = $this->getSiteLanguages();
+            if (!isset($languages[0])) {
+                // $languages may not contain the default (0) in case the user does not have access to it.
+                // However, as for selected pages, it should also be displayed readonly in the "all languages" view
+                $languages = [
+                    $this->site->getDefaultLanguage(),
+                    ...$languages,
+                ];
+            }
+            return $languages;
         }
         if ($selectedLanguageId > 0) {
             // A specific language is selected; compose a list of default language plus selected language
-- 
GitLab