diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index dba51a040..cfb5dd87b 100755 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -1375,41 +1375,54 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid } function syncLinkTracking() { - // Set LinkTracking appropriately - $links = HTTP::getLinksIn($this->Content); + // Build a list of HTMLText fields + $allFields = $this->db(); + $htmlFields = array(); + foreach($allFields as $field => $fieldSpec) { + if(preg_match('/([^(]+)/', $fieldSpec, $matches)) { + $class = $matches[0]; + if($class == 'HTMLText' || is_subclass_of($class, 'HTMLText')) $htmlFields[] = $field; + } + } + $linkedPages = array(); $linkedFiles = array(); $this->HasBrokenLink = false; $this->HasBrokenFile = false; - if($links) foreach($links as $link) { - if(preg_match('/^([A-Za-z0-9_-]+)\/?(#.*)?$/', $link, $parts)) { - $candidatePage = DataObject::get_one("SiteTree", "\"URLSegment\" = '" . urldecode( $parts[1] ). "'", false); - if($candidatePage) { - $linkedPages[] = $candidatePage->ID; - } else { + foreach($htmlFields as $field) { + // Set LinkTracking appropriately + $links = HTTP::getLinksIn($this->$field); + + if($links) foreach($links as $link) { + if(preg_match('/^([A-Za-z0-9_-]+)\/?(#.*)?$/', $link, $parts)) { + $candidatePage = DataObject::get_one("SiteTree", "\"URLSegment\" = '" . urldecode( $parts[1] ). "'", false); + if($candidatePage) { + $linkedPages[] = $candidatePage->ID; + } else { + $this->HasBrokenLink = true; + } + } else if(substr($link,0,7) == 'assets/') { + $candidateFile = File::find(Convert::raw2sql(urldecode($link))); + if($candidateFile) { + $linkedFiles[] = $candidateFile->ID; + } else { + $this->HasBrokenFile = true; + } + } else if($link == '' || $link[0] == '/') { $this->HasBrokenLink = true; } - } else if(substr($link,0,7) == 'assets/') { - $candidateFile = File::find(Convert::raw2sql(urldecode($link))); - if($candidateFile) { - $linkedFiles[] = $candidateFile->ID; - } else { - $this->HasBrokenFile = true; - } - } else if($link == '' || $link[0] == '/') { - $this->HasBrokenLink = true; } - } - $images = HTTP::getImagesIn($this->Content); - if($images) { - foreach($images as $image) { - $image = Director::makeRelative($image); - if(substr($image,0,7) == 'assets/') { - $candidateImage = File::find($image); - if($candidateImage) $linkedFiles[] = $candidateImage->ID; - else $this->HasBrokenFile = true; + $images = HTTP::getImagesIn($this->$field); + if($images) { + foreach($images as $image) { + $image = Director::makeRelative($image); + if(substr($image,0,7) == 'assets/') { + $candidateImage = File::find($image); + if($candidateImage) $linkedFiles[] = $candidateImage->ID; + else $this->HasBrokenFile = true; + } } } } @@ -2019,8 +2032,10 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid if($original->URLSegment && $original->URLSegment != $this->URLSegment) { foreach($this->BackLinkTracking() as $linkedPage) { $linkedPageLive = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $linkedPage->ID); - $linkedPageLive->rewriteLink($original->URLSegment . '/', $this->URLSegment . '/'); - $linkedPageLive->writeToStage('Live'); + if($linkedPageLive) { + $linkedPageLive->rewriteLink($original->URLSegment . '/', $this->URLSegment . '/'); + $linkedPageLive->writeToStage('Live'); + } } } diff --git a/tests/SiteTreeBacklinksTest.php b/tests/SiteTreeBacklinksTest.php index 82993c8fb..ceebe0c8f 100644 --- a/tests/SiteTreeBacklinksTest.php +++ b/tests/SiteTreeBacklinksTest.php @@ -2,6 +2,10 @@ class SiteTreeBacklinksTest extends SapphireTest { static $fixture_file = "sapphire/tests/SiteTreeBacklinksTest.yml"; + + protected $requiredExtensions = array( + 'SiteTree' => array('SiteTreeBacklinksTest_DOD'), + ); static function set_up_once() { SiteTreeTest::set_up_once(); @@ -60,6 +64,191 @@ class SiteTreeBacklinksTest extends SapphireTest { // assert backlink to page 3 exists $this->assertFalse($page1->BackLinkTracking()->containsIDs(array($page3->ID)), 'Assert backlink to page 3 doesn\'t exist'); } + + function testChangingUrlOnDraftSiteRewritesLink() { + // load page 1 + $page1 = $this->objFromFixture('Page', 'page1'); + + // assert backlink to page 3 exists + $page3 = $this->objFromFixture('Page', 'page3'); + $this->assertTrue($page1->BackLinkTracking()->containsIDs(array($page3->ID)), 'Assert backlink to page 3 exists'); + + // assert hyperlink to page 1's current url exists on page 3 + $links = HTTP::getLinksIn($page3->Content); + $this->assertContains('page1/', $links, 'Assert hyperlink to page 1\'s current url exists on page 3'); + + // change url of page 1 + $page1->URLSegment = 'new-url-segment'; + $page1->write(); + + // load page 3 + $page3 = $this->objFromFixture('Page', 'page3'); + + // assert hyperlink to page 1's new url exists + $links = HTTP::getLinksIn($page3->Content); + $this->assertContains('new-url-segment/', $links, 'Assert hyperlink to page 1\'s new url exists on page 3'); + } + + function testChangingUrlOnLiveSiteRewritesLink() { + // publish page 1 & 3 + $page1 = $this->objFromFixture('Page', 'page1'); + $page3 = $this->objFromFixture('Page', 'page3'); + $this->assertTrue($page1->doPublish()); + $this->assertTrue($page3->doPublish()); + + // load pages from live + $page1live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page1->ID); + $page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID); + + // assert backlink to page 3 exists + $this->assertTrue($page1live->BackLinkTracking()->containsIDs(array($page3live->ID)), 'Assert backlink to page 3 exists'); + + // assert hyperlink to page 1's current url exists on page 3 + $links = HTTP::getLinksIn($page3live->Content); + $this->assertContains('page1/', $links, 'Assert hyperlink to page 1\'s current url exists on page 3'); + + // change url of page 1 + $page1live->URLSegment = 'new-url-segment'; + $page1live->writeToStage('Live'); + + // load page 3 from live + $page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID); + + // assert hyperlink to page 1's new url exists + $links = HTTP::getLinksIn($page3live->Content); + $this->assertContains('new-url-segment/', $links, 'Assert hyperlink to page 1\'s new url exists on page 3'); + } + + function testPublishingPageWithModifiedUrlRewritesLink() { + // publish page 1 & 3 + $page1 = $this->objFromFixture('Page', 'page1'); + $page3 = $this->objFromFixture('Page', 'page3'); + + $this->assertTrue($page1->doPublish()); + $this->assertTrue($page3->doPublish()); + + // load page 3 from live + $page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID); + + // assert hyperlink to page 1's current url exists + $links = HTTP::getLinksIn($page3live->Content); + $this->assertContains('page1/', $links, 'Assert hyperlink to page 1\'s current url exists on page 3'); + + // rename url of page 1 on stage + $page1->URLSegment = 'new-url-segment'; + $page1->write(); + + // assert hyperlink to page 1's current publish url exists + $page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID); + $links = HTTP::getLinksIn($page3live->Content); + $this->assertContains('page1/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3'); + + + // publish page 1 + $this->assertTrue($page1->doPublish()); + + // assert hyperlink to page 1's new published url exists + $page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID); + $links = HTTP::getLinksIn($page3live->Content); + $this->assertContains('new-url-segment/', $links, 'Assert hyperlink to page 1\'s new published url exists on page 3'); + } + + function testPublishingPageWithModifiedLinksRewritesLinks() { + // publish page 1 & 3 + $page1 = $this->objFromFixture('Page', 'page1'); + $page3 = $this->objFromFixture('Page', 'page3'); + $this->assertTrue($page1->doPublish()); + $this->assertTrue($page3->doPublish()); + + // assert hyperlink to page 1's current url exists + $links = HTTP::getLinksIn($page3->Content); + $this->assertContains('page1/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3'); + + // change page 1 url on draft + $page1->URLSegment = 'new-url-segment'; + + // save page 1 + $page1->write(); + + // assert page 3 on draft contains new page 1 url + $page3 = $this->objFromFixture('Page', 'page3'); + $links = HTTP::getLinksIn($page3->Content); + $this->assertContains('new-url-segment/', $links, 'Assert hyperlink to page 1\'s current draft url exists on page 3'); + + // publish page 3 + $this->assertTrue($page3->doPublish()); + + // assert page 3 on published site contains old page 1 url + $page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID); + $links = HTTP::getLinksIn($page3live->Content); + $this->assertContains('page1/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3'); + + // publish page 1 + $this->assertTrue($page1->doPublish()); + + // assert page 3 on published site contains new page 1 url + $page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID); + $links = HTTP::getLinksIn($page3live->Content); + $this->assertContains('new-url-segment/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3'); + } + + function testLinkTrackingOnExtraContentFields() { + $page1 = $this->objFromFixture('Page', 'page1'); + $page2 = $this->objFromFixture('Page', 'page2'); + $page1->doPublish(); + $page2->doPublish(); + + // assert backlink to page 2 doesn't exist + $this->assertFalse($page1->BackLinkTracking()->containsIDs(array($page2->ID)), 'Assert backlink to page 2 doesn\'t exist'); + + // add hyperlink to page 1 on page 2 + $page2->ExtraContent .= '

Testing page 1 link

'; + $page2->write(); + $page2->doPublish(); + + // assert backlink to page 2 exists + $this->assertTrue($page1->BackLinkTracking()->containsIDs(array($page2->ID)), 'Assert backlink to page 2 exists'); + + // update page1 url + $page1 = $this->objFromFixture('Page', 'page1'); + $page1->URLSegment = "page1-new-url"; + $page1->write(); + + // confirm that draft link on page2 has been rewritten + $page2 = $this->objFromFixture('Page', 'page2'); + $this->assertEquals('

Testing page 1 link

', $page2->ExtraContent); + + // confirm that published link hasn't + $page2Live = Versioned::get_one_by_stage("Page", "Live", "\"SiteTree\".\"ID\" = $page2->ID"); + $this->assertEquals('

Testing page 1 link

', $page2Live->ExtraContent); + + // publish page1 and confirm that the link on the published page2 has now been updated + $page1->doPublish(); + $page2Live = Versioned::get_one_by_stage("Page", "Live", "\"SiteTree\".\"ID\" = $page2->ID"); + $this->assertEquals('

Testing page 1 link

', $page2Live->ExtraContent); + + + // remove hyperlink to page 1 + $page2->ExtraContent = '

No links anymore!

'; + $page2->write(); + + // assert backlink to page 2 no longer exists + $this->assertFalse($page1->BackLinkTracking()->containsIDs(array($page2->ID)), 'Assert backlink to page 2 has been removed'); + } + } +class SiteTreeBacklinksTest_DOD extends DataObjectDecorator implements TestOnly { + function extraStatics() { + return array( + 'db' => array( + 'ExtraContent' => 'HTMLText', + ), + ); + } + + function updateCMSFields(&$fields) { + $fields->addFieldToTab("Root.Content.Main", new HTMLEditorField("ExtraContent")); + } +} ?>