Issues (144)

tests/php/SubsiteTest.php (5 issues)

1
<?php
2
3
namespace SilverStripe\Subsites\Tests;
4
5
use Page;
6
use SilverStripe\CMS\Model\SiteTree;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Core\Config\Config;
9
use SilverStripe\ORM\DataObject;
10
use SilverStripe\Security\Member;
11
use SilverStripe\Subsites\Model\Subsite;
12
use SilverStripe\Subsites\Model\SubsiteDomain;
13
use SilverStripe\Subsites\State\SubsiteState;
14
use UnexpectedValueException;
15
16
class SubsiteTest extends BaseSubsiteTest
17
{
18
    protected static $fixture_file = 'SubsiteTest.yml';
19
20
    protected $usesTransactions = false;
21
22
    /**
23
     * Original value of $_REQUEST
24
     *
25
     * @var array
26
     */
27
    protected $origServer = [];
28
29
    protected function setUp()
30
    {
31
        parent::setUp();
32
33
        Config::modify()
34
            ->set(Director::class, 'alternate_base_url', '/')
35
            ->set(Subsite::class, 'strict_subdomain_matching', false)
36
            ->set(Subsite::class, 'write_hostmap', false);
37
38
        $this->origServer = $_SERVER;
39
    }
40
41
    protected function tearDown()
42
    {
43
        $_SERVER = $this->origServer;
44
45
        parent::tearDown();
46
    }
47
48
    /**
49
     * Create a new subsite from the template and verify that all the template's pages are copied
50
     */
51
    public function testSubsiteCreation()
52
    {
53
        // Create the instance
54
        $template = $this->objFromFixture(Subsite::class, 'main');
55
56
        // Test that changeSubsite is working
57
        Subsite::changeSubsite($template->ID);
58
        $this->assertEquals($template->ID, SubsiteState::singleton()->getSubsiteId());
59
        $tmplStaff = $this->objFromFixture('Page', 'staff');
0 ignored issues
show
The assignment to $tmplStaff is dead and can be removed.
Loading history...
60
        $tmplHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'");
0 ignored issues
show
The assignment to $tmplHome is dead and can be removed.
Loading history...
61
62
        // Publish all the pages in the template, testing that DataObject::get only returns pages
63
        // from the chosen subsite
64
        $pages = DataObject::get(SiteTree::class);
65
        $totalPages = $pages->count();
0 ignored issues
show
The assignment to $totalPages is dead and can be removed.
Loading history...
66
        foreach ($pages as $page) {
67
            $this->assertEquals($template->ID, $page->SubsiteID);
68
            $page->copyVersionToStage('Stage', 'Live');
69
        }
70
71
        // Create a new site
72
        $subsite = $template->duplicate();
73
74
        // Check title
75
        $this->assertEquals($subsite->Title, $template->Title);
76
77
        // Another test that changeSubsite is working
78
        $subsite->activate();
79
80
        $siteHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'");
81
        $this->assertNotEquals($siteHome, false, 'Home Page for subsite not found');
82
        $this->assertEquals(
83
            $subsite->ID,
84
            $siteHome->SubsiteID,
85
            'createInstance() copies existing pages retaining the same URLSegment'
86
        );
87
88
        Subsite::changeSubsite(0);
89
    }
90
91
    /**
92
     * Confirm that domain lookup is working
93
     */
94
    public function testDomainLookup()
95
    {
96
        // Clear existing fixtures
97
        foreach (DataObject::get(Subsite::class) as $subsite) {
98
            $subsite->delete();
99
        }
100
        foreach (DataObject::get(SubsiteDomain::class) as $domain) {
101
            $domain->delete();
102
        }
103
104
        // Much more expressive than YML in this case
105
        $subsite1 = $this->createSubsiteWithDomains([
106
            'one.example.org' => true,
107
            'one.*' => false,
108
        ]);
109
        $subsite2 = $this->createSubsiteWithDomains([
110
            'two.mysite.com' => true,
111
            '*.mysite.com' => false,
112
            'subdomain.onmultiplesubsites.com' => false,
113
        ]);
114
        $subsite3 = $this->createSubsiteWithDomains([
115
            'three.*' => true, // wildcards in primary domain are not recommended
116
            'subdomain.unique.com' => false,
117
            '*.onmultiplesubsites.com' => false,
118
        ]);
119
120
        $this->assertEquals(
121
            $subsite3->ID,
122
            Subsite::getSubsiteIDForDomain('subdomain.unique.com'),
123
            'Full unique match'
124
        );
125
126
        $this->assertEquals(
127
            $subsite1->ID,
128
            Subsite::getSubsiteIDForDomain('one.example.org'),
129
            'Full match, doesn\'t complain about multiple matches within a single subsite'
130
        );
131
132
        $failed = false;
133
        try {
134
            Subsite::getSubsiteIDForDomain('subdomain.onmultiplesubsites.com');
135
        } catch (UnexpectedValueException $e) {
136
            $failed = true;
137
        }
138
        $this->assertTrue(
139
            $failed,
140
            'Fails on multiple matches with wildcard vs. www across multiple subsites'
141
        );
142
143
        $this->assertEquals(
144
            $subsite1->ID,
145
            Subsite::getSubsiteIDForDomain('one.unique.com'),
146
            'Fuzzy match suffixed with wildcard (rule "one.*")'
147
        );
148
149
        $this->assertEquals(
150
            $subsite2->ID,
151
            Subsite::getSubsiteIDForDomain('two.mysite.com'),
152
            'Matches correct subsite for rule'
153
        );
154
155
        $this->assertEquals(
156
            $subsite2->ID,
157
            Subsite::getSubsiteIDForDomain('other.mysite.com'),
158
            'Fuzzy match prefixed with wildcard (rule "*.mysite.com")'
159
        );
160
161
        $this->assertEquals(
162
            0,
163
            Subsite::getSubsiteIDForDomain('unknown.madeup.com'),
164
            "Doesn't match unknown subsite"
165
        );
166
    }
167
168
    public function testStrictSubdomainMatching()
169
    {
170
        // Clear existing fixtures
171
        foreach (DataObject::get(Subsite::class) as $subsite) {
172
            $subsite->delete();
173
        }
174
        foreach (DataObject::get(SubsiteDomain::class) as $domain) {
175
            $domain->delete();
176
        }
177
178
        // Much more expressive than YML in this case
179
        $subsite1 = $this->createSubsiteWithDomains([
180
            'example.org' => true,
181
            'example.com' => false,
182
            '*.wildcard.com' => false,
183
        ]);
184
        $subsite2 = $this->createSubsiteWithDomains([
185
            'www.example.org' => true,
186
            'www.wildcard.com' => false,
187
        ]);
188
189
        Config::modify()->set(Subsite::class, 'strict_subdomain_matching', false);
190
191
        $this->assertEquals(
192
            $subsite1->ID,
193
            Subsite::getSubsiteIDForDomain('example.org'),
194
            'Exact matches without strict checking when not using www prefix'
195
        );
196
        $this->assertEquals(
197
            $subsite1->ID,
198
            Subsite::getSubsiteIDForDomain('example.org:1123'),
199
            'Exact matches without strict checking when not using www prefix and ignores port'
200
        );
201
        $this->assertEquals(
202
            $subsite1->ID,
203
            Subsite::getSubsiteIDForDomain('www.example.org'),
204
            'Matches without strict checking when using www prefix, '
205
            .'still matching first domain regardless of www prefix  (falling back to subsite primary key ordering)'
206
        );
207
        $this->assertEquals(
208
            $subsite1->ID,
209
            Subsite::getSubsiteIDForDomain('www.example.org:9923'),
210
            'Matches without strict checking when using www prefix, '
211
            .'still matching first domain without prefix  (falling back to primary key ordering and ignoring port)'
212
        );
213
        $this->assertEquals(
214
            $subsite1->ID,
215
            Subsite::getSubsiteIDForDomain('www.example.com'),
216
            'Fuzzy matches without strict checking with www prefix'
217
        );
218
        $this->assertEquals(
219
            0,
220
            Subsite::getSubsiteIDForDomain('www.wildcard.com'),
221
            'Doesn\'t match www prefix without strict check, even if a wildcard subdomain is in place'
222
        );
223
224
        Config::modify()->set(Subsite::class, 'strict_subdomain_matching', true);
225
226
        $this->assertEquals(
227
            $subsite1->ID,
228
            Subsite::getSubsiteIDForDomain('example.org'),
229
            'Matches with strict checking when not using www prefix'
230
        );
231
        $this->assertEquals(
232
            $subsite1->ID,
233
            Subsite::getSubsiteIDForDomain('example.org:123'),
234
            'Matches with strict checking when not using www prefix and ignores port'
235
        );
236
        $this->assertEquals(
237
            $subsite2->ID, // not 1
238
            Subsite::getSubsiteIDForDomain('www.example.org'),
239
            'Matches with strict checking when using www prefix'
240
        );
241
        $this->assertEquals(
242
            0,
243
            Subsite::getSubsiteIDForDomain('www.example.com'),
244
            'Doesn\'t fuzzy match with strict checking when using www prefix'
245
        );
246
        $failed = false;
247
        try {
248
            Subsite::getSubsiteIDForDomain('www.wildcard.com');
249
        } catch (UnexpectedValueException $e) {
250
            $failed = true;
251
        }
252
        $this->assertTrue(
253
            $failed,
254
            'Fails on multiple matches with strict checking and wildcard vs. www'
255
        );
256
    }
257
258
    protected function createSubsiteWithDomains($domains)
259
    {
260
        $subsite = new Subsite([
261
            'Title' => 'My Subsite'
262
        ]);
263
        $subsite->write();
264
        foreach ($domains as $domainStr => $isPrimary) {
265
            $domain = new SubsiteDomain([
266
                'Domain' => $domainStr,
267
                'IsPrimary' => $isPrimary,
268
                'SubsiteID' => $subsite->ID
269
            ]);
270
            $domain->write();
271
        }
272
273
        return $subsite;
274
    }
275
276
    /**
277
     * Test the Subsite->domain() method
278
     */
279
    public function testDefaultDomain()
280
    {
281
        $this->assertEquals(
282
            'one.example.org',
283
            $this->objFromFixture(Subsite::class, 'domaintest1')->domain()
284
        );
285
286
        $this->assertEquals(
287
            'two.mysite.com',
288
            $this->objFromFixture(Subsite::class, 'domaintest2')->domain()
289
        );
290
291
        $_SERVER['HTTP_HOST'] = 'www.example.org';
292
        $this->assertEquals(
293
            'three.example.org',
294
            $this->objFromFixture(Subsite::class, 'domaintest3')->domain()
295
        );
296
297
        $_SERVER['HTTP_HOST'] = 'mysite.example.org';
298
        $this->assertEquals(
299
            'three.mysite.example.org',
300
            $this->objFromFixture(Subsite::class, 'domaintest3')->domain()
301
        );
302
303
        $this->assertEquals($_SERVER['HTTP_HOST'], singleton(Subsite::class)->PrimaryDomain);
304
        $this->assertEquals(
305
            'http://' . $_SERVER['HTTP_HOST'] . Director::baseURL(),
306
            singleton(Subsite::class)->absoluteBaseURL()
307
        );
308
    }
309
310
    /**
311
     * Tests that Subsite and SubsiteDomain both respect http protocol correctly
312
     *
313
     * @param string $class Fixture class name
314
     * @param string $identifier Fixture identifier
315
     * @param bool $currentIsSecure Whether the current base URL should be secure
316
     * @param string $expected The expected base URL for the subsite or subsite domain
317
     * @dataProvider domainProtocolProvider
318
     */
319
    public function testDomainProtocol($class, $identifier, $currentIsSecure, $expected)
320
    {
321
        /** @var Subsite|SubsiteDomain $model */
322
        $model = $this->objFromFixture($class, $identifier);
323
        $protocol = $currentIsSecure ? 'https' : 'http';
324
        Config::modify()->set(Director::class, 'alternate_base_url', $protocol . '://www.mysite.com');
325
        $this->assertSame($expected, $model->absoluteBaseURL());
326
    }
327
328
    public function domainProtocolProvider()
329
    {
330
        return [
331
            [Subsite::class, 'domaintest2', false, 'http://two.mysite.com/'],
332
            [SubsiteDomain::class, 'dt2a', false, 'http://two.mysite.com/'],
333
            [SubsiteDomain::class, 'dt2b', false, 'http://subsite.mysite.com/'],
334
            [Subsite::class, 'domaintest4', false, 'https://www.primary.com/'],
335
            [SubsiteDomain::class, 'dt4a', false, 'https://www.primary.com/'],
336
            [SubsiteDomain::class, 'dt4b', false, 'http://www.secondary.com/'],
337
            [Subsite::class, 'domaintest5', false, 'http://www.tertiary.com/'],
338
            [SubsiteDomain::class, 'dt5', false,  'http://www.tertiary.com/'],
339
            [Subsite::class, 'domaintest2', true, 'https://two.mysite.com/'],
340
            [SubsiteDomain::class, 'dt2a', true, 'https://two.mysite.com/'],
341
            [SubsiteDomain::class, 'dt2b', true, 'https://subsite.mysite.com/'],
342
            [Subsite::class, 'domaintest4', true, 'https://www.primary.com/'],
343
            [SubsiteDomain::class, 'dt4a', true, 'https://www.primary.com/'],
344
            [SubsiteDomain::class, 'dt4b', true, 'http://www.secondary.com/'],
345
            [Subsite::class, 'domaintest5', true, 'http://www.tertiary.com/'],
346
            [SubsiteDomain::class, 'dt5', true, 'http://www.tertiary.com/'],
347
        ];
348
    }
349
350
    public function testAllSites()
351
    {
352
        $subsites = Subsite::all_sites();
353
        $this->assertDOSEquals([
354
            ['Title' => 'Main site'],
355
            ['Title' => 'Template'],
356
            ['Title' => 'Subsite1 Template'],
357
            ['Title' => 'Subsite2 Template'],
358
            ['Title' => 'Test 1'],
359
            ['Title' => 'Test 2'],
360
            ['Title' => 'Test 3'],
361
            ['Title' => 'Test Non-SSL'],
362
            ['Title' => 'Test SSL'],
363
            ['Title' => 'Test Vagrant VM on port 8080']
364
        ], $subsites, 'Lists all subsites');
0 ignored issues
show
The call to SilverStripe\Dev\SapphireTest::assertDOSEquals() has too many arguments starting with 'Lists all subsites'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

364
        $this->/** @scrutinizer ignore-call */ 
365
               assertDOSEquals([

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
365
    }
366
367
    public function testAllAccessibleSites()
368
    {
369
        $member = $this->objFromFixture(Member::class, 'subsite1member');
370
371
        $subsites = Subsite::all_accessible_sites(true, 'Main site', $member);
372
        $this->assertDOSEquals([
373
            ['Title' => 'Subsite1 Template']
374
        ], $subsites, 'Lists member-accessible sites.');
0 ignored issues
show
The call to SilverStripe\Dev\SapphireTest::assertDOSEquals() has too many arguments starting with 'Lists member-accessible sites.'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

374
        $this->/** @scrutinizer ignore-call */ 
375
               assertDOSEquals([

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
375
    }
376
377
    /**
378
     * Test Subsite::accessible_sites()
379
     */
380
    public function testAccessibleSites()
381
    {
382
        $member1Sites = Subsite::accessible_sites(
383
            'CMS_ACCESS_CMSMain',
384
            false,
385
            null,
386
            $this->objFromFixture(Member::class, 'subsite1member')
387
        );
388
        $member1SiteTitles = $member1Sites->column('Title');
389
        sort($member1SiteTitles);
390
        $this->assertEquals('Subsite1 Template', $member1SiteTitles[0], 'Member can get to a subsite via a group');
391
392
        $adminSites = Subsite::accessible_sites(
393
            'CMS_ACCESS_CMSMain',
394
            false,
395
            null,
396
            $this->objFromFixture(Member::class, 'admin')
397
        );
398
        $adminSiteTitles = $adminSites->column('Title');
399
        sort($adminSiteTitles);
400
        $this->assertEquals([
401
            'Subsite1 Template',
402
            'Subsite2 Template',
403
            'Template',
404
            'Test 1',
405
            'Test 2',
406
            'Test 3',
407
            'Test Non-SSL',
408
            'Test SSL',
409
            'Test Vagrant VM on port 8080'
410
        ], array_values($adminSiteTitles));
411
412
        $member2Sites = Subsite::accessible_sites(
413
            'CMS_ACCESS_CMSMain',
414
            false,
415
            null,
416
            $this->objFromFixture(Member::class, 'subsite1member2')
417
        );
418
        $member2SiteTitles = $member2Sites->column('Title');
419
        sort($member2SiteTitles);
420
        $this->assertEquals('Subsite1 Template', $member2SiteTitles[0], 'Member can get to subsite via a group role');
421
    }
422
423
    public function testhasMainSitePermission()
424
    {
425
        $admin = $this->objFromFixture(Member::class, 'admin');
426
        $subsite1member = $this->objFromFixture(Member::class, 'subsite1member');
427
        $subsite1admin = $this->objFromFixture(Member::class, 'subsite1admin');
428
        $allsubsitesauthor = $this->objFromFixture(Member::class, 'allsubsitesauthor');
429
430
        $this->assertTrue(
431
            Subsite::hasMainSitePermission($admin),
432
            'Default permissions granted for super-admin'
433
        );
434
        $this->assertTrue(
435
            Subsite::hasMainSitePermission($admin, ['ADMIN']),
436
            'ADMIN permissions granted for super-admin'
437
        );
438
        $this->assertFalse(
439
            Subsite::hasMainSitePermission($subsite1admin, ['ADMIN']),
440
            'ADMIN permissions (on main site) denied for subsite1 admin'
441
        );
442
        $this->assertFalse(
443
            Subsite::hasMainSitePermission($subsite1admin, ['CMS_ACCESS_CMSMain']),
444
            'CMS_ACCESS_CMSMain (on main site) denied for subsite1 admin'
445
        );
446
        $this->assertFalse(
447
            Subsite::hasMainSitePermission($allsubsitesauthor, ['ADMIN']),
448
            'ADMIN permissions (on main site) denied for CMS author with edit rights on all subsites'
449
        );
450
        $this->assertTrue(
451
            Subsite::hasMainSitePermission($allsubsitesauthor, ['CMS_ACCESS_CMSMain']),
452
            'CMS_ACCESS_CMSMain (on main site) granted for CMS author with edit rights on all subsites'
453
        );
454
        $this->assertFalse(
455
            Subsite::hasMainSitePermission($subsite1member, ['ADMIN']),
456
            'ADMIN (on main site) denied for subsite1 subsite1 cms author'
457
        );
458
        $this->assertFalse(
459
            Subsite::hasMainSitePermission($subsite1member, ['CMS_ACCESS_CMSMain']),
460
            'CMS_ACCESS_CMSMain (on main site) denied for subsite1 cms author'
461
        );
462
    }
463
464
    public function testDuplicateSubsite()
465
    {
466
        // get subsite1 & create page
467
        $subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
468
        $subsite1->activate();
469
        $page1 = new Page();
470
        $page1->Title = 'MyAwesomePage';
471
        $page1->write();
472
        $page1->doPublish();
473
        $this->assertEquals($page1->SubsiteID, $subsite1->ID);
474
475
        // duplicate
476
        $subsite2 = $subsite1->duplicate();
477
        $subsite2->activate();
478
        // change content on dupe
479
        $page2 = DataObject::get_one('Page', "\"Title\" = 'MyAwesomePage'");
480
        $page2->Title = 'MyNewAwesomePage';
481
        $page2->write();
482
        $page2->doPublish();
483
484
        // check change & check change has not affected subiste1
485
        $subsite1->activate();
486
        $this->assertEquals('MyAwesomePage', DataObject::get_by_id('Page', $page1->ID)->Title);
487
        $subsite2->activate();
488
        $this->assertEquals('MyNewAwesomePage', DataObject::get_by_id('Page', $page2->ID)->Title);
489
    }
490
491
    public function testDefaultPageCreatedWhenCreatingSubsite()
492
    {
493
        $subsite = new Subsite();
494
        $subsite->Title = 'New Subsite';
495
        $subsite->write();
496
        $subsite->activate();
497
498
        $pages = SiteTree::get();
499
        $this->assertGreaterThanOrEqual(1, $pages->count());
500
    }
501
}
502