[FEATURE] Allow multiple domain variants per site 06/58206/10
authorGeorg Ringer <georg.ringer@gmail.com>
Fri, 7 Sep 2018 07:42:13 +0000 (09:42 +0200)
committerSusanne Moog <susanne.moog@typo3.org>
Fri, 21 Sep 2018 10:14:54 +0000 (12:14 +0200)
Extend the site configuration by allowing multiple base domains
which are checked by the ConditionMatcher.

Resolves: #86303
Releases: master
Change-Id: Ib80821c80e88d86656b03d29b746e526f300fd95
Reviewed-on: https://review.typo3.org/58206
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
typo3/sysext/backend/Classes/Form/FormDataProvider/SiteDatabaseEditRow.php
typo3/sysext/backend/Classes/Form/FormDataProvider/SiteTcaInline.php
typo3/sysext/backend/Configuration/SiteConfiguration/site.php
typo3/sysext/backend/Configuration/SiteConfiguration/site_base_variant.php [new file with mode: 0644]
typo3/sysext/backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf
typo3/sysext/core/Classes/ExpressionLanguage/FunctionsProvider/DefaultFunctionsProvider.php
typo3/sysext/core/Classes/ExpressionLanguage/FunctionsProvider/Typo3ConditionFunctionsProvider.php
typo3/sysext/core/Classes/ExpressionLanguage/SiteConditionProvider.php [new file with mode: 0644]
typo3/sysext/core/Classes/Site/Entity/Site.php
typo3/sysext/core/Configuration/ExpressionLanguage.php
typo3/sysext/core/Documentation/Changelog/master/Feature-86303-VariantsForSitesBase.rst [new file with mode: 0644]

index ae3eb2c..8b01683 100644 (file)
@@ -46,7 +46,7 @@ class SiteDatabaseEditRow implements FormDataProviderInterface
             $rowData = $siteFinder->getSiteByRootPageId($siteConfigurationForPageUid)->getConfiguration();
             $result['databaseRow']['uid'] = $rowData['rootPageId'];
             $result['databaseRow']['identifier'] = $result['customData']['siteIdentifier'];
-        } elseif (in_array($tableName, ['site_errorhandling', 'site_language', 'site_route'])) {
+        } elseif (in_array($tableName, ['site_errorhandling', 'site_language', 'site_route', 'site_base_variant'], true)) {
             $siteConfigurationForPageUid = (int)($result['inlineTopMostParentUid'] ?? $result['inlineParentUid']);
             $rowData = $siteFinder->getSiteByRootPageId($siteConfigurationForPageUid)->getConfiguration();
             $parentFieldName = $result['inlineParentFieldName'];
index 630771e..37a6291 100644 (file)
@@ -47,7 +47,7 @@ class SiteTcaInline extends AbstractDatabaseRecordProvider implements FormDataPr
                 continue;
             }
             $childTableName = $fieldConfig['config']['foreign_table'] ?? '';
-            if (!in_array($childTableName, ['site_errorhandling', 'site_language', 'site_route'])) {
+            if (!in_array($childTableName, ['site_errorhandling', 'site_language', 'site_route', 'site_base_variant'], true)) {
                 throw new \RuntimeException('Inline relation to other tables not implemented', 1522494737);
             }
             $result['processedTca']['columns'][$fieldName]['children'] = [];
index 2183f63..3c19215 100644 (file)
@@ -39,6 +39,18 @@ return [
                 'eval' => 'required',
             ],
         ],
+        'baseVariants' => [
+            'label' => 'LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site.baseVariants',
+            'config' => [
+                'type' => 'inline',
+                'foreign_table' => 'site_base_variant',
+                'appearance' => [
+                    'enabledControls' => [
+                        'info' => false,
+                    ],
+                ],
+            ],
+        ],
         'languages' => [
             'label' => 'LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site.languages',
             'config' => [
@@ -82,7 +94,7 @@ return [
     ],
     'types' => [
         '0' => [
-            'showitem' => 'identifier, rootPageId, base,
+            'showitem' => 'identifier, rootPageId, base,baseVariants,
                 --div--;LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site.tab.languages, languages,
                 --div--;LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site.tab.errorHandling, errorHandling,
                 --div--;LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site.tab.routes, routes',
diff --git a/typo3/sysext/backend/Configuration/SiteConfiguration/site_base_variant.php b/typo3/sysext/backend/Configuration/SiteConfiguration/site_base_variant.php
new file mode 100644 (file)
index 0000000..477f0e3
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+return [
+    'ctrl' => [
+        'label' => 'base',
+        'label_alt' => 'condition',
+        'label_alt_force' => true,
+        'title' => 'LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site_base_variant.ctrl.title',
+        'typeicon_classes' => [
+            'default' => 'mimetypes-x-content-domain',
+        ],
+    ],
+    'columns' => [
+        'base' => [
+            'label' => 'LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site_base_variant.base',
+            'config' => [
+                'type' => 'input',
+                'eval' => 'required',
+            ],
+        ],
+        'condition' => [
+            'label' => 'LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site_base_variant.condition',
+            'config' => [
+                'type' => 'input',
+                'eval' => 'required',
+                'valuePicker' => [
+                    'items' => [
+                        [ 'LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site_base_variant.condition.applicationContext', 'applicationContext == "Production"'],
+                        [ 'LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_tca.xlf:site_base_variant.condition.environmentVariable', 'getenv("mycontext") == "production"'],
+                    ],
+                ],
+            ],
+        ],
+    ],
+    'types' => [
+        '1' => [
+            'showitem' => 'base,condition',
+        ],
+    ],
+];
index 9a9fa5d..911b7e1 100644 (file)
@@ -15,6 +15,9 @@
                        <trans-unit id="site.base">
                                <source>Entry point (can be https://www.mydomain/ or just /, if it is just / you can not rely on TYPO3 creating full URLs)</source>
                        </trans-unit>
+                       <trans-unit id="site.baseVariants">
+                               <source>Variants for the entry point</source>
+                       </trans-unit>
                        <trans-unit id="site.languages">
                                <source>Available Languages for this site</source>
                        </trans-unit>
                        <trans-unit id="site_language.fallbacks">
                                <source>Fallback to other language(s) - order is important!</source>
                        </trans-unit>
+                       <trans-unit id="site_base_variant.ctrl.title">
+                               <source>Base</source>
+                       </trans-unit>
+                       <trans-unit id="site_base_variant.base">
+                               <source>Base</source>
+                       </trans-unit>
+                       <trans-unit id="site_base_variant.condition">
+                               <source>Condition</source>
+                       </trans-unit>
+                       <trans-unit id="site_base_variant.condition.applicationContext">
+                               <source>Application Context</source>
+                       </trans-unit>
+                       <trans-unit id="site_base_variant.condition.environmentVariable">
+                               <source>Environment variable</source>
+                       </trans-unit>
 
                        <trans-unit id="site_errorhandling.ctrl.title">
                                <source>Error Handling</source>
index e4a7699..3055217 100644 (file)
@@ -20,6 +20,7 @@ use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\StringUtility;
+use TYPO3\CMS\Core\Utility\VersionNumberUtility;
 
 /**
  * Class DefaultFunctionsProvider
@@ -33,12 +34,44 @@ class DefaultFunctionsProvider implements ExpressionFunctionProviderInterface
     public function getFunctions()
     {
         return [
+            $this->getIpFunction(),
+            $this->getCompatVersionFunction(),
             $this->getLikeFunction(),
             $this->getEnvFunction(),
             $this->getDateFunction(),
         ];
     }
 
+    protected function getIpFunction(): ExpressionFunction
+    {
+        return new ExpressionFunction(
+            'ip',
+            function ($str) {
+                // Not implemented, we only use the evaluator
+            },
+            function ($arguments, $str) {
+                if ($str === 'devIP') {
+                    $str = trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] ?? '');
+                }
+                return (bool)GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $str);
+            }
+        );
+    }
+
+    protected function getCompatVersionFunction(): ExpressionFunction
+    {
+        return new ExpressionFunction(
+            'compatVersion',
+            function ($str) {
+                // Not implemented, we only use the evaluator
+            },
+            function ($arguments, $str) {
+                return VersionNumberUtility::convertVersionNumberToInteger(TYPO3_branch) >=
+                   VersionNumberUtility::convertVersionNumberToInteger($str);
+            }
+        );
+    }
+
     protected function getLikeFunction(): ExpressionFunction
     {
         return new ExpressionFunction('like', function ($str) {
index e3d3245..5407605 100644 (file)
@@ -21,7 +21,6 @@ use TYPO3\CMS\Core\ExpressionLanguage\RequestWrapper;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\VersionNumberUtility;
 
 /**
  * Class TypoScriptConditionProvider
@@ -35,8 +34,6 @@ class Typo3ConditionFunctionsProvider implements ExpressionFunctionProviderInter
     public function getFunctions()
     {
         return [
-            $this->getIpFunction(),
-            $this->getCompatVersionFunction(),
             $this->getLoginUserFunction(),
             $this->getTSFEFunction(),
             $this->getUsergroupFunction(),
@@ -46,27 +43,6 @@ class Typo3ConditionFunctionsProvider implements ExpressionFunctionProviderInter
         ];
     }
 
-    protected function getIpFunction(): ExpressionFunction
-    {
-        return new ExpressionFunction('ip', function ($str) {
-            // Not implemented, we only use the evaluator
-        }, function ($arguments, $str) {
-            if ($str === 'devIP') {
-                $str = trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']);
-            }
-            return (bool)GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $str);
-        });
-    }
-
-    protected function getCompatVersionFunction(): ExpressionFunction
-    {
-        return new ExpressionFunction('compatVersion', function ($str) {
-            // Not implemented, we only use the evaluator
-        }, function ($arguments, $str) {
-            return VersionNumberUtility::convertVersionNumberToInteger(TYPO3_branch) >= VersionNumberUtility::convertVersionNumberToInteger($str);
-        });
-    }
-
     protected function getLoginUserFunction(): ExpressionFunction
     {
         return new ExpressionFunction('loginUser', function ($str) {
diff --git a/typo3/sysext/core/Classes/ExpressionLanguage/SiteConditionProvider.php b/typo3/sysext/core/Classes/ExpressionLanguage/SiteConditionProvider.php
new file mode 100644 (file)
index 0000000..2c5eb89
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Core\ExpressionLanguage;
+
+/*
+ * 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\Core\Utility\GeneralUtility;
+
+class SiteConditionProvider extends AbstractProvider
+{
+    public function __construct()
+    {
+        $typo3 = new \stdClass();
+        $typo3->version = TYPO3_version;
+        $typo3->branch = TYPO3_branch;
+        $typo3->devIpMask = trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']);
+        $this->expressionLanguageVariables = [
+            'applicationContext' => (string)GeneralUtility::getApplicationContext(),
+            'typo3' => $typo3,
+        ];
+    }
+}
index 31ea263..9c5e3c6 100644 (file)
@@ -23,6 +23,7 @@ use TYPO3\CMS\Core\Error\PageErrorHandler\InvalidPageErrorHandlerException;
 use TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler;
 use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
 use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerNotConfiguredException;
+use TYPO3\CMS\Core\ExpressionLanguage\Resolver;
 use TYPO3\CMS\Core\Http\Uri;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Routing\PageRouter;
@@ -94,7 +95,23 @@ class Site implements SiteInterface
                 'direction' => '',
             ]
         ];
-        $this->base = new Uri($this->sanitizeBaseUrl($configuration['base'] ?? ''));
+        $baseUrl = $configuration['base'] ?? '';
+        if (isset($configuration['baseVariants']) && is_array($configuration['baseVariants'])) {
+            $expressionLanguageResolver = GeneralUtility::makeInstance(
+                Resolver::class,
+                'site',
+                []
+            );
+            foreach ($configuration['baseVariants'] as $baseVariant) {
+                $result = $expressionLanguageResolver->evaluate($baseVariant['condition']);
+                if ($result) {
+                    $baseUrl = $baseVariant['base'];
+                    break;
+                }
+            }
+        }
+        $this->base = new Uri($this->sanitizeBaseUrl($baseUrl));
+
         foreach ($configuration['languages'] as $languageConfiguration) {
             $languageUid = (int)$languageConfiguration['languageId'];
             // site language has defined its own base, this is the case most of the time.
index 77e46ca..7067210 100644 (file)
@@ -7,5 +7,8 @@ return [
     ],
     'typoscript' => [
         \TYPO3\CMS\Core\ExpressionLanguage\TypoScriptConditionProvider::class,
+    ],
+    'site' => [
+        \TYPO3\CMS\Core\ExpressionLanguage\SiteConditionProvider::class,
     ]
 ];
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-86303-VariantsForSitesBase.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-86303-VariantsForSitesBase.rst
new file mode 100644 (file)
index 0000000..86b9367
--- /dev/null
@@ -0,0 +1,26 @@
+.. include:: ../../Includes.txt
+
+==========================================
+Feature: #86303 - Variants for site's base
+==========================================
+
+See :issue:`86303`
+
+Description
+===========
+
+The site configuration allows now to specify variants of the site's base.
+Take the following example: The base of a site is set to `www.domain.tld` but the staging environment uses
+`staging.domain.tld` and the local development uses `www.domain.local`.
+
+The expression language feature is used to define which variant is taken into account
+
+Impact
+======
+
+The base of a site can be changed depending on a conditiion. Typical examples are:
+
+- :typoscript:`applicationContext == "Production"`: Check the application context
+- :typoscript:`getenv("mycontext") == "production`: Check a custom environment variable
+
+.. index:: Backend, Frontend, TypoScript, ext:core
\ No newline at end of file