Index: t3lib/config_default.php =================================================================== --- t3lib/config_default.php (Revision 5213) +++ t3lib/config_default.php (Arbeitskopie) @@ -229,6 +229,7 @@ 'notificationPrefix' => '[TYPO3 Note]', // String: Used to prefix the subject of mails sent in the taskcenter 'accessListRenderMode' => 'singlebox', // Can be "singlebox", "checkbox" or blank. Refers to the "renderMode" for the selector boxes in be-groups configuration. 'explicitADmode' => 'explicitDeny', // Sets the general allow/deny mode for selector box values. Value can be either "explicitAllow" or "explicitDeny", nothing else! + 'TSconfigConditions' => 0, // Boolean. When set it is possible to have TypoScript like conditions in BE Page/User/Group TS-Config 'niceFlexFormXMLtags' => TRUE, // If set, the flexform XML will be stored with meaningful tags which can be validated with DTD schema. If you rely on custom reading of the XML from pre-4.0 versions you should set this to false if you don't like to change your reader code (internally it is insignificant since t3lib_div::xml2array() doesn't care for the tags if the index-attribute value is set) 'flexFormXMLincludeDiffBase' => TRUE, // If set, an additional tag with index "vXX.vDEFbase" is created for translations in flexforms holding the value of the default language when translation was changed. Used to show diff of value. This setting will change whether the system thinks flexform XML looks clean. For example when FALSE XX.vDEFbase fields will be removed in cleaning while accepted if TRUE (of course) 'compactFlexFormXML' => 0, // If set, the flexform XML will not contain indentation spaces making XML more compact Index: t3lib/class.t3lib_tsparser_tsconfig.php =================================================================== --- t3lib/class.t3lib_tsparser_tsconfig.php (Revision 0) +++ t3lib/class.t3lib_tsparser_tsconfig.php (Revision 0) @@ -0,0 +1,207 @@ + + */ +/** + * [CLASS/FUNCTION INDEX of SCRIPT] + */ + +require_once (PATH_t3lib.'class.t3lib_tsparser.php'); +require_once (PATH_t3lib.'class.t3lib_matchcondition.php'); + +class t3lib_TSparser_TSconfig extends t3lib_TSparser { + + + /** + * Parses the passed TS-Config using conditions and caching + * + * @param integer The uid of the page being handled + * @param array The rootline of the page being handled + * @param string The TSConfig being parsed + * @param string The type of TSConfig (either "userTS" or "PAGES") + * @return array Array containing the parsed TSConfig and a flag wheter the content was retrieved from cache + * @access private + * @see t3lib_TSparser + */ + function parseTSconfig($TStext, $id, $rootLine, $type) { + $this->type = $type; + $this->id = $id; + $this->rootLine = $rootLine; + $hash = md5($type.':'.$TStext); + $cachedContent = $this->getHash($hash, 0); + if ($cachedContent) { + $storedData = unserialize($cachedContent); + $storedMD5 = substr($cachedContent, -strlen($hash)); + $storedData['match'] = array(); + $storedData = $this->matching($storedData); + $checkMD5 = md5(serialize($storedData)); + if ($checkMD5 == $storedMD5) { + $res = array( + 'TSconfig' => $storedData['TSconfig'], + 'cached' => 1, + ); + } else { + $shash = md5($checkMD5.$hash); + $cachedSpec = $this->getHash($shash, 0); + if ($cachedSpec) { + $storedData = unserialize($cachedSpec); + $res = array( + 'TSconfig' => $storedData['TSconfig'], + 'cached' => 1, + ); + } else { + $storeData = $this->parseWithConditions($TStext); + $serData = serialize($storeData); + $this->storeHash($shash, $serData, $type.'_TSconfig'); + $res = array( + 'TSconfig' => $storeData['TSconfig'], + 'cached' => 0, + ); + } + } + } else { + $storeData = $this->parseWithConditions($TStext); + $serData = serialize($storeData); + $md5 = md5($serData); + $this->storeHash($hash, $serData.$md5, $type.'_TSconfig'); + $res = array( + 'TSconfig' => $storeData['TSconfig'], + 'cached' => 0, + ); + } + return $res; + } + + + /** + * Retrieves the string content stored with hash key, $hash, in cache_hash + * Wrapper to the functions by same name found in t3lib_page and t3lib_BEfunc + * Usage: 1 + * + * @param string Hash key, 32 bytes hex + * @param integer $expTime represents the expire time in seconds. For instance a value of 3600 would allow cached content within the last hour, otherwise nothing is returned. + * @return string + * @see t3lib_page::getHash() + * @see t3lib_befunc::getHash() + */ + function getHash($hash, $expTime) { + if (TYPO3_MODE=='BE') { + return t3lib_BEfunc::getHash($hash, $expTime); + } elseif (TYPO3_MODE=='FE') { + if ($GLOBALS['TSFE']->sys_page) { + // After initialization of sys_page + return $GLOBALS['TSFE']->sys_page->getHash($hash, $expTime); + } else { + // When BE user is logged in while viewing frontend (FE-Editing) we have to use the t3lib_BEfunc + // method before TSFE->sys_page is initialized + return t3lib_BEfunc::getHash($hash, $expTime); + } + } + } + + + /** + * Stores the string value $data in the 'cache_hash' table with the hash key, $hash, and visual/symbolic identification, $ident + * Wrapper to the functions by same name found in t3lib_page and t3lib_BEfunc + * Usage: 1 + * + * @param string 32 bit hash string (eg. a md5 hash of a serialized array identifying the data being stored) + * @param string The data string. If you want to store an array, then just serialize it first. + * @param string $ident is just a textual identification in order to inform about the content! May be 20 characters long. + * @return void + * @see t3lib_page::storeHash() + * @see t3lib_befunc::storeHash() + */ + function storeHash($hash, $data, $ident) { + if (TYPO3_MODE=='BE') { + t3lib_BEfunc::storeHash($hash, $data, $ident); + } elseif (TYPO3_MODE=='FE') { + if ($GLOBALS['TSFE']->sys_page) { + // After initialization of sys_page + $GLOBALS['TSFE']->sys_page->storeHash($hash, $data, $ident); + } else { + // When BE user is logged in while viewing frontend (FE-Editing) we have to use the t3lib_BEfunc + // method before TSFE->sys_page is initialized + t3lib_BEfunc::storeHash($hash, $data, $ident); + } + } + } + + + /** + * Does the actual parsing using the parent objects "parse" method. Creates the match-Object + * + * @param string The TSConfig being parsed + * @return array Array containing the parsed TSConfig, the encountered sectiosn, the matched sections + * @access private + */ + function parseWithConditions($TSconfig) { + $matchObj = t3lib_div::makeInstance('t3lib_matchCondition'); + $matchObj->initBEmode($this); + $this->parse($TSconfig, $matchObj); + $storeData = array( + 'TSconfig' => $this->setup, + 'sections' => $this->sections, + 'match' => $this->sectionsMatch, + ); + return $storeData; + } + + + /** + * Is just going through an array of conditions to determine which are matching (for getting correct cache entry) + * + * @param array An array containing the sectiosn to match + * @return array The input array with matching sections filled into the "match" key + * @access private + */ + function matching($cc) { + if (is_array($cc['sections'])) { + $matchObj = t3lib_div::makeInstance('t3lib_matchCondition'); + $matchObj->initBEmode($this); + foreach ($cc['sections'] as $key => $pre) { + if ($matchObj->match($pre)) { + $cc['match'][$key] = $pre; + } + } + } + return $cc; + } + +} + + +if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tsparser_tsconfig.php']) { + include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tsparser_tsconfig.php']); +} +?> Index: t3lib/class.t3lib_befunc.php =================================================================== --- t3lib/class.t3lib_befunc.php (Revision 5213) +++ t3lib/class.t3lib_befunc.php (Arbeitskopie) @@ -172,6 +172,9 @@ */ require_once (PATH_t3lib.'class.t3lib_loaddbgroup.php'); +if ($GLOBALS['TYPO3_CONF_VARS']['BE']['TSconfigConditions']) { + require_once (PATH_t3lib.'class.t3lib_tsparser_tsconfig.php'); +} /** @@ -1279,18 +1282,26 @@ return $TSdataArray; } - // Parsing the user TS (or getting from cache) - $userTS = implode(chr(10) . '[GLOBAL]' . chr(10), $TSdataArray); - $hash = md5('pageTS:'.$userTS); - $cachedContent = t3lib_BEfunc::getHash($hash); - $TSconfig = array(); - if (isset($cachedContent)) { - $TSconfig = unserialize($cachedContent); + // Parsing the page TS-Config (or getting from cache) + $pageTS = implode(chr(10) . '[GLOBAL]' . chr(10), $TSdataArray); + if ($GLOBALS['TYPO3_CONF_VARS']['BE']['TSconfigConditions']) { + $parseObj = t3lib_div::makeInstance('t3lib_TSparser_TSconfig'); + $res = $parseObj->parseTSconfig($pageTS, $id, $rootLine, 'PAGES'); + if ($res) { + $TSconfig = $res['TSconfig']; + } } else { - $parseObj = t3lib_div::makeInstance('t3lib_TSparser'); - $parseObj->parse($userTS); - $TSconfig = $parseObj->setup; - t3lib_BEfunc::storeHash($hash, serialize($TSconfig), 'PAGES_TSconfig'); + $hash = md5('pageTS:' . $pageTS); + $cachedContent = t3lib_BEfunc::getHash($hash); + $TSconfig = array(); + if (isset($cachedContent)) { + $TSconfig = unserialize($cachedContent); + } else { + $parseObj = t3lib_div::makeInstance('t3lib_TSparser'); + $parseObj->parse($pageTS); + $TSconfig = $parseObj->setup; + t3lib_BEfunc::storeHash($hash, serialize($TSconfig), 'PAGES_TSconfig'); + } } // get User TSconfig overlay Index: t3lib/class.t3lib_matchcondition.php =================================================================== --- t3lib/class.t3lib_matchcondition.php (Revision 5213) +++ t3lib/class.t3lib_matchcondition.php (Arbeitskopie) @@ -83,6 +83,9 @@ var $altRootLine=array(); var $hookObjectsArr = array(); + var $backendMode = false; + var $backendPage = false; + var $backendRootline = false; /** * Constructor for this class @@ -111,7 +114,21 @@ $this->__construct(); } + /** + * Init BE mode + * + * @param object Parent object containing "id" and "rootLine" member variables + * @return boolean True if matched + */ + function initBEmode(&$pObj) { + $this->backendMode = true; + $this->backendPage = $pObj->id; + $this->backendRootline = $pObj->rootLine; + } + + + /** * Matching TS condition * * @param string Line to match @@ -288,20 +305,23 @@ } break; case 'usergroup': - if ($GLOBALS['TSFE']->gr_list != '0,-1') { // '0,-1' is the default usergroups when not logged in! + $groupList = $this->getGroupList(); + // '0,-1' is the default usergroups when not logged in! + if ($groupList != '0,-1') { $values = t3lib_div::trimExplode(',', $value, true); foreach ($values as $test) { - if ($test == '*' || t3lib_div::inList($GLOBALS['TSFE']->gr_list, $test)) { + if ($test == '*' || t3lib_div::inList($groupList, $test)) { return true; } } } break; case 'loginUser': - if ($GLOBALS['TSFE']->loginUser) { + if ($this->isUserLoggedIn()) { $values = t3lib_div::trimExplode(',', $value, true); + $userId = $this->getUserId(); foreach ($values as $test) { - if ($test == '*' || !strcmp($GLOBALS['TSFE']->fe_user->user['uid'], $test)) { + if ($test == '*' || !strcmp($userId, $test)) { return true; } } @@ -335,10 +355,14 @@ break; case 'treeLevel': $values = t3lib_div::trimExplode(',', $value, true); - $theRootLine = is_array($GLOBALS['TSFE']->tmpl->rootLine) ? $GLOBALS['TSFE']->tmpl->rootLine : $this->altRootLine; + $theRootLine = $this->getRootline(); + $theRootLine = is_array($theRootLine) ? $theRootLine : $this->altRootLine; $treeLevel = count($theRootLine)-1; foreach ($values as $test) { - if ($test == $treeLevel) { + if (t3lib_div::testInt($test)) { + $test = '=' . $test; + } + if ($this->testNumber($test, $treeLevel)) { return true; } } @@ -346,8 +370,10 @@ case 'PIDupinRootline': case 'PIDinRootline': $values = t3lib_div::trimExplode(',', $value, true); - if (($key=='PIDinRootline') || (!in_array($GLOBALS['TSFE']->id, $values))) { - $theRootLine = is_array($GLOBALS['TSFE']->tmpl->rootLine) ? $GLOBALS['TSFE']->tmpl->rootLine : $this->altRootLine; + $pageId = $this->getPageId(); + $theRootLine = $this->getRootline(); + if ($key == 'PIDinRootline' || !in_array($pageId, $values) || $this->isNewPageWithPid($pageId)) { + $theRootLine = is_array($theRootLine) ? $theRootLine : $this->altRootLine; foreach ($values as $test) { foreach ($theRootLine as $rl_dat) { if ($rl_dat['uid'] == $test) { @@ -363,16 +389,16 @@ case 'userFunc': $values = preg_split('/[\(\)]/', $value); $funcName = trim($values[0]); - $funcValue = t3lib_div::trimExplode(',', $values[1]); - $pre = $GLOBALS['TSFE']->TYPO3_CONF_VARS['FE']['userFuncClassPrefix']; + $funcValues = t3lib_div::trimExplode(',', $values[1]); + $pre = $this->getUserFuncClassPrefix(); if ($pre && - !t3lib_div::isFirstPartOfStr(trim($funcName),$pre) && + !t3lib_div::isFirstPartOfStr(trim($funcName), $pre) && !t3lib_div::isFirstPartOfStr(trim($funcName),'tx_') - ) { - if (is_object($GLOBALS['TT'])) $GLOBALS['TT']->setTSlogMessage('Match condition: Function "'.$funcName.'" was not prepended with "'.$pre.'"',3); + ) { + $this->setLogMessage('Match condition: Function "' . $funcName . '" was not prepended with "' . $pre . '"'); return false; } - if (function_exists($funcName) && call_user_func($funcName, $funcValue[0])) { + if (function_exists($funcName) && call_user_func_array($funcName, $funcValues)) { return true; } break; @@ -722,6 +748,204 @@ } return $val; } + + /** + * Hybrid FE/BE: Get the usergroup list of the current user. + * + * @return string The usergroup list of the current user + */ + function getGroupList() { + if (!$this->backendMode) { + $groupList = $GLOBALS['TSFE']->gr_list; + } else { + $groupList = $GLOBALS['BE_USER']->groupList; + } + return $groupList; + } + + /** + * Hybrid FE/BE: Get the id of the current page. + * + * @return integer The id of the current page + */ + function getPageId() { + if (!$this->backendMode) { + $pageId = $GLOBALS['TSFE']->id; + } else { + if ($this->backendPage !== false) { + $pageId = $this->backendPage; + } else { + $pageId = $this->backendPage = $this->evalPageId(); + } + } + return $pageId; + } + + + /** + * Tries to determine the ID of the page currently processed. User/Group TS-Config is also parsed when no specific + * page is handled (i.e. in the Extension Manager, etc.) + * + * @return integer evaluated ID or 0 if none + * @access private + */ + function evalPageId() { + $ret = 0; + if ($id = intval(t3lib_div::_GP('id'))) { + $ret = $id; + } elseif ($edit = t3lib_div::_GP('edit')) { + list($table, $val) = each($edit); + list($uid, $action) = each($val); + if ($action=='edit') { + if ($table=='pages') { + $ret = intval($uid); + } else { + $rec = t3lib_BEfunc::getRecord($table, $uid); + $ret = $rec['pid']; + } + } elseif ($action=='new') { + $ret = intval($uid); + } + } elseif ($cmd = t3lib_div::_GP('cmd')) { + list($table, $val) = each($cmd); + list($uid, $val2) = each($val); + list($action, $param) = each($val2); + if ($action=='delete') { + if ($table=='pages') { + $ret = intval($uid); + } else { + $rec = t3lib_BEfunc::getRecord($table, $uid); + $ret = $rec['pid']; + } + } elseif (($action=='copy') || ($action=='move')) { + $param = intval($param); + if ($param>=0) { + $ret = $param; + } else { + $rec = t3lib_BEfunc::getRecord($table, $uid); + $ret = $rec['pid']; + } + } + } + return $ret; + } + + /** + * Determine if record of table 'pages' with the given $pid is currently created in TCEforms. + * This information is required for conditions in BE for PIDupinRootline. + * + * @param integer $pid: The pid the check for as parent page + * @return boolean true if the is currently a new page record being edited with $pid as uid of the parent page + */ + function isNewPageWithPid($pid) { + if (isset($GLOBALS['SOBE']) && is_object($GLOBALS['SOBE']) && is_a($GLOBALS['SOBE'], 'SC_alt_doc')) { + $pid = intval($pid); + $elementsData = $GLOBALS['SOBE']->elementsData; + $data = $GLOBALS['SOBE']->data; + // If editing a new page record: + if (is_array($elementsData)) { + foreach ($elementsData as $element) { + if ($element['cmd'] == 'new' && $element['table'] == 'pages') { + if ($element['pid'] < 0) { + $pageRecord = t3lib_BEfunc::getRecord('pages', abs($element['pid']), 'pid'); + $element['pid'] = $pageRecord['pid']; + } + if ($element['pid'] == $pid) { + return true; + } + } + } + // If saving a new page record: + } elseif (is_array($data) && isset($data['pages']) && is_array($data['pages'])) { + foreach ($data['pages'] as $uid => $fields) { + if (substr($uid, 0, 3) == 'NEW' && $fields['pid'] == $pid) { + return true; + } + } + } + } + return false; + } + + /** + * Hybrid FE/BE: Get the rootline for the current page. + * + * @return array The rootline for the current page. + */ + function getRootline() { + if (!$this->backendMode) { + $rootline = $GLOBALS['TSFE']->tmpl->rootLine; + } else { + $pageId = $this->getPageId(); + $rootline = t3lib_BEfunc::BEgetRootLine($pageId, '', true); + } + return $rootline; + } + + /** + * Hybrid FE/BE: Get prefix for user functions (normally 'user_'). + * + * @return string The prefix for user functions (normally 'user_'). + */ + function getUserFuncClassPrefix() { + if (!$this->backendMode) { + $userFuncClassPrefix = $GLOBALS['TSFE']->TYPO3_CONF_VARS['FE']['userFuncClassPrefix']; + } else { + $userFuncClassPrefix = 'user_'; + } + return $userFuncClassPrefix; + } + + /** + * Hybrid FE/BE: Get the id of the current user. + * + * @return integer The id of the current user + */ + function getUserId() { + if (!$this->backendMode) { + $userId = $GLOBALS['TSFE']->fe_user->user['uid']; + } else { + $userId = $GLOBALS['BE_USER']->user['uid']; + } + return $userId; + } + + /** + * Hybrid FE/BE: Determines if a user is logged in. + * + * @return boolean Determines if a user is logged in + */ + function isUserLoggedIn() { + $userLoggedIn = false; + if (!$this->backendMode) { + if ($GLOBALS['TSFE']->loginUser) { + $userLoggedIn = true; + } + } else { + if ($GLOBALS['BE_USER']->user['uid']) { + $userLoggedIn = true; + } + } + return $userLoggedIn; + } + + /** + * Hybrid FE/BE: Set/write a log message. + * + * @param string $message: The log message to set/write + * @return void + */ + function setLogMessage($message) { + if (!$this->backendMode) { + if (is_object($GLOBALS['TT'])) { + $GLOBALS['TT']->setTSlogMessage($message,3); + } + } else { + if (is_object($GLOBALS['BE_USER'])) { + $GLOBALS['BE_USER']->writelog(3, 0, 1, 0, $message, array()); + } + } + } } Index: t3lib/class.t3lib_userauthgroup.php =================================================================== --- t3lib/class.t3lib_userauthgroup.php (Revision 5213) +++ t3lib/class.t3lib_userauthgroup.php (Arbeitskopie) @@ -101,6 +101,9 @@ // Need this for parsing User TSconfig require_once (PATH_t3lib.'class.t3lib_tsparser.php'); +if ($GLOBALS['TYPO3_CONF_VARS']['BE']['TSconfigConditions']) { + require_once (PATH_t3lib.'class.t3lib_tsparser_tsconfig.php'); +} @@ -1141,19 +1144,30 @@ // Check include lines. $this->TSdataArray = t3lib_TSparser::checkIncludeLines_array($this->TSdataArray); - // Parsing the user TSconfig (or getting from cache) $this->userTS_text = implode(chr(10).'[GLOBAL]'.chr(10),$this->TSdataArray); // Imploding with "[global]" will make sure that non-ended confinements with braces are ignored. - $hash = md5('userTS:'.$this->userTS_text); - $cachedContent = t3lib_BEfunc::getHash($hash); - if (isset($cachedContent) && !$this->userTS_dontGetCached) { - $this->userTS = unserialize($cachedContent); + + if ($GLOBALS['TYPO3_CONF_VARS']['BE']['TSconfigConditions'] && !$this->userTS_dontGetCached) { + // Perform TS-Config parsing with condition matching + $parseObj = t3lib_div::makeInstance('t3lib_TSparser_TSconfig'); + $res = $parseObj->parseTSconfig($this->userTS_text, false, false, 'userTS'); + if ($res) { + $this->userTS = $res['TSconfig']; + $this->userTSUpdated = ($res['cached'] ? 0 : 1); + } } else { - $parseObj = t3lib_div::makeInstance('t3lib_TSparser'); - $parseObj->parse($this->userTS_text); - $this->userTS = $parseObj->setup; - t3lib_BEfunc::storeHash($hash,serialize($this->userTS),'BE_USER_TSconfig'); - // Update UC: - $this->userTSUpdated=1; + // Parsing the user TSconfig (or getting from cache) + $hash = md5('userTS:' . $this->userTS_text); + $cachedContent = t3lib_BEfunc::getHash($hash); + if (isset($cachedContent) && !$this->userTS_dontGetCached) { + $this->userTS = unserialize($cachedContent); + } else { + $parseObj = t3lib_div::makeInstance('t3lib_TSparser'); + $parseObj->parse($this->userTS_text); + $this->userTS = $parseObj->setup; + t3lib_BEfunc::storeHash($hash, serialize($this->userTS), 'BE_USER_TSconfig'); + // Update UC: + $this->userTSUpdated=1; + } } // Processing webmounts