[BUGFIX] Correctly determine previously used language in localization wizard 98/47298/2
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Tue, 15 Mar 2016 10:56:12 +0000 (11:56 +0100)
committerAndreas Fernandez <typo3@scripting-base.de>
Thu, 17 Mar 2016 07:48:01 +0000 (08:48 +0100)
If a column in a certain language has already content, the origin
language of that content is used now for further translation processes.

Change-Id: Ib6e1206adc6ccfd5b1e4816b9eb6562f8fc0e0ef
Resolves: #75126
Releases: master, 7.6
Reviewed-on: https://review.typo3.org/47298
Reviewed-by: Andreas Fernandez <typo3@scripting-base.de>
Tested-by: Andreas Fernandez <typo3@scripting-base.de>
typo3/sysext/backend/Classes/Controller/Page/LocalizationController.php
typo3/sysext/backend/Classes/Domain/Repository/Localization/LocalizationRepository.php [new file with mode: 0644]
typo3/sysext/backend/Classes/View/PageLayoutView.php

index c0f87c6..98da2af 100644 (file)
@@ -17,7 +17,7 @@ namespace TYPO3\CMS\Backend\Controller\Page;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
-use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
@@ -34,11 +34,17 @@ class LocalizationController
     protected $iconFactory;
 
     /**
+     * @var LocalizationRepository
+     */
+    protected $localizationRepository;
+
+    /**
      * Constructor
      */
     public function __construct()
     {
         $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
+        $this->localizationRepository = GeneralUtility::makeInstance(LocalizationRepository::class);
     }
 
     /**
@@ -59,55 +65,28 @@ class LocalizationController
         $pageId = (int)$params['pageId'];
         $colPos = (int)$params['colPos'];
         $languageId = (int)$params['languageId'];
-        $databaseConnection = $this->getDatabaseConnection();
-        $backendUser = $this->getBackendUser();
 
         /** @var TranslationConfigurationProvider $translationProvider */
         $translationProvider = GeneralUtility::makeInstance(TranslationConfigurationProvider::class);
         $systemLanguages = $translationProvider->getSystemLanguages($pageId);
 
         $availableLanguages = [];
-        $availableLanguages[0] = $systemLanguages[0];
-
-        $excludeQueryPart = BackendUtility::deleteClause('tt_content')
-            . BackendUtility::versioningPlaceholderClause('tt_content');
 
-        // First check whether column is empty and then load additional languages
-        $elementsInColumnCount = $databaseConnection->exec_SELECTcountRows(
-            'uid',
-            'tt_content',
-            'tt_content.sys_language_uid=' . (int)$languageId
-                . ' AND tt_content.colPos = ' . (int)$colPos
-                . ' AND tt_content.pid=' . (int)$pageId
-                . $excludeQueryPart
-        );
-        $additionalWhere = '';
-        if (!$backendUser->isAdmin()) {
-            $additionalWhere .= ' AND sys_language.hidden=0';
+        // First check whether column has localized records
+        $elementsInColumnCount = $this->localizationRepository->getLocalizedRecordCount($pageId, $colPos, $languageId);
 
-            if (!empty($backendUser->user['allowed_languages'])) {
-                $additionalWhere .= ' AND sys_language.uid IN(' . $databaseConnection->cleanIntList($backendUser->user['allowed_languages']) . ')';
-            }
-        }
         if ($elementsInColumnCount === 0) {
-            $res = $databaseConnection->exec_SELECTquery(
-                'sys_language.uid',
-                'tt_content,sys_language',
-                'tt_content.sys_language_uid=sys_language.uid'
-                    . ' AND tt_content.colPos = ' . (int)$colPos
-                    . ' AND tt_content.pid=' . (int)$pageId
-                    . ' AND sys_language.uid <> ' . (int)$languageId
-                    . $additionalWhere
-                    . $excludeQueryPart,
-                'sys_language.uid',
-                'sys_language.title'
-            );
-            while ($row = $databaseConnection->sql_fetch_assoc($res)) {
-                if (isset($systemLanguages[$row['uid']])) {
-                    $availableLanguages[] = $systemLanguages[$row['uid']];
+            $fetchedAvailableLanguages = $this->localizationRepository->fetchAvailableLanguages($pageId, $colPos, $languageId);
+            $availableLanguages[] = $systemLanguages[0];
+
+            foreach ($fetchedAvailableLanguages as $language) {
+                if (isset($systemLanguages[$language['uid']])) {
+                    $availableLanguages[] = $systemLanguages[$language['uid']];
                 }
             }
-            $databaseConnection->sql_free_result($res);
+        } else {
+            $result = $this->localizationRepository->fetchOriginLanguage($pageId, $colPos, $languageId);
+            $availableLanguages[] = $systemLanguages[$result['sys_language_uid']];
         }
 
         // Pre-render all flag icons
@@ -140,7 +119,7 @@ class LocalizationController
 
         $records = [];
         $databaseConnection = $this->getDatabaseConnection();
-        $res = $this->getRecordsToCopyDatabaseResult($params['pageId'], $params['colPos'], $params['destLanguageId'], $params['languageId'], '*');
+        $res = $this->localizationRepository->getRecordsToCopyDatabaseResult($params['pageId'], $params['colPos'], $params['destLanguageId'], $params['languageId'], '*');
         while ($row = $databaseConnection->sql_fetch_assoc($res)) {
             $records[] = [
                 'icon' => $this->iconFactory->getIconForRecord('tt_content', $row, Icon::SIZE_SMALL)->render(),
@@ -172,7 +151,7 @@ class LocalizationController
         $languageId = (int)$params['languageId'];
         $databaseConnection = $this->getDatabaseConnection();
 
-        $res = $this->getRecordsToCopyDatabaseResult($pageId, $colPos, $languageId, 'uid');
+        $res = $this->localizationRepository->getRecordsToCopyDatabaseResult($pageId, $colPos, $languageId, 'uid');
         $uids = [];
         while ($row = $databaseConnection->sql_fetch_assoc($res)) {
             $uids[] = (int)$row['uid'];
@@ -255,60 +234,6 @@ class LocalizationController
     }
 
     /**
-     * Get records for copy process
-     *
-     * @param int $pageId
-     * @param int $colPos
-     * @param int $destLanguageId
-     * @param int $languageId
-     * @param string $fields
-     * @return bool|\mysqli_result|object
-     */
-    protected function getRecordsToCopyDatabaseResult($pageId, $colPos, $destLanguageId, $languageId, $fields = '*')
-    {
-        $db = $this->getDatabaseConnection();
-
-        // Get original uid of existing elements triggered language / colpos
-        $originalUids = $db->exec_SELECTgetRows(
-            't3_origuid',
-            'tt_content',
-            'sys_language_uid=' . (int)$destLanguageId
-                . ' AND tt_content.colPos = ' . (int)$colPos
-                . ' AND tt_content.pid=' . (int)$pageId
-                . BackendUtility::deleteClause('tt_content')
-                . BackendUtility::versioningPlaceholderClause('tt_content'),
-            '',
-            '',
-            '',
-            't3_origuid'
-        );
-        $originalUidList = $db->cleanIntList(implode(',', array_keys($originalUids)));
-
-        return $db->exec_SELECTquery(
-            $fields,
-            'tt_content',
-            'tt_content.sys_language_uid=' . (int)$languageId
-            . ' AND tt_content.colPos = ' . (int)$colPos
-            . ' AND tt_content.pid=' . (int)$pageId
-            . ' AND tt_content.uid NOT IN (' . $originalUidList . ')'
-            . BackendUtility::deleteClause('tt_content')
-            . BackendUtility::versioningPlaceholderClause('tt_content'),
-            '',
-            'tt_content.sorting'
-        );
-    }
-
-    /**
-     * Returns the current BE user.
-     *
-     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
-     */
-    protected function getBackendUser()
-    {
-        return $GLOBALS['BE_USER'];
-    }
-
-    /**
      * Returns the database connection
      *
      * @return \TYPO3\CMS\Core\Database\DatabaseConnection
diff --git a/typo3/sysext/backend/Classes/Domain/Repository/Localization/LocalizationRepository.php b/typo3/sysext/backend/Classes/Domain/Repository/Localization/LocalizationRepository.php
new file mode 100644 (file)
index 0000000..42616cc
--- /dev/null
@@ -0,0 +1,191 @@
+<?php
+namespace TYPO3\CMS\Backend\Domain\Repository\Localization;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+
+/**
+ * Repository for record localizations
+ */
+class LocalizationRepository
+{
+    /**
+     * Fetch the language from which the records of a colPos in a certain language were initially localized
+     *
+     * @param int $pageId
+     * @param int $colPos
+     * @param int $localizedLanguage
+     * @return array|false|null
+     */
+    public function fetchOriginLanguage($pageId, $colPos, $localizedLanguage)
+    {
+        $record = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
+            'tt_content_orig.sys_language_uid',
+            'tt_content,tt_content AS tt_content_orig',
+            'tt_content.colPos = ' . (int)$colPos
+            . ' AND tt_content.pid = ' . (int)$pageId
+            . ' AND tt_content.sys_language_uid = ' . (int)$localizedLanguage
+            . ' AND tt_content.t3_origuid = tt_content_orig.uid'
+            . $this->getExcludeQueryPart()
+            . $this->getAllowedLanguagesForBackendUser(),
+            'tt_content_orig.sys_language_uid'
+        );
+
+        return $record;
+    }
+
+    /**
+     * @param $pageId
+     * @param $colPos
+     * @param $languageId
+     * @return int
+     */
+    public function getLocalizedRecordCount($pageId, $colPos, $languageId)
+    {
+        $rows = (int)$this->getDatabaseConnection()->exec_SELECTcountRows(
+            'uid',
+            'tt_content',
+            'tt_content.sys_language_uid=' . (int)$languageId
+            . ' AND tt_content.colPos = ' . (int)$colPos
+            . ' AND tt_content.pid=' . (int)$pageId
+            . ' AND tt_content.t3_origuid <> 0'
+            . $this->getExcludeQueryPart()
+        );
+
+        return $rows;
+    }
+
+    /**
+     * Fetch all available languages
+     *
+     * @param int $pageId
+     * @param int $colPos
+     * @param int $languageId
+     * @return array
+     */
+    public function fetchAvailableLanguages($pageId, $colPos, $languageId)
+    {
+        $result = $this->getDatabaseConnection()->exec_SELECTgetRows(
+            'sys_language.uid',
+            'tt_content,sys_language',
+            'tt_content.sys_language_uid=sys_language.uid'
+            . ' AND tt_content.colPos = ' . (int)$colPos
+            . ' AND tt_content.pid = ' . (int)$pageId
+            . ' AND sys_language.uid <> ' . (int)$languageId
+            . $this->getExcludeQueryPart()
+            . $this->getAllowedLanguagesForBackendUser(),
+            'sys_language.uid',
+            'sys_language.title'
+        );
+
+        return $result;
+    }
+
+    /**
+     * Builds an additional where clause to exclude deleted records and setting the versioning placeholders
+     *
+     * @return string
+     */
+    public function getExcludeQueryPart()
+    {
+        return BackendUtility::deleteClause('tt_content')
+            . BackendUtility::versioningPlaceholderClause('tt_content');
+    }
+
+    /**
+     * Builds an additional where clause to exclude hidden languages and limit a backend user to its allowed languages,
+     * if the user is not an admin.
+     *
+     * @return string
+     */
+    public function getAllowedLanguagesForBackendUser()
+    {
+        $backendUser = $this->getBackendUser();
+        $additionalWhere = '';
+        if (!$backendUser->isAdmin()) {
+            $additionalWhere .= ' AND sys_language.hidden=0';
+
+            if (!empty($backendUser->user['allowed_languages'])) {
+                $additionalWhere .= ' AND sys_language.uid IN(' . $this->getDatabaseConnection()->cleanIntList($backendUser->user['allowed_languages']) . ')';
+            }
+        }
+
+        return $additionalWhere;
+    }
+
+    /**
+     * Get records for copy process
+     *
+     * @param int $pageId
+     * @param int $colPos
+     * @param int $destLanguageId
+     * @param int $languageId
+     * @param string $fields
+     * @return bool|\mysqli_result|object
+     */
+    public function getRecordsToCopyDatabaseResult($pageId, $colPos, $destLanguageId, $languageId, $fields = '*')
+    {
+        $db = $this->getDatabaseConnection();
+
+        // Get original uid of existing elements triggered language / colpos
+        $originalUids = $db->exec_SELECTgetRows(
+            't3_origuid',
+            'tt_content',
+            'sys_language_uid=' . (int)$destLanguageId
+            . ' AND tt_content.colPos = ' . (int)$colPos
+            . ' AND tt_content.pid=' . (int)$pageId
+            . $this->getExcludeQueryPart(),
+            '',
+            '',
+            '',
+            't3_origuid'
+        );
+        $originalUidList = $db->cleanIntList(implode(',', array_keys($originalUids)));
+
+        $res = $db->exec_SELECTquery(
+            $fields,
+            'tt_content',
+            'tt_content.sys_language_uid=' . (int)$languageId
+            . ' AND tt_content.colPos = ' . (int)$colPos
+            . ' AND tt_content.pid=' . (int)$pageId
+            . ' AND tt_content.uid NOT IN (' . $originalUidList . ')'
+            . $this->getExcludeQueryPart(),
+            '',
+            'tt_content.sorting'
+        );
+
+        return $res;
+    }
+
+    /**
+     * Returns the current BE user.
+     *
+     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
+     */
+    protected function getBackendUser()
+    {
+        return $GLOBALS['BE_USER'];
+    }
+
+    /**
+     * Returns the database connection
+     *
+     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
+     */
+    protected function getDatabaseConnection()
+    {
+        return $GLOBALS['TYPO3_DB'];
+    }
+}
index f88c3f0..57addb6 100644 (file)
@@ -14,6 +14,7 @@ namespace TYPO3\CMS\Backend\View;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Backend\Controller\Page\LocalizationController;
 use TYPO3\CMS\Backend\Controller\PageLayoutController;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
@@ -210,11 +211,17 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
     protected $languageHasTranslationsCache = array();
 
     /**
+     * @var LocalizationController
+     */
+    protected $localizationController;
+
+    /**
      * Construct to initialize class variables.
      */
     public function __construct()
     {
         parent::__construct();
+        $this->localizationController = GeneralUtility::makeInstance(LocalizationController::class);
         $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
         $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
         $pageRenderer->addInlineLanguageLabelFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');