From 7650ede4f5c629ab561de9600baa25289657ea02 Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Thu, 2 Nov 2017 12:04:34 +0100
Subject: [PATCH] [FEATURE] Show page translations in list module

Translations of the current page can now be listed and edited through the
list module again.

This is a follow-up change to the pages_language_overlay removal.

Resolves: #83016
Releases: master
Change-Id: I38b41ba6b1b3164ab1b9cdfc03bca14780c38c08
Reviewed-on: https://review.typo3.org/54530
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Stefan Neufeind <typo3.neufeind@speedpartner.de>
Reviewed-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
Tested-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
---
 ...-ListingOfPageTranslationsInListModule.rst |  27 +++++
 .../Private/Language/locallang_core.xlf       |   3 +
 .../sysext/recordlist/Classes/RecordList.php  |  10 +-
 .../Classes/RecordList/DatabaseRecordList.php | 104 +++++++++++++++---
 4 files changed, 125 insertions(+), 19 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-83016-ListingOfPageTranslationsInListModule.rst

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-83016-ListingOfPageTranslationsInListModule.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-83016-ListingOfPageTranslationsInListModule.rst
new file mode 100644
index 000000000000..9d0d985b65e5
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-83016-ListingOfPageTranslationsInListModule.rst
@@ -0,0 +1,27 @@
+.. include:: ../../Includes.txt
+
+=============================================================
+Feature: #83016 - Listing of page translations in list module
+=============================================================
+
+See :issue:`83016`
+
+Description
+===========
+
+Listing and editing translations of the current page are re-introduced for the List module. This feature
+was previously available for v8 and below due to the concept of "pages_language_overlay", which resided on the
+actually current page.
+
+However, due to the removal of "pages_language_overlay" database table, page translations are only accessible
+when visiting the list module of the parent page.
+
+The original behaviour was now reintroduced, but with an improved visibility and additional restrictions.
+
+
+Impact
+======
+
+When inside the list module, page translations are always shown as first table listing on top of the module.
+
+.. index:: Backend, NotScanned
\ No newline at end of file
diff --git a/typo3/sysext/lang/Resources/Private/Language/locallang_core.xlf b/typo3/sysext/lang/Resources/Private/Language/locallang_core.xlf
index b311af99d2e0..923c4279c0a3 100644
--- a/typo3/sysext/lang/Resources/Private/Language/locallang_core.xlf
+++ b/typo3/sysext/lang/Resources/Private/Language/locallang_core.xlf
@@ -1323,6 +1323,9 @@ Check also the following points:\n
 			<trans-unit id="labels.editTitle">
 				<source>Edit this Constant</source>
 			</trans-unit>
+			<trans-unit id="pageTranslation">
+				<source>Page Translation</source>
+			</trans-unit>
 		</body>
 	</file>
 </xliff>
diff --git a/typo3/sysext/recordlist/Classes/RecordList.php b/typo3/sysext/recordlist/Classes/RecordList.php
index 246e6cded89c..541fc92bcd70 100644
--- a/typo3/sysext/recordlist/Classes/RecordList.php
+++ b/typo3/sysext/recordlist/Classes/RecordList.php
@@ -461,10 +461,18 @@ class RecordList
         $this->moduleTemplate->setTitle($title);
 
         $output = '';
-        // Show the selector for new translations of the current page
+        // Show the selector to add page translations and the list of translations of the current page
         // but only when in "default" mode
         if ($this->id && !$dblist->csvOutput && !$this->search_field && !$this->cmd && !$this->table) {
             $output .= $this->languageSelector($this->id);
+            $pageTranslationsDatabaseRecordList = clone $dblist;
+            $pageTranslationsDatabaseRecordList->listOnlyInSingleTableMode = false;
+            $pageTranslationsDatabaseRecordList->disableSingleTableView = true;
+            $pageTranslationsDatabaseRecordList->deniedNewTables = ['pages'];
+            $pageTranslationsDatabaseRecordList->hideTranslations = '';
+            $pageTranslationsDatabaseRecordList->iLimit = $pageTranslationsDatabaseRecordList->itemsLimitPerTable;
+            $pageTranslationsDatabaseRecordList->showOnlyTranslatedRecords(true);
+            $output .= $pageTranslationsDatabaseRecordList->getTable('pages', $this->id);
         }
 
         if (!empty($dblist->HTMLcode)) {
diff --git a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
index 8330a7e2663b..3afa560b1fc5 100644
--- a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
+++ b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
@@ -588,6 +588,13 @@ class DatabaseRecordList
      */
     protected $overrideUrlParameters = [];
 
+    /**
+     * Only used to render translated records, used in list module to show page translations
+     *
+     * @var bool
+     */
+    protected $showOnlyTranslatedRecords = false;
+
     /**
      * Constructor
      */
@@ -938,9 +945,13 @@ class DatabaseRecordList
         // Localization
         if ($l10nEnabled) {
             $this->fieldArray[] = '_LOCALIZATION_';
-            $this->fieldArray[] = '_LOCALIZATION_b';
+            // Do not show the "Localize to:" field when only translated records should be shown
+            if (!$this->showOnlyTranslatedRecords) {
+                $this->fieldArray[] = '_LOCALIZATION_b';
+            }
             // Only restrict to the default language if no search request is in place
-            if ($this->searchString === '') {
+            // And if only translations should be shown
+            if ($this->searchString === '' && !$this->showOnlyTranslatedRecords) {
                 $addWhere = (string)$queryBuilder->expr()->orX(
                     $queryBuilder->expr()->lte($GLOBALS['TCA'][$table]['ctrl']['languageField'], 0),
                     $queryBuilder->expr()->eq($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], 0)
@@ -1077,9 +1088,14 @@ class DatabaseRecordList
         }
         // If any records was selected, render the list:
         if ($dbCount) {
-            $tableTitle = htmlspecialchars($lang->sL($GLOBALS['TCA'][$table]['ctrl']['title']));
-            if ($tableTitle === '') {
-                $tableTitle = $table;
+            // Use a custom table title for translated pages
+            if ($table == 'pages' && $this->showOnlyTranslatedRecords) {
+                $tableTitle = htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:pageTranslation'));
+            } else {
+                $tableTitle = htmlspecialchars($lang->sL($GLOBALS['TCA'][$table]['ctrl']['title']));
+                if ($tableTitle === '') {
+                    $tableTitle = $table;
+                }
             }
             // Header line is drawn
             $theData = [];
@@ -1269,6 +1285,36 @@ class DatabaseRecordList
         return $out;
     }
 
+    /**
+     * Get viewOnClick link for pages or tt_content records
+     *
+     * @param string $table
+     * @param array $row
+     *
+     * @return string
+     */
+    protected function getOnClickForRow(string $table, array $row): string
+    {
+        if ($table === 'tt_content') {
+            // Link to a content element
+            $onClick = BackendUtility::viewOnClick($this->id, '', null, '#' . $row['uid']);
+        } elseif ($table === 'pages' && $row[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']] > 0) {
+            // Link to a page translation
+            $onClick = BackendUtility::viewOnClick(
+                $row[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']],
+                '',
+                null,
+                '',
+                '',
+                '&L=' . $row[$GLOBALS['TCA']['pages']['ctrl']['languageField']]
+            );
+        } else {
+            // Link to a page in the default language
+            $onClick = BackendUtility::viewOnClick($row['uid']);
+        }
+        return $onClick;
+    }
+
     /**
      * Check if all row listing conditions are fulfilled.
      *
@@ -1858,14 +1904,10 @@ class DatabaseRecordList
         $permsEdit = $this->overlayEditLockPermissions($table, $row, $permsEdit);
         // "Show" link (only pages and tt_content elements)
         if ($table === 'pages' || $table === 'tt_content') {
+            $onClick = $this->getOnClickForRow($table, $row);
             $viewAction = '<a class="btn btn-default" href="#" onclick="'
-                . htmlspecialchars(
-                    BackendUtility::viewOnClick(
-                        ($table === 'tt_content' ? $this->id : $row['uid']),
-                        '',
-                        null,
-                        ($table === 'tt_content' ? '#c' . $row['uid'] : '')
-                    )
+                          . htmlspecialchars(
+                              $onClick
                 ) . '" title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage')) . '">';
             if ($table === 'pages') {
                 $viewAction .= $this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL)->render();
@@ -3189,6 +3231,15 @@ class DatabaseRecordList
                     0
                 )
             );
+        } elseif (!empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) && $this->showOnlyTranslatedRecords) {
+            // When only translated records should be shown, it is necessary to use l10n_parent=pageId, instead of
+            // a check to the PID
+            $queryBuilder->andWhere(
+                $queryBuilder->expr()->eq(
+                    $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
+                    $this->id
+                )
+            );
         }
 
         $hookName = static::class;
@@ -3478,11 +3529,8 @@ class DatabaseRecordList
             case 'show':
                 // "Show" link (only pages and tt_content elements)
                 if ($table === 'pages' || $table === 'tt_content') {
-                    $code = '<a href="#" onclick="' . htmlspecialchars(
-                            BackendUtility::viewOnClick(
-                                ($table === 'tt_content' ? $this->id . '#' . $row['uid'] : $row['uid'])
-                            )
-                        ) . '" title="' . htmlspecialchars(
+                    $onClick = $this->getOnClickForRow($table, $row);
+                    $code = '<a href="#" onclick="' . htmlspecialchars($onClick) . '" title="' . htmlspecialchars(
                             $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage')
                         ) . '">' . $code . '</a>';
                 }
@@ -3830,7 +3878,16 @@ class DatabaseRecordList
             $searchLevels = 999;
         }
 
-        if ($searchLevels === 0) {
+        // When querying translated pages, the PID of the translated pages should be the same as the
+        // the PID of the current page
+        if ($tableName === 'pages' && $this->showOnlyTranslatedRecords) {
+            $queryBuilder->andWhere(
+                $queryBuilder->expr()->eq(
+                    $tableName . '.pid',
+                    $queryBuilder->createNamedParameter($this->pageRecord['pid'], \PDO::PARAM_INT)
+                )
+            );
+        } elseif ($searchLevels === 0) {
             $queryBuilder->andWhere(
                 $queryBuilder->expr()->eq(
                     $tableName . '.pid',
@@ -4201,6 +4258,17 @@ class DatabaseRecordList
         return $htmlCode;
     }
 
+    /**
+     * If enabled, only translations are shown (= only with l10n_parent)
+     * See the use case in RecordList class, where a list of page translations is rendered before.
+     *
+     * @param bool $showOnlyTranslatedRecords
+     */
+    public function showOnlyTranslatedRecords(bool $showOnlyTranslatedRecords)
+    {
+        $this->showOnlyTranslatedRecords = $showOnlyTranslatedRecords;
+    }
+
     /**
      * Returns the language service
      * @return LanguageService
-- 
GitLab