diff --git a/_.htaccess b/_.htaccess index 8b8b81a5855cc3fb7d22f306ad9b5f338a5e4ed1..79d8b0719cbd0e8fdd7f632354aad3030710b9ed 100644 --- a/_.htaccess +++ b/_.htaccess @@ -83,6 +83,14 @@ RewriteEngine On # Change this path, if your TYPO3 installation is located in a subdirectory of the website root. #RewriteBase / +# Rules to set ApplicationContext based on hostname +#RewriteCond %{HTTP_HOST} ^dev\.example\.com$ +#RewriteRule (.*) $1 [E=TYPO3_CONTEXT:Development] +#RewriteCond %{HTTP_HOST} ^staging\.example\.com$ +#RewriteRule (.*) $1 [E=TYPO3_CONTEXT:Production/Staging] +#RewriteCond %{HTTP_HOST} ^www\.example\.com$ +#RewriteRule (.*) $1 [E=TYPO3_CONTEXT:Production] + # Rule for versioned static files, configured through: # - $TYPO3_CONF_VARS['BE']['versionNumberInFilename'] # - $TYPO3_CONF_VARS['FE']['versionNumberInFilename'] diff --git a/typo3/sysext/core/Classes/Core/ApplicationContext.php b/typo3/sysext/core/Classes/Core/ApplicationContext.php new file mode 100644 index 0000000000000000000000000000000000000000..9643c10b05eaad8885939f6e466c745de93f629e --- /dev/null +++ b/typo3/sysext/core/Classes/Core/ApplicationContext.php @@ -0,0 +1,144 @@ +<?php +namespace TYPO3\CMS\Core\Core; + +/*************************************************************** + * Copyright notice + * + * (c) 2013 The respective TYPO3 Flow framework authors + * (c) 2013 Steffen Müller <typo3@t3node.com> (Backport to TYPO3 CMS) + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +/** + * The TYPO3 Context object. + * + * A TYPO3 Application context is something like "Production", "Development", + * "Production/StagingSystem", and is set using the TYPO3_CONTEXT environment variable. + * + * A context can contain arbitrary sub-contexts, which are delimited with slash + * ("Production/StagingSystem", "Production/Staging/Server1"). The top-level + * contexts, however, must be one of "Testing", "Development" and "Production". + * + * Mainly, you will use $context->isProduction(), $context->isTesting() and + * $context->isDevelopment() inside your custom code. + * + * This class is derived from the TYPO3 Flow framework. + * Credits go to the respective authors. + * + * @author Steffen Müller <typo3@t3node.com> (Backport to TYPO3 CMS) + */ +class ApplicationContext { + + /** + * The (internal) context string; could be something like "Development" or "Development/MyLocalMacBook" + * + * @var string + */ + protected $contextString; + + /** + * The root context; must be one of "Development", "Testing" or "Production" + * + * @var string + */ + protected $rootContextString; + + /** + * The parent context, or NULL if there is no parent context + * + * @var \TYPO3\CMS\Core\Core\ApplicationContext + */ + protected $parentContext; + + /** + * Initialize the context object. + * + * @param string $contextString + * @throws \Exception if the parent context is none of "Development", "Production" or "Testing" + */ + public function __construct($contextString) { + if (strstr($contextString, '/') === FALSE) { + $this->rootContextString = $contextString; + $this->parentContext = NULL; + } else { + $contextStringParts = explode('/', $contextString); + $this->rootContextString = $contextStringParts[0]; + array_pop($contextStringParts); + $this->parentContext = new ApplicationContext(implode('/', $contextStringParts)); + } + + if (!in_array($this->rootContextString, array('Development', 'Production', 'Testing'))) { + throw new \TYPO3\CMS\Core\Exception('The given context "' . $contextString . '" was not valid. Only allowed are Development, Production and Testing, including their sub-contexts', 1335436551); + } + + $this->contextString = $contextString; + } + + /** + * Returns the full context string, for example "Development", or "Production/LiveSystem" + * + * @return string + * @api + */ + public function __toString() { + return $this->contextString; + } + + /** + * Returns TRUE if this context is the Development context or a sub-context of it + * + * @return boolean + * @api + */ + public function isDevelopment() { + return ($this->rootContextString === 'Development'); + } + + /** + * Returns TRUE if this context is the Production context or a sub-context of it + * + * @return boolean + * @api + */ + + public function isProduction() { + return ($this->rootContextString === 'Production'); + } + + /** + * Returns TRUE if this context is the Testing context or a sub-context of it + * + * @return boolean + * @api + */ + public function isTesting() { + return ($this->rootContextString === 'Testing'); + } + + /** + * Returns the parent context object, if any + * + * @return \TYPO3\CMS\Core\Core\ApplicationContext the parent context or NULL, if there is none + * @api + */ + public function getParent() { + return $this->parentContext; + } +} +?> \ No newline at end of file diff --git a/typo3/sysext/core/Classes/Core/Bootstrap.php b/typo3/sysext/core/Classes/Core/Bootstrap.php index 54114aebdfa9abbbc0e004d8935bb73d7f2da549..81e060d141642a7cc7960f3ebce09b7437ea200b 100644 --- a/typo3/sysext/core/Classes/Core/Bootstrap.php +++ b/typo3/sysext/core/Classes/Core/Bootstrap.php @@ -58,11 +58,22 @@ class Bootstrap { */ protected $requestId; + /** + * The application context + * + * @var \TYPO3\CMS\Core\Core\ApplicationContext + */ + protected $context; + /** * Disable direct creation of this object. + * Set unique requestId and the application context + * + * @var string Application context */ - protected function __construct() { + protected function __construct($context) { $this->requestId = uniqid(); + $this->context = new ApplicationContext($context); } /** @@ -80,7 +91,9 @@ class Bootstrap { */ static public function getInstance() { if (is_null(self::$instance)) { - self::$instance = new \TYPO3\CMS\Core\Core\Bootstrap(); + require_once(__DIR__ . '/ApplicationContext.php'); + $context = trim(getenv('TYPO3_CONTEXT'), '"\' ') ? : 'Production'; + self::$instance = new \TYPO3\CMS\Core\Core\Bootstrap($context); } return self::$instance; } @@ -95,6 +108,15 @@ class Bootstrap { return $this->requestId; } + /** + * Returns the context this bootstrap was started in. + * + * @return \TYPO3\CMS\Core\Core\ApplicationContext The context encapsulated in an object + */ + public function getContext() { + return $this->context; + } + /** * Prevent any unwanted output that may corrupt AJAX/compression. * This does not interfere with "die()" or "echo"+"exit()" messages! diff --git a/typo3/sysext/core/Tests/Unit/Core/ApplicationContextTest.php b/typo3/sysext/core/Tests/Unit/Core/ApplicationContextTest.php new file mode 100644 index 0000000000000000000000000000000000000000..312e8af01fe66e314c798061df0fffe51495796a --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Core/ApplicationContextTest.php @@ -0,0 +1,148 @@ +<?php +namespace TYPO3\CMS\Core\Tests\Unit\Core; + +/* * + * This script belongs to the TYPO3 Flow framework. * + * * + * It is free software; you can redistribute it and/or modify it under * + * the terms of the GNU Lesser General Public License, either version 3 * + * of the License, or (at your option) any later version. * + * * + * The TYPO3 project - inspiring people to share! * + * */ + +use TYPO3\CMS\Core\Core\ApplicationContext; + +/** + * Testcase for the ApplicationContext class + */ +class ApplicationContextTest extends \TYPO3\CMS\Core\Tests\UnitTestCase { + + /** + * Data provider with allowed contexts. + * + * @return array + */ + public function allowedContexts() { + return array( + array('Production'), + array('Testing'), + array('Development'), + + array('Development/MyLocalComputer'), + array('Development/MyLocalComputer/Foo'), + array('Production/SpecialDeployment/LiveSystem'), + ); + } + + /** + * @test + * @dataProvider allowedContexts + */ + public function contextStringCanBeSetInConstructorAndReadByCallingToString($allowedContext) { + $context = new ApplicationContext($allowedContext); + $this->assertSame($allowedContext, (string)$context); + } + + /** + * Data provider with forbidden contexts. + * + * @return array + */ + public function forbiddenContexts() { + return array( + array('MySpecialContexz'), + array('Testing123'), + array('DevelopmentStuff'), + array('DevelopmentStuff/FooBar'), + ); + } + + /** + * @test + * @dataProvider forbiddenContexts + * @expectedException \TYPO3\CMS\Core\Exception + */ + public function constructorThrowsExceptionIfMainContextIsForbidden($forbiddenContext) { + new ApplicationContext($forbiddenContext); + } + + /** + * Data provider with expected is*() values for various contexts. + * + * @return array + */ + public function isMethods() { + return array( + 'Development' => array( + 'contextName' => 'Development', + 'isDevelopment' => TRUE, + 'isProduction' => FALSE, + 'isTesting' => FALSE, + 'parentContext' => NULL + ), + 'Development/YourSpecialContext' => array( + 'contextName' => 'Development/YourSpecialContext', + 'isDevelopment' => TRUE, + 'isProduction' => FALSE, + 'isTesting' => FALSE, + 'parentContext' => 'Development' + ), + + 'Production' => array( + 'contextName' => 'Production', + 'isDevelopment' => FALSE, + 'isProduction' => TRUE, + 'isTesting' => FALSE, + 'parentContext' => NULL + ), + 'Production/MySpecialContext' => array( + 'contextName' => 'Production/MySpecialContext', + 'isDevelopment' => FALSE, + 'isProduction' => TRUE, + 'isTesting' => FALSE, + 'parentContext' => 'Production' + ), + + 'Testing' => array( + 'contextName' => 'Testing', + 'isDevelopment' => FALSE, + 'isProduction' => FALSE, + 'isTesting' => TRUE, + 'parentContext' => NULL + ), + 'Testing/MySpecialContext' => array( + 'contextName' => 'Testing/MySpecialContext', + 'isDevelopment' => FALSE, + 'isProduction' => FALSE, + 'isTesting' => TRUE, + 'parentContext' => 'Testing' + ) + ); + } + + /** + * @test + * @dataProvider isMethods + */ + public function contextMethodsReturnTheCorrectValues($contextName, $isDevelopment, $isProduction, $isTesting, $parentContext) { + $context = new ApplicationContext($contextName); + $this->assertSame($isDevelopment, $context->isDevelopment()); + $this->assertSame($isProduction, $context->isProduction()); + $this->assertSame($isTesting, $context->isTesting()); + $this->assertSame((string)$parentContext, (string)$context->getParent()); + } + + /** + * @test + */ + public function parentContextIsConnectedRecursively() { + $context = new ApplicationContext('Production/Foo/Bar'); + $parentContext = $context->getParent(); + $this->assertSame('Production/Foo', (string) $parentContext); + + $rootContext = $parentContext->getParent(); + $this->assertSame('Production', (string) $rootContext); + } +} +?> \ No newline at end of file