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 ![]() |
|||||||
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.
![]() |
|||||||
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.
![]() |
|||||||
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.
![]() |
|||||||
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.
![]() |
|||||||
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.
![]() |
|||||||
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.
![]() |
|||||||
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
![]() |
|||||||
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
![]() |
|||||||
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
![]() |
|||||||
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