silverstripe /
silverstripe-cms
| 1 | <?php |
||||||
| 2 | |||||||
| 3 | namespace SilverStripe\CMS\Tests\Model; |
||||||
| 4 | |||||||
| 5 | use SilverStripe\CMS\Controllers\ModelAsController; |
||||||
| 6 | use SilverStripe\CMS\Model\RedirectorPage; |
||||||
| 7 | use SilverStripe\CMS\Model\SiteTree; |
||||||
| 8 | use SilverStripe\CMS\Model\VirtualPage; |
||||||
| 9 | use SilverStripe\Core\Config\Config; |
||||||
| 10 | use SilverStripe\Dev\FunctionalTest; |
||||||
| 11 | use SilverStripe\ORM\DataObject; |
||||||
| 12 | use SilverStripe\ORM\ValidationException; |
||||||
| 13 | use SilverStripe\Security\Member; |
||||||
| 14 | use SilverStripe\Subsites\Extensions\SiteTreeSubsites; |
||||||
|
0 ignored issues
–
show
|
|||||||
| 15 | use SilverStripe\Versioned\Versioned; |
||||||
| 16 | use TractorCow\Fluent\Extension\FluentSiteTreeExtension; |
||||||
|
0 ignored issues
–
show
The type
TractorCow\Fluent\Extens...FluentSiteTreeExtension was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths Loading history...
|
|||||||
| 17 | |||||||
| 18 | class VirtualPageTest extends FunctionalTest |
||||||
| 19 | { |
||||||
| 20 | protected static $fixture_file = 'VirtualPageTest.yml'; |
||||||
| 21 | |||||||
| 22 | protected $autoFollowRedirection = false; |
||||||
| 23 | |||||||
| 24 | protected static $extra_dataobjects = [ |
||||||
| 25 | VirtualPageTest_ClassA::class, |
||||||
| 26 | VirtualPageTest_ClassB::class, |
||||||
| 27 | VirtualPageTest_ClassC::class, |
||||||
| 28 | VirtualPageTest_NotRoot::class, |
||||||
| 29 | VirtualPageTest_PageExtension::class, |
||||||
| 30 | VirtualPageTest_PageWithAllowedChildren::class, |
||||||
| 31 | VirtualPageTest_TestDBField::class, |
||||||
| 32 | VirtualPageTest_VirtualPageSub::class, |
||||||
| 33 | ]; |
||||||
| 34 | |||||||
| 35 | protected static $illegal_extensions = [ |
||||||
| 36 | SiteTree::class => [ |
||||||
| 37 | SiteTreeSubsites::class, |
||||||
| 38 | FluentSiteTreeExtension::class, |
||||||
| 39 | ], |
||||||
| 40 | ]; |
||||||
| 41 | |||||||
| 42 | protected static $required_extensions = [ |
||||||
| 43 | SiteTree::class => [ |
||||||
| 44 | VirtualPageTest_PageExtension::class, |
||||||
| 45 | ], |
||||||
| 46 | ]; |
||||||
| 47 | |||||||
| 48 | protected function setUp() : void |
||||||
| 49 | { |
||||||
| 50 | parent::setUp(); |
||||||
| 51 | |||||||
| 52 | // Ensure we always have permission to save/publish |
||||||
| 53 | $this->logInWithPermission("ADMIN"); |
||||||
| 54 | |||||||
| 55 | // Add extra fields |
||||||
| 56 | Config::modify()->merge(VirtualPage::class, 'initially_copied_fields', ['MyInitiallyCopiedField']); |
||||||
| 57 | Config::modify()->merge( |
||||||
| 58 | VirtualPage::class, |
||||||
| 59 | 'non_virtual_fields', |
||||||
| 60 | ['MyNonVirtualField', 'MySharedNonVirtualField'] |
||||||
| 61 | ); |
||||||
| 62 | |||||||
| 63 | // Ensure all pages are published |
||||||
| 64 | /** @var SiteTree $page */ |
||||||
| 65 | foreach (SiteTree::get() as $page) { |
||||||
| 66 | $page->publishSingle(); |
||||||
| 67 | } |
||||||
| 68 | } |
||||||
| 69 | |||||||
| 70 | /** |
||||||
| 71 | * Test that, after you update the source page of a virtual page, all the virtual pages |
||||||
| 72 | * are updated |
||||||
| 73 | */ |
||||||
| 74 | public function testEditingSourcePageUpdatesVirtualPages() |
||||||
| 75 | { |
||||||
| 76 | /** @var SiteTree $master */ |
||||||
| 77 | $master = $this->objFromFixture(SiteTree::class, 'master'); |
||||||
| 78 | $master->Title = "New title"; |
||||||
| 79 | $master->MenuTitle = "New menutitle"; |
||||||
| 80 | $master->Content = "<p>New content</p>"; |
||||||
| 81 | $master->write(); |
||||||
| 82 | $master->publishSingle(); |
||||||
| 83 | |||||||
| 84 | $vp1 = $this->objFromFixture(VirtualPage::class, 'vp1'); |
||||||
| 85 | $vp2 = $this->objFromFixture(VirtualPage::class, 'vp2'); |
||||||
| 86 | |||||||
| 87 | $this->assertEquals("New title", $vp1->Title); |
||||||
| 88 | $this->assertEquals("New title", $vp2->Title); |
||||||
| 89 | $this->assertEquals("New menutitle", $vp1->MenuTitle); |
||||||
| 90 | $this->assertEquals("New menutitle", $vp2->MenuTitle); |
||||||
| 91 | $this->assertEquals("<p>New content</p>", $vp1->Content); |
||||||
| 92 | $this->assertEquals("<p>New content</p>", $vp2->Content); |
||||||
| 93 | } |
||||||
| 94 | |||||||
| 95 | /** |
||||||
| 96 | * Test that, after you publish the source page of a virtual page, all the already published |
||||||
| 97 | * virtual pages are published |
||||||
| 98 | */ |
||||||
| 99 | public function testPublishingSourcePagePublishesAlreadyPublishedVirtualPages() |
||||||
| 100 | { |
||||||
| 101 | $this->logInWithPermission('ADMIN'); |
||||||
| 102 | |||||||
| 103 | /** @var SiteTree $master */ |
||||||
| 104 | $master = $this->objFromFixture(SiteTree::class, 'master'); |
||||||
| 105 | $master->publishRecursive(); |
||||||
| 106 | |||||||
| 107 | $master->Title = "New title"; |
||||||
| 108 | $master->MenuTitle = "New menutitle"; |
||||||
| 109 | $master->Content = "<p>New content</p>"; |
||||||
| 110 | $master->write(); |
||||||
| 111 | |||||||
| 112 | /** @var VirtualPage $vp1 */ |
||||||
| 113 | $vp1 = DataObject::get_by_id(VirtualPage::class, $this->idFromFixture(VirtualPage::class, 'vp1')); |
||||||
| 114 | /** @var VirtualPage $vp2 */ |
||||||
| 115 | $vp2 = DataObject::get_by_id(VirtualPage::class, $this->idFromFixture(VirtualPage::class, 'vp2')); |
||||||
| 116 | $this->assertTrue($vp1->publishRecursive()); |
||||||
| 117 | $this->assertTrue($vp2->publishRecursive()); |
||||||
| 118 | |||||||
| 119 | $master->publishRecursive(); |
||||||
| 120 | |||||||
| 121 | Versioned::set_stage(Versioned::LIVE); |
||||||
| 122 | $vp1 = DataObject::get_by_id(VirtualPage::class, $this->idFromFixture(VirtualPage::class, 'vp1')); |
||||||
| 123 | $vp2 = DataObject::get_by_id(VirtualPage::class, $this->idFromFixture(VirtualPage::class, 'vp2')); |
||||||
| 124 | |||||||
| 125 | $this->assertNotNull($vp1); |
||||||
| 126 | $this->assertNotNull($vp2); |
||||||
| 127 | |||||||
| 128 | $this->assertEquals("New title", $vp1->Title); |
||||||
| 129 | $this->assertEquals("New title", $vp2->Title); |
||||||
| 130 | $this->assertEquals("New menutitle", $vp1->MenuTitle); |
||||||
| 131 | $this->assertEquals("New menutitle", $vp2->MenuTitle); |
||||||
| 132 | $this->assertEquals("<p>New content</p>", $vp1->Content); |
||||||
| 133 | $this->assertEquals("<p>New content</p>", $vp2->Content); |
||||||
| 134 | Versioned::set_stage(Versioned::DRAFT); |
||||||
| 135 | } |
||||||
| 136 | |||||||
| 137 | /** |
||||||
| 138 | * Test that virtual pages get the content from the master page when they are created. |
||||||
| 139 | */ |
||||||
| 140 | public function testNewVirtualPagesGrabTheContentFromTheirMaster() |
||||||
| 141 | { |
||||||
| 142 | $vp = new VirtualPage(); |
||||||
| 143 | $vp->write(); |
||||||
| 144 | |||||||
| 145 | $vp->CopyContentFromID = $this->idFromFixture(SiteTree::class, 'master'); |
||||||
| 146 | $vp->write(); |
||||||
| 147 | |||||||
| 148 | $this->assertEquals("My Page", $vp->Title); |
||||||
| 149 | $this->assertEquals("My Page Nav", $vp->MenuTitle); |
||||||
| 150 | |||||||
| 151 | $vp->CopyContentFromID = $this->idFromFixture(SiteTree::class, 'master2'); |
||||||
| 152 | $vp->write(); |
||||||
| 153 | |||||||
| 154 | $this->assertEquals("My Other Page", $vp->Title); |
||||||
| 155 | $this->assertEquals("My Other Page Nav", $vp->MenuTitle); |
||||||
| 156 | } |
||||||
| 157 | |||||||
| 158 | /** |
||||||
| 159 | * Virtual pages are always supposed to chose the same content as the published source page. |
||||||
| 160 | * This means that when you publish them, they should show the published content of the source |
||||||
| 161 | * page, not the draft content at the time when you clicked 'publish' in the CMS. |
||||||
| 162 | */ |
||||||
| 163 | public function testPublishingAVirtualPageCopiedPublishedContentNotDraftContent() |
||||||
| 164 | { |
||||||
| 165 | $p = SiteTree::create(); |
||||||
| 166 | $p->Content = "published content"; |
||||||
| 167 | $p->write(); |
||||||
| 168 | $p->publishRecursive(); |
||||||
| 169 | |||||||
| 170 | // Virtual page has this content |
||||||
| 171 | $vp = new VirtualPage(); |
||||||
| 172 | $vp->CopyContentFromID = $p->ID; |
||||||
| 173 | $vp->write(); |
||||||
| 174 | |||||||
| 175 | $vp->publishRecursive(); |
||||||
| 176 | |||||||
| 177 | // Don't publish this change - published page will still say 'published content' |
||||||
| 178 | $p->Content = "draft content"; |
||||||
| 179 | $p->write(); |
||||||
| 180 | |||||||
| 181 | // The draft content of the virtual page should say 'draft content' |
||||||
| 182 | /** @var VirtualPage $vpDraft */ |
||||||
| 183 | $vpDraft = Versioned::get_by_stage(VirtualPage::class, Versioned::DRAFT)->byID($vp->ID); |
||||||
| 184 | $this->assertEquals('draft content', $vpDraft->CopyContentFrom()->Content); |
||||||
| 185 | $this->assertEquals('draft content', $vpDraft->Content); |
||||||
| 186 | |||||||
| 187 | // The published content of the virtual page should say 'published content' |
||||||
| 188 | /** @var VirtualPage $vpLive */ |
||||||
| 189 | $vpLive = Versioned::get_by_stage(VirtualPage::class, Versioned::LIVE)->byID($vp->ID); |
||||||
| 190 | $this->assertEquals('published content', $vpLive->CopyContentFrom()->Content); |
||||||
| 191 | $this->assertEquals('published content', $vpLive->Content); |
||||||
| 192 | |||||||
| 193 | // Publishing the virtualpage should, however, trigger publishing of the live page |
||||||
| 194 | $vpDraft->publishRecursive(); |
||||||
| 195 | |||||||
| 196 | // Everything is published live |
||||||
| 197 | $vpLive = Versioned::get_by_stage(VirtualPage::class, Versioned::LIVE)->byID($vp->ID); |
||||||
| 198 | $this->assertEquals('draft content', $vpLive->CopyContentFrom()->Content); |
||||||
| 199 | $this->assertEquals('draft content', $vpLive->Content); |
||||||
| 200 | } |
||||||
| 201 | |||||||
| 202 | public function testCantPublishVirtualPagesBeforeTheirSource() |
||||||
| 203 | { |
||||||
| 204 | // An unpublished source page |
||||||
| 205 | $p = SiteTree::create(); |
||||||
| 206 | $p->Content = "test content"; |
||||||
| 207 | $p->write(); |
||||||
| 208 | |||||||
| 209 | // With no source page, we can't publish |
||||||
| 210 | $vp = new VirtualPage(); |
||||||
| 211 | $vp->write(); |
||||||
| 212 | $this->assertFalse($vp->canPublish()); |
||||||
| 213 | |||||||
| 214 | // When the source page isn't published, we can't publish |
||||||
| 215 | $vp->CopyContentFromID = $p->ID; |
||||||
| 216 | $vp->write(); |
||||||
| 217 | $this->assertFalse($vp->canPublish()); |
||||||
| 218 | |||||||
| 219 | // Once the source page gets published, then we can publish |
||||||
| 220 | $p->publishRecursive(); |
||||||
| 221 | $this->assertTrue($vp->canPublish()); |
||||||
| 222 | } |
||||||
| 223 | |||||||
| 224 | public function testCanEdit() |
||||||
| 225 | { |
||||||
| 226 | $parentPage = $this->objFromFixture(SiteTree::class, 'master3'); |
||||||
| 227 | $virtualPage = $this->objFromFixture(VirtualPage::class, 'vp3'); |
||||||
| 228 | $bob = $this->objFromFixture(Member::class, 'bob'); |
||||||
| 229 | $andrew = $this->objFromFixture(Member::class, 'andrew'); |
||||||
| 230 | |||||||
| 231 | // Bob can edit the mirrored page, but he shouldn't be able to edit the virtual page. |
||||||
| 232 | $this->logInAs($bob); |
||||||
| 233 | $this->assertTrue($parentPage->canEdit()); |
||||||
| 234 | $this->assertFalse($virtualPage->canEdit()); |
||||||
| 235 | |||||||
| 236 | // Andrew can only edit the virtual page, but not the original. |
||||||
| 237 | $this->logInAs($andrew); |
||||||
| 238 | $this->assertFalse($parentPage->canEdit()); |
||||||
| 239 | $this->assertTrue($virtualPage->canEdit()); |
||||||
| 240 | } |
||||||
| 241 | |||||||
| 242 | public function testCanView() |
||||||
| 243 | { |
||||||
| 244 | /** @var SiteTree $parentPage */ |
||||||
| 245 | $parentPage = $this->objFromFixture(SiteTree::class, 'master3'); |
||||||
| 246 | $parentPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); |
||||||
| 247 | |||||||
| 248 | /** @var VirtualPage $virtualPage */ |
||||||
| 249 | $virtualPage = $this->objFromFixture(VirtualPage::class, 'vp3'); |
||||||
| 250 | $virtualPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); |
||||||
| 251 | $cindy = $this->objFromFixture(Member::class, 'cindy'); |
||||||
| 252 | $alice = $this->objFromFixture(Member::class, 'alice'); |
||||||
| 253 | |||||||
| 254 | // Cindy can see both pages |
||||||
| 255 | $this->logInAs($cindy); |
||||||
| 256 | $this->assertTrue($parentPage->canView()); |
||||||
| 257 | $this->assertTrue($virtualPage->canView()); |
||||||
| 258 | |||||||
| 259 | // Alice can't see the virtual page, since it's restricted to cindy |
||||||
| 260 | $this->logInAs($alice); |
||||||
| 261 | $this->assertTrue($parentPage->canView()); |
||||||
| 262 | $this->assertFalse($virtualPage->canView()); |
||||||
| 263 | } |
||||||
| 264 | |||||||
| 265 | public function testVirtualPagesArentInappropriatelyPublished() |
||||||
| 266 | { |
||||||
| 267 | // Fixture |
||||||
| 268 | $p = SiteTree::create(); |
||||||
| 269 | $p->Content = "test content"; |
||||||
| 270 | $p->write(); |
||||||
| 271 | $vp = new VirtualPage(); |
||||||
| 272 | $vp->CopyContentFromID = $p->ID; |
||||||
| 273 | $vp->write(); |
||||||
| 274 | |||||||
| 275 | // VP is oragne |
||||||
| 276 | $this->assertTrue($vp->isOnDraftOnly()); |
||||||
| 277 | |||||||
| 278 | // VP is still orange after we publish |
||||||
| 279 | $p->publishRecursive(); |
||||||
| 280 | $this->assertTrue($vp->isOnDraftOnly()); |
||||||
| 281 | |||||||
| 282 | // A new VP created after P's initial construction |
||||||
| 283 | $vp2 = new VirtualPage(); |
||||||
| 284 | $vp2->CopyContentFromID = $p->ID; |
||||||
| 285 | $vp2->write(); |
||||||
| 286 | $this->assertTrue($vp2->isOnDraftOnly()); |
||||||
| 287 | |||||||
| 288 | // Also remains orange after a republish |
||||||
| 289 | $p->Content = "new content"; |
||||||
| 290 | $p->write(); |
||||||
| 291 | $p->publishRecursive(); |
||||||
| 292 | $this->assertTrue($vp2->isOnDraftOnly()); |
||||||
| 293 | |||||||
| 294 | // VP is now published |
||||||
| 295 | $vp->publishRecursive(); |
||||||
| 296 | |||||||
| 297 | $this->assertTrue($vp->isPublished()); |
||||||
| 298 | $this->assertFalse($vp->isModifiedOnDraft()); |
||||||
| 299 | |||||||
| 300 | // P edited, P goes green. Change set interface should indicate to the user that the owned page has |
||||||
| 301 | // modifications, although the virtual page record itself will not appear as having pending changes. |
||||||
| 302 | $p->Content = "third content"; |
||||||
| 303 | $p->write(); |
||||||
| 304 | |||||||
| 305 | $this->assertTrue($p->isModifiedOnDraft()); |
||||||
| 306 | $this->assertFalse($vp->isModifiedOnDraft()); |
||||||
| 307 | |||||||
| 308 | // Publish, VP goes black |
||||||
| 309 | $p->publishRecursive(); |
||||||
| 310 | $this->assertTrue($vp->isPublished()); |
||||||
| 311 | $this->assertFalse($vp->isModifiedOnDraft()); |
||||||
| 312 | } |
||||||
| 313 | |||||||
| 314 | public function testUnpublishingSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage() |
||||||
| 315 | { |
||||||
| 316 | // Create page and virutal page |
||||||
| 317 | $p = SiteTree::create(); |
||||||
| 318 | $p->Title = "source"; |
||||||
| 319 | $p->write(); |
||||||
| 320 | $this->assertTrue($p->publishRecursive()); |
||||||
| 321 | $vp = new VirtualPage(); |
||||||
| 322 | $vp->CopyContentFromID = $p->ID; |
||||||
| 323 | $vp->write(); |
||||||
| 324 | $vpID = $vp->ID; |
||||||
| 325 | $this->assertTrue($vp->publishRecursive()); |
||||||
| 326 | |||||||
| 327 | // All is fine, the virtual page doesn't have a broken link |
||||||
| 328 | $this->assertFalse($vp->HasBrokenLink); |
||||||
| 329 | |||||||
| 330 | // Unpublish the source page, confirm that the virtual page has also been unpublished |
||||||
| 331 | $p->doUnpublish(); |
||||||
| 332 | |||||||
| 333 | // The draft VP still has the CopyContentFromID link |
||||||
| 334 | $vp->flushCache(); |
||||||
| 335 | $vp = DataObject::get_by_id(SiteTree::class, $vpID); |
||||||
| 336 | $this->assertEquals($p->ID, $vp->CopyContentFromID); |
||||||
| 337 | $vpLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)->byID($vpID); |
||||||
| 338 | $this->assertNull($vpLive); |
||||||
| 339 | // Delete from draft, ensure virtual page deletion cascades |
||||||
| 340 | $p->delete(); |
||||||
| 341 | $vp->flushCache(); |
||||||
| 342 | $vp = DataObject::get_by_id(SiteTree::class, $vpID); |
||||||
| 343 | $this->assertNull($vp); |
||||||
| 344 | } |
||||||
| 345 | |||||||
| 346 | public function testDeletingFromLiveSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage() |
||||||
| 347 | { |
||||||
| 348 | // Create page and virutal page |
||||||
| 349 | $p = SiteTree::create(); |
||||||
| 350 | $p->Title = "source"; |
||||||
| 351 | $p->write(); |
||||||
| 352 | $this->assertTrue($p->publishRecursive()); |
||||||
| 353 | $vp = new VirtualPage(); |
||||||
| 354 | $vp->CopyContentFromID = $p->ID; |
||||||
| 355 | $vp->write(); |
||||||
| 356 | $vpID = $vp->ID; |
||||||
| 357 | $this->assertTrue($vp->publishRecursive()); |
||||||
| 358 | |||||||
| 359 | // All is fine, the virtual page doesn't have a broken link |
||||||
| 360 | $this->assertFalse($vp->HasBrokenLink); |
||||||
| 361 | // Delete the source page from draft, cascades to virtual page |
||||||
| 362 | $pID = $p->ID; |
||||||
| 363 | $p->delete(); |
||||||
| 364 | $vp->flushCache(); |
||||||
| 365 | $vpDraft = Versioned::get_by_stage(SiteTree::class, Versioned::DRAFT) |
||||||
| 366 | ->byID($pID); |
||||||
| 367 | $this->assertNull($vpDraft); |
||||||
| 368 | // Delete the source page form live, confirm that the virtual page has also been unpublished |
||||||
| 369 | /** @var SiteTree $pLive */ |
||||||
| 370 | $pLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE) |
||||||
| 371 | ->byID($pID); |
||||||
| 372 | $this->assertTrue($pLive->doUnpublish()); |
||||||
| 373 | $vpLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE) |
||||||
| 374 | ->byID($vpID); |
||||||
| 375 | $this->assertNull($vpLive); |
||||||
| 376 | } |
||||||
| 377 | |||||||
| 378 | /** |
||||||
| 379 | * Base functionality tested in {@link SiteTreeTest->testAllowedChildrenValidation()}. |
||||||
| 380 | */ |
||||||
| 381 | public function testAllowedChildrenLimitedOnVirtualPages() |
||||||
| 382 | { |
||||||
| 383 | $classA = new SiteTreeTest_ClassA(); |
||||||
| 384 | $classA->write(); |
||||||
| 385 | $classB = new SiteTreeTest_ClassB(); |
||||||
| 386 | $classB->write(); |
||||||
| 387 | $classBVirtual = new VirtualPage(); |
||||||
| 388 | $classBVirtual->CopyContentFromID = $classB->ID; |
||||||
| 389 | $classBVirtual->write(); |
||||||
| 390 | $classC = new SiteTreeTest_ClassC(); |
||||||
| 391 | $classC->write(); |
||||||
| 392 | $classCVirtual = new VirtualPage(); |
||||||
| 393 | $classCVirtual->CopyContentFromID = $classC->ID; |
||||||
| 394 | $classCVirtual->write(); |
||||||
| 395 | |||||||
| 396 | $classBVirtual->ParentID = $classA->ID; |
||||||
| 397 | $valid = $classBVirtual->doValidate(); |
||||||
| 398 | $this->assertTrue($valid->isValid(), "Does allow child linked to virtual page type allowed by parent"); |
||||||
| 399 | |||||||
| 400 | $classCVirtual->ParentID = $classA->ID; |
||||||
| 401 | $valid = $classCVirtual->doValidate(); |
||||||
| 402 | $this->assertFalse($valid->isValid(), "Doesn't allow child linked to virtual page type disallowed by parent"); |
||||||
| 403 | } |
||||||
| 404 | |||||||
| 405 | public function testGetVirtualFields() |
||||||
| 406 | { |
||||||
| 407 | // Needs association with an original, otherwise will just return the "base" virtual fields |
||||||
| 408 | $page = new VirtualPageTest_ClassA(); |
||||||
| 409 | $page->write(); |
||||||
| 410 | $virtual = new VirtualPage(); |
||||||
| 411 | $virtual->CopyContentFromID = $page->ID; |
||||||
| 412 | $virtual->write(); |
||||||
| 413 | |||||||
| 414 | $this->assertContains('MyVirtualField', $virtual->getVirtualFields()); |
||||||
| 415 | $this->assertNotContains('MyNonVirtualField', $virtual->getVirtualFields()); |
||||||
| 416 | $this->assertNotContains('MyInitiallyCopiedField', $virtual->getVirtualFields()); |
||||||
| 417 | } |
||||||
| 418 | |||||||
| 419 | public function testCopyFrom() |
||||||
| 420 | { |
||||||
| 421 | $original = new VirtualPageTest_ClassA(); |
||||||
| 422 | $original->MyInitiallyCopiedField = 'original'; |
||||||
|
0 ignored issues
–
show
The property
MyInitiallyCopiedField does not exist on SilverStripe\CMS\Tests\M...\VirtualPageTest_ClassA. Since you implemented __set, consider adding a @property annotation.
Loading history...
|
|||||||
| 423 | $original->MyVirtualField = 'original'; |
||||||
|
0 ignored issues
–
show
The property
MyVirtualField does not exist on SilverStripe\CMS\Tests\M...\VirtualPageTest_ClassA. Since you implemented __set, consider adding a @property annotation.
Loading history...
|
|||||||
| 424 | $original->MyNonVirtualField = 'original'; |
||||||
|
0 ignored issues
–
show
The property
MyNonVirtualField does not exist on SilverStripe\CMS\Tests\M...\VirtualPageTest_ClassA. Since you implemented __set, consider adding a @property annotation.
Loading history...
|
|||||||
| 425 | $original->write(); |
||||||
| 426 | |||||||
| 427 | $virtual = new VirtualPage(); |
||||||
| 428 | $virtual->CopyContentFromID = $original->ID; |
||||||
| 429 | $virtual->write(); |
||||||
| 430 | |||||||
| 431 | // Using getField() to avoid side effects from an overloaded __get() |
||||||
| 432 | $this->assertEquals( |
||||||
| 433 | 'original', |
||||||
| 434 | $virtual->getField('MyInitiallyCopiedField'), |
||||||
| 435 | 'Fields listed in $initially_copied_fields are copied on first copyFrom() invocation' |
||||||
| 436 | ); |
||||||
| 437 | $this->assertEquals( |
||||||
| 438 | 'original', |
||||||
| 439 | $virtual->getField('MyVirtualField'), |
||||||
| 440 | 'Fields not listed in $initially_copied_fields are copied in copyFrom()' |
||||||
| 441 | ); |
||||||
| 442 | $this->assertNull( |
||||||
| 443 | $virtual->getField('MyNonVirtualField'), |
||||||
| 444 | 'Fields listed in $non_virtual_fields are not copied in copyFrom()' |
||||||
| 445 | ); |
||||||
| 446 | |||||||
| 447 | $original->MyInitiallyCopiedField = 'changed'; |
||||||
| 448 | $original->write(); |
||||||
| 449 | $this->assertEquals( |
||||||
| 450 | 'original', |
||||||
| 451 | $virtual->MyInitiallyCopiedField, |
||||||
| 452 | 'Fields listed in $initially_copied_fields are not copied on subsequent copyFrom() invocations' |
||||||
| 453 | ); |
||||||
| 454 | } |
||||||
| 455 | |||||||
| 456 | public function testCanBeRoot() |
||||||
| 457 | { |
||||||
| 458 | $page = SiteTree::create(); |
||||||
| 459 | $page->ParentID = 0; |
||||||
| 460 | $page->write(); |
||||||
| 461 | |||||||
| 462 | $notRootPage = new VirtualPageTest_NotRoot(); |
||||||
| 463 | // we don't want the original on root, but rather the VirtualPage pointing to it |
||||||
| 464 | $notRootPage->ParentID = $page->ID; |
||||||
|
0 ignored issues
–
show
|
|||||||
| 465 | $notRootPage->write(); |
||||||
| 466 | |||||||
| 467 | $virtual = new VirtualPage(); |
||||||
| 468 | $virtual->CopyContentFromID = $page->ID; |
||||||
| 469 | $virtual->write(); |
||||||
| 470 | |||||||
| 471 | $virtual = DataObject::get_by_id(VirtualPage::class, $virtual->ID, false); |
||||||
| 472 | $virtual->CopyContentFromID = $notRootPage->ID; |
||||||
| 473 | $virtual->flushCache(); |
||||||
| 474 | |||||||
| 475 | $isDetected = false; |
||||||
| 476 | try { |
||||||
| 477 | $virtual->write(); |
||||||
| 478 | } catch (ValidationException $e) { |
||||||
| 479 | $this->assertStringContainsString('is not allowed on the root level', $e->getMessage()); |
||||||
| 480 | $isDetected = true; |
||||||
| 481 | } |
||||||
| 482 | |||||||
| 483 | if (!$isDetected) { |
||||||
| 484 | $this->fail('Fails validation with $can_be_root=false'); |
||||||
| 485 | } |
||||||
| 486 | } |
||||||
| 487 | |||||||
| 488 | public function testPageTypeChangePropagatesToLive() |
||||||
| 489 | { |
||||||
| 490 | $page = SiteTree::create(); |
||||||
| 491 | $page->Title = 'published title'; |
||||||
| 492 | $page->MySharedNonVirtualField = 'original'; |
||||||
| 493 | $page->write(); |
||||||
| 494 | $page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); |
||||||
| 495 | |||||||
| 496 | $virtual = new VirtualPageTest_VirtualPageSub(); |
||||||
| 497 | $virtual->CopyContentFromID = $page->ID; |
||||||
| 498 | $virtual->MySharedNonVirtualField = 'virtual published field'; |
||||||
| 499 | $virtual->write(); |
||||||
| 500 | $virtual->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); |
||||||
| 501 | |||||||
| 502 | $page->Title = 'original'; // 'Title' is a virtual field |
||||||
| 503 | // Publication would causes the virtual field to copy through onBeforeWrite(), |
||||||
| 504 | // but we want to test that it gets copied on class name change instead |
||||||
| 505 | $page->write(); |
||||||
| 506 | |||||||
| 507 | |||||||
| 508 | $nonVirtual = $virtual; |
||||||
| 509 | $nonVirtual->ClassName = VirtualPageTest_ClassA::class; |
||||||
| 510 | $nonVirtual->MySharedNonVirtualField = 'changed on new type'; |
||||||
| 511 | $nonVirtual->write(); // not publishing the page type change here |
||||||
| 512 | |||||||
| 513 | // Stage record is changed to the new type and no longer acts as a virtual page |
||||||
| 514 | $nonVirtualStage = Versioned::get_one_by_stage( |
||||||
| 515 | SiteTree::class, |
||||||
| 516 | 'Stage', |
||||||
| 517 | '"SiteTree"."ID" = ' . $nonVirtual->ID, |
||||||
|
0 ignored issues
–
show
The property
ID does not exist on SilverStripe\CMS\Tests\M...PageTest_VirtualPageSub. Since you implemented __get, consider adding a @property annotation.
Loading history...
|
|||||||
| 518 | false |
||||||
| 519 | ); |
||||||
| 520 | $this->assertNotNull($nonVirtualStage); |
||||||
| 521 | $this->assertEquals(VirtualPageTest_ClassA::class, $nonVirtualStage->ClassName); |
||||||
| 522 | $this->assertEquals('changed on new type', $nonVirtualStage->MySharedNonVirtualField); |
||||||
| 523 | $this->assertEquals( |
||||||
| 524 | 'original', |
||||||
| 525 | $nonVirtualStage->Title, |
||||||
| 526 | 'Copies virtual fields from original draft into new instance on type change ' |
||||||
| 527 | ); |
||||||
| 528 | |||||||
| 529 | // Virtual page on live keeps working as it should |
||||||
| 530 | $virtualLive = Versioned::get_one_by_stage( |
||||||
| 531 | SiteTree::class, |
||||||
| 532 | Versioned::LIVE, |
||||||
| 533 | '"SiteTree_Live"."ID" = ' . $virtual->ID, |
||||||
| 534 | false |
||||||
| 535 | ); |
||||||
| 536 | $this->assertNotNull($virtualLive); |
||||||
| 537 | $this->assertEquals(VirtualPageTest_VirtualPageSub::class, $virtualLive->ClassName); |
||||||
| 538 | $this->assertEquals('virtual published field', $virtualLive->MySharedNonVirtualField); |
||||||
| 539 | $this->assertEquals('published title', $virtualLive->Title); |
||||||
| 540 | |||||||
| 541 | // Change live page |
||||||
| 542 | $page->Title = 'title changed on original'; |
||||||
| 543 | $page->MySharedNonVirtualField = 'changed only on original'; |
||||||
| 544 | $page->write(); |
||||||
| 545 | $page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); |
||||||
| 546 | |||||||
| 547 | // Virtual page only notices changes to virtualised fields (Title) |
||||||
| 548 | $virtualLive = Versioned::get_one_by_stage( |
||||||
| 549 | SiteTree::class, |
||||||
| 550 | Versioned::LIVE, |
||||||
| 551 | '"SiteTree_Live"."ID" = ' . $virtual->ID, |
||||||
| 552 | false |
||||||
| 553 | ); |
||||||
| 554 | $this->assertEquals('virtual published field', $virtualLive->MySharedNonVirtualField); |
||||||
| 555 | $this->assertEquals('title changed on original', $virtualLive->Title); |
||||||
| 556 | } |
||||||
| 557 | |||||||
| 558 | public function testVirtualPageFindsCorrectCasting() |
||||||
| 559 | { |
||||||
| 560 | $page = new VirtualPageTest_ClassA(); |
||||||
| 561 | $page->CastingTest = "Some content"; |
||||||
|
0 ignored issues
–
show
The property
CastingTest does not exist on SilverStripe\CMS\Tests\M...\VirtualPageTest_ClassA. Since you implemented __set, consider adding a @property annotation.
Loading history...
|
|||||||
| 562 | $page->write(); |
||||||
| 563 | $virtual = new VirtualPage(); |
||||||
| 564 | $virtual->CopyContentFromID = $page->ID; |
||||||
| 565 | $virtual->write(); |
||||||
| 566 | |||||||
| 567 | $this->assertEquals(VirtualPageTest_TestDBField::class, $virtual->castingHelper('CastingTest')); |
||||||
| 568 | $this->assertEquals('SOME CONTENT', $virtual->obj('CastingTest')->forTemplate()); |
||||||
| 569 | } |
||||||
| 570 | |||||||
| 571 | public function testVirtualPageAsAnAllowedChild() |
||||||
| 572 | { |
||||||
| 573 | $parentPage = new VirtualPageTest_PageWithAllowedChildren(); |
||||||
| 574 | $parentPage->write(); |
||||||
| 575 | |||||||
| 576 | $childPage = new VirtualPageTest_ClassA(); |
||||||
| 577 | $childPage->ParentID = $parentPage->ID; |
||||||
|
0 ignored issues
–
show
|
|||||||
| 578 | $childPage->write(); |
||||||
| 579 | |||||||
| 580 | // Check we're allowed to create a VirtualPage without linking it to a page yet |
||||||
| 581 | $childVirtualPage = new VirtualPage(); |
||||||
| 582 | $childVirtualPage->ParentID = $parentPage->ID; |
||||||
| 583 | try { |
||||||
| 584 | $childVirtualPage->write(); |
||||||
| 585 | } catch (ValidationException $e) { |
||||||
| 586 | $this->fail('Failed to write VirtualPage when it is an allowed child'); |
||||||
| 587 | } |
||||||
| 588 | |||||||
| 589 | // Check that we can link a VirtualPage to a page type that's an allowed child |
||||||
| 590 | $childVirtualPage->CopyContentFromID = $childPage->ID; |
||||||
| 591 | try { |
||||||
| 592 | $childVirtualPage->write(); |
||||||
| 593 | } catch (ValidationException $e) { |
||||||
| 594 | $this->fail('Failed to write VirtualPage when it is linked to an allowed child'); |
||||||
| 595 | } |
||||||
| 596 | |||||||
| 597 | // Check that we CAN'T link a VirtualPage to a page that is NOT an allowed child |
||||||
| 598 | $disallowedChild = new VirtualPageTest_ClassB(); |
||||||
| 599 | $disallowedChild->write(); |
||||||
| 600 | $childVirtualPage->CopyContentFromID = $disallowedChild->ID; |
||||||
| 601 | $isDetected = false; |
||||||
| 602 | try { |
||||||
| 603 | $childVirtualPage->write(); |
||||||
| 604 | } catch (ValidationException $e) { |
||||||
| 605 | $this->assertStringContainsString('not allowed as child of this parent page', $e->getMessage()); |
||||||
| 606 | $isDetected = true; |
||||||
| 607 | } |
||||||
| 608 | |||||||
| 609 | if (!$isDetected) { |
||||||
| 610 | $this->fail("Shouldn't be allowed to write a VirtualPage that links to a disallowed child"); |
||||||
| 611 | } |
||||||
| 612 | } |
||||||
| 613 | |||||||
| 614 | public function testVirtualPagePointingToRedirectorPage() |
||||||
| 615 | { |
||||||
| 616 | $rp = new RedirectorPage(['ExternalURL' => 'http://google.com', 'RedirectionType' => 'External']); |
||||||
| 617 | $rp->write(); |
||||||
| 618 | $rp->publishRecursive(); |
||||||
| 619 | |||||||
| 620 | $vp = new VirtualPage(['URLSegment' => 'vptest', 'CopyContentFromID' => $rp->ID]); |
||||||
| 621 | $vp->write(); |
||||||
| 622 | $vp->publishRecursive(); |
||||||
| 623 | |||||||
| 624 | $response = $this->get($vp->Link()); |
||||||
| 625 | $this->assertEquals(301, $response->getStatusCode()); |
||||||
| 626 | $this->assertEquals('http://google.com', $response->getHeader('Location')); |
||||||
| 627 | } |
||||||
| 628 | |||||||
| 629 | public function testVirtualPageRendersCorrectTemplate() |
||||||
| 630 | { |
||||||
| 631 | $this->useTestTheme(dirname(__FILE__), 'virtualpagetest', function () { |
||||||
| 632 | $page = new VirtualPageTest_ClassA(); |
||||||
| 633 | $page->Title = 'Test Page'; |
||||||
| 634 | $page->Content = 'NotThisContent'; |
||||||
| 635 | $page->MyInitiallyCopiedField = 'TestContent'; |
||||||
|
0 ignored issues
–
show
The property
MyInitiallyCopiedField does not exist on SilverStripe\CMS\Tests\M...\VirtualPageTest_ClassA. Since you implemented __set, consider adding a @property annotation.
Loading history...
|
|||||||
| 636 | $page->write(); |
||||||
| 637 | $page->publishSingle(); |
||||||
|
0 ignored issues
–
show
The method
publishSingle() does not exist on SilverStripe\CMS\Tests\M...\VirtualPageTest_ClassA. Since you implemented __call, consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
| 638 | |||||||
| 639 | $vp = new VirtualPage(); |
||||||
| 640 | $vp->CopyContentFromID = $page->ID; |
||||||
| 641 | $vp->write(); |
||||||
| 642 | $vp->publishSingle(); |
||||||
| 643 | |||||||
| 644 | $response = $this->get($vp->Link()); |
||||||
| 645 | $this->assertEquals(200, $response->getStatusCode()); |
||||||
| 646 | $this->assertStringContainsString('TestContent', $response->getBody()); |
||||||
| 647 | $this->assertStringNotContainsString('NotThisContent', $response->getBody()); |
||||||
| 648 | |||||||
| 649 | // VirtualPageTest_ClassB doesn't have an associated controller for |
||||||
| 650 | // ModelAsController::controller_for() to find |
||||||
| 651 | $page = new VirtualPageTest_ClassB(); |
||||||
| 652 | $page->Title = 'Test Page B'; |
||||||
| 653 | $page->write(); |
||||||
| 654 | $page->publishSingle(); |
||||||
|
0 ignored issues
–
show
The method
publishSingle() does not exist on SilverStripe\CMS\Tests\M...\VirtualPageTest_ClassB. Since you implemented __call, consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
| 655 | |||||||
| 656 | $vp = new VirtualPage(); |
||||||
| 657 | $vp->CopyContentFromID = $page->ID; |
||||||
| 658 | $vp->write(); |
||||||
| 659 | $vp->publishSingle(); |
||||||
| 660 | |||||||
| 661 | $response = $this->get($vp->Link()); |
||||||
| 662 | $this->assertEquals(200, $response->getStatusCode()); |
||||||
| 663 | $this->assertStringContainsString('Test Page B', $response->getBody()); |
||||||
| 664 | }); |
||||||
| 665 | } |
||||||
| 666 | |||||||
| 667 | public function testMethod() |
||||||
| 668 | { |
||||||
| 669 | /** @var VirtualPage $virtualPage */ |
||||||
| 670 | $virtualPage = $this->objFromFixture(VirtualPage::class, 'vp4'); |
||||||
| 671 | /** @var VirtualPageTest_ClassAController $controller */ |
||||||
| 672 | $controller = ModelAsController::controller_for($virtualPage); |
||||||
| 673 | $this->assertInstanceOf(VirtualPageTest_ClassAController::class, $controller); |
||||||
| 674 | $this->assertTrue($controller->hasMethod('testMethod')); |
||||||
| 675 | $this->assertEquals('hello', $controller->testMethod()); |
||||||
| 676 | $this->assertTrue($controller->hasMethod('modelMethod')); |
||||||
| 677 | $this->assertEquals('hi there', $controller->modelMethod()); |
||||||
|
0 ignored issues
–
show
The method
modelMethod() does not exist on SilverStripe\CMS\Tests\M...geTest_ClassAController. Since you implemented __call, consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
| 678 | } |
||||||
| 679 | |||||||
| 680 | public function testAllowedActions() |
||||||
| 681 | { |
||||||
| 682 | /** @var VirtualPage $virtualPage */ |
||||||
| 683 | $virtualPage = $this->objFromFixture(VirtualPage::class, 'vp4'); |
||||||
| 684 | $controller = ModelAsController::controller_for($virtualPage); |
||||||
| 685 | $this->assertContains('testaction', $controller->allowedActions()); |
||||||
| 686 | } |
||||||
| 687 | } |
||||||
| 688 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths