diff --git a/code/Model/SiteTree.php b/code/Model/SiteTree.php index 754899f4f8..18da07a96b 100755 --- a/code/Model/SiteTree.php +++ b/code/Model/SiteTree.php @@ -193,13 +193,17 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi ); private static $has_many = array( - "VirtualPages" => "SilverStripe\\CMS\\Model\\VirtualPage.CopyContentFrom" + "VirtualPages" => VirtualPage::class . '.CopyContentFrom' ); private static $owned_by = array( "VirtualPages" ); + private static $cascade_deletes = [ + 'VirtualPages', + ]; + private static $casting = array( "Breadcrumbs" => "HTMLFragment", "LastEdited" => "Datetime", diff --git a/code/Model/VirtualPage.php b/code/Model/VirtualPage.php index fc47d9299b..2154610ee0 100644 --- a/code/Model/VirtualPage.php +++ b/code/Model/VirtualPage.php @@ -66,7 +66,7 @@ class VirtualPage extends Page ); private static $has_one = array( - "CopyContentFrom" => "SilverStripe\\CMS\\Model\\SiteTree", + "CopyContentFrom" => SiteTree::class, ); private static $owns = array( diff --git a/tests/model/SiteTreeBrokenLinksTest.php b/tests/model/SiteTreeBrokenLinksTest.php index 6e12d46cc0..179b3a92ef 100644 --- a/tests/model/SiteTreeBrokenLinksTest.php +++ b/tests/model/SiteTreeBrokenLinksTest.php @@ -37,6 +37,7 @@ public function tearDown() public function testBrokenLinksBetweenPages() { + /** @var Page $obj */ $obj = $this->objFromFixture('Page', 'content'); $obj->Content = '<a href="[sitetree_link,id=3423423]">this is a broken link</a>'; @@ -50,6 +51,7 @@ public function testBrokenLinksBetweenPages() public function testBrokenAnchorBetweenPages() { + /** @var Page $obj */ $obj = $this->objFromFixture('Page', 'content'); $target = $this->objFromFixture('Page', 'about'); @@ -141,7 +143,6 @@ public function testDeletingMarksBackLinkedPagesAsBroken() $this->assertEquals(0, (int)$linkSrc->HasBrokenLink); // Delete page from draft - $linkDestID = $linkDest->ID; $linkDest->delete(); // Confirm draft has broken link @@ -156,10 +157,12 @@ public function testPublishingSourceBeforeDestHasBrokenLink() $this->logInWithPermission('ADMIN'); // Set up two draft pages with a link from content -> about + /** @var Page $linkDest */ $linkDest = $this->objFromFixture('Page', 'about'); // Ensure that it's not on the published site $linkDest->doUnpublish(); + /** @var Page $linkSrc */ $linkSrc = $this->objFromFixture('Page', 'content'); $linkSrc->Content = "<p><a href=\"[sitetree_link,id=$linkDest->ID]\">about us</a></p>"; $linkSrc->write(); @@ -191,11 +194,6 @@ public function testRestoreFixesBrokenLinks() $p2->write(); $this->assertTrue($p2->publishRecursive()); - // Virtual pages are another - $vp = new VirtualPage(); - $vp->CopyContentFromID = $p->ID; - $vp->write(); - // Redirector links are a third $rp = new RedirectorPage(); $rp->Title = "redirector"; @@ -206,7 +204,6 @@ public function testRestoreFixesBrokenLinks() // Confirm that there are no broken links to begin with $this->assertFalse($p2->HasBrokenLink); - $this->assertFalse($vp->HasBrokenLink); $this->assertFalse($rp->HasBrokenLink); // Unpublishing doesn't affect broken state on live (draft is source of truth) @@ -218,14 +215,11 @@ public function testRestoreFixesBrokenLinks() // Delete the source page, confirm that the VP, RP and page 2 have broken links on draft $p->delete(); - $vp->flushCache(); - $vp = DataObject::get_by_id(SiteTree::class, $vp->ID); $p2->flushCache(); $p2 = DataObject::get_by_id(SiteTree::class, $p2->ID); $rp->flushCache(); $rp = DataObject::get_by_id(SiteTree::class, $rp->ID); $this->assertEquals(1, $p2->HasBrokenLink); - $this->assertEquals(1, $vp->HasBrokenLink); $this->assertEquals(1, $rp->HasBrokenLink); // Restore the page to stage, confirm that this fixes the links @@ -235,12 +229,9 @@ public function testRestoreFixesBrokenLinks() $p2->flushCache(); $p2 = DataObject::get_by_id(SiteTree::class, $p2->ID); - $vp->flushCache(); - $vp = DataObject::get_by_id(SiteTree::class, $vp->ID); $rp->flushCache(); $rp = DataObject::get_by_id(SiteTree::class, $rp->ID); $this->assertFalse((bool)$p2->HasBrokenLink); - $this->assertFalse((bool)$vp->HasBrokenLink); $this->assertFalse((bool)$rp->HasBrokenLink); // Publish and confirm that the p2 and RP broken links are fixed on published @@ -254,68 +245,57 @@ public function testRestoreFixesBrokenLinks() public function testRevertToLiveFixesBrokenLinks() { // Create page and virutal page - $p = new Page(); - $p->Title = "source"; - $p->write(); - $pageID = $p->ID; - $this->assertTrue($p->publishRecursive()); + $page = new Page(); + $page->Title = "source"; + $page->write(); + $pageID = $page->ID; + $this->assertTrue($page->publishRecursive()); // Content links are one kind of link to pages - $p2 = new Page(); - $p2->Title = "regular link"; - $p2->Content = "<a href=\"[sitetree_link,id=$p->ID]\">test</a>"; - $p2->write(); - $this->assertTrue($p2->publishRecursive()); - - // Virtual pages are another - $vp = new VirtualPage(); - $vp->CopyContentFromID = $p->ID; - $vp->write(); + $page2 = new Page(); + $page2->Title = "regular link"; + $page2->Content = "<a href=\"[sitetree_link,id={$pageID}]\">test</a>"; + $page2->write(); + $this->assertTrue($page2->publishRecursive()); // Redirector links are a third - $rp = new RedirectorPage(); - $rp->Title = "redirector"; - $rp->LinkType = 'Internal'; - $rp->LinkToID = $p->ID; - $rp->write(); - $this->assertTrue($rp->publishRecursive()); + $redirectorPage = new RedirectorPage(); + $redirectorPage->Title = "redirector"; + $redirectorPage->LinkType = 'Internal'; + $redirectorPage->LinkToID = $page->ID; + $redirectorPage->write(); + $this->assertTrue($redirectorPage->publishRecursive()); // Confirm that there are no broken links to begin with - $this->assertFalse($p2->HasBrokenLink); - $this->assertFalse($vp->HasBrokenLink); - $this->assertFalse($rp->HasBrokenLink); + $this->assertFalse($page2->HasBrokenLink); + $this->assertFalse($redirectorPage->HasBrokenLink); // Delete from draft and confirm that broken links are marked - $pID = $p->ID; - $p->delete(); + $page->delete(); - $vp->flushCache(); - $vp = DataObject::get_by_id(SiteTree::class, $vp->ID); - $p2->flushCache(); - $p2 = DataObject::get_by_id(SiteTree::class, $p2->ID); - $rp->flushCache(); - $rp = DataObject::get_by_id(SiteTree::class, $rp->ID); - $this->assertEquals(1, $p2->HasBrokenLink); - $this->assertEquals(1, $vp->HasBrokenLink); - $this->assertEquals(1, $rp->HasBrokenLink); + $page2->flushCache(); + $page2 = DataObject::get_by_id(SiteTree::class, $page2->ID); + $redirectorPage->flushCache(); + $redirectorPage = DataObject::get_by_id(SiteTree::class, $redirectorPage->ID); + $this->assertEquals(1, $page2->HasBrokenLink); + $this->assertEquals(1, $redirectorPage->HasBrokenLink); // Call doRevertToLive and confirm that broken links are restored - $pLive = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $pID); - $pLive->doRevertToLive(); - - $p2->flushCache(); - $p2 = DataObject::get_by_id(SiteTree::class, $p2->ID); - $vp->flushCache(); - $vp = DataObject::get_by_id(SiteTree::class, $vp->ID); - $rp->flushCache(); - $rp = DataObject::get_by_id(SiteTree::class, $rp->ID); - $this->assertFalse((bool)$p2->HasBrokenLink); - $this->assertFalse((bool)$vp->HasBrokenLink); - $this->assertFalse((bool)$rp->HasBrokenLink); + /** @var Page $pageLive */ + $pageLive = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $pageID); + $pageLive->doRevertToLive(); + + $page2->flushCache(); + $page2 = DataObject::get_by_id(SiteTree::class, $page2->ID); + $redirectorPage->flushCache(); + $redirectorPage = DataObject::get_by_id(SiteTree::class, $redirectorPage->ID); + $this->assertFalse((bool)$page2->HasBrokenLink); + $this->assertFalse((bool)$redirectorPage->HasBrokenLink); } public function testBrokenAnchorLinksInAPage() { + /** @var Page $obj */ $obj = $this->objFromFixture('Page', 'content'); $origContent = $obj->Content; diff --git a/tests/model/VirtualPageTest.php b/tests/model/VirtualPageTest.php index 300d0d2879..9b7c325301 100644 --- a/tests/model/VirtualPageTest.php +++ b/tests/model/VirtualPageTest.php @@ -1,18 +1,16 @@ <?php -use SilverStripe\ORM\DataObject; -use SilverStripe\Versioned\Versioned; -use SilverStripe\ORM\DB; -use SilverStripe\ORM\ValidationException; -use SilverStripe\ORM\FieldType\DBVarchar; -use SilverStripe\ORM\DataExtension; -use SilverStripe\CMS\Model\VirtualPage; -use SilverStripe\CMS\Model\SiteTree; -use SilverStripe\CMS\Model\RedirectorPage; use SilverStripe\CMS\Controllers\ModelAsController; +use SilverStripe\CMS\Model\RedirectorPage; +use SilverStripe\CMS\Model\SiteTree; +use SilverStripe\CMS\Model\VirtualPage; use SilverStripe\Core\Config\Config; use SilverStripe\Dev\FunctionalTest; -use SilverStripe\Dev\TestOnly; +use SilverStripe\ORM\DataObject; +use SilverStripe\ORM\DB; +use SilverStripe\ORM\ValidationException; +use SilverStripe\Security\Member; +use SilverStripe\Versioned\Versioned; class VirtualPageTest extends FunctionalTest { @@ -200,51 +198,12 @@ public function testCantPublishVirtualPagesBeforeTheirSource() $this->assertTrue($vp->canPublish()); } - public function testCanDeleteOrphanedVirtualPagesFromLive() - { - // An unpublished source page - $p = new Page(); - $p->Content = "test content"; - $p->write(); - $p->publishRecursive(); - $pID = $p->ID; - - $vp = new VirtualPage(); - $vp->CopyContentFromID = $p->ID; - $vp->write(); - $this->assertTrue($vp->canPublish()); - $this->assertTrue($vp->publishRecursive()); - - // Delete the source page semi-manually, without triggering - // the cascade publish back to the virtual page. - Versioned::set_stage(Versioned::LIVE); - $livePage = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)->byID($pID); - $livePage->delete(); - Versioned::set_stage(Versioned::DRAFT); - - // Confirm that we can unpublish, but not publish - $this->assertFalse($p->IsPublished(), 'Copied page has orphaned the virtual page on live'); - $this->assertTrue($vp->isPublished(), 'Virtual page remains on live'); - $this->assertTrue($vp->canUnpublish()); - $this->assertFalse($vp->canPublish()); - - // Confirm that the action really works - $this->assertTrue($vp->doUnpublish()); - $this->assertEquals( - 0, - DB::prepared_query( - "SELECT count(*) FROM \"SiteTree_Live\" WHERE \"ID\" = ?", - array($vp->ID) - )->value() - ); - } - public function testCanEdit() { $parentPage = $this->objFromFixture('Page', 'master3'); $virtualPage = $this->objFromFixture(VirtualPage::class, 'vp3'); - $bob = $this->objFromFixture('SilverStripe\\Security\\Member', 'bob'); - $andrew = $this->objFromFixture('SilverStripe\\Security\\Member', 'andrew'); + $bob = $this->objFromFixture(Member::class, 'bob'); + $andrew = $this->objFromFixture(Member::class, 'andrew'); // Bob can edit the mirrored page, but he shouldn't be able to edit the virtual page. $this->logInAs($bob); @@ -259,12 +218,13 @@ public function testCanEdit() public function testCanView() { + /** @var Page $parentPage */ $parentPage = $this->objFromFixture('Page', 'master3'); $parentPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); $virtualPage = $this->objFromFixture(VirtualPage::class, 'vp3'); $virtualPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); - $cindy = $this->objFromFixture('SilverStripe\\Security\\Member', 'cindy'); - $alice = $this->objFromFixture('SilverStripe\\Security\\Member', 'alice'); + $cindy = $this->objFromFixture(Member::class, 'cindy'); + $alice = $this->objFromFixture(Member::class, 'alice'); // Cindy can see both pages $this->logInAs($cindy); @@ -336,6 +296,7 @@ public function testUnpublishingSourcePageOfAVirtualPageAlsoUnpublishesVirtualPa $vp = new VirtualPage(); $vp->CopyContentFromID = $p->ID; $vp->write(); + $vpID = $vp->ID; $this->assertTrue($vp->publishRecursive()); // All is fine, the virtual page doesn't have a broken link @@ -346,17 +307,17 @@ public function testUnpublishingSourcePageOfAVirtualPageAlsoUnpublishesVirtualPa // The draft VP still has the CopyContentFromID link $vp->flushCache(); - $vp = DataObject::get_by_id(SiteTree::class, $vp->ID); + $vp = DataObject::get_by_id(SiteTree::class, $vpID); $this->assertEquals($p->ID, $vp->CopyContentFromID); - $vpLive = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, '"SiteTree"."ID" = ' . $vp->ID); + $vpLive = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, '"SiteTree"."ID" = ' . $vpID); $this->assertNull($vpLive); - // Delete from draft, confirm that the virtual page has a broken link on the draft site + // Delete from draft, ensure virtual page deletion cascades $p->delete(); $vp->flushCache(); - $vp = DataObject::get_by_id(SiteTree::class, $vp->ID); - $this->assertEquals(1, $vp->HasBrokenLink); + $vp = DataObject::get_by_id(SiteTree::class, $vpID); + $this->assertNull($vp); } public function testDeletingFromLiveSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage() @@ -369,29 +330,27 @@ public function testDeletingFromLiveSourcePageOfAVirtualPageAlsoUnpublishesVirtu $vp = new VirtualPage(); $vp->CopyContentFromID = $p->ID; $vp->write(); + $vpID = $vp->ID; $this->assertTrue($vp->publishRecursive()); // All is fine, the virtual page doesn't have a broken link $this->assertFalse($vp->HasBrokenLink); - // Delete the source page from draft, confirm that this creates a broken link + // Delete the source page from draft, cascades to virtual page $pID = $p->ID; $p->delete(); $vp->flushCache(); - $vp = DataObject::get_by_id(SiteTree::class, $vp->ID); - $this->assertEquals(1, $vp->HasBrokenLink); + $vpDraft = Versioned::get_by_stage(SiteTree::class, Versioned::DRAFT) + ->byID($pID); + $this->assertNull($vpDraft); // Delete the source page form live, confirm that the virtual page has also been unpublished - $pLive = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, '"SiteTree"."ID" = ' . $pID); + $pLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE) + ->byID($pID); $this->assertTrue($pLive->doUnpublish()); - $vpLive = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, '"SiteTree"."ID" = ' . $vp->ID); + $vpLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE) + ->byID($vpID); $this->assertNull($vpLive); - - // Delete from draft, confirm that the virtual page has a broken link on the draft site - $pLive->delete(); - $vp->flushCache(); - $vp = DataObject::get_by_id(SiteTree::class, $vp->ID); - $this->assertEquals(1, $vp->HasBrokenLink); } /**