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;
}
/**
* 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')
$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')
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'];
}
'typeicon_classes' => [
'default' => 'mimetypes-x-content-domain'
],
- 'searchFields' => 'domainName,redirectTo'
+ 'searchFields' => 'domainName'
],
'interface' => [
- 'showRecordFieldList' => 'hidden,domainName,redirectTo'
+ 'showRecordFieldList' => 'hidden,domainName'
],
'columns' => [
'domainName' => [
'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,
'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,
],
],
'palettes' => [
- '1' => [
- 'showitem' => 'redirectTo, redirectHttpStatusCode',
- ],
]
];
--- /dev/null
+<?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']]);
+ }
+ }
+}