diff --git a/t3lib/class.t3lib_page.php b/t3lib/class.t3lib_page.php index 333ed21692001fa620e6766af9431c95e945f638..1450327a51824dea372cc5dd598e0fe482508f1e 100644 --- a/t3lib/class.t3lib_page.php +++ b/t3lib/class.t3lib_page.php @@ -579,133 +579,20 @@ class t3lib_pageSelect { * @see tslib_fe::getPageAndRootline() */ function getRootLine($uid, $MP = '', $ignoreMPerrors = FALSE) { - $cacheUid = $uid = intval($uid); - $cacheIgnoreMPerrors = ($ignoreMPerrors ? 1 : 0); - - if (is_array($this->cache_getRootLine[$cacheUid][$this->sys_language_uid][$MP][$cacheIgnoreMPerrors])) { - return $this->cache_getRootLine[$cacheUid][$this->sys_language_uid][$MP][$cacheIgnoreMPerrors]; - } - - // Initialize: - $selFields = t3lib_div::uniqueList('pid,uid,t3ver_oid,t3ver_wsid,t3ver_state,title,alias,nav_title,media,layout,hidden,starttime,endtime,fe_group,extendToSubpages,doktype,TSconfig,storage_pid,is_siteroot,mount_pid,mount_pid_ol,fe_login_mode,backend_layout_next_level,' . $GLOBALS['TYPO3_CONF_VARS']['FE']['addRootLineFields']); - $this->error_getRootLine = ''; - $this->error_getRootLine_failPid = 0; - - // Splitting the $MP parameters if present - $MPA = array(); - if ($MP) { - $MPA = explode(',', $MP); - foreach ($MPA as $MPAk => $v) { - $MPA[$MPAk] = explode('-', $MPA[$MPAk]); - } - } - - $loopCheck = 0; - $theRowArray = array(); - - // Max 99 levels in the page tree. - while ($uid != 0 && $loopCheck < 99) { - $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($selFields, 'pages', 'uid=' . intval($uid) . ' AND pages.deleted=0 AND pages.doktype<>255'); - $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); - $GLOBALS['TYPO3_DB']->sql_free_result($res); - if ($row) { - $this->versionOL('pages', $row, FALSE, TRUE); - $this->fixVersioningPid('pages', $row); - - if (is_array($row)) { - // Mount Point page types are allowed ONLY a) if they are the outermost record in rootline and b) if the overlay flag is not set: - if ($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids'] && $row['doktype'] == self::DOKTYPE_MOUNTPOINT && !$ignoreMPerrors) { - $mount_info = $this->getMountPointInfo($row['uid'], $row); - if ($loopCheck > 0 || $mount_info['overlay']) { - $this->error_getRootLine = 'Illegal Mount Point found in rootline'; - return array(); - } - } - - // Next uid - $uid = $row['pid']; - - if (count($MPA) && $GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) { - $curMP = end($MPA); - if (!strcmp($row['uid'], $curMP[0])) { - - array_pop($MPA); - $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($selFields, 'pages', 'uid=' . intval($curMP[1]) . ' AND pages.deleted=0 AND pages.doktype<>255'); - $mp_row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); - $GLOBALS['TYPO3_DB']->sql_free_result($res); - - $this->versionOL('pages', $mp_row, FALSE, TRUE); - $this->fixVersioningPid('pages', $mp_row); - - if (is_array($mp_row)) { - $mount_info = $this->getMountPointInfo($mp_row['uid'], $mp_row); - if (is_array($mount_info) && $mount_info['mount_pid'] == $curMP[0]) { - // Setting next uid - $uid = $mp_row['pid']; - - // Symlink style: Keep mount point (current row). - if ($mount_info['overlay']) { - // Set overlay mode: - $row['_MOUNT_OL'] = TRUE; - $row['_MOUNT_PAGE'] = array( - 'uid' => $mp_row['uid'], - 'pid' => $mp_row['pid'], - 'title' => $mp_row['title'], - ); - } else { // Normal operation: Insert the mount page row in rootline instead mount point. - if ($loopCheck > 0) { - $row = $mp_row; - } else { - $this->error_getRootLine = 'Current Page Id is a mounted page of the overlay type and cannot be accessed directly!'; - // Matching the page id (first run, $loopCheck = 0) with the MPvar is ONLY allowed if the mount point is the "overlay" type (otherwise it could be forged!) - return array(); - } - } - - $row['_MOUNTED_FROM'] = $curMP[0]; - $row['_MP_PARAM'] = $mount_info['MPvar']; - } else { - $this->error_getRootLine = 'MP var was corrupted'; - // The MP variables did NOT connect proper mount points: - return array(); - } - } else { - $this->error_getRootLine = 'No moint point record found according to PID in MP var'; - // The second PID in MP var was NOT a valid page. - return array(); - } - } - } + $rootline = t3lib_div::makeInstance('t3lib_rootline', $uid, $MP, $this); + if ($ignoreMPerrors) { + try { + return $rootline->get(); + } catch (Exception $e) { + $this->error_getRootLine = $e->getMessage(); + if (substr($e->getMessage(), -7) == 'uid -1.') { + $this->error_getRootLine_failPid = -1; } - // Add row to rootline with language overlaid: - $theRowArray[] = $this->getPageOverlay($row); - } else { - $this->error_getRootLine = 'Broken rootline (failed on page with uid ' . $uid . ')'; - $this->error_getRootLine_failPid = $uid; - // Broken rootline. return array(); } - - $loopCheck++; - } - - // If the MPA array is NOT empty, we have to return an error; All MP elements were not resolved! - if (count($MPA)) { - $this->error_getRootLine = 'MP value remain!'; - return array(); - } - - // Create output array (with reversed order of numeric keys): - $output = Array(); - $c = count($theRowArray); - foreach ($theRowArray as $key => $val) { - $c--; - $output[$c] = $val; + } else { + return $rootline->get(); } - - // Note: rootline errors are not cached - $this->cache_getRootLine[$cacheUid][$this->sys_language_uid][$MP][$cacheIgnoreMPerrors] = $output; - return $output; } /** diff --git a/t3lib/class.t3lib_rootline.php b/t3lib/class.t3lib_rootline.php new file mode 100644 index 0000000000000000000000000000000000000000..962bc301692f05780c54b4544ace9eed582c338a --- /dev/null +++ b/t3lib/class.t3lib_rootline.php @@ -0,0 +1,432 @@ +<?php +/*************************************************************** + * Copyright notice + * + * (c) 2012 Steffen Ritter <steffen.ritter@typo3.org> + * 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! + ***************************************************************/ + +/** + * A utility resolving and Caching the Rootline generation + * + * @author Steffen Ritter <steffen.ritter@typo3.org> + * + * @package TYPO3 + * @subpackage t3lib + */ +class t3lib_rootline { + + /** + * @var integer + */ + protected $pageUid; + + /** + * @var string + */ + protected $mountPointParameter; + + /** + * @var array + */ + protected $parsedMountPointParameters = array(); + + /** + * @var integer + */ + protected $languageUid = 0; + + /** + * @var integer + */ + protected $workspaceUid = 0; + + /** + * @var boolean + */ + protected $versionPreview = FALSE; + + /** + * @var t3lib_cache_frontend_Frontend + */ + protected static $cache = NULL; + + /** + * @var array + */ + protected static $localCache = array(); + + /** + * Fields to fetch when populating rootline data + * + * @var array + */ + protected static $rootlineFields = array( + 'pid', + 'uid', + 't3ver_oid', + 't3ver_wsid', + 't3ver_state', + 'title', + 'alias', + 'nav_title', + 'media', + 'layout', + 'hidden', + 'starttime', + 'endtime', + 'fe_group', + 'extendToSubpages', + 'doktype', + 'TSconfig', + 'storage_pid', + 'is_siteroot', + 'mount_pid', + 'mount_pid_ol', + 'fe_login_mode', + 'backend_layout_next_level' + ); + + /** + * Rootline Context + * + * @var t3lib_pageSelect + */ + protected $pageContext; + + /** + * @var array + */ + protected static $pageRecordCache = array(); + + /** + * @param int $uid + * @param string $mountPointParameter + * @param t3lib_pageSelect $context + * @throws RuntimeException + */ + public function __construct($uid, $mountPointParameter = '', t3lib_pageSelect $context = NULL) { + $this->pageUid = intval($uid); + $this->mountPointParameter = trim($mountPointParameter); + + if ($context === NULL) { + if ($GLOBALS['TSFE']->sys_page !== NULL) { + $this->pageContext = $GLOBALS['TSFE']->sys_page; + } else { + $this->pageContext = t3lib_div::makeInstance('t3lib_pageSelect'); + } + } else { + $this->pageContext = $context; + } + $this->initializeObject(); + } + + /** + * Initialize a state to work with + * + * @throws RuntimeException + * @return void + */ + protected function initializeObject() { + + $this->languageUid = intval($this->pageContext->sys_language_uid); + $this->workspaceUid = intval($this->pageContext->versioningWorkspaceId); + $this->versionPreview = $this->pageContext->versioningPreview; + + if ($this->mountPointParameter !== '') { + if (!$GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) { + throw new RuntimeException('Mount-Point Pages are disabled for this installation. Cannot resolve a Rootline for a page with Mount-Points', 1343462896); + } else { + $this->parseMountPointParameter(); + } + } + + if (self::$cache === NULL) { + self::$cache = $GLOBALS['typo3CacheManager']->getCache('cache_rootline'); + } + + self::$rootlineFields = array_merge( + self::$rootlineFields, + t3lib_div::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['addRootLineFields'], TRUE) + ); + array_unique(self::$rootlineFields); + } + + /** + * Constructs the cache Identifier + * + * @param integer $otherUid + * @return string + */ + public function getCacheIdentifier($otherUid = NULL) { + return implode('_', array( + $otherUid !== NULL ? intval($otherUid) : $this->pageUid, + $this->mountPointParameter, + $this->languageUid, + $this->workspaceUid, + $this->versionPreview ? 1 : 0 + )); + } + + /** + * Returns the actual rootline + * @return array + */ + public function get() { + $cacheIdentifier = $this->getCacheIdentifier(); + if (!isset(self::$localCache[$cacheIdentifier])) { + if (!self::$cache->has($cacheIdentifier)) { + $this->generateRootlineCache(); + } else { + self::$localCache[$cacheIdentifier] = self::$cache->get($cacheIdentifier); + } + } + return self::$localCache[$cacheIdentifier]; + } + + /** + * Queries the database for the page record and returns it. + * + * @param integer $uid Page id + * @throws RuntimeException + * @return array + */ + protected function getRecordArray($uid) { + if (!isset(self::$pageRecordCache[$this->getCacheIdentifier($uid)])) { + + if (!is_array($GLOBALS['TCA']['pages']['columns'])) { + if (isset($GLOBALS['TSFE'])) { + $GLOBALS['TSFE']->includeTCA($GLOBALS['TSFE']->TCAloaded); + } + t3lib_div::loadTCA('pages'); + } + + $row = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow( + implode(',', self::$rootlineFields), + 'pages', + 'uid = ' . intval($uid) . ' AND pages.deleted = 0 AND pages.doktype <> ' . t3lib_pageSelect::DOKTYPE_RECYCLER + ); + + if (empty($row)) { + throw new RuntimeException('Could not fetch page data for uid ' . $uid . '.', 1343589451); + } + + $this->pageContext->versionOL('pages', $row, FALSE, TRUE); + $this->pageContext->fixVersioningPid('pages', $row); + + if (is_array($row)) { + $row = $this->enrichWithRelationFields($uid, $row); + $this->pageContext->getPageOverlay($row, $this->languageUid); + self::$pageRecordCache[$this->getCacheIdentifier($uid)] = $row; + } + } + + if (!is_array(self::$pageRecordCache[$this->getCacheIdentifier($uid)])) { + throw new RuntimeException('Broken rootline. Could not resolve page with uid ' . $uid . '.', 1343464101); + } + + return self::$pageRecordCache[$this->getCacheIdentifier($uid)]; + } + + /** + * Resolve relations as defined in TCA and add them to the provided $pageRecord array. + * + * @param integer $uid Page id + * @param array $pageRecord Array with page data to add relation data to. + * @throws RuntimeException + * @return array $pageRecord with additional relations + */ + protected function enrichWithRelationFields($uid, array $pageRecord) { + foreach ($GLOBALS['TCA']['pages']['columns'] as $column => $configuration) { + if ($this->columnHasRelationToResolve($configuration)) { + $configuration = $configuration['config']; + if ($configuration['MM']) { + /** @var $loadDBGroup t3lib_loadDBGroup */ + $loadDBGroup = t3lib_div::makeInstance('t3lib_loadDBGroup'); + $loadDBGroup->start( + $pageRecord[$column], + $configuration['foreign_table'], + $configuration['MM'], + $uid, + 'pages', + $configuration + ); + $relatedUids = $loadDBGroup->tableArray[$configuration['foreign_table']]; + } elseif ($configuration['foreign_field']) { + $table = $configuration['foreign_table']; + $field = $configuration['foreign_field']; + $whereClauseParts = array('`' . $field . '` = ' . intval($uid)); + if (isset($configuration['foreign_match_fields']) && is_array($configuration['foreign_match_fields'])) { + foreach ($configuration['foreign_match_fields'] as $field => $value) { + $whereClauseParts[] = '`' . $field . '` = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($value); + } + + } + if (isset($configuration['foreign_table_field'])) { + $whereClauseParts[] = '`' . trim($configuration['foreign_table_field']) . '` = \'pages\''; + } + $whereClause = implode(' AND ', $whereClauseParts); + $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', $table, $whereClause); + if (!is_array($rows)) { + throw new RuntimeException('Could to resolve related records for page ' . $uid . ' and foreign_table ' . htmlspecialchars($configuration['foreign_table']), 1343589452); + } + $relatedUids = array(); + foreach ($rows as $row) { + $relatedUids[] = $row['uid']; + } + } + + $pageRecord[$column] = implode(',', $relatedUids); + } + } + return $pageRecord; + } + + /** + * Checks whether the TCA Configuration array of a column + * describes a relation which is not stored as CSV in the record + * + * @param array $configuration TCA configuration to check + * @return boolean TRUE, if it describes a non-CSV relation + */ + protected function columnHasRelationToResolve(array $configuration) { + $configuration = $configuration['config']; + if (isset($configuration['MM']) && isset($configuration['type']) && in_array($configuration['type'], array('select', 'inline', 'group'))) { + return TRUE; + } + if (isset($configuration['foreign_field']) && isset($configuration['type']) && in_array($configuration['type'], array('select', 'inline'))) { + return TRUE; + } + return FALSE; + } + + /** + * Actual function to generate the rootline and cache it + * + * @throws RuntimeException + * @return void + */ + protected function generateRootlineCache() { + $page = $this->getRecordArray($this->pageUid); + + // If the current page is a mounted (according to the MP parameter) handle the mount-point + if ($this->isMountedPage()) { + $mountPoint = $this->getRecordArray($this->parsedMountPointParameters[$this->pageUid]); + + $page = $this->processMountedPage($page, $mountPoint); + $parentUid = $mountPoint['pid']; + // Anyhow after reaching the mount-point, we have to go up that rootline + unset($this->parsedMountPointParameters[$this->pageUid]); + } else { + $parentUid = $page['pid']; + } + + $cacheTags = array('pageId_' . $page['uid']); + + if ($parentUid > 0) { + // Get rootline of (and including) parent page + $mountPointParameter = count($this->parsedMountPointParameters) > 0 ? $this->mountPointParameter : ''; + /** @var $rootline t3lib_rootline */ + $rootline = t3lib_div::makeInstance('t3lib_rootline', $parentUid, $mountPointParameter, $this->pageContext); + $rootline = $rootline->get(); + + // retrieve cache tags of parent rootline + foreach ($rootline as $entry) { + $cacheTags[] = 'pageId_' . $entry['uid']; + if ($entry['uid'] == $this->pageUid) { + throw new RuntimeException('Circular connection in rootline for page with uid ' . $this->pageUid . ' found. Check your mountpoint configuration.', 1343464103); + } + } + } else { + $rootline = array(); + } + + array_push($rootline, $page); + + krsort($rootline); + self::$cache->set( + $this->getCacheIdentifier(), + $rootline, + $cacheTags + ); + self::$localCache[$this->getCacheIdentifier()] = $rootline; + } + + /** + * Checks whether the current Page is a Mounted Page + * (according to the MP-URL-Parameter) + * + * @return boolean + */ + protected function isMountedPage() { + return in_array($this->pageUid, array_keys($this->parsedMountPointParameters)); + } + + /** + * Enhances with mount point information or replaces the node if needed + * + * @param array $mountedPageData page record array of mounted page + * @param array $mountPointPageData page record array of mount point page + * @throws RuntimeException + * @return array + */ + protected function processMountedPage(array $mountedPageData, array $mountPointPageData) { + if ($mountPointPageData['mount_pid'] != $mountedPageData['uid']) { + throw new RuntimeException( + 'Broken rootline. Mountpoint parameter does not match the actual rootline. mount_pid (' . $mountPointPageData['mount_pid'] . ') does not match page uid (' . $mountedPageData['uid'] . ').', + 1343464100); + } + + // Current page replaces the original mount-page + if ($mountPointPageData['mount_pid_ol']) { + $mountedPageData['_MOUNT_OL'] = TRUE; + $mountedPageData['_MOUNT_PAGE'] = array( + 'uid' => $mountPointPageData['uid'], + 'pid' => $mountPointPageData['pid'], + 'title' => $mountPointPageData['title'] + ); + } else { + // The mount-page is not replaced, the mount-page itself has to be used + $mountedPageData = $mountPointPageData; + } + + $mountedPageData['_MOUNTED_FROM'] = $this->pageUid; + $mountedPageData['_MP_PARAM'] = $this->pageUid . '-' . $mountPointPageData['uid']; + return $mountedPageData; + } + + /** + * Parse the MountPoint Parameters + * Splits the MP-Param via "," for several nested mountpoints + * and afterwords registers the mountpoint configurations + * + * @return void + */ + protected function parseMountPointParameter() { + $mountPoints = t3lib_div::trimExplode(',', $this->mountPointParameter); + + foreach ($mountPoints as $mP) { + list($mountedPageUid, $mountPageUid) = t3lib_div::intExplode('-', $mP); + $this->parsedMountPointParameters[$mountedPageUid] = $mountPageUid; + } + } + +} + +?> \ No newline at end of file diff --git a/t3lib/class.t3lib_tcemain.php b/t3lib/class.t3lib_tcemain.php index f3c62fc65faa502f770e8e6517c35c1bdabb5a92..184b82e4e04d60e21739cb708a297abb638816c7 100644 --- a/t3lib/class.t3lib_tcemain.php +++ b/t3lib/class.t3lib_tcemain.php @@ -6893,12 +6893,9 @@ class t3lib_TCEmain { // Delete cache for selected pages: if (is_array($list_cache)) { - $pageCache = $GLOBALS['typo3CacheManager']->getCache('cache_pages'); - $pageSectionCache = $GLOBALS['typo3CacheManager']->getCache('cache_pagesection'); $pageIds = $GLOBALS['TYPO3_DB']->cleanIntArray($list_cache); foreach ($pageIds as $pageId) { - $pageCache->flushByTag('pageId_' . $pageId); - $pageSectionCache->flushByTag('pageId_' . $pageId); + $GLOBALS['typo3CacheManager']->flushCachesByTag('pageId_' . $pageId); } } } diff --git a/t3lib/core_autoload.php b/t3lib/core_autoload.php index c1b32a20fdccfffd93e0bb9b20f1d15fe3210276..e86a0971c99c51ea000d462bd8c577c84b3f4f06 100644 --- a/t3lib/core_autoload.php +++ b/t3lib/core_autoload.php @@ -236,6 +236,7 @@ $t3libClasses = array( 't3lib_recordlist' => PATH_t3lib . 'class.t3lib_recordlist.php', 't3lib_refindex' => PATH_t3lib . 'class.t3lib_refindex.php', 't3lib_registry' => PATH_t3lib . 'class.t3lib_registry.php', + 't3lib_rootline' => PATH_t3lib . 'class.t3lib_rootline.php', 't3lib_rteapi' => PATH_t3lib . 'class.t3lib_rteapi.php', 't3lib_scbase' => PATH_t3lib . 'class.t3lib_scbase.php', 't3lib_search_livesearch' => PATH_t3lib . 'search/class.t3lib_search_livesearch.php', @@ -380,4 +381,4 @@ $typo3Classes = array( $tslibClasses = require(PATH_typo3 . 'sysext/cms/ext_autoload.php'); return array_merge($t3libClasses, $typo3Classes, $tslibClasses); -?> \ No newline at end of file +?> diff --git a/t3lib/stddb/DefaultConfiguration.php b/t3lib/stddb/DefaultConfiguration.php index dc75471c2d1f70faa3a78e750a9cdb349eeb6157..949fb2e57a009e9ec0f406ebe69308e2c22ac287 100644 --- a/t3lib/stddb/DefaultConfiguration.php +++ b/t3lib/stddb/DefaultConfiguration.php @@ -167,6 +167,11 @@ return array( 'backend' => 't3lib_cache_backend_TransientMemoryBackend', 'options' => array(), ), + 'cache_rootline' => array( + 'frontend' => 't3lib_cache_frontend_VariableFrontend', + 'backend' => 't3lib_cache_backend_DbBackend', + 'options' => array(), + ) ), ), '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> diff --git a/tests/Unit/t3lib/class.t3lib_rootlineTest.php b/tests/Unit/t3lib/class.t3lib_rootlineTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0fe3028cb27f9755502ae5f872f62e1f6cb73c98 --- /dev/null +++ b/tests/Unit/t3lib/class.t3lib_rootlineTest.php @@ -0,0 +1,305 @@ +<?php +/*************************************************************** + * Copyright notice + * + * (c) 2012 Steffen Ritter <steffen.ritter@typo3.org> + * 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! + ***************************************************************/ + +require_once(__DIR__ . '/fixtures/AccessibleRootline.php'); +/** + * Testcase for class t3lib_rootline + * + * @author Steffen Ritter <steffen.ritter@typo3.org> + * + * @package TYPO3 + * @subpackage t3lib + */ +class t3lib_rootlineTest extends tx_phpunit_testcase { + + /*** + * + * UTILITY FUNCTIONS + * + */ + + + /** + * Tests that $subsetCandidate is completely part of $superset + * and keys match. + * + * @see (A ^ B) = A <=> A c B + * + * @param array $subsetCandidate + * @param array $superset + */ + protected function assertIsSubset(array $subsetCandidate, array $superset) { + $this->assertSame( + $subsetCandidate, + array_intersect_assoc($subsetCandidate, $superset) + ); + } + + + + + /*** + * + * >TEST CASES + * + */ + + /** + * @test + */ + public function isMountedPageWithoutMountPointsReturnsFalse() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1); + + $this->assertFalse( + $fixture->isMountedPage() + ); + } + + /** + * @test + */ + public function isMountedPageWithMatchingMountPointParameterReturnsTrue() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1, '1-99'); + + $this->assertTrue( + $fixture->isMountedPage() + ); + } + + /** + * @test + */ + public function isMountedPageWithNonMatchingMountPointParameterReturnsFalse() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1, '99-99'); + + $this->assertFalse( + $fixture->isMountedPage() + ); + } + + /** + * @test + * @expectedException RuntimeException + */ + public function processMountedPageWithNonMountedPageThrowsException() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1, '1-99'); + $fixture->processMountedPage( + array('uid' => 1), + array('uid' => 99, 'doktype' => t3lib_pageSelect::DOKTYPE_DEFAULT) + ); + } + + /** + * @test + */ + public function processMountedPageWithMountedPageNotThrowsException() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1, '1-99'); + $fixture->processMountedPage( + array('uid' => 1), + array('uid' => 99, 'doktype' => t3lib_pageSelect::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1) + ); + } + + /** + * @test + */ + public function processMountedPageWithMountedPageAddsMountedFromParameter() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1, '1-99'); + $result = $fixture->processMountedPage( + array('uid' => 1), + array('uid' => 99, 'doktype' => t3lib_pageSelect::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1) + ); + + $this->assertTrue(isset($result['_MOUNTED_FROM'])); + $this->assertSame(1, $result['_MOUNTED_FROM']); + } + + /** + * @test + */ + public function processMountedPageWithMountedPageAddsMountPointParameterToReturnValue() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1, '1-99'); + $result = $fixture->processMountedPage( + array('uid' => 1), + array('uid' => 99, 'doktype' => t3lib_pageSelect::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1) + ); + + $this->assertTrue(isset($result['_MP_PARAM'])); + $this->assertSame( + '1-99', + $result['_MP_PARAM'] + ); + } + + + + /** + * @test + */ + public function processMountedPageForMountPageIsOverlayAddsMountOLParameter() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1, '1-99'); + $result = $fixture->processMountedPage( + array('uid' => 1), + array('uid' => 99, 'doktype' => t3lib_pageSelect::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1, 'mount_pid_ol' => 1) + ); + + $this->assertTrue(isset($result['_MOUNT_OL'])); + $this->assertSame(TRUE, $result['_MOUNT_OL']); + } + + /** + * @test + */ + public function processMountedPageForMountPageIsOverlayAddsDataInformationAboutMountPage() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1, '1-99'); + $result = $fixture->processMountedPage( + array('uid' => 1), + array('uid' => 99, 'doktype' => t3lib_pageSelect::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1, 'mount_pid_ol' => 1, 'pid' => 5, 'title' => 'TestCase') + ); + + $this->assertTrue(isset($result['_MOUNT_PAGE'])); + $this->assertSame( + array('uid' => 99, 'pid' => 5, 'title' => 'TestCase'), + $result['_MOUNT_PAGE'] + ); + } + + /** + * @test + */ + public function processMountedPageForMountPageWithoutOverlayReplacesMountedPageWithMountPage() { + $a = array('uid' => 99, 'doktype' => t3lib_pageSelect::DOKTYPE_MOUNTPOINT, 'mount_pid' => 1, 'mount_pid_ol' => 0); + $fixture = new Tests_unit_t3lib_AccessibleRootline(1, '1-99'); + $result = $fixture->processMountedPage( + array('uid' => 1), + $a + ); + + $this->assertIsSubset($a, $result); + } + + /** + * @test + */ + public function columnHasRelationToResolveDetectsGroupFieldAsLocal() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1); + $this->assertFalse($fixture->columnHasRelationToResolve(array( + 'type' => 'group' + ))); + } + + /** + * @test + */ + public function columnHasRelationToResolveDetectsGroupFieldWithMMAsRemote() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1); + $this->assertTrue($fixture->columnHasRelationToResolve(array('config' => array( + 'type' => 'group', + 'MM' => 'tx_xyz' + )))); + } + + /** + * @test + */ + public function columnHasRelationToResolveDetectsInlineFieldAsLocal() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1); + $this->assertFalse($fixture->columnHasRelationToResolve(array('config' => array( + 'type' => 'inline' + )))); + } + + /** + * @test + */ + public function columnHasRelationToResolveDetectsInlineFieldWithForeignKeyAsRemote() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1); + $this->assertTrue($fixture->columnHasRelationToResolve(array('config' => array( + 'type' => 'inline', + 'foreign_field' => 'xyz' + )))); + } + + /** + * @test + */ + public function columnHasRelationToResolveDetectsInlineFieldWithFMMAsRemote() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1); + $this->assertTrue($fixture->columnHasRelationToResolve(array('config' => array( + 'type' => 'inline', + 'MM' => 'xyz' + )))); + } + + /** + * @test + */ + public function columnHasRelationToResolveDetectsSelectFieldAsLocal() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1); + $this->assertFalse($fixture->columnHasRelationToResolve(array('config' => array( + 'type' => 'select' + )))); + } + + /** + * @test + */ + public function columnHasRelationToResolveDetectsSelectFieldWithMMAsRemote() { + $fixture = new Tests_unit_t3lib_AccessibleRootline(1); + $this->assertTrue($fixture->columnHasRelationToResolve(array('config' => array( + 'type' => 'select', + 'MM' => 'xyz' + )))); + } + + /** + * @test + */ + public function getCacheIdentifierContainsAllContextParameters() { + $pageContext = t3lib_div::makeInstance('t3lib_pageSelect'); + $pageContext->sys_language_uid = 8; + $pageContext->versioningWorkspaceId = 15; + $pageContext->versioningPreview = TRUE; + $fixture = new Tests_unit_t3lib_AccessibleRootline(42, '47-11', $pageContext); + $this->assertSame( + '42_47-11_8_15_1', + $fixture->getCacheIdentifier() + ); + + $pageContext->versioningPreview = FALSE; + $fixture = new Tests_unit_t3lib_AccessibleRootline(42, '47-11', $pageContext); + $this->assertSame( + '42_47-11_8_15_0', + $fixture->getCacheIdentifier() + ); + + $pageContext->versioningWorkspaceId = 0; + $fixture = new Tests_unit_t3lib_AccessibleRootline(42, '47-11', $pageContext); + $this->assertSame( + '42_47-11_8_0_0', + $fixture->getCacheIdentifier() + ); + } + +} diff --git a/tests/Unit/t3lib/fixtures/AccessibleRootline.php b/tests/Unit/t3lib/fixtures/AccessibleRootline.php new file mode 100644 index 0000000000000000000000000000000000000000..a5b72bb70141bf61f5d5ed9ba94ef73cf637701c --- /dev/null +++ b/tests/Unit/t3lib/fixtures/AccessibleRootline.php @@ -0,0 +1,48 @@ +<?php +/*************************************************************** + * Copyright notice + * + * (c) 2012 Steffen Ritter <steffen.ritter@typo3.org> + * 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! + ***************************************************************/ + +/** + * Make method public + * + * @author Steffen Ritter <steffen.ritter@typo3.org> + * + * @package TYPO3 + * @subpackage test + */ +class Tests_unit_t3lib_AccessibleRootline extends t3lib_rootline { + + public function isMountedPage() { + return parent::isMountedPage(); + } + + public function processMountedPage($mountedPageData, $mountPointPageData) { + return parent::processMountedPage($mountedPageData, $mountPointPageData); + } + + public function columnHasRelationToResolve($configuration) { + return parent::columnHasRelationToResolve($configuration); + } + + +} \ No newline at end of file