From 70b3a05b63a94f3a06e0054124643ba5a9c8ea6c Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Sun, 8 Mar 2020 10:23:07 +0100
Subject: [PATCH] [BUGFIX] Align new page module behavior with PageLayoutView

This fixes some missing or faulty checks related to the display
and behaviour of the fluid based page module to bring it into
alignment with the old PageLayoutView.

* Correctly consider localization TSConfig settings
* Add checks for content-editing allowed for user
* Add check for presence of untranslated records
* Pass all variables to record header/footer partial
* Do not link preview content if user has no content edit access

Resolves: #90559
Resolves: #90605
Related: #90608
Releases: master
Change-Id: Ie2f200a280f60ed08b5850ead81644ee52508c1a
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63615
Tested-by: Oliver Bartsch <bo@cedev.de>
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Daniel Goerz <daniel.goerz@posteo.de>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Reviewed-by: Daniel Goerz <daniel.goerz@posteo.de>
---
 .../StandardContentPreviewRenderer.php        |  3 +-
 .../View/BackendLayout/BackendLayout.php      |  7 +--
 .../View/BackendLayout/ContentFetcher.php     | 14 +++++
 .../BackendLayout/Grid/LanguageColumn.php     | 62 ++-----------------
 .../View/Drawing/BackendLayoutRenderer.php    |  1 +
 .../Partials/PageLayout/Grid/Column.html      |  4 +-
 .../Partials/PageLayout/LanguageColumns.html  |  2 +-
 .../Private/Partials/PageLayout/Record.html   |  6 +-
 .../Partials/PageLayout/Record/Header.html    |  4 +-
 9 files changed, 32 insertions(+), 71 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Preview/StandardContentPreviewRenderer.php b/typo3/sysext/backend/Classes/Preview/StandardContentPreviewRenderer.php
index 5c7adb56fec7..38b0e1d1235b 100644
--- a/typo3/sysext/backend/Classes/Preview/StandardContentPreviewRenderer.php
+++ b/typo3/sysext/backend/Classes/Preview/StandardContentPreviewRenderer.php
@@ -400,7 +400,8 @@ class StandardContentPreviewRenderer implements PreviewRendererInterface, Logger
      */
     protected function linkEditContent(string $linkText, $row): string
     {
-        if ($this->getBackendUser()->recordEditAccessInternals('tt_content', $row)) {
+        $backendUser = $this->getBackendUser();
+        if ($backendUser->check('tables_modify', 'tt_content') && $backendUser->recordEditAccessInternals('tt_content', $row)) {
             $urlParameters = [
                 'edit' => [
                     'tt_content' => [
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php b/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php
index 4bffb76b62f6..e0c467149685 100644
--- a/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php
+++ b/typo3/sysext/backend/Classes/View/BackendLayout/BackendLayout.php
@@ -280,18 +280,13 @@ class BackendLayout
     public function getLanguageColumns(): iterable
     {
         if (empty($this->languageColumns)) {
-            $defaultLanguageElements = [];
-            $contentByColumn = $this->getContentFetcher()->getContentRecordsPerColumn(null, 0);
-            if (!empty($contentByColumn)) {
-                $defaultLanguageElements = array_merge(...$contentByColumn);
-            }
             foreach ($this->getDrawingConfiguration()->getSiteLanguages() as $siteLanguage) {
                 if (!in_array($siteLanguage->getLanguageId(), $this->getDrawingConfiguration()->getLanguageColumns())) {
                     continue;
                 }
                 $backendLayout = clone $this;
                 $backendLayout->getDrawingConfiguration()->setLanguageColumnsPointer($siteLanguage->getLanguageId());
-                $this->languageColumns[] = GeneralUtility::makeInstance(LanguageColumn::class, $backendLayout, $siteLanguage, $defaultLanguageElements);
+                $this->languageColumns[] = GeneralUtility::makeInstance(LanguageColumn::class, $backendLayout, $siteLanguage);
             }
         }
         return $this->languageColumns;
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/ContentFetcher.php b/typo3/sysext/backend/Classes/View/BackendLayout/ContentFetcher.php
index 757ce29ab57f..1d79bec530c2 100644
--- a/typo3/sysext/backend/Classes/View/BackendLayout/ContentFetcher.php
+++ b/typo3/sysext/backend/Classes/View/BackendLayout/ContentFetcher.php
@@ -128,6 +128,16 @@ class ContentFetcher
         }
 
         if (!isset($this->languageHasTranslationsCache[$language])) {
+            if ($language) {
+                $contentRecordsInDefaultLanguage = $this->getContentRecordsPerColumn(null, 0);
+                if (!empty($contentRecordsInDefaultLanguage)) {
+                    $contentRecordsInDefaultLanguage = array_merge(...$contentRecordsInDefaultLanguage);
+                }
+            } else {
+                $contentRecordsInDefaultLanguage = $contentElements;
+            }
+            $untranslatedRecordUids = array_flip(array_column($contentRecordsInDefaultLanguage, 'uid'));
+
             foreach ($contentElements as $contentElement) {
                 if ((int)$contentElement['l18n_parent'] === 0) {
                     $this->languageHasTranslationsCache[$language]['hasStandAloneContent'] = true;
@@ -137,10 +147,14 @@ class ContentFetcher
                     $this->languageHasTranslationsCache[$language]['hasTranslations'] = true;
                     $this->languageHasTranslationsCache[$language]['mode'] = 'connected';
                 }
+                if ((int)$contentElement['l10n_source'] > 0) {
+                    unset($untranslatedRecordUids[(int)$contentElement['l10n_source']]);
+                }
             }
             if (!isset($this->languageHasTranslationsCache[$language])) {
                 $this->languageHasTranslationsCache[$language]['hasTranslations'] = false;
             }
+            $this->languageHasTranslationsCache[$language]['untranslatedRecordUids'] = array_keys($untranslatedRecordUids);
 
             // Check for inconsistent translations, force "mixed" mode and dispatch a FlashMessage to user if such a case is encountered.
             if (isset($this->languageHasTranslationsCache[$language]['hasStandAloneContent'])
diff --git a/typo3/sysext/backend/Classes/View/BackendLayout/Grid/LanguageColumn.php b/typo3/sysext/backend/Classes/View/BackendLayout/Grid/LanguageColumn.php
index 4e0196813a4d..3ee084a47b72 100644
--- a/typo3/sysext/backend/Classes/View/BackendLayout/Grid/LanguageColumn.php
+++ b/typo3/sysext/backend/Classes/View/BackendLayout/Grid/LanguageColumn.php
@@ -54,23 +54,18 @@ class LanguageColumn extends AbstractGridObject
     /**
      * @var array
      */
-    protected $defaultLanguageElements = [];
-
-    /**
-     * @var array
-     */
-    protected $flatContentOfLanguage = [];
+    protected $localizationConfiguration = [];
 
     /**
      * @var GridColumn|null
      */
     protected $grid;
 
-    public function __construct(BackendLayout $backendLayout, SiteLanguage $language, array $defaultLanguageElements)
+    public function __construct(BackendLayout $backendLayout, SiteLanguage $language)
     {
         parent::__construct($backendLayout);
         $this->siteLanguage = $language;
-        $this->defaultLanguageElements = $defaultLanguageElements;
+        $this->localizationConfiguration = BackendUtility::getPagesTSconfig($backendLayout->getDrawingConfiguration()->getPageId())['mod.']['web_layout.']['localization.'] ?? [];
         if ($this->siteLanguage->getLanguageId() > 0) {
             $pageLocalizationRecord = BackendUtility::getRecordLocalization(
                 'pages',
@@ -85,14 +80,6 @@ class LanguageColumn extends AbstractGridObject
         } else {
             $this->localizedPageRecord = $backendLayout->getDrawingConfiguration()->getPageRecord();
         }
-
-        $contentFetcher = $backendLayout->getContentFetcher();
-        $contentRecords = $contentFetcher->getContentRecordsPerColumn(null, $language->getLanguageId());
-        if (!empty($contentRecords)) {
-            $this->flatContentOfLanguage = array_merge(...$contentRecords);
-        } else {
-            $this->flatContentOfLanguage = [];
-        }
     }
 
     public function getLocalizedPageRecord(): ?array
@@ -149,55 +136,18 @@ class LanguageColumn extends AbstractGridObject
 
     public function getAllowTranslate(): bool
     {
-        if ($this->siteLanguage->getLanguageId() === 0) {
-            return false;
-        }
-
-        $localizationTsConfig = BackendUtility::getPagesTSconfig($this->backendLayout->getDrawingConfiguration()->getPageId())['mod.']['web_layout.']['localization.'] ?? [];
-        $allowTranslate = (bool)($localizationTsConfig['enableTranslate'] ?? true);
-        if (!$allowTranslate) {
-            return false;
-        }
-
-        $translationData = $this->backendLayout->getContentFetcher()->getTranslationData($this->flatContentOfLanguage, $this->siteLanguage->getLanguageId());
-        if (!empty($translationData)) {
-            if (isset($translationData['hasStandAloneContent'])) {
-                return false;
-            }
-        }
-
-        $defaultLanguageUids = array_flip(array_column($this->defaultLanguageElements, 'uid'));
-        $translatedLanguageUids = array_column($this->flatContentOfLanguage, 'l10n_source');
-        if (empty($translatedLanguageUids)) {
-            return true;
-        }
-
-        foreach ($translatedLanguageUids as $translatedUid) {
-            unset($defaultLanguageUids[$translatedUid]);
-        }
-
-        return !empty($defaultLanguageUids);
+        return ($this->localizationConfiguration['enableTranslate'] ?? true) && !($this->getTranslationData()['hasStandAloneContent'] ?? false);
     }
 
     public function getTranslationData(): array
     {
         $contentFetcher = $this->backendLayout->getContentFetcher();
-        return $contentFetcher->getTranslationData($this->defaultLanguageElements, $this->siteLanguage->getLanguageId());
+        return $contentFetcher->getTranslationData($contentFetcher->getFlatContentRecords(), $this->siteLanguage->getLanguageId());
     }
 
     public function getAllowTranslateCopy(): bool
     {
-        $localizationTsConfig = BackendUtility::getPagesTSconfig($this->backendLayout->getDrawingConfiguration()->getPageId())['mod.']['web_layout.']['localization.'] ?? [];
-        $allowCopy = (bool)($localizationTsConfig['enableCopy'] ?? true);
-        if (!empty($translationData)) {
-            if (isset($translationData['hasStandAloneContent'])) {
-                return false;
-            }
-            if (isset($translationData['hasTranslations'])) {
-                $allowCopy = $allowCopy && !$translationData['hasTranslations'];
-            }
-        }
-        return $allowCopy;
+        return ($this->localizationConfiguration['enableCopy'] ?? true) && !($this->getTranslationData()['hasTranslations'] ?? false);
     }
 
     public function getTranslatePageTitle(): string
diff --git a/typo3/sysext/backend/Classes/View/Drawing/BackendLayoutRenderer.php b/typo3/sysext/backend/Classes/View/Drawing/BackendLayoutRenderer.php
index dace807aab14..fe6b6ddaee42 100644
--- a/typo3/sysext/backend/Classes/View/Drawing/BackendLayoutRenderer.php
+++ b/typo3/sysext/backend/Classes/View/Drawing/BackendLayoutRenderer.php
@@ -98,6 +98,7 @@ class BackendLayoutRenderer
         }
         $this->view->assign('newContentTitle', $this->getLanguageService()->getLL('newContentElement'));
         $this->view->assign('newContentTitleShort', $this->getLanguageService()->getLL('content'));
+        $this->view->assign('allowEditContent', $this->getBackendUser()->check('tables_modify', 'tt_content'));
 
         $rendered = $this->view->render('PageLayout');
         if ($renderUnused) {
diff --git a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Grid/Column.html b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Grid/Column.html
index e1935eb681f1..838fa0d8be81 100644
--- a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Grid/Column.html
+++ b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Grid/Column.html
@@ -8,7 +8,7 @@
         <f:if condition="{column.active}">
             <f:then>
                 <div class="t3-page-column-header-icons">
-                    <f:if condition="{column.editUrl}">
+                    <f:if condition="{allowEditContent} && {column.editUrl}">
                         <a href="{column.editUrl}" title="{column.editLinkTitle}"><core:icon identifier="actions-document-open" /></a>
                     </f:if>
                 </div>
@@ -25,7 +25,7 @@
             </f:else>
         </f:if>
     </div>
-    <f:if condition="{column.contentEditable} && {grid.allowNewContent}">
+    <f:if condition="{allowEditContent} && {column.contentEditable} && {grid.allowNewContent}">
         <div class="t3-page-ce t3js-page-ce" data-page="{column.backendLayout.drawingConfiguration.pageId}" id="{column.uniqueId}">
             <div class="t3js-page-new-ce t3-page-ce-wrapper-new-ce" id="colpos-{column.columnNumber}-page-{column.backendLayout.drawingConfiguration.pageId}-{column.uniqueId}">
                 <a href="{column.newContentUrl}" title="{newContentTitle}" data-title="{newContentTitle}"
diff --git a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/LanguageColumns.html b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/LanguageColumns.html
index d7cbb286e890..46711817a802 100644
--- a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/LanguageColumns.html
+++ b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/LanguageColumns.html
@@ -35,7 +35,7 @@
                                 <core:icon identifier="actions-open" />
                             </a>
                         </f:if>
-                        <f:if condition="{languageColumn.allowTranslate}">
+                        <f:if condition="{allowEditContent} && {languageColumn.siteLanguage.languageId} && {languageColumn.translationData.untranslatedRecordUids}">
                             <a href="#" class="btn btn-default btn-sm t3js-localize disabled"
                                 title="{languageColumn.translatePageTitle}"
                                 data-page="{languageColumn.localizedPageRecord.title}"
diff --git a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record.html b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record.html
index 8dc2731d8386..bfaaaeb252f9 100644
--- a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record.html
+++ b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record.html
@@ -2,7 +2,7 @@
 <div class="t3-page-ce {item.wrapperClassName} t3js-page-ce t3js-page-ce-sortable" id="element-tt_content-{item.record.uid}" data-table="tt_content" data-uid="{item.record.uid}" style="{style}">
     <div class="t3-page-ce-dragitem" id="{item.uniqueId}">
         <f:render partial="PageLayout/Record/{item.record.CType}/Header" optional="1">
-            <f:render partial="PageLayout/Record/Header" arguments="{item: item}" />
+            <f:render partial="PageLayout/Record/Header" arguments="{_all}" />
         </f:render>
         <div class="t3-page-ce-body">
             <div class="t3-page-ce-body-inner">
@@ -13,11 +13,11 @@
                 </div>
             </div>
             <f:render partial="PageLayout/Record/{item.record.CType}/Footer" optional="1">
-                <f:render partial="PageLayout/Record/Footer" arguments="{item: item}" />
+                <f:render partial="PageLayout/Record/Footer" arguments="{_all}" />
             </f:render>
         </div>
     </div>
-    <f:if condition="{item.column.contentEditable} && {grid.allowNewContent}">
+    <f:if condition="{allowEditContent} && {item.column.contentEditable} && {grid.allowNewContent}">
         <div class="t3js-page-new-ce t3-page-ce-wrapper-new-ce" id="colpos-{item.column.columnNumber}-page-{item.column.backendLayout.drawingConfiguration.pageId}-{item.column.uniqueId}">
             <a href="{item.newContentAfterUrl}" title="{item.newContentAfterLinkTitle}" data-title="{item.newContentAfterLinkTitle}" class="btn btn-default btn-sm t3js-toggle-new-content-element-wizard">
                 <core:icon identifier="actions-add" />
diff --git a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record/Header.html b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record/Header.html
index 2cf6917020e7..4f3b5e74bdea 100644
--- a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record/Header.html
+++ b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record/Header.html
@@ -1,9 +1,9 @@
-<div class="t3-page-ce-header {f:if(condition: item.dragAndDropAllowed, then: 't3-page-ce-header-draggable t3js-page-ce-draghandle')}">
+<div class="t3-page-ce-header {f:if(condition: '{allowEditContent} && {item.dragAndDropAllowed}', then: 't3-page-ce-header-draggable t3js-page-ce-draghandle')}">
     <div class="t3-page-ce-header-icons-left">
         {item.icons -> f:format.raw()}
     </div>
     <div class="t3-page-ce-header-icons-right">
-        <f:if condition="{item.editable}">
+        <f:if condition="{item.editable} && {allowEditContent}">
             <div class="btn-toolbar">
                 <div class="btn-group btn-group-sm">
                     <a href="{item.editUrl}" class="btn btn-default">
-- 
GitLab