From 4ab8055c29e047879c37da3c08c3739974256d2a Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Thu, 15 Oct 2009 21:46:13 +0000 Subject: [PATCH] #63 - Stable against restructures (from r84861) git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@89155 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- core/model/SiteTree.php | 83 ++++++++++++++- forms/HtmlEditorField.php | 3 +- tests/SiteTreeBacklinksTest.php | 172 ++++++++++++++++++++++++++++++++ tests/SiteTreeBacklinksTest.yml | 16 +++ 4 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 tests/SiteTreeBacklinksTest.php create mode 100644 tests/SiteTreeBacklinksTest.yml diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index b7abcc49d..f0eef5fe8 100755 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -1299,10 +1299,51 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid } DataObject::set_context_obj(null); - + + $this->syncLinkTracking(); + parent::onBeforeWrite(); } + function syncLinkTracking() { + // Set LinkTracking appropriately + $links = HTTP::getLinksIn($this->Content); + $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 { + $this->HasBrokenLink = true; + } + } else if($link == '' || $link[0] == '/') { + $this->HasBrokenLink = true; + } else if($candidateFile = DataObject::get_one("File", "\"Filename\" = '" . Convert::raw2sql(urldecode($link)) . "'", false)) { + $linkedFiles[] = $candidateFile->ID; + } + } + + $images = HTTP::getImagesIn($this->Content); + if($images) { + foreach($images as $image) { + $image = Director::makeRelative($image); + if(substr($image,0,7) == 'assets/') { + $candidateImage = DataObject::get_one("File", "\"Filename\" = '$image'"); + if($candidateImage) $linkedFiles[] = $candidateImage->ID; + else $this->HasBrokenFile = true; + } + } + } + + $this->LinkTracking()->setByIDList($linkedPages); + $this->ImageTracking()->setByIDList($linkedFiles); + } + function onAfterWrite() { // Need to flush cache to avoid outdated versionnumber references $this->flushCache(); @@ -1314,6 +1355,19 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid $page->write(); } + // If the URLSegment has been changed, rewrite links + if($this->isChanged('URLSegment', 2)) { + if($this->hasMethod('BackLinkTracking')) { + $links = $this->BackLinkTracking(); + if($links) { + foreach($links as $link) { + $link->rewriteLink($this->original['URLSegment'] . '/', $this->URLSegment . '/'); + $link->write(); + } + } + } + } + parent::onAfterWrite(); } @@ -1739,12 +1793,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid // Handle activities undertaken by decorators $this->extend('onBeforePublish', $original); - $this->Status = "Published"; //$this->PublishedByID = Member::currentUser()->ID; $this->write(); $this->publish("Stage", "Live"); + if(DB::getConn() instanceof MySQLDatabase) { // Special syntax for MySQL (grr!) // More ANSI-compliant syntax @@ -1768,6 +1822,31 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid $page->copyFrom($page->CopyContentFrom()); $page->doPublish(); } + + // Fix links that are different on staging vs live + $needsWriting = false; + $this->syncLinkTracking(); + foreach($this->LinkTracking() as $linkedPage) { + $livePage = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $linkedPage->ID); + if($livePage && $livePage->URLSegment != $linkedPage->URLSegment) { + + $needsWriting = true; + $this->rewriteLink($linkedPage->URLSegment . '/', $livePage->URLSegment . '/'); + } + } + if($needsWriting) { + $this->writeToStage('Live'); + } + + // If this url has changed, then we need to fix pages linked to this one + 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'); + } + } + // Handle activities undertaken by decorators $this->extend('onAfterPublish', $original); diff --git a/forms/HtmlEditorField.php b/forms/HtmlEditorField.php index 9ed0c2fac..67257f2c5 100755 --- a/forms/HtmlEditorField.php +++ b/forms/HtmlEditorField.php @@ -140,7 +140,8 @@ class HtmlEditorField extends TextareaField { if($record->ID && $record->many_many('ImageTracking') && $tracker = $record->ImageTracking()) { $tracker->removeByFilter(sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d', $this->name, $record->ID)); - + + $fieldName = $this->name; if($linkedFiles) foreach($linkedFiles as $item) { $tracker->add($item, array('FieldName' => $this->name)); } diff --git a/tests/SiteTreeBacklinksTest.php b/tests/SiteTreeBacklinksTest.php new file mode 100644 index 000000000..0f72b0f82 --- /dev/null +++ b/tests/SiteTreeBacklinksTest.php @@ -0,0 +1,172 @@ +objFromFixture('Page', 'page1'); + + // assert backlink to page 2 doesn't exist + $page2 = $this->objFromFixture('Page', 'page2'); + $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->Content .= '

Testing page 1 link

'; + $page2->write(); + + // load page 1 + $page1 = $this->objFromFixture('Page', 'page1'); + + // assert backlink to page 2 exists + $this->assertTrue($page1->BackLinkTracking()->containsIDs(array($page2->ID)), 'Assert backlink to page 2 exists'); + } + + function testRemovingLinkFromPageRemovesBacklink() { + // load page 1 + $page1 = $this->objFromFixture('Page', 'page1'); + + // assert backlink to page 3 exits + $page3 = $this->objFromFixture('Page', 'page3'); + $this->assertTrue($page1->BackLinkTracking()->containsIDs(array($page3->ID)), 'Assert backlink to page 3 exists'); + + // remove hyperlink to page 1 + $page3->Content = '

No links anymore!

'; + $page3->write(); + + // load page 1 + $page1 = $this->objFromFixture('Page', 'page1'); + + // 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'); + $page1->doPublish(); + $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'); + + $page1->doPublish(); + $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 + $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'); + $page1->doPublish(); + $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 + $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 + $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'); + } +} + +?> diff --git a/tests/SiteTreeBacklinksTest.yml b/tests/SiteTreeBacklinksTest.yml new file mode 100644 index 000000000..5dcc5db44 --- /dev/null +++ b/tests/SiteTreeBacklinksTest.yml @@ -0,0 +1,16 @@ +Page: + page1: + Title: page1 + URLSegment: page1 + + page2: + Title: page2 + URLSegment: page2 + + page3: + Title: page3 + URLSegment: page3 + Content:

Testing page 1 link

+ LinkTracking: =>Page.page1 + +