[!!!][TASK] Remove sys_domain redirect functionality 27/55427/5
authorBenni Mack <benni@typo3.org>
Mon, 22 Jan 2018 15:43:37 +0000 (16:43 +0100)
committerSusanne Moog <susanne.moog@typo3.org>
Tue, 23 Jan 2018 16:55:47 +0000 (17:55 +0100)
The existing functionality of sys_domain database records that redirect to a
different target has been migrated into the new "redirects" core extension.

An upgrade wizard ensures that old (and even deleted) sys_domain records
with a redirect target (database field "redirectTo") are properly migrated into
the redirects extension.

The following database fields have been dropped:
- sys_domain.redirectTo
- sys_domain.redirectHttpStatusCode
- sys_domain.prepend_params

These values are not evaluated anymore in the TYPO3 Frontend.

Resolves: #83638
Releases: master
Change-Id: I0577a5bfb9db9e0b5d06b7c5f2d0c6f428c8a4ba
Reviewed-on: https://review.typo3.org/55427
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Joerg Boesche <typo3@joergboesche.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
12 files changed:
typo3/sysext/backend/Classes/Controller/Page/TreeController.php
typo3/sysext/backend/Classes/Utility/BackendUtility.php
typo3/sysext/backend/Tests/Functional/Utility/Fixtures/sys_domain.xml
typo3/sysext/core/Documentation/Changelog/master/Breaking-83638-RedirectFunctionalityMovedFromSys_domainToRedirectsModule.rst [new file with mode: 0644]
typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
typo3/sysext/frontend/Classes/Page/PageRepository.php
typo3/sysext/frontend/Configuration/TCA/sys_domain.php
typo3/sysext/frontend/Resources/Private/Language/locallang_tca.xlf
typo3/sysext/frontend/Tests/Functional/Tca/DomainVisibleFieldsTest.php
typo3/sysext/frontend/ext_tables.sql
typo3/sysext/install/Classes/Updates/RedirectsExtensionUpdate.php [new file with mode: 0644]
typo3/sysext/install/ext_localconf.php

index a5255cb..5ededca 100644 (file)
@@ -372,9 +372,6 @@ class TreeController
             $result = $queryBuilder
                 ->select('domainName', 'pid')
                 ->from('sys_domain')
-                ->where(
-                    $queryBuilder->expr()->neq('redirectTo', $queryBuilder->createNamedParameter('""'))
-                )
                 ->orderBy('sorting')
                 ->execute()
                 ->fetchAll();
index 851e51c..551b11d 100644 (file)
@@ -3475,10 +3475,6 @@ class BackendUtility
                     $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT, ':pid')
                 ),
                 $queryBuilder->expr()->eq(
-                    'redirectTo',
-                    $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
-                ),
-                $queryBuilder->expr()->eq(
                     'hidden',
                     $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
                 )
index 87123b5..849138e 100644 (file)
@@ -13,7 +13,5 @@
         <tstamp>1487563945</tstamp>
         <hidden>0</hidden>
         <domainName>www.example.net</domainName>
-        <redirectTo>http://example.com/</redirectTo>
-        <redirectHttpStatusCode>301</redirectHttpStatusCode>
     </sys_domain>
 </dataset>
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-83638-RedirectFunctionalityMovedFromSys_domainToRedirectsModule.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-83638-RedirectFunctionalityMovedFromSys_domainToRedirectsModule.rst
new file mode 100644 (file)
index 0000000..4cee495
--- /dev/null
@@ -0,0 +1,38 @@
+.. include:: ../../Includes.txt
+
+===================================================================================
+Breaking: #83638 - Redirect functionality moved from sys_domain to redirects module
+===================================================================================
+
+See :issue:`83638`
+
+Description
+===========
+
+Records of type `sys_domain` previously provided the possibility to provide a redirect via the fields `redirectTo`,
+`redirectHttpStatusCode` and `prepend_params`. The functionality has been moved from `sys_domain` to the
+new `sys_redirect` database records.
+
+
+Impact
+======
+
+The database fields `redirectTo`, `redirectHttpStatusCode` and `prepend_params` of table `sys_domain` were removed.
+Domain selection logic has been simplified to not consider these fields anymore.
+
+
+Affected Installations
+======================
+
+All installations directly accessing the database fields `redirectTo`, `redirectHttpStatusCode` and `prepend_params` of
+table `sys_domain` and having configured redirects in `sys_domain`.
+
+
+Migration
+=========
+
+An upgrade wizard is provided to migrate `sys_domain` records with redirects to `sys_redirect` records.
+If you directly accessed the named database fields above, change the queries accordingly to select the corresponding
+`sys_redirect` records.
+
+.. index:: Frontend, NotScanned
index e4036fd..c340323 100644 (file)
@@ -4465,12 +4465,6 @@ class TypoScriptFrontendController implements LoggerAwareInterface
             $result = $queryBuilder
                 ->select('uid', 'pid', 'domainName')
                 ->from('sys_domain')
-                ->where(
-                    $queryBuilder->expr()->eq(
-                        'redirectTo',
-                        $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
-                    )
-                )
                 ->orderBy('sorting', 'ASC')
                 ->execute();
 
index 5c0c643..3a09f2f 100644 (file)
@@ -27,7 +27,6 @@ use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
 use TYPO3\CMS\Core\Resource\FileRepository;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\HttpUtility;
 use TYPO3\CMS\Core\Utility\RootlineUtility;
 use TYPO3\CMS\Core\Versioning\VersionState;
 
@@ -880,8 +879,6 @@ class PageRepository implements LoggerAwareInterface
     }
     /**
      * Will find the page carrying the domain record matching the input domain.
-     * Might exit after sending a redirect-header IF a found domain record
-     * instructs to do so.
      *
      * @param string $domain Domain name to search for. Eg. "www.typo3.com". Typical the HTTP_HOST value.
      * @param string $path Path for the current script in domain. Eg. "/somedir/subdir". Typ. supplied by \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('SCRIPT_NAME')
@@ -902,10 +899,7 @@ class PageRepository implements LoggerAwareInterface
         $queryBuilder->getRestrictions()->removeAll();
         $row = $queryBuilder
             ->select(
-                'pages.uid',
-                'sys_domain.redirectTo',
-                'sys_domain.redirectHttpStatusCode',
-                'sys_domain.prepend_params'
+                'pages.uid'
             )
             ->from('pages')
             ->from('sys_domain')
@@ -935,22 +929,6 @@ class PageRepository implements LoggerAwareInterface
         if (!$row) {
             return '';
         }
-
-        if ($row['redirectTo']) {
-            $redirectUrl = $row['redirectTo'];
-            if ($row['prepend_params']) {
-                $redirectUrl = rtrim($redirectUrl, '/');
-                $prependStr = ltrim(substr($request_uri, strlen($path)), '/');
-                $redirectUrl .= '/' . $prependStr;
-            }
-            $statusCode = (int)$row['redirectHttpStatusCode'];
-            if ($statusCode && defined(HttpUtility::class . '::HTTP_STATUS_' . $statusCode)) {
-                HttpUtility::redirect($redirectUrl, constant(HttpUtility::class . '::HTTP_STATUS_' . $statusCode));
-            } else {
-                HttpUtility::redirect($redirectUrl, HttpUtility::HTTP_STATUS_301);
-            }
-            die;
-        }
         return $row['uid'];
     }
 
index 27ffcf1..5695793 100644 (file)
@@ -13,10 +13,10 @@ return [
         'typeicon_classes' => [
             'default' => 'mimetypes-x-content-domain'
         ],
-        'searchFields' => 'domainName,redirectTo'
+        'searchFields' => 'domainName'
     ],
     'interface' => [
-        'showRecordFieldList' => 'hidden,domainName,redirectTo'
+        'showRecordFieldList' => 'hidden,domainName'
     ],
     'columns' => [
         'domainName' => [
@@ -29,33 +29,6 @@ return [
                 'softref' => 'substitute'
             ]
         ],
-        'redirectTo' => [
-            'label' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:sys_domain.redirectTo',
-            'config' => [
-                'type' => 'input',
-                'size' => 35,
-                'max' => 255,
-                'default' => '',
-                'eval' => 'trim',
-                'softref' => 'substitute'
-            ]
-        ],
-        'redirectHttpStatusCode' => [
-            'exclude' => true,
-            'label' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:sys_domain.redirectHttpStatusCode',
-            'config' => [
-                'type' => 'select',
-                'renderType' => 'selectSingle',
-                'items' => [
-                    ['LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:sys_domain.redirectHttpStatusCode.301', '301'],
-                    ['LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:sys_domain.redirectHttpStatusCode.302', '302'],
-                    ['LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:sys_domain.redirectHttpStatusCode.303', '303'],
-                    ['LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:sys_domain.redirectHttpStatusCode.307', '307']
-                ],
-                'size' => 1,
-                'maxitems' => 1
-            ]
-        ],
         'hidden' => [
             'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.disable',
             'exclude' => true,
@@ -63,21 +36,13 @@ return [
                 'type' => 'check',
                 'default' => 0
             ]
-        ],
-        'prepend_params' => [
-            'label' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:sys_domain.prepend_params',
-            'exclude' => true,
-            'config' => [
-                'type' => 'check',
-                'default' => 0
-            ]
         ]
     ],
     'types' => [
         '1' => [
             'showitem' => '
                 --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
-                    domainName,--palette--;;1, prepend_params,
+                    domainName,
                 --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
                     hidden,
                 --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended,
@@ -85,8 +50,5 @@ return [
         ],
     ],
     'palettes' => [
-        '1' => [
-            'showitem' => 'redirectTo, redirectHttpStatusCode',
-        ],
     ]
 ];
index 3c5069f..7db359a 100644 (file)
                        <trans-unit id="sys_domain.domainName">
                                <source>Domain:</source>
                        </trans-unit>
-                       <trans-unit id="sys_domain.redirectTo">
-                               <source>Redirect to:</source>
-                       </trans-unit>
-                       <trans-unit id="sys_domain.redirectHttpStatusCode">
-                               <source>HTTP Status Code for the Redirect:</source>
-                       </trans-unit>
-                       <trans-unit id="sys_domain.redirectHttpStatusCode.301">
-                               <source>Moved permanently (301)</source>
-                       </trans-unit>
-                       <trans-unit id="sys_domain.redirectHttpStatusCode.302">
-                               <source>Found (302)</source>
-                       </trans-unit>
-                       <trans-unit id="sys_domain.redirectHttpStatusCode.303">
-                               <source>See Other (303)</source>
-                       </trans-unit>
-                       <trans-unit id="sys_domain.redirectHttpStatusCode.307">
-                               <source>Moved Temporarily (307)</source>
-                       </trans-unit>
-                       <trans-unit id="sys_domain.prepend_params">
-                               <source>Transfer parameters to Redirect URL:</source>
-                       </trans-unit>
                        <trans-unit id="sys_template">
                                <source>Template</source>
                        </trans-unit>
index ccfab84..6ae63c4 100644 (file)
@@ -22,10 +22,7 @@ class DomainVisibleFieldsTest extends \TYPO3\TestingFramework\Core\Functional\Fu
 {
     protected static $domainFields = [
         'hidden',
-        'domainName',
-        'redirectTo',
-        'redirectHttpStatusCode',
-        'prepend_params'
+        'domainName'
     ];
 
     /**
index 9c65091..cab70b4 100644 (file)
@@ -148,14 +148,11 @@ CREATE TABLE sys_domain (
        cruser_id int(11) unsigned DEFAULT '0' NOT NULL,
        hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
        domainName varchar(255) DEFAULT '' NOT NULL,
-       redirectTo varchar(255) DEFAULT '' NOT NULL,
-       redirectHttpStatusCode int(4) unsigned DEFAULT '301' NOT NULL,
        sorting int(10) unsigned DEFAULT '0' NOT NULL,
-       prepend_params int(10) DEFAULT '0' NOT NULL,
 
        PRIMARY KEY (uid),
        KEY parent (pid),
-       KEY getSysDomain (redirectTo,hidden),
+       KEY getSysDomain (hidden),
        KEY getDomainStartPage (pid,hidden,domainName)
 );
 
diff --git a/typo3/sysext/install/Classes/Updates/RedirectsExtensionUpdate.php b/typo3/sysext/install/Classes/Updates/RedirectsExtensionUpdate.php
new file mode 100644 (file)
index 0000000..832d0ff
--- /dev/null
@@ -0,0 +1,207 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Install\Updates;
+
+/*
+ * 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 Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Object\ObjectManager;
+use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
+use TYPO3\CMS\Extensionmanager\Utility\InstallUtility;
+use TYPO3\CMS\Extensionmanager\Utility\ListUtility;
+
+/**
+ * Installs EXT:redirect if sys_domain.redirectTo is filled, and migrates the values from redirectTo
+ * to a proper sys_redirect entry.
+ */
+class RedirectsExtensionUpdate extends AbstractUpdate implements LoggerAwareInterface
+{
+    use LoggerAwareTrait;
+
+    /**
+     * @var string
+     */
+    protected $title = 'Install system extension "redirects" if a sys_domain entry with redirectTo is necessary';
+
+    /**
+     * @var string
+     */
+    protected $extensionKey = 'redirects';
+
+    /**
+     * Checks if an update is needed
+     *
+     * @param string $description The description for the update
+     * @return bool Whether an update is needed (true) or not (false)
+     */
+    public function checkForUpdate(&$description): bool
+    {
+        $description = 'The extension "redirects" includes functionality to handle any kind of redirects. '
+            . 'The functionality superseds sys_domain entries with the only purpose of redirecting to a different domain or entry. '
+            . 'This upgrade wizard installs the redirect extension if necessary and migrates the sys_domain entries to standard redirects.';
+
+        $updateNeeded = false;
+
+        // Check if table exists and table is not empty, and the wizard has not been run already
+        if ($this->checkIfWizardIsRequired() && !$this->isWizardDone()) {
+            $updateNeeded = true;
+        }
+
+        return $updateNeeded;
+    }
+
+    /**
+     * Performs the update:
+     * - Install EXT:redirect
+     * - Migrate DB records
+     *
+     * @param array $databaseQueries Queries done in this update
+     * @param string $customMessage Custom message
+     * @return bool
+     */
+    public function performUpdate(array &$databaseQueries, &$customMessage): bool
+    {
+        // Install the EXT:redirects extension if not happened yet
+        $installationSuccessful = $this->installExtension($this->extensionKey);
+        if ($installationSuccessful) {
+            // Migrate the database entries
+            $this->migrateRedirectDomainsToSysRedirect();
+            $this->markWizardAsDone();
+        }
+        return $installationSuccessful;
+    }
+
+    /**
+     * Check if the database field "sys_domain.redirectTo" exists and if so, if there are entries in the DB table with the field filled.
+     *
+     * @return bool
+     * @throws \InvalidArgumentException
+     */
+    protected function checkIfWizardIsRequired(): bool
+    {
+        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
+        $connection = $connectionPool->getConnectionByName('Default');
+        $columns = $connection->getSchemaManager()->listTableColumns('sys_domain');
+        if (isset($columns['redirectto'])) {
+            // table is available, now check if there are entries in it
+            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
+                ->getQueryBuilderForTable('sys_domain');
+            $queryBuilder->getRestrictions()->removeAll();
+            $numberOfEntries = $queryBuilder->count('*')
+                ->from('sys_domain')
+                ->where(
+                    $queryBuilder->expr()->neq('redirectTo', $queryBuilder->createNamedParameter('', \PDO::PARAM_STR))
+                )
+                ->execute()
+                ->fetchColumn();
+            return (bool)$numberOfEntries;
+        }
+
+        return false;
+    }
+
+    /**
+     * This method can be called to install an extension following all proper processes
+     * (e.g. installing in extList, respecting priority, etc.)
+     *
+     * @param string $extensionKey
+     * @return bool
+     */
+    protected function installExtension(string $extensionKey): bool
+    {
+        $extensionInstalled = false;
+        /** @var $objectManager ObjectManager */
+        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
+
+        /** @var $extensionListUtility ListUtility */
+        $extensionListUtility = $objectManager->get(ListUtility::class);
+
+        $availableExtensions = $extensionListUtility->getAvailableExtensions();
+        $availableAndInstalledExtensions = $extensionListUtility->getAvailableAndInstalledExtensions($availableExtensions);
+
+        // Extension is not installed yet, install it
+        if (!isset($availableAndInstalledExtensions[$extensionKey]['installed']) || $availableAndInstalledExtensions[$extensionKey]['installed'] !== true) {
+            /** @var $extensionInstallUtility InstallUtility */
+            $extensionInstallUtility = $objectManager->get(InstallUtility::class);
+            try {
+                $extensionInstallUtility->install($extensionKey);
+                $extensionInstalled = true;
+            } catch (ExtensionManagerException $e) {
+                $this->logger->warning($e->getMessage());
+            }
+        } else {
+            $extensionInstalled = true;
+        }
+        return $extensionInstalled;
+    }
+
+    /**
+     * Move all sys_domain records with a "redirectTo" value filled (also deleted) to "sys_redirect" record
+     */
+    protected function migrateRedirectDomainsToSysRedirect()
+    {
+        $connDomains = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_domain');
+        $connRedirects = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_redirect');
+
+        $queryBuilder = $connDomains->createQueryBuilder();
+        $queryBuilder->getRestrictions()->removeAll();
+        $domainEntries = $queryBuilder->select('*')
+            ->from('sys_domain')
+            ->where(
+                $queryBuilder->expr()->neq('redirectTo', $queryBuilder->createNamedParameter('', \PDO::PARAM_STR))
+            )
+            ->execute()
+            ->fetchAll();
+
+        foreach ($domainEntries as $domainEntry) {
+            $domainName = $domainEntry['domainName'];
+            $target = $domainEntry['redirectTo'];
+            $sourceDetails = parse_url($domainName);
+            $targetDetails = parse_url($target);
+            $redirectRecord = [
+                'deleted' => (int)$domainEntry['deleted'],
+                'disabled' => (int)$domainEntry['hidden'],
+                'createdon' => (int)$domainEntry['crdate'],
+                'createdby' => (int)$domainEntry['cruser_id'],
+                'updatedon' => (int)$domainEntry['tstamp'],
+                'source_host' => $sourceDetails['host'] . ($sourceDetails['port'] ? ':' . $sourceDetails['port'] : ''),
+                'keep_query_parameters' => (int)$domainEntry['prepend_params'],
+                'target_statuscode' => (int)$domainEntry['redirectHttpStatusCode'],
+                'target' => $target
+            ];
+
+            if (isset($targetDetails['scheme']) && $targetDetails['scheme'] === 'https') {
+                $redirectRecord['force_https'] = 1;
+            }
+
+            if (empty($sourceDetails['path']) || $sourceDetails['path'] === '/') {
+                $redirectRecord['source_path'] = '.*';
+                $redirectRecord['is_regexp'] = 1;
+            } else {
+                // Remove the / and add a "/" always before, and at the very end, if path is not empty
+                $sourceDetails['path'] = trim($sourceDetails['path'], '/');
+                $redirectRecord['source_path'] = '/' . ($sourceDetails['path'] ? $sourceDetails['path'] . '/' : '');
+            }
+
+            // Add the redirect record
+            $connRedirects->insert('sys_redirect', $redirectRecord);
+
+            // Remove the sys_domain record (hard)
+            $connDomains->delete('sys_domain', ['uid' => (int)$domainEntry['uid']]);
+        }
+    }
+}
index f6dedfa..0c69890 100644 (file)
@@ -58,6 +58,8 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['pagesLanguag
     = \TYPO3\CMS\Install\Updates\MigratePagesLanguageOverlayBeGroupsAccessRights::class;
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['backendLayoutIcons']
     = \TYPO3\CMS\Install\Updates\BackendLayoutIconUpdateWizard::class;
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['redirects']
+    = \TYPO3\CMS\Install\Updates\RedirectsExtensionUpdate::class;
 
 $iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class);
 $icons = [