2011-03-18 04:27:06 +01:00
|
|
|
<?php
|
|
|
|
|
2017-08-09 04:53:38 +02:00
|
|
|
namespace SilverStripe\CMS\Tests\Model;
|
2017-08-09 03:25:12 +02:00
|
|
|
|
|
|
|
use Page;
|
2016-08-10 06:08:39 +02:00
|
|
|
use SilverStripe\CMS\Controllers\ModelAsController;
|
2017-08-09 00:56:08 +02:00
|
|
|
use SilverStripe\CMS\Model\RedirectorPage;
|
|
|
|
use SilverStripe\CMS\Model\SiteTree;
|
|
|
|
use SilverStripe\CMS\Model\VirtualPage;
|
2021-11-04 02:59:14 +01:00
|
|
|
use SilverStripe\Control\ContentNegotiator;
|
2016-08-23 04:36:06 +02:00
|
|
|
use SilverStripe\Core\Config\Config;
|
|
|
|
use SilverStripe\Dev\FunctionalTest;
|
2017-08-09 00:56:08 +02:00
|
|
|
use SilverStripe\ORM\DataObject;
|
|
|
|
use SilverStripe\ORM\ValidationException;
|
2017-08-09 04:53:38 +02:00
|
|
|
use SilverStripe\Security\Member;
|
2018-06-20 00:17:38 +02:00
|
|
|
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
|
2017-08-09 00:56:08 +02:00
|
|
|
use SilverStripe\Versioned\Versioned;
|
2018-06-20 00:17:38 +02:00
|
|
|
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
|
2016-08-23 04:36:06 +02:00
|
|
|
|
2017-01-25 21:59:25 +01:00
|
|
|
class VirtualPageTest extends FunctionalTest
|
|
|
|
{
|
|
|
|
protected static $fixture_file = 'VirtualPageTest.yml';
|
2018-03-21 05:44:24 +01:00
|
|
|
|
2017-01-25 21:59:25 +01:00
|
|
|
protected $autoFollowRedirection = false;
|
|
|
|
|
2017-08-09 04:53:38 +02:00
|
|
|
protected static $extra_dataobjects = [
|
|
|
|
VirtualPageTest_ClassA::class,
|
|
|
|
VirtualPageTest_ClassB::class,
|
|
|
|
VirtualPageTest_ClassC::class,
|
|
|
|
VirtualPageTest_NotRoot::class,
|
|
|
|
VirtualPageTest_PageExtension::class,
|
|
|
|
VirtualPageTest_PageWithAllowedChildren::class,
|
|
|
|
VirtualPageTest_TestDBField::class,
|
|
|
|
VirtualPageTest_VirtualPageSub::class,
|
|
|
|
];
|
2017-01-25 21:59:25 +01:00
|
|
|
|
2018-06-20 00:17:38 +02:00
|
|
|
protected static $illegal_extensions = [
|
2017-08-09 04:53:38 +02:00
|
|
|
SiteTree::class => [
|
2018-06-20 00:17:38 +02:00
|
|
|
SiteTreeSubsites::class,
|
|
|
|
FluentSiteTreeExtension::class,
|
2017-08-09 04:53:38 +02:00
|
|
|
],
|
2018-06-20 00:17:38 +02:00
|
|
|
];
|
2017-01-25 21:59:25 +01:00
|
|
|
|
2018-06-20 00:17:38 +02:00
|
|
|
protected static $required_extensions = [
|
2017-08-09 04:53:38 +02:00
|
|
|
SiteTree::class => [
|
|
|
|
VirtualPageTest_PageExtension::class
|
|
|
|
]
|
2018-06-20 00:17:38 +02:00
|
|
|
];
|
2017-01-25 21:59:25 +01:00
|
|
|
|
2021-10-27 23:40:52 +02:00
|
|
|
protected function setUp(): void
|
2017-01-25 21:59:25 +01:00
|
|
|
{
|
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
// Ensure we always have permission to save/publish
|
|
|
|
$this->logInWithPermission("ADMIN");
|
|
|
|
|
|
|
|
// Add extra fields
|
2018-06-20 00:17:38 +02:00
|
|
|
Config::modify()->merge(VirtualPage::class, 'initially_copied_fields', ['MyInitiallyCopiedField']);
|
2017-08-09 04:53:38 +02:00
|
|
|
Config::modify()->merge(
|
|
|
|
VirtualPage::class,
|
|
|
|
'non_virtual_fields',
|
2018-06-20 00:17:38 +02:00
|
|
|
['MyNonVirtualField', 'MySharedNonVirtualField']
|
2017-08-09 04:53:38 +02:00
|
|
|
);
|
2018-03-21 05:44:24 +01:00
|
|
|
|
|
|
|
// Ensure all pages are published
|
|
|
|
/** @var Page $page */
|
|
|
|
foreach (Page::get() as $page) {
|
|
|
|
$page->publishSingle();
|
|
|
|
}
|
2017-01-25 21:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that, after you update the source page of a virtual page, all the virtual pages
|
|
|
|
* are updated
|
|
|
|
*/
|
|
|
|
public function testEditingSourcePageUpdatesVirtualPages()
|
|
|
|
{
|
2018-03-21 05:44:24 +01:00
|
|
|
/** @var Page $master */
|
2017-01-25 21:59:25 +01:00
|
|
|
$master = $this->objFromFixture('Page', 'master');
|
|
|
|
$master->Title = "New title";
|
|
|
|
$master->MenuTitle = "New menutitle";
|
|
|
|
$master->Content = "<p>New content</p>";
|
|
|
|
$master->write();
|
2018-03-21 05:44:24 +01:00
|
|
|
$master->publishSingle();
|
2017-01-25 21:59:25 +01:00
|
|
|
|
2017-07-17 07:19:00 +02:00
|
|
|
$vp1 = $this->objFromFixture(VirtualPage::class, 'vp1');
|
|
|
|
$vp2 = $this->objFromFixture(VirtualPage::class, 'vp2');
|
2017-01-25 21:59:25 +01:00
|
|
|
|
|
|
|
$this->assertEquals("New title", $vp1->Title);
|
|
|
|
$this->assertEquals("New title", $vp2->Title);
|
|
|
|
$this->assertEquals("New menutitle", $vp1->MenuTitle);
|
|
|
|
$this->assertEquals("New menutitle", $vp2->MenuTitle);
|
|
|
|
$this->assertEquals("<p>New content</p>", $vp1->Content);
|
|
|
|
$this->assertEquals("<p>New content</p>", $vp2->Content);
|
|
|
|
}
|
|
|
|
|
2021-11-04 02:59:14 +01:00
|
|
|
public function testMetaTags()
|
|
|
|
{
|
|
|
|
$this->logInWithPermission('ADMIN');
|
|
|
|
$master = $this->objFromFixture('Page', 'master');
|
|
|
|
$vp1 = $this->objFromFixture(VirtualPage::class, 'vp1');
|
|
|
|
|
|
|
|
// Test with title
|
|
|
|
$meta = $vp1->MetaTags();
|
|
|
|
$charset = Config::inst()->get(ContentNegotiator::class, 'encoding');
|
2021-11-18 21:52:29 +01:00
|
|
|
$this->assertStringContainsString('<meta http-equiv="Content-Type" content="text/html; charset='.$charset.'"', $meta);
|
|
|
|
$this->assertStringContainsString('<link rel="canonical" href="'.$master->AbsoluteLink().'"', $meta);
|
|
|
|
$this->assertStringContainsString('<meta name="x-page-id" content="'.$vp1->ID.'"', $meta);
|
|
|
|
$this->assertStringContainsString('<meta name="x-cms-edit-link" content="'.$vp1->CMSEditLink().'"', $meta);
|
|
|
|
$this->assertStringContainsString('<title>'.$master->Title.'</title>', $meta);
|
2021-11-04 02:59:14 +01:00
|
|
|
}
|
|
|
|
|
2017-01-25 21:59:25 +01:00
|
|
|
/**
|
|
|
|
* Test that, after you publish the source page of a virtual page, all the already published
|
|
|
|
* virtual pages are published
|
|
|
|
*/
|
|
|
|
public function testPublishingSourcePagePublishesAlreadyPublishedVirtualPages()
|
|
|
|
{
|
|
|
|
$this->logInWithPermission('ADMIN');
|
|
|
|
|
2017-08-09 04:53:38 +02:00
|
|
|
/** @var Page $master */
|
2017-01-25 21:59:25 +01:00
|
|
|
$master = $this->objFromFixture('Page', 'master');
|
|
|
|
$master->publishRecursive();
|
|
|
|
|
|
|
|
$master->Title = "New title";
|
|
|
|
$master->MenuTitle = "New menutitle";
|
|
|
|
$master->Content = "<p>New content</p>";
|
|
|
|
$master->write();
|
|
|
|
|
2017-08-09 04:53:38 +02:00
|
|
|
/** @var VirtualPage $vp1 */
|
2017-07-17 07:19:00 +02:00
|
|
|
$vp1 = DataObject::get_by_id(VirtualPage::class, $this->idFromFixture(VirtualPage::class, 'vp1'));
|
2017-08-09 04:53:38 +02:00
|
|
|
/** @var VirtualPage $vp2 */
|
2017-07-17 07:19:00 +02:00
|
|
|
$vp2 = DataObject::get_by_id(VirtualPage::class, $this->idFromFixture(VirtualPage::class, 'vp2'));
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertTrue($vp1->publishRecursive());
|
|
|
|
$this->assertTrue($vp2->publishRecursive());
|
|
|
|
|
|
|
|
$master->publishRecursive();
|
|
|
|
|
|
|
|
Versioned::set_stage(Versioned::LIVE);
|
2017-07-17 07:19:00 +02:00
|
|
|
$vp1 = DataObject::get_by_id(VirtualPage::class, $this->idFromFixture(VirtualPage::class, 'vp1'));
|
|
|
|
$vp2 = DataObject::get_by_id(VirtualPage::class, $this->idFromFixture(VirtualPage::class, 'vp2'));
|
2017-01-25 21:59:25 +01:00
|
|
|
|
|
|
|
$this->assertNotNull($vp1);
|
|
|
|
$this->assertNotNull($vp2);
|
|
|
|
|
|
|
|
$this->assertEquals("New title", $vp1->Title);
|
|
|
|
$this->assertEquals("New title", $vp2->Title);
|
|
|
|
$this->assertEquals("New menutitle", $vp1->MenuTitle);
|
|
|
|
$this->assertEquals("New menutitle", $vp2->MenuTitle);
|
|
|
|
$this->assertEquals("<p>New content</p>", $vp1->Content);
|
|
|
|
$this->assertEquals("<p>New content</p>", $vp2->Content);
|
|
|
|
Versioned::set_stage(Versioned::DRAFT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that virtual pages get the content from the master page when they are created.
|
|
|
|
*/
|
|
|
|
public function testNewVirtualPagesGrabTheContentFromTheirMaster()
|
|
|
|
{
|
|
|
|
$vp = new VirtualPage();
|
|
|
|
$vp->write();
|
|
|
|
|
|
|
|
$vp->CopyContentFromID = $this->idFromFixture('Page', 'master');
|
|
|
|
$vp->write();
|
|
|
|
|
|
|
|
$this->assertEquals("My Page", $vp->Title);
|
|
|
|
$this->assertEquals("My Page Nav", $vp->MenuTitle);
|
|
|
|
|
|
|
|
$vp->CopyContentFromID = $this->idFromFixture('Page', 'master2');
|
|
|
|
$vp->write();
|
|
|
|
|
|
|
|
$this->assertEquals("My Other Page", $vp->Title);
|
|
|
|
$this->assertEquals("My Other Page Nav", $vp->MenuTitle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Virtual pages are always supposed to chose the same content as the published source page.
|
|
|
|
* This means that when you publish them, they should show the published content of the source
|
|
|
|
* page, not the draft content at the time when you clicked 'publish' in the CMS.
|
|
|
|
*/
|
|
|
|
public function testPublishingAVirtualPageCopiedPublishedContentNotDraftContent()
|
|
|
|
{
|
|
|
|
$p = new Page();
|
|
|
|
$p->Content = "published content";
|
|
|
|
$p->write();
|
|
|
|
$p->publishRecursive();
|
|
|
|
|
|
|
|
// Virtual page has this content
|
|
|
|
$vp = new VirtualPage();
|
|
|
|
$vp->CopyContentFromID = $p->ID;
|
|
|
|
$vp->write();
|
|
|
|
|
|
|
|
$vp->publishRecursive();
|
|
|
|
|
|
|
|
// Don't publish this change - published page will still say 'published content'
|
|
|
|
$p->Content = "draft content";
|
|
|
|
$p->write();
|
|
|
|
|
|
|
|
// The draft content of the virtual page should say 'draft content'
|
|
|
|
/** @var VirtualPage $vpDraft */
|
2017-03-21 05:26:46 +01:00
|
|
|
$vpDraft = Versioned::get_by_stage(VirtualPage::class, Versioned::DRAFT)->byID($vp->ID);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertEquals('draft content', $vpDraft->CopyContentFrom()->Content);
|
|
|
|
$this->assertEquals('draft content', $vpDraft->Content);
|
|
|
|
|
|
|
|
// The published content of the virtual page should say 'published content'
|
|
|
|
/** @var VirtualPage $vpLive */
|
2017-03-21 05:26:46 +01:00
|
|
|
$vpLive = Versioned::get_by_stage(VirtualPage::class, Versioned::LIVE)->byID($vp->ID);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertEquals('published content', $vpLive->CopyContentFrom()->Content);
|
|
|
|
$this->assertEquals('published content', $vpLive->Content);
|
|
|
|
|
|
|
|
// Publishing the virtualpage should, however, trigger publishing of the live page
|
|
|
|
$vpDraft->publishRecursive();
|
|
|
|
|
|
|
|
// Everything is published live
|
2017-03-21 05:26:46 +01:00
|
|
|
$vpLive = Versioned::get_by_stage(VirtualPage::class, Versioned::LIVE)->byID($vp->ID);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertEquals('draft content', $vpLive->CopyContentFrom()->Content);
|
|
|
|
$this->assertEquals('draft content', $vpLive->Content);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testCantPublishVirtualPagesBeforeTheirSource()
|
|
|
|
{
|
|
|
|
// An unpublished source page
|
|
|
|
$p = new Page();
|
|
|
|
$p->Content = "test content";
|
|
|
|
$p->write();
|
|
|
|
|
|
|
|
// With no source page, we can't publish
|
|
|
|
$vp = new VirtualPage();
|
|
|
|
$vp->write();
|
|
|
|
$this->assertFalse($vp->canPublish());
|
|
|
|
|
|
|
|
// When the source page isn't published, we can't publish
|
|
|
|
$vp->CopyContentFromID = $p->ID;
|
|
|
|
$vp->write();
|
|
|
|
$this->assertFalse($vp->canPublish());
|
|
|
|
|
|
|
|
// Once the source page gets published, then we can publish
|
|
|
|
$p->publishRecursive();
|
|
|
|
$this->assertTrue($vp->canPublish());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testCanEdit()
|
|
|
|
{
|
|
|
|
$parentPage = $this->objFromFixture('Page', 'master3');
|
2017-07-17 07:19:00 +02:00
|
|
|
$virtualPage = $this->objFromFixture(VirtualPage::class, 'vp3');
|
2017-08-09 00:56:08 +02:00
|
|
|
$bob = $this->objFromFixture(Member::class, 'bob');
|
|
|
|
$andrew = $this->objFromFixture(Member::class, 'andrew');
|
2017-01-25 21:59:25 +01:00
|
|
|
|
|
|
|
// Bob can edit the mirrored page, but he shouldn't be able to edit the virtual page.
|
|
|
|
$this->logInAs($bob);
|
|
|
|
$this->assertTrue($parentPage->canEdit());
|
|
|
|
$this->assertFalse($virtualPage->canEdit());
|
|
|
|
|
|
|
|
// Andrew can only edit the virtual page, but not the original.
|
|
|
|
$this->logInAs($andrew);
|
|
|
|
$this->assertFalse($parentPage->canEdit());
|
|
|
|
$this->assertTrue($virtualPage->canEdit());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testCanView()
|
|
|
|
{
|
2017-08-09 00:56:08 +02:00
|
|
|
/** @var Page $parentPage */
|
2017-01-25 21:59:25 +01:00
|
|
|
$parentPage = $this->objFromFixture('Page', 'master3');
|
|
|
|
$parentPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
2018-03-14 04:34:46 +01:00
|
|
|
|
2017-08-09 04:53:38 +02:00
|
|
|
/** @var VirtualPage $virtualPage */
|
2017-07-17 07:19:00 +02:00
|
|
|
$virtualPage = $this->objFromFixture(VirtualPage::class, 'vp3');
|
2017-01-25 21:59:25 +01:00
|
|
|
$virtualPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
2017-08-09 00:56:08 +02:00
|
|
|
$cindy = $this->objFromFixture(Member::class, 'cindy');
|
|
|
|
$alice = $this->objFromFixture(Member::class, 'alice');
|
2017-01-25 21:59:25 +01:00
|
|
|
|
|
|
|
// Cindy can see both pages
|
|
|
|
$this->logInAs($cindy);
|
|
|
|
$this->assertTrue($parentPage->canView());
|
|
|
|
$this->assertTrue($virtualPage->canView());
|
|
|
|
|
|
|
|
// Alice can't see the virtual page, since it's restricted to cindy
|
|
|
|
$this->logInAs($alice);
|
|
|
|
$this->assertTrue($parentPage->canView());
|
|
|
|
$this->assertFalse($virtualPage->canView());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testVirtualPagesArentInappropriatelyPublished()
|
|
|
|
{
|
|
|
|
// Fixture
|
|
|
|
$p = new Page();
|
|
|
|
$p->Content = "test content";
|
|
|
|
$p->write();
|
|
|
|
$vp = new VirtualPage();
|
|
|
|
$vp->CopyContentFromID = $p->ID;
|
|
|
|
$vp->write();
|
|
|
|
|
|
|
|
// VP is oragne
|
|
|
|
$this->assertTrue($vp->isOnDraftOnly());
|
|
|
|
|
|
|
|
// VP is still orange after we publish
|
|
|
|
$p->publishRecursive();
|
|
|
|
$this->assertTrue($vp->isOnDraftOnly());
|
|
|
|
|
|
|
|
// A new VP created after P's initial construction
|
|
|
|
$vp2 = new VirtualPage();
|
|
|
|
$vp2->CopyContentFromID = $p->ID;
|
|
|
|
$vp2->write();
|
|
|
|
$this->assertTrue($vp2->isOnDraftOnly());
|
|
|
|
|
|
|
|
// Also remains orange after a republish
|
|
|
|
$p->Content = "new content";
|
|
|
|
$p->write();
|
|
|
|
$p->publishRecursive();
|
|
|
|
$this->assertTrue($vp2->isOnDraftOnly());
|
|
|
|
|
|
|
|
// VP is now published
|
|
|
|
$vp->publishRecursive();
|
|
|
|
|
|
|
|
$this->assertTrue($vp->isPublished());
|
|
|
|
$this->assertFalse($vp->isModifiedOnDraft());
|
|
|
|
|
|
|
|
// P edited, P goes green. Change set interface should indicate to the user that the owned page has
|
|
|
|
// modifications, although the virtual page record itself will not appear as having pending changes.
|
|
|
|
$p->Content = "third content";
|
|
|
|
$p->write();
|
|
|
|
|
|
|
|
$this->assertTrue($p->isModifiedOnDraft());
|
|
|
|
$this->assertFalse($vp->isModifiedOnDraft());
|
|
|
|
|
|
|
|
// Publish, VP goes black
|
|
|
|
$p->publishRecursive();
|
|
|
|
$this->assertTrue($vp->isPublished());
|
|
|
|
$this->assertFalse($vp->isModifiedOnDraft());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testUnpublishingSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage()
|
|
|
|
{
|
|
|
|
// Create page and virutal page
|
|
|
|
$p = new Page();
|
|
|
|
$p->Title = "source";
|
|
|
|
$p->write();
|
|
|
|
$this->assertTrue($p->publishRecursive());
|
|
|
|
$vp = new VirtualPage();
|
|
|
|
$vp->CopyContentFromID = $p->ID;
|
|
|
|
$vp->write();
|
2017-08-09 00:56:08 +02:00
|
|
|
$vpID = $vp->ID;
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertTrue($vp->publishRecursive());
|
|
|
|
|
|
|
|
// All is fine, the virtual page doesn't have a broken link
|
|
|
|
$this->assertFalse($vp->HasBrokenLink);
|
|
|
|
|
|
|
|
// Unpublish the source page, confirm that the virtual page has also been unpublished
|
|
|
|
$p->doUnpublish();
|
|
|
|
|
|
|
|
// The draft VP still has the CopyContentFromID link
|
|
|
|
$vp->flushCache();
|
2017-08-09 00:56:08 +02:00
|
|
|
$vp = DataObject::get_by_id(SiteTree::class, $vpID);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertEquals($p->ID, $vp->CopyContentFromID);
|
2017-08-09 04:53:38 +02:00
|
|
|
$vpLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)->byID($vpID);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertNull($vpLive);
|
2017-08-09 00:56:08 +02:00
|
|
|
// Delete from draft, ensure virtual page deletion cascades
|
2017-01-25 21:59:25 +01:00
|
|
|
$p->delete();
|
|
|
|
$vp->flushCache();
|
2017-08-09 00:56:08 +02:00
|
|
|
$vp = DataObject::get_by_id(SiteTree::class, $vpID);
|
|
|
|
$this->assertNull($vp);
|
2017-01-25 21:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function testDeletingFromLiveSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage()
|
|
|
|
{
|
|
|
|
// Create page and virutal page
|
|
|
|
$p = new Page();
|
|
|
|
$p->Title = "source";
|
|
|
|
$p->write();
|
|
|
|
$this->assertTrue($p->publishRecursive());
|
|
|
|
$vp = new VirtualPage();
|
|
|
|
$vp->CopyContentFromID = $p->ID;
|
|
|
|
$vp->write();
|
2017-08-09 00:56:08 +02:00
|
|
|
$vpID = $vp->ID;
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertTrue($vp->publishRecursive());
|
|
|
|
|
|
|
|
// All is fine, the virtual page doesn't have a broken link
|
|
|
|
$this->assertFalse($vp->HasBrokenLink);
|
2017-08-09 00:56:08 +02:00
|
|
|
// Delete the source page from draft, cascades to virtual page
|
2017-01-25 21:59:25 +01:00
|
|
|
$pID = $p->ID;
|
|
|
|
$p->delete();
|
|
|
|
$vp->flushCache();
|
2017-08-09 00:56:08 +02:00
|
|
|
$vpDraft = Versioned::get_by_stage(SiteTree::class, Versioned::DRAFT)
|
|
|
|
->byID($pID);
|
|
|
|
$this->assertNull($vpDraft);
|
2017-01-25 21:59:25 +01:00
|
|
|
// Delete the source page form live, confirm that the virtual page has also been unpublished
|
2017-08-09 04:53:38 +02:00
|
|
|
/** @var Page $pLive */
|
2017-08-09 00:56:08 +02:00
|
|
|
$pLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)
|
|
|
|
->byID($pID);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertTrue($pLive->doUnpublish());
|
2017-08-09 00:56:08 +02:00
|
|
|
$vpLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)
|
|
|
|
->byID($vpID);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertNull($vpLive);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base functionality tested in {@link SiteTreeTest->testAllowedChildrenValidation()}.
|
|
|
|
*/
|
|
|
|
public function testAllowedChildrenLimitedOnVirtualPages()
|
|
|
|
{
|
|
|
|
$classA = new SiteTreeTest_ClassA();
|
|
|
|
$classA->write();
|
|
|
|
$classB = new SiteTreeTest_ClassB();
|
|
|
|
$classB->write();
|
|
|
|
$classBVirtual = new VirtualPage();
|
|
|
|
$classBVirtual->CopyContentFromID = $classB->ID;
|
|
|
|
$classBVirtual->write();
|
|
|
|
$classC = new SiteTreeTest_ClassC();
|
|
|
|
$classC->write();
|
|
|
|
$classCVirtual = new VirtualPage();
|
|
|
|
$classCVirtual->CopyContentFromID = $classC->ID;
|
|
|
|
$classCVirtual->write();
|
|
|
|
|
|
|
|
$classBVirtual->ParentID = $classA->ID;
|
|
|
|
$valid = $classBVirtual->doValidate();
|
|
|
|
$this->assertTrue($valid->isValid(), "Does allow child linked to virtual page type allowed by parent");
|
|
|
|
|
|
|
|
$classCVirtual->ParentID = $classA->ID;
|
|
|
|
$valid = $classCVirtual->doValidate();
|
|
|
|
$this->assertFalse($valid->isValid(), "Doesn't allow child linked to virtual page type disallowed by parent");
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testGetVirtualFields()
|
|
|
|
{
|
|
|
|
// Needs association with an original, otherwise will just return the "base" virtual fields
|
|
|
|
$page = new VirtualPageTest_ClassA();
|
|
|
|
$page->write();
|
|
|
|
$virtual = new VirtualPage();
|
|
|
|
$virtual->CopyContentFromID = $page->ID;
|
|
|
|
$virtual->write();
|
|
|
|
|
|
|
|
$this->assertContains('MyVirtualField', $virtual->getVirtualFields());
|
|
|
|
$this->assertNotContains('MyNonVirtualField', $virtual->getVirtualFields());
|
|
|
|
$this->assertNotContains('MyInitiallyCopiedField', $virtual->getVirtualFields());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testCopyFrom()
|
|
|
|
{
|
|
|
|
$original = new VirtualPageTest_ClassA();
|
|
|
|
$original->MyInitiallyCopiedField = 'original';
|
|
|
|
$original->MyVirtualField = 'original';
|
|
|
|
$original->MyNonVirtualField = 'original';
|
|
|
|
$original->write();
|
|
|
|
|
|
|
|
$virtual = new VirtualPage();
|
|
|
|
$virtual->CopyContentFromID = $original->ID;
|
|
|
|
$virtual->write();
|
|
|
|
|
|
|
|
// Using getField() to avoid side effects from an overloaded __get()
|
|
|
|
$this->assertEquals(
|
|
|
|
'original',
|
|
|
|
$virtual->getField('MyInitiallyCopiedField'),
|
|
|
|
'Fields listed in $initially_copied_fields are copied on first copyFrom() invocation'
|
|
|
|
);
|
|
|
|
$this->assertEquals(
|
|
|
|
'original',
|
|
|
|
$virtual->getField('MyVirtualField'),
|
|
|
|
'Fields not listed in $initially_copied_fields are copied in copyFrom()'
|
|
|
|
);
|
|
|
|
$this->assertNull(
|
|
|
|
$virtual->getField('MyNonVirtualField'),
|
|
|
|
'Fields listed in $non_virtual_fields are not copied in copyFrom()'
|
|
|
|
);
|
|
|
|
|
|
|
|
$original->MyInitiallyCopiedField = 'changed';
|
|
|
|
$original->write();
|
|
|
|
$this->assertEquals(
|
|
|
|
'original',
|
|
|
|
$virtual->MyInitiallyCopiedField,
|
|
|
|
'Fields listed in $initially_copied_fields are not copied on subsequent copyFrom() invocations'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testCanBeRoot()
|
|
|
|
{
|
|
|
|
$page = new SiteTree();
|
|
|
|
$page->ParentID = 0;
|
|
|
|
$page->write();
|
|
|
|
|
|
|
|
$notRootPage = new VirtualPageTest_NotRoot();
|
|
|
|
// we don't want the original on root, but rather the VirtualPage pointing to it
|
|
|
|
$notRootPage->ParentID = $page->ID;
|
|
|
|
$notRootPage->write();
|
|
|
|
|
|
|
|
$virtual = new VirtualPage();
|
|
|
|
$virtual->CopyContentFromID = $page->ID;
|
|
|
|
$virtual->write();
|
|
|
|
|
2017-07-17 07:19:00 +02:00
|
|
|
$virtual = DataObject::get_by_id(VirtualPage::class, $virtual->ID, false);
|
2017-01-25 21:59:25 +01:00
|
|
|
$virtual->CopyContentFromID = $notRootPage->ID;
|
|
|
|
$virtual->flushCache();
|
|
|
|
|
|
|
|
$isDetected = false;
|
|
|
|
try {
|
|
|
|
$virtual->write();
|
|
|
|
} catch (ValidationException $e) {
|
2021-10-27 23:40:52 +02:00
|
|
|
$this->assertStringContainsString('is not allowed on the root level', $e->getMessage());
|
2017-01-25 21:59:25 +01:00
|
|
|
$isDetected = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$isDetected) {
|
|
|
|
$this->fail('Fails validation with $can_be_root=false');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testPageTypeChangePropagatesToLive()
|
|
|
|
{
|
|
|
|
$page = new SiteTree();
|
|
|
|
$page->Title = 'published title';
|
|
|
|
$page->MySharedNonVirtualField = 'original';
|
|
|
|
$page->write();
|
|
|
|
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
|
|
|
|
|
|
|
$virtual = new VirtualPageTest_VirtualPageSub();
|
|
|
|
$virtual->CopyContentFromID = $page->ID;
|
|
|
|
$virtual->MySharedNonVirtualField = 'virtual published field';
|
|
|
|
$virtual->write();
|
|
|
|
$virtual->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
|
|
|
|
|
|
|
$page->Title = 'original'; // 'Title' is a virtual field
|
|
|
|
// Publication would causes the virtual field to copy through onBeforeWrite(),
|
|
|
|
// but we want to test that it gets copied on class name change instead
|
|
|
|
$page->write();
|
|
|
|
|
|
|
|
|
|
|
|
$nonVirtual = $virtual;
|
2017-08-09 04:53:38 +02:00
|
|
|
$nonVirtual->ClassName = VirtualPageTest_ClassA::class;
|
2017-01-25 21:59:25 +01:00
|
|
|
$nonVirtual->MySharedNonVirtualField = 'changed on new type';
|
|
|
|
$nonVirtual->write(); // not publishing the page type change here
|
|
|
|
|
|
|
|
// Stage record is changed to the new type and no longer acts as a virtual page
|
2017-08-09 04:53:38 +02:00
|
|
|
$nonVirtualStage = Versioned::get_one_by_stage(
|
|
|
|
SiteTree::class,
|
|
|
|
'Stage',
|
|
|
|
'"SiteTree"."ID" = ' . $nonVirtual->ID,
|
|
|
|
false
|
|
|
|
);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertNotNull($nonVirtualStage);
|
2017-08-09 04:53:38 +02:00
|
|
|
$this->assertEquals(VirtualPageTest_ClassA::class, $nonVirtualStage->ClassName);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertEquals('changed on new type', $nonVirtualStage->MySharedNonVirtualField);
|
|
|
|
$this->assertEquals(
|
|
|
|
'original',
|
|
|
|
$nonVirtualStage->Title,
|
|
|
|
'Copies virtual fields from original draft into new instance on type change '
|
|
|
|
);
|
|
|
|
|
|
|
|
// Virtual page on live keeps working as it should
|
2017-08-09 04:53:38 +02:00
|
|
|
$virtualLive = Versioned::get_one_by_stage(
|
|
|
|
SiteTree::class,
|
|
|
|
Versioned::LIVE,
|
|
|
|
'"SiteTree_Live"."ID" = ' . $virtual->ID,
|
|
|
|
false
|
|
|
|
);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertNotNull($virtualLive);
|
2017-08-09 04:53:38 +02:00
|
|
|
$this->assertEquals(VirtualPageTest_VirtualPageSub::class, $virtualLive->ClassName);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertEquals('virtual published field', $virtualLive->MySharedNonVirtualField);
|
|
|
|
$this->assertEquals('published title', $virtualLive->Title);
|
|
|
|
|
|
|
|
// Change live page
|
|
|
|
$page->Title = 'title changed on original';
|
|
|
|
$page->MySharedNonVirtualField = 'changed only on original';
|
|
|
|
$page->write();
|
|
|
|
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
|
|
|
|
|
|
|
// Virtual page only notices changes to virtualised fields (Title)
|
2017-08-09 04:53:38 +02:00
|
|
|
$virtualLive = Versioned::get_one_by_stage(
|
|
|
|
SiteTree::class,
|
|
|
|
Versioned::LIVE,
|
|
|
|
'"SiteTree_Live"."ID" = ' . $virtual->ID,
|
|
|
|
false
|
|
|
|
);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertEquals('virtual published field', $virtualLive->MySharedNonVirtualField);
|
|
|
|
$this->assertEquals('title changed on original', $virtualLive->Title);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testVirtualPageFindsCorrectCasting()
|
|
|
|
{
|
|
|
|
$page = new VirtualPageTest_ClassA();
|
|
|
|
$page->CastingTest = "Some content";
|
|
|
|
$page->write();
|
|
|
|
$virtual = new VirtualPage();
|
|
|
|
$virtual->CopyContentFromID = $page->ID;
|
|
|
|
$virtual->write();
|
|
|
|
|
2017-08-09 04:53:38 +02:00
|
|
|
$this->assertEquals(VirtualPageTest_TestDBField::class, $virtual->castingHelper('CastingTest'));
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertEquals('SOME CONTENT', $virtual->obj('CastingTest')->forTemplate());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testVirtualPageAsAnAllowedChild()
|
|
|
|
{
|
|
|
|
$parentPage = new VirtualPageTest_PageWithAllowedChildren();
|
|
|
|
$parentPage->write();
|
|
|
|
|
|
|
|
$childPage = new VirtualPageTest_ClassA();
|
|
|
|
$childPage->ParentID = $parentPage->ID;
|
|
|
|
$childPage->write();
|
|
|
|
|
|
|
|
// Check we're allowed to create a VirtualPage without linking it to a page yet
|
|
|
|
$childVirtualPage = new VirtualPage();
|
|
|
|
$childVirtualPage->ParentID = $parentPage->ID;
|
|
|
|
try {
|
|
|
|
$childVirtualPage->write();
|
|
|
|
} catch (ValidationException $e) {
|
|
|
|
$this->fail('Failed to write VirtualPage when it is an allowed child');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that we can link a VirtualPage to a page type that's an allowed child
|
|
|
|
$childVirtualPage->CopyContentFromID = $childPage->ID;
|
|
|
|
try {
|
|
|
|
$childVirtualPage->write();
|
|
|
|
} catch (ValidationException $e) {
|
|
|
|
$this->fail('Failed to write VirtualPage when it is linked to an allowed child');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that we CAN'T link a VirtualPage to a page that is NOT an allowed child
|
|
|
|
$disallowedChild = new VirtualPageTest_ClassB();
|
|
|
|
$disallowedChild->write();
|
|
|
|
$childVirtualPage->CopyContentFromID = $disallowedChild->ID;
|
|
|
|
$isDetected = false;
|
|
|
|
try {
|
|
|
|
$childVirtualPage->write();
|
|
|
|
} catch (ValidationException $e) {
|
2021-10-27 23:40:52 +02:00
|
|
|
$this->assertStringContainsString('not allowed as child of this parent page', $e->getMessage());
|
2017-01-25 21:59:25 +01:00
|
|
|
$isDetected = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$isDetected) {
|
|
|
|
$this->fail("Shouldn't be allowed to write a VirtualPage that links to a disallowed child");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testVirtualPagePointingToRedirectorPage()
|
|
|
|
{
|
2018-06-20 00:17:38 +02:00
|
|
|
$rp = new RedirectorPage(['ExternalURL' => 'http://google.com', 'RedirectionType' => 'External']);
|
2017-01-25 21:59:25 +01:00
|
|
|
$rp->write();
|
|
|
|
$rp->publishRecursive();
|
|
|
|
|
2018-06-20 00:17:38 +02:00
|
|
|
$vp = new VirtualPage(['URLSegment' => 'vptest', 'CopyContentFromID' => $rp->ID]);
|
2017-01-25 21:59:25 +01:00
|
|
|
$vp->write();
|
|
|
|
$vp->publishRecursive();
|
|
|
|
|
|
|
|
$response = $this->get($vp->Link());
|
|
|
|
$this->assertEquals(301, $response->getStatusCode());
|
|
|
|
$this->assertEquals('http://google.com', $response->getHeader('Location'));
|
|
|
|
}
|
|
|
|
|
2017-07-25 13:02:31 +02:00
|
|
|
public function testVirtualPageRendersCorrectTemplate()
|
|
|
|
{
|
|
|
|
$this->useTestTheme(dirname(__FILE__), 'virtualpagetest', function () {
|
|
|
|
$page = new VirtualPageTest_ClassA();
|
|
|
|
$page->Title = 'Test Page';
|
|
|
|
$page->Content = 'NotThisContent';
|
|
|
|
$page->MyInitiallyCopiedField = 'TestContent';
|
|
|
|
$page->write();
|
2018-03-21 05:44:24 +01:00
|
|
|
$page->publishSingle();
|
|
|
|
|
2017-07-25 13:02:31 +02:00
|
|
|
$vp = new VirtualPage();
|
|
|
|
$vp->CopyContentFromID = $page->ID;
|
|
|
|
$vp->write();
|
2018-03-21 05:44:24 +01:00
|
|
|
$vp->publishSingle();
|
|
|
|
|
2017-07-25 13:02:31 +02:00
|
|
|
$response = $this->get($vp->Link());
|
|
|
|
$this->assertEquals(200, $response->getStatusCode());
|
2021-10-27 23:40:52 +02:00
|
|
|
$this->assertStringContainsString('TestContent', $response->getBody());
|
|
|
|
$this->assertStringNotContainsString('NotThisContent', $response->getBody());
|
2017-12-05 23:59:30 +01:00
|
|
|
|
|
|
|
// VirtualPageTest_ClassB doesn't have an associated controller for
|
|
|
|
// ModelAsController::controller_for() to find
|
|
|
|
$page = new VirtualPageTest_ClassB();
|
|
|
|
$page->Title = 'Test Page B';
|
|
|
|
$page->write();
|
2018-03-21 05:44:24 +01:00
|
|
|
$page->publishSingle();
|
|
|
|
|
2017-12-05 23:59:30 +01:00
|
|
|
$vp = new VirtualPage();
|
|
|
|
$vp->CopyContentFromID = $page->ID;
|
|
|
|
$vp->write();
|
2018-03-21 05:44:24 +01:00
|
|
|
$vp->publishSingle();
|
|
|
|
|
2017-12-05 23:59:30 +01:00
|
|
|
$response = $this->get($vp->Link());
|
|
|
|
$this->assertEquals(200, $response->getStatusCode());
|
2021-10-27 23:40:52 +02:00
|
|
|
$this->assertStringContainsString('Test Page B', $response->getBody());
|
2017-07-25 13:02:31 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-01-25 21:59:25 +01:00
|
|
|
public function testMethod()
|
|
|
|
{
|
2017-08-09 04:53:38 +02:00
|
|
|
/** @var VirtualPage $virtualPage */
|
2017-07-17 07:19:00 +02:00
|
|
|
$virtualPage = $this->objFromFixture(VirtualPage::class, 'vp4');
|
2019-04-12 16:45:50 +02:00
|
|
|
$this->assertTrue($virtualPage->hasMethod('modelMethod'));
|
|
|
|
$this->assertEquals('hi there', $virtualPage->modelMethod());
|
|
|
|
|
2017-08-09 04:53:38 +02:00
|
|
|
/** @var VirtualPageTest_ClassAController $controller */
|
2017-01-25 21:59:25 +01:00
|
|
|
$controller = ModelAsController::controller_for($virtualPage);
|
2017-08-09 04:53:38 +02:00
|
|
|
$this->assertInstanceOf(VirtualPageTest_ClassAController::class, $controller);
|
2017-01-25 21:59:25 +01:00
|
|
|
$this->assertTrue($controller->hasMethod('testMethod'));
|
|
|
|
$this->assertEquals('hello', $controller->testMethod());
|
|
|
|
$this->assertTrue($controller->hasMethod('modelMethod'));
|
|
|
|
$this->assertEquals('hi there', $controller->modelMethod());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testAllowedActions()
|
|
|
|
{
|
2017-08-09 04:53:38 +02:00
|
|
|
/** @var VirtualPage $virtualPage */
|
2017-07-17 07:19:00 +02:00
|
|
|
$virtualPage = $this->objFromFixture(VirtualPage::class, 'vp4');
|
2017-01-25 21:59:25 +01:00
|
|
|
$controller = ModelAsController::controller_for($virtualPage);
|
|
|
|
$this->assertContains('testaction', $controller->allowedActions());
|
|
|
|
}
|
2011-10-06 16:47:59 +02:00
|
|
|
}
|