diff --git a/t3lib/class.t3lib_div.php b/t3lib/class.t3lib_div.php index 90b77eeb35608972d08624167eecc779bbadbd39..60dddc70c66a84a194311964ecc517b71d48e6b5 100644 --- a/t3lib/class.t3lib_div.php +++ b/t3lib/class.t3lib_div.php @@ -4602,7 +4602,7 @@ final class t3lib_div { * @param string $funcName Function/Method reference, '[file-reference":"]["&"]class/function["->"method-name]'. You can prefix this reference with "[file-reference]:" and t3lib_div::getFileAbsFileName() will then be used to resolve the filename and subsequently include it by "require_once()" which means you don't have to worry about including the class file either! Example: "EXT:realurl/class.tx_realurl.php:&tx_realurl->encodeSpURL". Finally; you can prefix the class name with "&" if you want to reuse a former instance of the same object call ("singleton"). * @param mixed $params Parameters to be pass along (typically an array) (REFERENCE!) * @param mixed $ref Reference to be passed along (typically "$this" - being a reference to the calling object) (REFERENCE!) - * @param string $checkPrefix Required prefix of class or function name + * @param string $checkPrefix Alternative allowed prefix of class or function name * @param integer $errorMode Error mode (when class/function could not be found): 0 - call debug(), 1 - do nothing, 2 - raise an exception (allows to call a user function that may return FALSE) * @return mixed Content from method/function call or FALSE if the class/method/function was not found * @see getUserObj() @@ -4780,14 +4780,14 @@ final class t3lib_div { * @return bool TRUE if name is allowed */ public static function hasValidClassPrefix($classRef, array $additionalPrefixes = array()) { - if(empty($classRef)) { + if (empty($classRef)) { return FALSE; } - if(!is_string($classRef)) { + if (!is_string($classRef)) { throw new InvalidArgumentException('$classRef has to be of type string', 1313917992); } $hasValidPrefix = FALSE; - $validPrefixes = array('tx_', 'Tx_', $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix']); + $validPrefixes = self::getValidClassPrefixes(); $classRef = trim($classRef); if (count($additionalPrefixes)) { @@ -4803,6 +4803,25 @@ final class t3lib_div { return $hasValidPrefix; } + /** + * Returns all valid class prefixes. + * + * @return array Array of valid prefixed of class names + */ + public static function getValidClassPrefixes() { + $validPrefixes = array('tx_', 'Tx_', 'user_', 'User_'); + if ( + isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes']) + && is_string($GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes']) + ) { + $validPrefixes = array_merge( + $validPrefixes, + t3lib_div::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes']) + ); + } + return $validPrefixes; + } + /** * Creates an instance of a class taking into account the class-extensions * API of TYPO3. USE THIS method instead of the PHP "new" keyword. diff --git a/t3lib/class.t3lib_extmgm.php b/t3lib/class.t3lib_extmgm.php index 3673177b48ae7ce98d96d319cffb72791eecec53..502e7fccfe007e65605894c9c29ac238a3378ed4 100644 --- a/t3lib/class.t3lib_extmgm.php +++ b/t3lib/class.t3lib_extmgm.php @@ -999,13 +999,7 @@ final class t3lib_extMgm { * @author René Fritz <r.fritz@colorcube.de> */ public static function addService($extKey, $serviceType, $serviceKey, $info) { - // even not available services will be included to make it possible to give the admin a feedback of non-available services. - // but maybe it's better to move non-available services to a different array?? - - if ($serviceType && - t3lib_div::hasValidClassPrefix($serviceKey, array('user_')) && - is_array($info)) { - + if ($serviceType && t3lib_div::hasValidClassPrefix($serviceKey) && is_array($info)) { $info['priority'] = max(0, min(100, $info['priority'])); $GLOBALS['T3_SERVICES'][$serviceType][$serviceKey] = $info; @@ -1705,4 +1699,4 @@ $GLOBALS[\'TYPO3_LOADED_EXT\'] = unserialize(stripslashes(\'' . addslashes(seria } } -?> +?> \ No newline at end of file diff --git a/t3lib/class.t3lib_registry.php b/t3lib/class.t3lib_registry.php index c6fed3b7aed8ad8b79948adb2742d06f8a80a002..e345f0c57546dbe2ce92a4787bfcd0dbd31e7884 100644 --- a/t3lib/class.t3lib_registry.php +++ b/t3lib/class.t3lib_registry.php @@ -181,7 +181,7 @@ class t3lib_Registry implements t3lib_Singleton { * @throws InvalidArgumentException Throws an exception if the given namespace is not valid */ protected function validateNamespace($namespace) { - if (t3lib_div::hasValidClassPrefix($namespace, array('user_'))) { + if (t3lib_div::hasValidClassPrefix($namespace)) { return; } diff --git a/t3lib/config_default.php b/t3lib/config_default.php index d7e4199d58e966ead5df32bee9db80b51bf2be44..58f1a0372f0154197f010a72df954e5a6c9901db 100644 --- a/t3lib/config_default.php +++ b/t3lib/config_default.php @@ -154,6 +154,7 @@ $TYPO3_CONF_VARS = array( ), ), 'useCachingFramework' => -1, // <i>Obsolete setting</i>. Please remove manually from <tt>localconf.php</tt>, if it is defined there. Caching Framework is now always enabled. + 'additionalAllowedClassPrefixes' => NULL, // Class names in TYPO3 must usually start with tx_, Tx, user_ or User_. This setting allows to register additional prefixes in a comma separated list. 'displayErrors' => -1, // <p>Integer (-1, 0, 1, 2). Configures whether PHP errors should be displayed.</p><dl><dt>0</dt><dd>Do not display any PHP error messages. Overrides the value of "exceptionalErrors" and sets it to 0 (= no errors are turned into exceptions), the configured "productionExceptionHandler" is used as exception handler</dd><dt>1</dt><dd>Display error messages with the registered errorhandler. The configured "debugExceptionHandler" is used as exception handler</dd><dt>2</dt><dd>Display errors only if client matches <a href="#SYS-devIPmask">[SYS][devIPmask]</a>. If devIPmask matches the users IP address the configured "debugExceptionHandler" is used for exceptions, if not "productionExceptionHandler" will be used</dd><dt>-1</dt><dd>Default setting. With this option, you can override the PHP setting "display_errors". If devIPmask matches the users IP address the configured "debugExceptionHandler" is used for exceptions, if not "productionExceptionHandler" will be used.</dd></dl> 'productionExceptionHandler' => 't3lib_error_ProductionExceptionHandler', // String: Classname to handle exceptions that might happen in the TYPO3-code. Leave empty to disable exception handling. Default: "t3lib_error_ProductionExceptionHandler". This exception handler displays a nice error message when something went wrong. The error message is logged to the configured logs. Note: The configured "productionExceptionHandler" is used if displayErrors is set to "0" or to "-1" and devIPmask doesn't match the users IP. 'debugExceptionHandler' => 't3lib_error_DebugExceptionHandler', // String: Classname to handle exceptions that might happen in the TYPO3-code. Leave empty to disable exception handling. Default: "t3lib_error_DebugExceptionHandler". This exception handler displays the complete stack trace of any encountered exception. The error message and the stack trace is logged to the configured logs. Note: The configured "debugExceptionHandler" is used if displayErrors is set to "1" and if displayErrors is "-1" or "2" and the devIPmask matches the users IP. @@ -542,7 +543,6 @@ $TYPO3_CONF_VARS = array( 'pageUnavailable_handling' => '', // <p>How TYPO3 should handle requests when pages are unavailable due to system problems.</p><dl><dt>empty (default)</dt><dd>An error message is shown.</dd><dt>String</dt><dd>HTML file or URL to show (reads content and outputs with correct headers), e.g. 'unavailable.html' or 'http://www.example.org/errors/unavailable.html'.</dd><dt>Prefix "REDIRECT:"</dt><dd>If prefixed "REDIRECT:" it will redirect to the URL/script after the prefix.</dd><dt>Prefix "READFILE:"</dt><dd>If prefixed with "READFILE:" then it will expect the remaining string to be a HTML file which will be read and outputted directly after having the marker "###CURRENT_URL###" substituted with REQUEST_URI and ###REASON### with reason text, for example: "READFILE:fileadmin/unavailable.html".</dd><dt>Prefix "USER_FUNCTION:"</dt><dd>If prefixed "USER_FUNCTION:" then it will call a user function, eg. "USER_FUNCTION:fileadmin/class.user_unavailable.php:user_unavailable->pageUnavailable" where the file must contain a class "user_unavailable" with a method "pageUnavailable" inside with two parameters $param and $ref. If the client matches <a href="#SYS-devIPmask">[SYS][devIPmask]</a>, this setting is ignored and the page is shown as normal.</dd></dl> 'pageUnavailable_handling_statheader' => 'HTTP/1.0 503 Service Temporarily Unavailable', // If 'pageUnavailable_handling' is enabled, this string will always be sent as header before the actual handling. 'pageUnavailable_force' => FALSE, // Boolean: If TRUE, pageUnavailable_handling is used for every frontend page. If the client matches <a href="#SYS-devIPmask">[SYS][devIPmask]</a>, the page is shown as normal. This is useful during temporary site maintenance. - 'userFuncClassPrefix' => 'user_', // This prefix must be the first part of any function or class name called from TypoScript, for instance in the stdWrap function. 'addRootLineFields' => '', // Comma-list of fields from the 'pages'-table. These fields are added to the select query for fields in the rootline. 'checkFeUserPid' => TRUE, // Boolean: If set, the pid of fe_user logins must be sent in the form as the field 'pid' and then the user must be located in the pid. If you unset this, you should change the fe_users.username eval-flag 'uniqueInPid' to 'unique' in $TCA. This will do: $TCA['fe_users']['columns']['username']['config']['eval']= 'nospace,lower,required,unique'; 'lockIP' => 2, // Integer (0-4). If >0, fe_users are locked to (a part of) their REMOTE_ADDR IP for their session. Enhances security but may throw off users that may change IP during their session (in which case you can lower it to 2 or 3). The integer indicates how many parts of the IP address to include in the check. Reducing to 1-3 means that only first, second or third part of the IP address is used. 4 is the FULL IP address and recommended. 0 (zero) disables checking of course. @@ -841,6 +841,19 @@ if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['useCachingFramework'] !== -1) { // Deprecation log since 4.6, can be removed in 4.8. Checks if obsolete useCachingFramework is set t3lib_div::deprecationLog('Setting $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'useCachingFramework\'] is obsolete since TYPO3 4.6 and should be removed from localconf.php.'); } + +if (isset($GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'])) { + if(is_string($GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'])) { + if(is_null($GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'])) { + $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix']; + } else if(is_string($GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'])) { + $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] .= ',' . $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix']; + } + } + // Deprecation log since 4.6, can be removed in 4.8 + t3lib_div::deprecationLog('$GLOBALS[\'TYPO3_CONF_VARS\'][\'FE\'][\'userFuncClassPrefix\'] is deprecated, use $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'additionalAllowedClassPrefixes\'] instead'); +} + // Force enabled caching framework // @deprecated, constant can be removed in 4.8 define('TYPO3_UseCachingFramework', TRUE); @@ -1098,4 +1111,4 @@ $SIM_EXEC_TIME = $EXEC_TIME; // $SIM_EXEC_TIME is set to $EXEC_TIME but can be $ACCESS_TIME = $EXEC_TIME - ($EXEC_TIME % 60); // $ACCESS_TIME is a common time in minutes for access control $SIM_ACCESS_TIME = $ACCESS_TIME; // if $SIM_EXEC_TIME is changed this value must be set accordingly -?> +?> \ No newline at end of file diff --git a/t3lib/matchcondition/class.t3lib_matchcondition_abstract.php b/t3lib/matchcondition/class.t3lib_matchcondition_abstract.php index cfebb396c7f1d3f578beab8c0d16a629691fe630..a9de498c91d40f6a94fc3aa5eb6e8aba53e42c3f 100644 --- a/t3lib/matchcondition/class.t3lib_matchcondition_abstract.php +++ b/t3lib/matchcondition/class.t3lib_matchcondition_abstract.php @@ -404,11 +404,9 @@ abstract class t3lib_matchCondition_abstract { $values = preg_split('/\(|\)/', $value); $funcName = trim($values[0]); $funcValue = t3lib_div::trimExplode(',', $values[1]); - $prefix = $this->getUserFuncClassPrefix(); - if ($prefix && - !t3lib_div::hasValidClassPrefix($funcName, array($prefix)) + if (!t3lib_div::hasValidClassPrefix($funcName) ) { - $this->log('Match condition: Function "' . $funcName . '" was not prepended with "' . $prefix . '"'); + $this->log('Match condition: Function "' . $funcName . '" was not prepended with one of "' . implode(', ', t3lib_div::getValidClassPrefixes()) . '"'); return FALSE; } if (function_exists($funcName) && call_user_func($funcName, $funcValue[0])) { @@ -634,13 +632,6 @@ abstract class t3lib_matchCondition_abstract { */ abstract protected function determineRootline(); - /** - * Gets prefix for user functions (normally 'user_'). - * - * @return string The prefix for user functions (normally 'user_'). - */ - abstract protected function getUserFuncClassPrefix(); - /** * Gets the id of the current user. * diff --git a/t3lib/matchcondition/class.t3lib_matchcondition_backend.php b/t3lib/matchcondition/class.t3lib_matchcondition_backend.php index 55176054b6de94108b1daf9d824fea5e80fe4d9a..aab4ff839d90d0fad683bcd251fcde288e07d8be 100644 --- a/t3lib/matchcondition/class.t3lib_matchcondition_backend.php +++ b/t3lib/matchcondition/class.t3lib_matchcondition_backend.php @@ -260,16 +260,6 @@ class t3lib_matchCondition_backend extends t3lib_matchCondition_abstract { return $rootline; } - /** - * Get prefix for user functions (normally 'user_'). - * - * @return string The prefix for user functions (normally 'user_'). - */ - protected function getUserFuncClassPrefix() { - $userFuncClassPrefix = 'user_'; - return $userFuncClassPrefix; - } - /** * Get the id of the current user. * diff --git a/t3lib/matchcondition/class.t3lib_matchcondition_frontend.php b/t3lib/matchcondition/class.t3lib_matchcondition_frontend.php index 26a06aa3c5c3abf6fa728fc2545126b3cbb5a493..bd5144fdd679ac9d7829bf7af6edfbfefc5114b7 100644 --- a/t3lib/matchcondition/class.t3lib_matchcondition_frontend.php +++ b/t3lib/matchcondition/class.t3lib_matchcondition_frontend.php @@ -156,16 +156,6 @@ class t3lib_matchCondition_frontend extends t3lib_matchCondition_abstract { return $rootline; } - /** - * Get prefix for user functions (normally 'user_'). - * - * @return string The prefix for user functions (normally 'user_'). - */ - protected function getUserFuncClassPrefix() { - $userFuncClassPrefix = $GLOBALS['TSFE']->TYPO3_CONF_VARS['FE']['userFuncClassPrefix']; - return $userFuncClassPrefix; - } - /** * Get the id of the current user. * diff --git a/tests/t3lib/class.t3lib_divTest.php b/tests/t3lib/class.t3lib_divTest.php index e46759016724301dacb139918a1d59b3fd593cc3..3c772b8afa32ec0c1a984a83052f6c94b979da82 100644 --- a/tests/t3lib/class.t3lib_divTest.php +++ b/tests/t3lib/class.t3lib_divTest.php @@ -3331,7 +3331,7 @@ class t3lib_divTest extends tx_phpunit_testcase { * @param string $className Class name to test */ public function hasValidClassPrefixAcceptsValidPrefixes($className) { - $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'] = 'user_'; + $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = 'foo_'; $this->assertTrue( t3lib_div::hasValidClassPrefix($className) ); @@ -3346,7 +3346,7 @@ class t3lib_divTest extends tx_phpunit_testcase { array('txfoo'), array('Txfoo'), array('userfoo'), - array('User_foo'), + array('aser_foo'), ); } @@ -3356,7 +3356,7 @@ class t3lib_divTest extends tx_phpunit_testcase { * @param string $className Class name to test */ public function hasValidClassPrefixRefusesInvalidPrefixes($className) { - $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'] = 'user_'; + $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = 'foo_'; $this->assertFalse( t3lib_div::hasValidClassPrefix($className) ); @@ -3399,12 +3399,37 @@ class t3lib_divTest extends tx_phpunit_testcase { * @param string $className Class name to test */ public function hasValidClassPrefixAllowsEmptyPrefix($className) { - $GLOBALS['TYPO3_CONF_VARS']['FE']['userFuncClassPrefix'] = ''; + $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = ''; $this->assertTrue( t3lib_div::hasValidClassPrefix($className) ); } + /** + * @return array + */ + public function getValidClassPrefixesReturnsListOfValidClassPrefixesDataProvider() { + return array( + array('user_'), + array('User_'), + array('Tx_'), + array('tx_'), + array('foo_'), + array('bar_'), + array('BAZ_') + ); + } + + /** + * @test + * @dataProvider getValidClassPrefixesReturnsListOfValidClassPrefixesDataProvider + * @param string $prefix prefix to test + */ + public function getValidClassPrefixesReturnsListOfValidClassPrefixes($prefix) { + $GLOBALS['TYPO3_CONF_VARS']['SYS']['additionalAllowedClassPrefixes'] = 'foo_,bar_,BAZ_'; + $this->assertTrue(in_array($prefix, t3lib_div::getValidClassPrefixes())); + } + /** * @test */ diff --git a/typo3/sysext/cms/tslib/class.tslib_content.php b/typo3/sysext/cms/tslib/class.tslib_content.php index 700fbbc0e149308e18c99252ac1023d4b3964d99..5723048031b0a40fa2014d5f915c091b0408ce4e 100644 --- a/typo3/sysext/cms/tslib/class.tslib_content.php +++ b/typo3/sysext/cms/tslib/class.tslib_content.php @@ -6338,9 +6338,8 @@ class tslib_cObj { * @see USER(), stdWrap(), typoLink(), _parseFunc() */ function callUserFunction($funcName, $conf, $content) { - $pre = $GLOBALS['TSFE']->TYPO3_CONF_VARS['FE']['userFuncClassPrefix']; - if ($pre && !t3lib_div::hasValidClassPrefix($funcName, array($pre))) { - $GLOBALS['TT']->setTSlogMessage('Function "' . $funcName . '" was not prepended with "' . $pre . '"', 3); + if (!t3lib_div::hasValidClassPrefix($funcName)) { + $GLOBALS['TT']->setTSlogMessage('Function "' . $funcName . '" was not prepended with one of "' . implode(', ', t3lib_div::getValidClassPrefixes()) . '"', 3); return $content; } // Split parts