diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index 70450a6f0..725318c15 100755 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -227,6 +227,72 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid self::$nested_urls = false; } + /** + * Fetches the {@link SiteTree} object that maps to a link. + * + * If you have enabled {@link SiteTree::nested_urls()} on this site, then you can use a nested link such as + * "about-us/staff/", and this function will traverse down the URL chain and grab the appropriate link. + * + * Note that if no model can be found, this method will fall over to a decorated alternateGetByLink method provided + * by a decorator attached to {@link SiteTree} + * + * @param string $link + * @param bool $cache + * @return SiteTree + */ + public static function get_by_link($link, $cache = true) { + if(trim($link, '/')) { + $link = trim(Director::makeRelative($link), '/'); + } else { + $link = RootURLController::get_homepage_urlsegment(); + } + + $parts = Convert::raw2sql(preg_split('|/+|', $link)); + + // Grab the initial root level page to traverse down from. + $URLSegment = array_shift($parts); + $sitetree = DataObject::get_one ( + 'SiteTree', "\"URLSegment\" = '$URLSegment'" . (self::nested_urls() ? ' AND "ParentID" = 0' : ''), $cache + ); + + /// Fall back on a unique URLSegment for b/c. + if(!$sitetree && self::nested_urls() && $pages = DataObject::get('SiteTree', "\"URLSegment\" = '$URLSegment'")) { + return ($pages->Count() == 1) ? $pages->First() : null; + } + + // Attempt to grab an alternative page from decorators. + if(!$sitetree) { + if($alternatives = singleton('SiteTree')->extend('alternateGetByLink', $link, $filter, $cache, $order)) { + foreach($alternatives as $alternative) if($alternative) return $alternative; + } + + return false; + } + + // Check if we have any more URL parts to parse. + if(!self::nested_urls() || !count($parts)) return $sitetree; + + // Traverse down the remaining URL segments and grab the relevant SiteTree objects. + foreach($parts as $segment) { + $next = DataObject::get_one ( + 'SiteTree', "\"URLSegment\" = '$segment' AND \"ParentID\" = $sitetree->ID", $cache + ); + + if(!$next) { + if($alternatives = singleton('SiteTree')->extend('alternateGetByLink', $link, $filter, $cache, $order)) { + foreach($alternatives as $alternative) if($alternative) return $alternative; + } + + return false; + } + + $sitetree->destroy(); + $sitetree = $next; + } + + return $sitetree; + } + /** * Return a subclass map of SiteTree * that shouldn't be hidden through @@ -263,26 +329,26 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid return $classes; } - /** - * Replace a "[sitetree_link id=n]" shortcode with a link to the page with the corresponding ID. - * - * @return string - */ - public static function link_shortcode_handler($arguments, $content = null, $parser = null) { - if(!isset($arguments['id']) || !is_numeric($arguments['id'])) return; - - if ( - !($page = DataObject::get_by_id('SiteTree', $arguments['id'])) // Get the current page by ID. - && !($page = Versioned::get_latest_version('SiteTree', $arguments['id'])) // Attempt link to old version. - && !($page = DataObject::get_one('ErrorPage', '"ErrorCode" = \'404\'')) // Link to 404 page directly. - ) { - return; // There were no suitable matches at all. - } - - if($content) { - return sprintf('%s', $page->Link(), $parser->parse($content)); - } else { - return $page->Link(); + /** + * Replace a "[sitetree_link id=n]" shortcode with a link to the page with the corresponding ID. + * + * @return string + */ + public static function link_shortcode_handler($arguments, $content = null, $parser = null) { + if(!isset($arguments['id']) || !is_numeric($arguments['id'])) return; + + if ( + !($page = DataObject::get_by_id('SiteTree', $arguments['id'])) // Get the current page by ID. + && !($page = Versioned::get_latest_version('SiteTree', $arguments['id'])) // Attempt link to old version. + && !($page = DataObject::get_one('ErrorPage', '"ErrorCode" = \'404\'')) // Link to 404 page directly. + ) { + return; // There were no suitable matches at all. + } + + if($content) { + return sprintf('%s', $page->Link(), $parser->parse($content)); + } else { + return $page->Link(); } } @@ -1365,27 +1431,14 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid } /** - * Return the SiteTree object with the given URL segment. - * - * @param string $urlSegment The URL segment, eg 'home' - * @param string $extraFilter - * @param boolean $cache - * @param string $orderby - * @return SiteTree The object with the given URL segment + * @deprecated 2.4 Use {@link SiteTree::get_by_link()}. */ - public static function get_by_url($urlSegment, $extraFilter = "", $cache = true, $orderby = "") { - $filter = sprintf("\"URLSegment\" = '%s'", Convert::raw2sql($urlSegment)); - if($extraFilter) $filter .= " AND $extraFilter"; - $matchedPage = DataObject::get_one("SiteTree", $filter, $cache, $orderby); - if($matchedPage) { - return $matchedPage; - } else { - $alternativeMatches = singleton('SiteTree')->extend('alternateGetByUrl', $urlSegment, $extraFilter, $cache, $orderby); - if($alternativeMatches) foreach($alternativeMatches as $alternativeMatch) { - if($alternativeMatch) return $alternativeMatch; - } - return false; - } + public static function get_by_url($link) { + user_error ( + 'SiteTree::get_by_url() is deprecated, please use SiteTree::get_by_link()', E_USER_NOTICE + ); + + return self::get_by_link($link); } /** diff --git a/tests/SiteTreeTest.php b/tests/SiteTreeTest.php old mode 100644 new mode 100755 index 2209790fa..f955577e8 --- a/tests/SiteTreeTest.php +++ b/tests/SiteTreeTest.php @@ -272,20 +272,37 @@ class SiteTreeTest extends SapphireTest { $this->assertEquals('Page', $requeriedPage->class); } - - /** - * Test SiteTree::get_by_url() - */ - function testGetByURL() { - // Test basic get by url - $this->assertEquals($this->idFromFixture('Page', 'home'), SiteTree::get_by_url("home")->ID); - - // Test the extraFilter argument - // Note: One day, it would be more appropriate to return null instead of false for queries such as these - $this->assertFalse(SiteTree::get_by_url("home", "1 = 2")); + + public function testGetByLink() { + $home = $this->objFromFixture('Page', 'home'); + $about = $this->objFromFixture('Page', 'about'); + $staff = $this->objFromFixture('Page', 'staff'); + $product = $this->objFromFixture('Page', 'product1'); + $notFound = $this->objFromFixture('ErrorPage', '404'); + + SiteTree::disable_nested_urls(); + + $this->assertEquals($home->ID, SiteTree::get_by_link('/', false)->ID); + $this->assertEquals($home->ID, SiteTree::get_by_link('/home/', false)->ID); + $this->assertEquals($about->ID, SiteTree::get_by_link($about->Link(), false)->ID); + $this->assertEquals($staff->ID, SiteTree::get_by_link($staff->Link(), false)->ID); + $this->assertEquals($product->ID, SiteTree::get_by_link($product->Link(), false)->ID); + $this->assertEquals($notFound->ID, SiteTree::get_by_link($notFound->Link(), false)->ID); + + SiteTree::enable_nested_urls(); + + $this->assertEquals($home->ID, SiteTree::get_by_link('/', false)->ID); + $this->assertEquals($home->ID, SiteTree::get_by_link('/home/', false)->ID); + $this->assertEquals($about->ID, SiteTree::get_by_link($about->Link(), false)->ID); + $this->assertEquals($staff->ID, SiteTree::get_by_link($staff->Link(), false)->ID); + $this->assertEquals($product->ID, SiteTree::get_by_link($product->Link(), false)->ID); + $this->assertEquals($notFound->ID, SiteTree::get_by_link($notFound->Link(), false)->ID); + + $this->assertEquals ( + $staff->ID, SiteTree::get_by_link('/my-staff/', false)->ID, 'Assert a unique URLSegment can be used for b/c.' + ); } - function testDeleteFromStageOperatesRecursively() { $pageAbout = $this->objFromFixture('Page', 'about'); $pageStaff = $this->objFromFixture('Page', 'staff');