SiteTreeSubsites   F
last analyzed

Complexity

Total Complexity 87

Size/Duplication

Total Lines 526
Duplicated Lines 0 %

Importance

Changes 9
Bugs 0 Features 1
Metric Value
eloc 196
c 9
b 0
f 1
dl 0
loc 526
rs 2
wmc 87

22 Methods

Rating   Name   Duplication   Size   Complexity  
A alternateAbsoluteLink() 0 9 2
A onBeforeWrite() 0 7 3
A updatePreviewLink() 0 5 1
A canEdit() 0 28 6
A canPublish() 0 7 3
A cacheKeyComponent() 0 3 1
A MetaTags() 0 7 2
A alternateSiteConfig() 0 14 3
A canAddChildren() 0 7 3
B augmentSQL() 0 38 9
C updateCMSFields() 0 61 12
A duplicateToSubsite() 0 15 2
A augmentValidURLSegment() 0 19 4
A duplicateToSubsitePrep() 0 38 5
A canCreate() 0 13 6
A contentcontrollerInit() 0 11 5
A alternatePreviewLink() 0 4 1
A duplicateSubsiteRelations() 0 12 5
A onBeforeDuplicate() 0 5 2
B augmentSyncLinkTracking() 0 42 8
A isMainSite() 0 3 1
A canDelete() 0 7 3

How to fix   Complexity   

Complex Class

Complex classes like SiteTreeSubsites often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SiteTreeSubsites, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\Subsites\Extensions;
4
5
use Page;
0 ignored issues
show
Bug introduced by Robbie Averill
The type Page 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use SilverStripe\CMS\Forms\SiteTreeURLSegmentField;
7
use SilverStripe\CMS\Model\SiteTree;
8
use SilverStripe\Control\Controller;
9
use SilverStripe\Control\Director;
10
use SilverStripe\Control\HTTP;
11
use SilverStripe\Core\Config\Config;
12
use SilverStripe\Core\Convert;
13
use SilverStripe\Forms\CheckboxField;
14
use SilverStripe\Forms\DropdownField;
15
use SilverStripe\Forms\FieldList;
16
use SilverStripe\Forms\FormAction;
17
use SilverStripe\Forms\ToggleCompositeField;
18
use SilverStripe\i18n\i18n;
19
use SilverStripe\ORM\ArrayList;
20
use SilverStripe\ORM\DataExtension;
21
use SilverStripe\ORM\DataObject;
22
use SilverStripe\ORM\DataQuery;
23
use SilverStripe\ORM\Map;
24
use SilverStripe\ORM\Queries\SQLSelect;
25
use SilverStripe\Security\Member;
26
use SilverStripe\Security\Security;
27
use SilverStripe\SiteConfig\SiteConfig;
28
use SilverStripe\Subsites\Model\Subsite;
29
use SilverStripe\Subsites\Service\ThemeResolver;
30
use SilverStripe\Subsites\State\SubsiteState;
31
use SilverStripe\View\SSViewer;
32
33
/**
34
 * Extension for the SiteTree object to add subsites support
35
 */
36
class SiteTreeSubsites extends DataExtension
37
{
38
    private static $has_one = [
39
        'Subsite' => Subsite::class, // The subsite that this page belongs to
40
    ];
41
42
    private static $many_many = [
43
        'CrossSubsiteLinkTracking' => SiteTree::class // Stored separately, as the logic for URL rewriting is different
44
    ];
45
46
    private static $many_many_extraFields = [
47
        'CrossSubsiteLinkTracking' => ['FieldName' => 'Varchar']
48
    ];
49
50
    public function isMainSite()
51
    {
52
        return $this->owner->SubsiteID == 0;
0 ignored issues
show
Bug Best Practice introduced by Werner M. Krauß
It seems like you are loosely comparing $this->owner->SubsiteID of type mixed|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
53
    }
54
55
    /**
56
     * Update any requests to limit the results to the current site
57
     * @param SQLSelect $query
58
     * @param DataQuery $dataQuery
59
     */
60
    public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
61
    {
62
        if (Subsite::$disable_subsite_filter) {
63
            return;
64
        }
65
        if ($dataQuery && $dataQuery->getQueryParam('Subsite.filter') === false) {
0 ignored issues
show
introduced by Werner M. Krauß
The condition $dataQuery->getQueryPara...site.filter') === false is always false.
Loading history...
66
            return;
67
        }
68
69
        // If you're querying by ID, ignore the sub-site - this is a bit ugly...
70
        // if(!$query->where
71
        // || (strpos($query->where[0], ".\"ID\" = ") === false
72
        // && strpos($query->where[0], ".`ID` = ") === false && strpos($query->where[0], ".ID = ") === false
73
        // && strpos($query->where[0], "ID = ") !== 0)) {
74
        if ($query->filtersOnID()) {
75
            return;
76
        }
77
78
        $subsiteID = null;
79
        if (Subsite::$force_subsite) {
80
            $subsiteID = Subsite::$force_subsite;
81
        } else {
82
            $subsiteID = SubsiteState::singleton()->getSubsiteId();
83
        }
84
85
        if ($subsiteID === null) {
86
            return;
87
        }
88
89
        // The foreach is an ugly way of getting the first key :-)
90
        foreach ($query->getFrom() as $tableName => $info) {
91
            // The tableName should be SiteTree or SiteTree_Live...
92
            $siteTreeTableName = SiteTree::getSchema()->tableName(SiteTree::class);
93
            if (strpos($tableName, $siteTreeTableName) === false) {
94
                break;
95
            }
96
            $query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
97
            break;
98
        }
99
    }
100
101
    public function onBeforeWrite()
102
    {
103
        if (!$this->owner->ID && !$this->owner->SubsiteID) {
104
            $this->owner->SubsiteID = SubsiteState::singleton()->getSubsiteId();
105
        }
106
107
        parent::onBeforeWrite();
108
    }
109
110
    public function updateCMSFields(FieldList $fields)
111
    {
112
        $subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain');
113
        if ($subsites && $subsites->count()) {
114
            $subsitesToMap = $subsites->exclude('ID', $this->owner->SubsiteID);
115
            $subsitesMap = $subsitesToMap->map('ID', 'Title');
116
        } else {
117
            $subsitesMap = new Map(ArrayList::create());
118
        }
119
120
        // Master page edit field (only allowed from default subsite to avoid inconsistent relationships)
121
        $isDefaultSubsite = $this->owner->SubsiteID == 0 || $this->owner->Subsite()->DefaultSite;
0 ignored issues
show
Bug Best Practice introduced by Werner M. Krauß
It seems like you are loosely comparing $this->owner->SubsiteID of type mixed|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
122
123
        if ($isDefaultSubsite && $subsitesMap->count()) {
124
            $fields->addFieldToTab(
125
                'Root.Main',
126
                ToggleCompositeField::create(
127
                    'SubsiteOperations',
128
                    _t(__CLASS__ . '.SubsiteOperations', 'Subsite Operations'),
129
                    [
130
                        DropdownField::create('CopyToSubsiteID', _t(
131
                            __CLASS__ . '.CopyToSubsite',
132
                            'Copy page to subsite'
133
                        ), $subsitesMap),
134
                        CheckboxField::create(
135
                            'CopyToSubsiteWithChildren',
136
                            _t(__CLASS__ . '.CopyToSubsiteWithChildren', 'Include children pages?')
137
                        ),
138
                        $copyAction = FormAction::create(
139
                            'copytosubsite',
140
                            _t(__CLASS__ . '.CopyAction', 'Copy')
141
                        )
142
                    ]
143
                )->setHeadingLevel(4)
144
            );
145
146
            $copyAction->addExtraClass('btn btn-primary font-icon-save ml-3');
147
148
            // @todo check if this needs re-implementation
149
//            $copyAction->includeDefaultJS(false);
150
        }
151
152
        // replace readonly link prefix
153
        $subsite = $this->owner->Subsite();
154
        $nested_urls_enabled = Config::inst()->get(SiteTree::class, 'nested_urls');
155
        /** @var Subsite $subsite */
156
        if ($subsite && $subsite->exists()) {
157
            // Use baseurl from domain
158
            $baseLink = $subsite->absoluteBaseURL();
159
160
            // Add parent page if enabled
161
            if ($nested_urls_enabled && $this->owner->ParentID) {
162
                $baseLink = Controller::join_links(
163
                    $baseLink,
164
                    $this->owner->Parent()->RelativeLink(true)
165
                );
166
            }
167
168
            $urlsegment = $fields->dataFieldByName('URLSegment');
169
            if ($urlsegment && $urlsegment instanceof SiteTreeURLSegmentField) {
170
                $urlsegment->setURLPrefix($baseLink);
171
            }
172
        }
173
    }
174
175
    /**
176
     * Does the basic duplication, but doesn't write anything this means we can subclass this easier and do more
177
     * complex relation duplication.
178
     *
179
     * Note that when duplicating including children, everything is written.
180
     *
181
     * @param Subsite|int $subsiteID
182
     * @param bool $includeChildren
183
     * @return SiteTree
184
     */
185
    public function duplicateToSubsitePrep($subsiteID, $includeChildren)
186
    {
187
        if (is_object($subsiteID)) {
188
            $subsiteID = $subsiteID->ID;
189
        }
190
191
        return SubsiteState::singleton()
192
            ->withState(function (SubsiteState $newState) use ($subsiteID, $includeChildren) {
193
                $newState->setSubsiteId($subsiteID);
194
195
                /** @var SiteTree $page */
196
                $page = $this->owner;
197
198
                try {
199
                    // We have no idea what the ParentID should be, but it shouldn't be the same as it was since
200
                    // we're now in a different subsite. As a workaround use the url-segment and subsite ID.
201
                    if ($page->Parent()) {
202
                        $parentSeg = $page->Parent()->URLSegment;
203
                        $newParentPage = Page::get()->filter('URLSegment', $parentSeg)->first();
204
                        $originalParentID = $page->ParentID;
205
                        if ($newParentPage) {
206
                            $page->ParentID = (int) $newParentPage->ID;
0 ignored issues
show
Bug Best Practice introduced by Robbie Averill
The property ParentID does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
207
                        } else {
208
                            // reset it to the top level, so the user can decide where to put it
209
                            $page->ParentID = 0;
210
                        }
211
                    }
212
213
                    // Disable query filtering by subsite during actual duplication
214
                    $originalFilter = Subsite::$disable_subsite_filter;
215
                    Subsite::disable_subsite_filter(true);
216
217
                    return $includeChildren ? $page->duplicateWithChildren() : $page->duplicate(false);
218
                } finally {
219
                    Subsite::disable_subsite_filter($originalFilter);
220
221
                    // Re-set the original parent ID for the current page
222
                    $page->ParentID = $originalParentID;
0 ignored issues
show
Comprehensibility Best Practice introduced by Robbie Averill
The variable $originalParentID does not seem to be defined for all execution paths leading up to this point.
Loading history...
223
                }
224
            });
225
    }
226
227
    /**
228
     * When duplicating a page, assign the current subsite ID from the state
229
     */
230
    public function onBeforeDuplicate()
231
    {
232
        $subsiteId = SubsiteState::singleton()->getSubsiteId();
233
        if ($subsiteId !== null) {
234
            $this->owner->SubsiteID = $subsiteId;
235
        }
236
    }
237
238
    /**
239
     * Create a duplicate of this page and save it to another subsite
240
     *
241
     * @param Subsite|int $subsiteID   The Subsite to copy to, or its ID
242
     * @param boolean $includeChildren Whether to duplicate child pages too
243
     * @return SiteTree                The duplicated page
244
     */
245
    public function duplicateToSubsite($subsiteID = null, $includeChildren = false)
246
    {
247
        $clone = $this->owner->duplicateToSubsitePrep($subsiteID, $includeChildren);
248
        $clone->invokeWithExtensions('onBeforeDuplicateToSubsite', $this->owner);
249
250
        if (!$includeChildren) {
251
            // Write the new page if "include children" is false, because it is written by default when it's true.
252
            $clone->write();
253
        }
254
        // Deprecated: manually duplicate any configured relationships
255
        $clone->duplicateSubsiteRelations($this->owner);
256
257
        $clone->invokeWithExtensions('onAfterDuplicateToSubsite', $this->owner);
258
259
        return $clone;
260
    }
261
262
    /**
263
     * Duplicate relations using a static property to define
264
     * which ones we want to duplicate
265
     *
266
     * It may be that some relations are not diostinct to sub site so can stay
267
     * whereas others may need to be duplicated
268
     *
269
     * @deprecated 2.2..3.0 Use the "cascade_duplicates" config API instead
270
     * @param SiteTree $originalPage
271
     */
272
    public function duplicateSubsiteRelations($originalPage)
273
    {
274
        $thisClass = $originalPage->ClassName;
275
        $relations = Config::inst()->get($thisClass, 'duplicate_to_subsite_relations');
276
277
        if ($relations && !empty($relations)) {
278
            foreach ($relations as $relation) {
279
                $items = $originalPage->$relation();
280
                foreach ($items as $item) {
281
                    $duplicateItem = $item->duplicate(false);
282
                    $duplicateItem->{$thisClass.'ID'} = $this->owner->ID;
283
                    $duplicateItem->write();
284
                }
285
            }
286
        }
287
    }
288
289
    /**
290
     * @return SiteConfig
291
     */
292
    public function alternateSiteConfig()
293
    {
294
        if (!$this->owner->SubsiteID) {
295
            return false;
0 ignored issues
show
Bug Best Practice introduced by Werner M. Krauß
The expression return false returns the type false which is incompatible with the documented return type SilverStripe\SiteConfig\SiteConfig.
Loading history...
296
        }
297
        $sc = DataObject::get_one(SiteConfig::class, '"SubsiteID" = ' . $this->owner->SubsiteID);
298
        if (!$sc) {
299
            $sc = new SiteConfig();
300
            $sc->SubsiteID = $this->owner->SubsiteID;
301
            $sc->Title = _t('SilverStripe\\Subsites\\Model\\Subsite.SiteConfigTitle', 'Your Site Name');
302
            $sc->Tagline = _t('SilverStripe\\Subsites\\Model\\Subsite.SiteConfigSubtitle', 'Your tagline here');
303
            $sc->write();
304
        }
305
        return $sc;
306
    }
307
308
    /**
309
     * Only allow editing of a page if the member satisfies one of the following conditions:
310
     * - Is in a group which has access to the subsite this page belongs to
311
     * - Is in a group with edit permissions on the "main site"
312
     *
313
     * If there are no subsites configured yet, this logic is skipped.
314
     *
315
     * @param Member|null $member
316
     * @return bool|null
317
     */
318
    public function canEdit($member = null)
319
    {
320
        if (!$member) {
321
            $member = Security::getCurrentUser();
322
        }
323
324
        // Do not provide any input if there are no subsites configured
325
        if (!Subsite::get()->exists()) {
326
            return null;
327
        }
328
329
        // Find the sites that this user has access to
330
        $goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true, 'all', $member)->column('ID');
331
332
        if (!is_null($this->owner->SubsiteID)) {
333
            $subsiteID = $this->owner->SubsiteID;
334
        } else {
335
            // The relationships might not be available during the record creation when using a GridField.
336
            // In this case the related objects will have empty fields, and SubsiteID will not be available.
337
            //
338
            // We do the second best: fetch the likely SubsiteID from the session. The drawback is this might
339
            // make it possible to force relations to point to other (forbidden) subsites.
340
            $subsiteID = SubsiteState::singleton()->getSubsiteId();
341
        }
342
343
        // Return true if they have access to this object's site
344
        if (!(in_array(0, $goodSites) || in_array($subsiteID, $goodSites))) {
345
            return false;
346
        }
347
    }
348
349
    /**
350
     * @param null $member
0 ignored issues
show
Documentation Bug introduced by Werner M. Krauß
Are you sure the doc-type for parameter $member is correct as it would always require null to be passed?
Loading history...
351
     * @return bool
352
     */
353
    public function canDelete($member = null)
354
    {
355
        if (!$member && $member !== false) {
0 ignored issues
show
introduced by Werner M. Krauß
$member is of type null, thus it always evaluated to false.
Loading history...
introduced by Werner M. Krauß
The condition $member !== false is always true.
Loading history...
356
            $member = Security::getCurrentUser();
357
        }
358
359
        return $this->canEdit($member);
360
    }
361
362
    /**
363
     * @param null $member
0 ignored issues
show
Documentation Bug introduced by Werner M. Krauß
Are you sure the doc-type for parameter $member is correct as it would always require null to be passed?
Loading history...
364
     * @return bool
365
     */
366
    public function canAddChildren($member = null)
367
    {
368
        if (!$member && $member !== false) {
0 ignored issues
show
introduced by Werner M. Krauß
$member is of type null, thus it always evaluated to false.
Loading history...
introduced by Werner M. Krauß
The condition $member !== false is always true.
Loading history...
369
            $member = Security::getCurrentUser();
370
        }
371
372
        return $this->canEdit($member);
373
    }
374
375
    /**
376
     * @param Member|null $member
377
     * @return bool|null
378
     */
379
    public function canPublish($member = null)
380
    {
381
        if (!$member && $member !== false) {
0 ignored issues
show
introduced by Werner M. Krauß
The condition $member !== false is always false.
Loading history...
382
            $member = Security::getCurrentUser();
383
        }
384
385
        return $this->canEdit($member);
386
    }
387
388
    /**
389
     * Called by ContentController::init();
390
     * @param $controller
391
     */
392
    public static function contentcontrollerInit($controller)
393
    {
394
        /** @var Subsite $subsite */
395
        $subsite = Subsite::currentSubsite();
396
397
        if ($subsite && $subsite->Theme) {
0 ignored issues
show
Bug Best Practice introduced by Werner M. Krauß
The property Theme does not exist on SilverStripe\Subsites\Model\Subsite. Since you implemented __get, consider adding a @property annotation.
Loading history...
398
            SSViewer::set_themes(ThemeResolver::singleton()->getThemeList($subsite));
399
        }
400
401
        if ($subsite && i18n::getData()->validate($subsite->Language)) {
0 ignored issues
show
Bug Best Practice introduced by Robbie Averill
The property Language does not exist on SilverStripe\Subsites\Model\Subsite. Since you implemented __get, consider adding a @property annotation.
Loading history...
402
            i18n::set_locale($subsite->Language);
403
        }
404
    }
405
406
    /**
407
     * @param null $action
0 ignored issues
show
Documentation Bug introduced by Werner M. Krauß
Are you sure the doc-type for parameter $action is correct as it would always require null to be passed?
Loading history...
408
     * @return string
409
     */
410
    public function alternateAbsoluteLink($action = null)
411
    {
412
        // Generate the existing absolute URL and replace the domain with the subsite domain.
413
        // This helps deal with Link() returning an absolute URL.
414
        $url = Director::absoluteURL($this->owner->Link($action));
415
        if ($this->owner->SubsiteID) {
416
            $url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url);
417
        }
418
        return $url;
419
    }
420
421
    /**
422
     * Use the CMS domain for iframed CMS previews to prevent single-origin violations
423
     * and SSL cert problems. Always set SubsiteID to avoid errors because a page doesn't
424
     * exist on the CMS domain.
425
     *
426
     * @param string &$link
427
     * @param string|null $action
428
     * @return string
429
     */
430
    public function updatePreviewLink(&$link, $action = null)
431
    {
432
        $url = Director::absoluteURL($this->owner->Link($action));
433
        $link = HTTP::setGetVar('SubsiteID', $this->owner->SubsiteID, $url);
434
        return $link;
435
    }
436
437
    /**
438
     * This function is marked as deprecated for removal in 5.0.0 in silverstripe/cms
439
     * so now simply passes execution to where the functionality exists for backwards compatiblity.
440
     * CMS 4.0.0 SiteTree already throws a SilverStripe deprecation error before calling this function.
441
     * @deprecated 2.2...3.0 use updatePreviewLink instead
442
     *
443
     * @param string|null $action
444
     * @return string
445
     */
446
    public function alternatePreviewLink($action = null)
447
    {
448
        $link = '';
449
        return $this->updatePreviewLink($link, $action);
450
    }
451
452
    /**
453
     * Inject the subsite ID into the content so it can be used by frontend scripts.
454
     * @param $tags
455
     * @return string
456
     */
457
    public function MetaTags(&$tags)
458
    {
459
        if ($this->owner->SubsiteID) {
460
            $tags .= '<meta name="x-subsite-id" content="' . $this->owner->SubsiteID . "\" />\n";
461
        }
462
463
        return $tags;
464
    }
465
466
    public function augmentSyncLinkTracking()
467
    {
468
        // Set LinkTracking appropriately
469
        $links = HTTP::getLinksIn($this->owner->Content);
470
        $linkedPages = [];
471
472
        if ($links) {
473
            foreach ($links as $link) {
474
                if (substr($link, 0, strlen('http://')) == 'http://') {
475
                    $withoutHttp = substr($link, strlen('http://'));
476
                    if (strpos($withoutHttp, '/') && strpos($withoutHttp, '/') < strlen($withoutHttp)) {
477
                        $domain = substr($withoutHttp, 0, strpos($withoutHttp, '/'));
478
                        $rest = substr($withoutHttp, strpos($withoutHttp, '/') + 1);
479
480
                        $subsiteID = Subsite::getSubsiteIDForDomain($domain);
481
                        if ($subsiteID == 0) {
482
                            continue;
483
                        } // We have no idea what the domain for the main site is, so cant track links to it
484
485
                        $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
486
                        Subsite::disable_subsite_filter(true);
487
                        $candidatePage = DataObject::get_one(
488
                            SiteTree::class,
489
                            "\"URLSegment\" = '"
490
                            . Convert::raw2sql(urldecode($rest))
491
                            . "' AND \"SubsiteID\" = "
492
                            . $subsiteID,
493
                            false
494
                        );
495
                        Subsite::disable_subsite_filter($origDisableSubsiteFilter);
496
497
                        if ($candidatePage) {
498
                            $linkedPages[] = $candidatePage->ID;
499
                        } else {
500
                            $this->owner->HasBrokenLink = true;
501
                        }
502
                    }
503
                }
504
            }
505
        }
506
507
        $this->owner->CrossSubsiteLinkTracking()->setByIDList($linkedPages);
508
    }
509
510
    /**
511
     * Ensure that valid url segments are checked within the correct subsite of the owner object,
512
     * even if the current subsiteID is set to some other subsite.
513
     *
514
     * @return null|bool Either true or false, or null to not influence result
515
     */
516
    public function augmentValidURLSegment()
517
    {
518
        // If this page is being filtered in the current subsite, then no custom validation query is required.
519
        $subsite = Subsite::$force_subsite ?: SubsiteState::singleton()->getSubsiteId();
520
        if (empty($this->owner->SubsiteID) || $subsite == $this->owner->SubsiteID) {
521
            return null;
522
        }
523
524
        // Backup forced subsite
525
        $prevForceSubsite = Subsite::$force_subsite;
526
        Subsite::$force_subsite = $this->owner->SubsiteID;
527
528
        // Repeat validation in the correct subsite
529
        $isValid = $this->owner->validURLSegment();
530
531
        // Restore
532
        Subsite::$force_subsite = $prevForceSubsite;
533
534
        return (bool)$isValid;
535
    }
536
537
    /**
538
     * Return a piece of text to keep DataObject cache keys appropriately specific
539
     */
540
    public function cacheKeyComponent()
541
    {
542
        return 'subsite-' . SubsiteState::singleton()->getSubsiteId();
543
    }
544
545
    /**
546
     * @param Member $member
547
     * @return boolean|null
548
     */
549
    public function canCreate($member = null)
550
    {
551
        // Typically called on a singleton, so we're not using the Subsite() relation
552
        $subsite = Subsite::currentSubsite();
553
        if ($subsite && $subsite->exists() && $subsite->PageTypeBlacklist) {
554
            // SS 4.1: JSON encoded. SS 4.0, comma delimited
555
            $blacklist = json_decode($subsite->PageTypeBlacklist, true);
556
            if ($blacklist === false) {
557
                $blacklist = explode(',', $subsite->PageTypeBlacklist);
558
            }
559
560
            if (in_array(get_class($this->owner), (array) $blacklist)) {
561
                return false;
562
            }
563
        }
564
    }
565
}
566