1 | <?php |
||
2 | |||
3 | namespace SilverStripe\CMS\Tests\Controllers; |
||
4 | |||
5 | use Psr\SimpleCache\CacheInterface; |
||
6 | use SilverStripe\Admin\CMSBatchActionHandler; |
||
7 | use SilverStripe\CMS\Controllers\CMSMain; |
||
8 | use SilverStripe\CMS\Model\RedirectorPage; |
||
9 | use SilverStripe\CMS\Model\SiteTree; |
||
10 | use SilverStripe\Control\Controller; |
||
11 | use SilverStripe\Control\HTTPRequest; |
||
12 | use SilverStripe\Control\HTTPResponse_Exception; |
||
13 | use SilverStripe\Core\ClassInfo; |
||
14 | use SilverStripe\Core\Config\Config; |
||
15 | use SilverStripe\Core\Convert; |
||
16 | use SilverStripe\Core\Injector\Injector; |
||
17 | use SilverStripe\Dev\CSSContentParser; |
||
18 | use SilverStripe\Dev\FunctionalTest; |
||
19 | use SilverStripe\Dev\TestOnly; |
||
20 | use SilverStripe\Forms\FieldList; |
||
21 | use SilverStripe\ORM\DataObject; |
||
22 | use SilverStripe\ORM\DB; |
||
23 | use SilverStripe\Security\Member; |
||
24 | use SilverStripe\Security\Security; |
||
25 | use SilverStripe\SiteConfig\SiteConfig; |
||
26 | use SilverStripe\Versioned\Versioned; |
||
27 | |||
28 | class CMSMainTest extends FunctionalTest |
||
29 | { |
||
30 | protected static $fixture_file = 'CMSMainTest.yml'; |
||
31 | |||
32 | protected static $orig = []; |
||
33 | |||
34 | protected function setUp() |
||
35 | { |
||
36 | parent::setUp(); |
||
37 | |||
38 | // Clear automatically created siteconfigs (in case one was created outside of the specified fixtures). |
||
39 | $ids = $this->allFixtureIDs(SiteConfig::class); |
||
40 | if ($ids) { |
||
0 ignored issues
–
show
|
|||
41 | foreach (SiteConfig::get()->exclude('ID', $ids) as $config) { |
||
42 | $config->delete(); |
||
43 | } |
||
44 | } |
||
45 | } |
||
46 | |||
47 | public function testSiteTreeHints() |
||
48 | { |
||
49 | $cache = Injector::inst()->get(CacheInterface::class . '.CMSMain_SiteTreeHints'); |
||
50 | // Login as user with root creation privileges |
||
51 | $user = $this->objFromFixture(Member::class, 'rootedituser'); |
||
52 | Security::setCurrentUser($user); |
||
53 | $cache->clear(); |
||
54 | |||
55 | $rawHints = singleton(CMSMain::class)->SiteTreeHints(); |
||
56 | $this->assertNotNull($rawHints); |
||
57 | |||
58 | $rawHints = preg_replace('/^"(.*)"$/', '$1', Convert::xml2raw($rawHints)); |
||
59 | $hints = json_decode($rawHints, true); |
||
60 | |||
61 | $this->assertArrayHasKey('Root', $hints); |
||
62 | $this->assertArrayHasKey('Page', $hints); |
||
63 | $this->assertArrayHasKey('All', $hints); |
||
64 | |||
65 | $this->assertArrayHasKey( |
||
66 | CMSMainTest_ClassA::class, |
||
67 | $hints['All'], |
||
68 | 'Global list shows allowed classes' |
||
69 | ); |
||
70 | |||
71 | $this->assertArrayNotHasKey( |
||
72 | CMSMainTest_HiddenClass::class, |
||
73 | $hints['All'], |
||
74 | 'Global list does not list hidden classes' |
||
75 | ); |
||
76 | |||
77 | $this->assertNotContains( |
||
78 | CMSMainTest_ClassA::class, |
||
79 | $hints['Root']['disallowedChildren'], |
||
80 | 'Limits root classes' |
||
81 | ); |
||
82 | |||
83 | $this->assertContains( |
||
84 | CMSMainTest_NotRoot::class, |
||
85 | $hints['Root']['disallowedChildren'], |
||
86 | 'Limits root classes' |
||
87 | ); |
||
88 | } |
||
89 | |||
90 | public function testChildFilter() |
||
91 | { |
||
92 | $this->logInWithPermission('ADMIN'); |
||
93 | |||
94 | // Check page A |
||
95 | $pageA = new CMSMainTest_ClassA(); |
||
96 | $pageA->write(); |
||
97 | $pageB = new CMSMainTest_ClassB(); |
||
98 | $pageB->write(); |
||
99 | |||
100 | // Check query |
||
101 | $response = $this->get('admin/pages/childfilter?ParentID=' . $pageA->ID); |
||
102 | $children = json_decode($response->getBody()); |
||
103 | $this->assertFalse($response->isError()); |
||
104 | |||
105 | // Page A can't have unrelated children |
||
106 | $this->assertContains( |
||
107 | 'Page', |
||
108 | $children, |
||
109 | 'Limited parent lists disallowed classes' |
||
110 | ); |
||
111 | |||
112 | // But it can create a ClassB |
||
113 | $this->assertNotContains( |
||
114 | CMSMainTest_ClassB::class, |
||
115 | $children, |
||
116 | 'Limited parent omits explicitly allowed classes in disallowedChildren' |
||
117 | ); |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * @todo Test the results of a publication better |
||
122 | */ |
||
123 | public function testPublish() |
||
124 | { |
||
125 | $page1 = $this->objFromFixture(SiteTree::class, "page1"); |
||
126 | $page2 = $this->objFromFixture(SiteTree::class, "page2"); |
||
127 | $this->session()->set('loggedInAs', $this->idFromFixture(Member::class, 'admin')); |
||
128 | |||
129 | $response = $this->get('admin/pages/publishall?confirm=1'); |
||
130 | $this->assertContains( |
||
131 | 'Done: Published 30 pages', |
||
132 | $response->getBody() |
||
133 | ); |
||
134 | |||
135 | // Some modules (e.g., cmsworkflow) will remove this action |
||
136 | $actions = CMSBatchActionHandler::config()->batch_actions; |
||
137 | if (isset($actions['publish'])) { |
||
138 | $response = $this->get( |
||
139 | 'admin/pages/batchactions/publish?ajax=1&csvIDs=' . implode(',', [$page1->ID, $page2->ID]) |
||
140 | ); |
||
141 | $responseData = json_decode($response->getBody(), true); |
||
142 | $this->assertArrayHasKey($page1->ID, $responseData['modified']); |
||
143 | $this->assertArrayHasKey($page2->ID, $responseData['modified']); |
||
144 | } |
||
145 | |||
146 | // Get the latest version of the redirector page |
||
147 | $pageID = $this->idFromFixture(RedirectorPage::class, 'page5'); |
||
148 | $latestID = DB::prepared_query( |
||
149 | 'SELECT MAX("Version") FROM "RedirectorPage_Versions" WHERE "RecordID" = ?', |
||
150 | [$pageID] |
||
151 | )->value(); |
||
152 | $dsCount = DB::prepared_query( |
||
153 | 'SELECT COUNT("Version") FROM "RedirectorPage_Versions" WHERE "RecordID" = ? AND "Version"= ?', |
||
154 | [$pageID, $latestID] |
||
155 | )->value(); |
||
156 | $this->assertEquals( |
||
157 | 1, |
||
158 | $dsCount, |
||
159 | "Published page has no duplicate version records: it has " . $dsCount . " for version " . $latestID |
||
160 | ); |
||
161 | |||
162 | $this->session()->clear('loggedInAs'); |
||
163 | } |
||
164 | |||
165 | /** |
||
166 | * Test that getCMSFields works on each page type. |
||
167 | * Mostly, this is just checking that the method doesn't return an error |
||
168 | */ |
||
169 | public function testThatGetCMSFieldsWorksOnEveryPageType() |
||
170 | { |
||
171 | $classes = ClassInfo::subclassesFor(SiteTree::class); |
||
172 | array_shift($classes); |
||
173 | |||
174 | foreach ($classes as $class) { |
||
175 | $page = new $class(); |
||
176 | if ($page instanceof TestOnly) { |
||
177 | continue; |
||
178 | } |
||
179 | if (!$page->config()->get('can_be_root')) { |
||
180 | continue; |
||
181 | } |
||
182 | |||
183 | $page->Title = "Test $class page"; |
||
184 | $page->write(); |
||
185 | $page->flushCache(); |
||
186 | $page = DataObject::get_by_id(SiteTree::class, $page->ID); |
||
187 | |||
188 | $this->assertTrue($page->getCMSFields() instanceof FieldList); |
||
189 | } |
||
190 | } |
||
191 | |||
192 | public function testCanPublishPageWithUnpublishedParentWithStrictHierarchyOff() |
||
193 | { |
||
194 | $this->logInWithPermission('ADMIN'); |
||
195 | |||
196 | Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true); |
||
197 | $parentPage = $this->objFromFixture(SiteTree::class, 'page3'); |
||
198 | $childPage = $this->objFromFixture(SiteTree::class, 'page1'); |
||
199 | |||
200 | $parentPage->doUnpublish(); |
||
201 | $childPage->doUnpublish(); |
||
202 | |||
203 | $actions = $childPage->getCMSActions()->dataFields(); |
||
204 | $this->assertArrayHasKey( |
||
205 | 'action_publish', |
||
206 | $actions, |
||
207 | 'Can publish a page with an unpublished parent with strict hierarchy off' |
||
208 | ); |
||
209 | Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false); |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Test that a draft-deleted page can still be opened in the CMS |
||
214 | */ |
||
215 | public function testDraftDeletedPageCanBeOpenedInCMS() |
||
216 | { |
||
217 | $this->logInWithPermission('ADMIN'); |
||
218 | |||
219 | // Set up a page that is delete from live |
||
220 | $page = $this->objFromFixture(SiteTree::class, 'page1'); |
||
221 | $pageID = $page->ID; |
||
222 | $page->publishRecursive(); |
||
223 | $page->delete(); |
||
224 | |||
225 | $response = $this->get('admin/pages/edit/show/' . $pageID); |
||
226 | |||
227 | $livePage = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, [ |
||
228 | '"SiteTree"."ID"' => $pageID, |
||
229 | ]); |
||
230 | $this->assertInstanceOf(SiteTree::class, $livePage); |
||
231 | $this->assertTrue($livePage->canDelete()); |
||
232 | |||
233 | // Check that the 'restore' button exists as a simple way of checking that the correct page is returned. |
||
234 | $this->assertRegExp('/<button type="submit"[^>]+name="action_(restore|revert)"/i', $response->getBody()); |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Test CMSMain::getRecord() |
||
239 | */ |
||
240 | public function testGetRecord() |
||
241 | { |
||
242 | $this->logInWithPermission('ADMIN'); |
||
243 | |||
244 | // Set up a page that is delete from live |
||
245 | $page1 = $this->objFromFixture(SiteTree::class, 'page1'); |
||
246 | $page1ID = $page1->ID; |
||
247 | $page1->publishRecursive(); |
||
248 | $page1->delete(); |
||
249 | |||
250 | $cmsMain = CMSMain::create(); |
||
251 | $cmsMain->setRequest(Controller::curr()->getRequest()); |
||
252 | |||
253 | // Bad calls |
||
254 | $this->assertNull($cmsMain->getRecord('0')); |
||
255 | $this->assertNull($cmsMain->getRecord('asdf')); |
||
256 | |||
257 | // Pages that are on draft and aren't on draft should both work |
||
258 | $this->assertInstanceOf(SiteTree::class, $cmsMain->getRecord($page1ID)); |
||
259 | $this->assertInstanceOf(SiteTree::class, $cmsMain->getRecord($this->idFromFixture(SiteTree::class, 'page2'))); |
||
260 | |||
261 | // This functionality isn't actually used any more. |
||
262 | $newPage = $cmsMain->getRecord('new-Page-5'); |
||
263 | $this->assertInstanceOf(SiteTree::class, $newPage); |
||
264 | $this->assertEquals('5', $newPage->ParentID); |
||
265 | } |
||
266 | |||
267 | public function testDeletedPagesSiteTreeFilter() |
||
268 | { |
||
269 | $id = $this->idFromFixture(SiteTree::class, 'page3'); |
||
270 | $this->logInWithPermission('ADMIN'); |
||
271 | $result = $this->get('admin/pages/getsubtree?filter=CMSSiteTreeFilter_DeletedPages&ajax=1&ID=' . $id); |
||
272 | $this->assertEquals(200, $result->getStatusCode()); |
||
273 | } |
||
274 | |||
275 | public function testCreationOfTopLevelPage() |
||
276 | { |
||
277 | $origFollow = $this->autoFollowRedirection; |
||
278 | $this->autoFollowRedirection = false; |
||
279 | |||
280 | $cmsUser = $this->objFromFixture(Member::class, 'allcmssectionsuser'); |
||
281 | $rootEditUser = $this->objFromFixture(Member::class, 'rootedituser'); |
||
282 | |||
283 | // with insufficient permissions |
||
284 | Security::setCurrentUser($cmsUser); |
||
285 | $this->get('admin/pages/add'); |
||
286 | $response = $this->post( |
||
287 | 'admin/pages/add/AddForm', |
||
288 | [ |
||
289 | 'ParentID' => '0', |
||
290 | 'PageType' => RedirectorPage::class, |
||
291 | 'Locale' => 'en_US', |
||
292 | 'action_doAdd' => 1, |
||
293 | 'ajax' => 1, |
||
294 | ], |
||
295 | [ |
||
296 | 'X-Pjax' => 'CurrentForm,Breadcrumbs', |
||
297 | ] |
||
298 | ); |
||
299 | // should redirect, which is a permission error |
||
300 | $this->assertEquals(403, $response->getStatusCode(), 'Add TopLevel page must fail for normal user'); |
||
301 | |||
302 | // with correct permissions |
||
303 | Security::setCurrentUser($rootEditUser); |
||
304 | $response = $this->get('admin/pages/add'); |
||
305 | |||
306 | $response = $this->post( |
||
307 | 'admin/pages/add/AddForm', |
||
308 | [ |
||
309 | 'ParentID' => '0', |
||
310 | 'PageType' => RedirectorPage::class, |
||
311 | 'Locale' => 'en_US', |
||
312 | 'action_doAdd' => 1, |
||
313 | 'ajax' => 1, |
||
314 | ], |
||
315 | [ |
||
316 | 'X-Pjax' => 'CurrentForm,Breadcrumbs', |
||
317 | ] |
||
318 | ); |
||
319 | |||
320 | $location = $response->getHeader('X-ControllerURL'); |
||
321 | $this->assertNotEmpty($location, 'Must be a redirect on success'); |
||
322 | $this->assertContains('/show/', $location, 'Must redirect to /show/ the new page'); |
||
323 | // TODO Logout |
||
324 | Security::setCurrentUser(null); |
||
325 | |||
326 | $this->autoFollowRedirection = $origFollow; |
||
327 | } |
||
328 | |||
329 | public function testCreationOfRestrictedPage() |
||
330 | { |
||
331 | $origFollow = $this->autoFollowRedirection; |
||
332 | $this->autoFollowRedirection = false; |
||
333 | |||
334 | $adminUser = $this->objFromFixture(Member::class, 'admin'); |
||
335 | Security::setCurrentUser($adminUser); |
||
336 | |||
337 | // Create toplevel page |
||
338 | $this->get('admin/pages/add'); |
||
339 | $response = $this->post( |
||
340 | 'admin/pages/add/AddForm', |
||
341 | [ |
||
342 | 'ParentID' => '0', |
||
343 | 'PageType' => CMSMainTest_ClassA::class, |
||
344 | 'Locale' => 'en_US', |
||
345 | 'action_doAdd' => 1, |
||
346 | 'ajax' => 1, |
||
347 | ], |
||
348 | [ |
||
349 | 'X-Pjax' => 'CurrentForm,Breadcrumbs', |
||
350 | ] |
||
351 | ); |
||
352 | $this->assertFalse($response->isError()); |
||
353 | $ok = preg_match('/edit\/show\/(\d*)/', $response->getHeader('X-ControllerURL'), $matches); |
||
354 | $this->assertNotEmpty($ok); |
||
355 | $newPageId = $matches[1]; |
||
356 | |||
357 | // Create allowed child |
||
358 | $this->get('admin/pages/add'); |
||
359 | $response = $this->post( |
||
360 | 'admin/pages/add/AddForm', |
||
361 | [ |
||
362 | 'ParentID' => $newPageId, |
||
363 | 'PageType' => CMSMainTest_ClassB::class, |
||
364 | 'Locale' => 'en_US', |
||
365 | 'action_doAdd' => 1, |
||
366 | 'ajax' => 1, |
||
367 | ], |
||
368 | [ |
||
369 | 'X-Pjax' => 'CurrentForm,Breadcrumbs', |
||
370 | ] |
||
371 | ); |
||
372 | $this->assertFalse($response->isError()); |
||
373 | $this->assertEmpty($response->getBody()); |
||
374 | |||
375 | // Verify that the page was created and redirected to accurately |
||
376 | $newerPage = SiteTree::get()->byID($newPageId)->AllChildren()->first(); |
||
377 | $this->assertNotEmpty($newerPage); |
||
378 | $ok = preg_match('/edit\/show\/(\d*)/', $response->getHeader('X-ControllerURL'), $matches); |
||
379 | $this->assertNotEmpty($ok); |
||
380 | $newerPageID = $matches[1]; |
||
381 | $this->assertEquals($newerPage->ID, $newerPageID); |
||
382 | |||
383 | // Create disallowed child |
||
384 | $this->get('admin/pages/add'); |
||
385 | $response = $this->post( |
||
386 | 'admin/pages/add/AddForm', |
||
387 | [ |
||
388 | 'ParentID' => $newPageId, |
||
389 | 'PageType' => RedirectorPage::class, |
||
390 | 'Locale' => 'en_US', |
||
391 | 'action_doAdd' => 1, |
||
392 | 'ajax' => 1, |
||
393 | ], |
||
394 | [ |
||
395 | 'X-Pjax' => 'CurrentForm,Breadcrumbs', |
||
396 | ] |
||
397 | ); |
||
398 | $this->assertEquals(403, $response->getStatusCode(), 'Add disallowed child should fail'); |
||
399 | |||
400 | Security::setCurrentUser(null); |
||
401 | |||
402 | $this->autoFollowRedirection = $origFollow; |
||
403 | } |
||
404 | |||
405 | public function testBreadcrumbs() |
||
406 | { |
||
407 | $page3 = $this->objFromFixture(SiteTree::class, 'page3'); |
||
408 | $page31 = $this->objFromFixture(SiteTree::class, 'page31'); |
||
409 | $adminuser = $this->objFromFixture(Member::class, 'admin'); |
||
410 | Security::setCurrentUser($adminuser); |
||
411 | |||
412 | $response = $this->get('admin/pages/edit/show/' . $page31->ID); |
||
413 | $parser = new CSSContentParser($response->getBody()); |
||
414 | $crumbs = $parser->getBySelector('.breadcrumbs-wrapper .crumb'); |
||
415 | |||
416 | $this->assertNotNull($crumbs); |
||
417 | $this->assertEquals(2, count($crumbs)); |
||
418 | $this->assertEquals('Page 3', (string)$crumbs[0]); |
||
419 | $this->assertEquals('Page 3.1', (string)$crumbs[1]); |
||
420 | |||
421 | Security::setCurrentUser(null); |
||
422 | } |
||
423 | |||
424 | public function testGetNewItem() |
||
425 | { |
||
426 | $controller = CMSMain::create(); |
||
427 | $controller->setRequest(Controller::curr()->getRequest()); |
||
428 | $id = 'new-Page-0'; |
||
429 | |||
430 | // Test success |
||
431 | $page = $controller->getNewItem($id, false); |
||
432 | |||
433 | $this->assertEquals($page->Title, 'New Page'); |
||
434 | $this->assertNotEquals($page->Sort, 0); |
||
435 | $this->assertInstanceOf(SiteTree::class, $page); |
||
436 | |||
437 | // Test failure |
||
438 | try { |
||
439 | $id = 'new-Member-0'; |
||
440 | $member = $controller->getNewItem($id, false); |
||
441 | $this->fail('Should not be able to create a Member object'); |
||
442 | } catch (HTTPResponse_Exception $e) { |
||
443 | $this->assertEquals($controller->getResponse()->getStatusCode(), 302); |
||
444 | } |
||
445 | } |
||
446 | |||
447 | /** |
||
448 | * Tests filtering in {@see CMSMain::getList()} |
||
449 | */ |
||
450 | public function testGetList() |
||
451 | { |
||
452 | $controller = CMSMain::create(); |
||
453 | $controller->setRequest(Controller::curr()->getRequest()); |
||
454 | |||
455 | // Test all pages (stage) |
||
456 | $pages = $controller->getList()->sort('Title'); |
||
457 | $this->assertEquals(28, $pages->count()); |
||
458 | $this->assertEquals( |
||
459 | ['Home', 'Page 1', 'Page 10', 'Page 11', 'Page 12'], |
||
460 | $pages->Limit(5)->column('Title') |
||
461 | ); |
||
462 | |||
463 | // Change state of tree |
||
464 | $page1 = $this->objFromFixture(SiteTree::class, 'page1'); |
||
465 | $page3 = $this->objFromFixture(SiteTree::class, 'page3'); |
||
466 | $page11 = $this->objFromFixture(SiteTree::class, 'page11'); |
||
467 | $page12 = $this->objFromFixture(SiteTree::class, 'page12'); |
||
468 | // Deleted |
||
469 | $page1->doUnpublish(); |
||
470 | $page1->delete(); |
||
471 | // Live and draft |
||
472 | $page11->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); |
||
473 | // Live only |
||
474 | $page12->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); |
||
475 | $page12->delete(); |
||
476 | |||
477 | // Re-test all pages (stage) |
||
478 | $pages = $controller->getList()->sort('Title'); |
||
479 | $this->assertEquals(26, $pages->count()); |
||
480 | $this->assertEquals( |
||
481 | ['Home', 'Page 10', 'Page 11', 'Page 13', 'Page 14'], |
||
482 | $pages->Limit(5)->column('Title') |
||
483 | ); |
||
484 | |||
485 | // Test deleted page filter |
||
486 | $params = [ |
||
487 | 'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusDeletedPages', |
||
488 | ]; |
||
489 | $pages = $controller->getList($params); |
||
490 | $this->assertEquals(1, $pages->count()); |
||
491 | $this->assertEquals( |
||
492 | ['Page 1'], |
||
493 | $pages->column('Title') |
||
494 | ); |
||
495 | |||
496 | // Test live, but not on draft filter |
||
497 | $params = [ |
||
498 | 'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusRemovedFromDraftPages', |
||
499 | ]; |
||
500 | $pages = $controller->getList($params); |
||
501 | $this->assertEquals(1, $pages->count()); |
||
502 | $this->assertEquals( |
||
503 | ['Page 12'], |
||
504 | $pages->column('Title') |
||
505 | ); |
||
506 | |||
507 | // Test live pages filter |
||
508 | $params = [ |
||
509 | 'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_PublishedPages', |
||
510 | ]; |
||
511 | $pages = $controller->getList($params); |
||
512 | $this->assertEquals(2, $pages->count()); |
||
513 | $this->assertEquals( |
||
514 | ['Page 11', 'Page 12'], |
||
515 | $pages->column('Title') |
||
516 | ); |
||
517 | |||
518 | // Test that parentID is ignored when filtering |
||
519 | $pages = $controller->getList($params, $page3->ID); |
||
520 | $this->assertEquals(2, $pages->count()); |
||
521 | $this->assertEquals( |
||
522 | ['Page 11', 'Page 12'], |
||
523 | $pages->column('Title') |
||
524 | ); |
||
525 | |||
526 | // Test that parentID is respected when not filtering |
||
527 | $pages = $controller->getList([], $page3->ID); |
||
528 | $this->assertEquals(2, $pages->count()); |
||
529 | $this->assertEquals( |
||
530 | ['Page 3.1', 'Page 3.2'], |
||
531 | $pages->column('Title') |
||
532 | ); |
||
533 | } |
||
534 | |||
535 | /** |
||
536 | * Testing retrieval and type of CMS edit form. |
||
537 | */ |
||
538 | public function testGetEditForm() |
||
539 | { |
||
540 | // Login is required prior to accessing a CMS form. |
||
541 | $this->loginWithPermission('ADMIN'); |
||
542 | |||
543 | // Get a associated with a fixture page. |
||
544 | $page = $this->objFromFixture(SiteTree::class, 'page1'); |
||
545 | $controller = CMSMain::create(); |
||
546 | $controller->setRequest(Controller::curr()->getRequest()); |
||
547 | $form = $controller->getEditForm($page->ID); |
||
548 | $this->assertInstanceOf("SilverStripe\\Forms\\Form", $form); |
||
549 | |||
550 | // Ensure that the form will not "validate" on delete or "unpublish" actions. |
||
551 | $exemptActions = $form->getValidationExemptActions(); |
||
552 | $this->assertContains("delete", $exemptActions); |
||
553 | $this->assertContains("unpublish", $exemptActions); |
||
554 | } |
||
555 | |||
556 | /** |
||
557 | * Test that changed classes save with the correct class name |
||
558 | */ |
||
559 | public function testChangeClass() |
||
560 | { |
||
561 | $this->logInWithPermission('ADMIN'); |
||
562 | $cms = CMSMain::create(); |
||
563 | $cms->setRequest(Controller::curr()->getRequest()); |
||
564 | $page = new CMSMainTest_ClassA(); |
||
565 | $page->Title = 'Class A'; |
||
566 | $page->write(); |
||
567 | |||
568 | $form = $cms->getEditForm($page->ID); |
||
569 | $form->loadDataFrom(['ClassName' => CMSMainTest_ClassB::class]); |
||
570 | $result = $cms->save([ |
||
571 | 'ID' => $page->ID, |
||
572 | 'ClassName' => CMSMainTest_ClassB::class, |
||
573 | ], $form); |
||
574 | $this->assertEquals(200, $result->getStatusCode()); |
||
575 | |||
576 | $newPage = SiteTree::get()->byID($page->ID); |
||
577 | |||
578 | $this->assertInstanceOf(CMSMainTest_ClassB::class, $newPage); |
||
579 | $this->assertEquals(CMSMainTest_ClassB::class, $newPage->ClassName); |
||
580 | $this->assertEquals('Class A', $newPage->Title); |
||
581 | } |
||
582 | |||
583 | public function testSiteTreeHintsCache() |
||
584 | { |
||
585 | $cms = CMSMain::create(); |
||
586 | /** @var Member $user */ |
||
587 | $user = $this->objFromFixture(Member::class, 'rootedituser'); |
||
588 | Security::setCurrentUser($user); |
||
589 | $pageClass = array_values(SiteTree::page_type_classes())[0]; |
||
590 | $mockPageMissesCache = $this->getMockBuilder($pageClass) |
||
591 | ->setMethods(['canCreate']) |
||
592 | ->getMock(); |
||
593 | $mockPageMissesCache |
||
594 | ->expects($this->exactly(3)) |
||
595 | ->method('canCreate'); |
||
596 | |||
597 | $mockPageHitsCache = $this->getMockBuilder($pageClass) |
||
598 | ->setMethods(['canCreate']) |
||
599 | ->getMock(); |
||
600 | $mockPageHitsCache |
||
601 | ->expects($this->never()) |
||
602 | ->method('canCreate'); |
||
603 | |||
604 | |||
605 | // Initially, cache misses (1) |
||
606 | Injector::inst()->registerService($mockPageMissesCache, $pageClass); |
||
607 | $hints = $cms->SiteTreeHints(); |
||
608 | $this->assertNotNull($hints); |
||
609 | |||
610 | // Now it hits |
||
611 | Injector::inst()->registerService($mockPageHitsCache, $pageClass); |
||
612 | $hints = $cms->SiteTreeHints(); |
||
613 | $this->assertNotNull($hints); |
||
614 | |||
615 | // Mutating member record invalidates cache. Misses (2) |
||
616 | $user->FirstName = 'changed'; |
||
617 | $user->write(); |
||
618 | Injector::inst()->registerService($mockPageMissesCache, $pageClass); |
||
619 | $hints = $cms->SiteTreeHints(); |
||
620 | $this->assertNotNull($hints); |
||
621 | |||
622 | // Now it hits again |
||
623 | Injector::inst()->registerService($mockPageHitsCache, $pageClass); |
||
624 | $hints = $cms->SiteTreeHints(); |
||
625 | $this->assertNotNull($hints); |
||
626 | |||
627 | // Different user. Misses. (3) |
||
628 | $user = $this->objFromFixture(Member::class, 'allcmssectionsuser'); |
||
629 | Security::setCurrentUser($user); |
||
630 | Injector::inst()->registerService($mockPageMissesCache, $pageClass); |
||
631 | $hints = $cms->SiteTreeHints(); |
||
632 | $this->assertNotNull($hints); |
||
633 | } |
||
634 | |||
635 | public function testSearchField() |
||
636 | { |
||
637 | $cms = CMSMain::create(); |
||
638 | $searchSchema = $cms->getSearchFieldSchema(); |
||
639 | |||
640 | $this->assertJsonStringEqualsJsonString( |
||
641 | json_encode([ |
||
642 | 'formSchemaUrl' => 'admin/pages/schema/SearchForm', |
||
643 | 'name' => 'Term', |
||
644 | 'placeholder' => 'Search "Pages"', |
||
645 | 'filters' => new \stdClass() |
||
646 | ]), |
||
647 | $searchSchema |
||
648 | ); |
||
649 | |||
650 | $request = new HTTPRequest( |
||
651 | 'GET', |
||
652 | 'admin/pages/schema/SearchForm', |
||
653 | ['q' => [ |
||
654 | 'Term' => 'test', |
||
655 | 'FilterClass' => 'SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search' |
||
656 | ]] |
||
657 | ); |
||
658 | $cms->setRequest($request); |
||
659 | $searchSchema = $cms->getSearchFieldSchema(); |
||
660 | |||
661 | $this->assertJsonStringEqualsJsonString( |
||
662 | json_encode([ |
||
663 | 'formSchemaUrl' => 'admin/pages/schema/SearchForm', |
||
664 | 'name' => 'Term', |
||
665 | 'placeholder' => 'Search "Pages"', |
||
666 | 'filters' => [ |
||
667 | 'Search__Term' => 'test', |
||
668 | 'Search__FilterClass' => 'SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search' |
||
669 | ] |
||
670 | ]), |
||
671 | $searchSchema |
||
672 | ); |
||
673 | } |
||
674 | |||
675 | public function testCanOrganiseSitetree() |
||
676 | { |
||
677 | $cms = CMSMain::create(); |
||
678 | |||
679 | $this->assertFalse($cms->CanOrganiseSitetree()); |
||
680 | |||
681 | $this->logInWithPermission('CMS_ACCESS_CMSMain'); |
||
682 | $this->assertFalse($cms->CanOrganiseSitetree()); |
||
683 | |||
684 | $this->logOut(); |
||
685 | $this->logInWithPermission('SITETREE_REORGANISE'); |
||
686 | $this->assertTrue($cms->CanOrganiseSitetree()); |
||
687 | |||
688 | $this->logOut(); |
||
689 | $this->logInWithPermission('ADMIN'); |
||
690 | $this->assertTrue($cms->CanOrganiseSitetree()); |
||
691 | } |
||
692 | } |
||
693 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.