Completed
Push — 1.1 ( a4c925...1876b7 )
by Daniel
07:54 queued 04:30
created

SiteTreeSubsites::updateCMSFields()   C

Complexity

Conditions 10
Paths 24

Size

Total Lines 50
Code Lines 31

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 50
rs 5.7647
cc 10
eloc 31
nc 24
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Extension for the SiteTree object to add subsites support
5
 */
6
class SiteTreeSubsites extends DataExtension
7
{
8
    private static $has_one = array(
9
        'Subsite' => 'Subsite', // The subsite that this page belongs to
10
    );
11
12
    private static $many_many = array(
13
        'CrossSubsiteLinkTracking' => 'SiteTree' // Stored separately, as the logic for URL rewriting is different
14
    );
15
16
    private static $many_many_extraFields = array(
17
        "CrossSubsiteLinkTracking" => array("FieldName" => "Varchar")
18
    );
19
20
    public function isMainSite()
21
    {
22
        if ($this->owner->SubsiteID == 0) {
23
            return true;
24
        }
25
        return false;
26
    }
27
    
28
    /**
29
     * Update any requests to limit the results to the current site
30
     */
31
    public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null)
32
    {
33
        if (Subsite::$disable_subsite_filter) {
34
            return;
35
        }
36
        if ($dataQuery->getQueryParam('Subsite.filter') === false) {
0 ignored issues
show
Bug introduced by
It seems like $dataQuery is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
37
            return;
38
        }
39
        
40
        // If you're querying by ID, ignore the sub-site - this is a bit ugly...
41
        // if(!$query->where || (strpos($query->where[0], ".\"ID\" = ") === false && strpos($query->where[0], ".`ID` = ") === false && strpos($query->where[0], ".ID = ") === false && strpos($query->where[0], "ID = ") !== 0)) {
42
        if ($query->filtersOnID()) {
43
            return;
44
        }
45
46
        if (Subsite::$force_subsite) {
47
            $subsiteID = Subsite::$force_subsite;
48
        } else {
49
            /*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
50
            else */$subsiteID = (int)Subsite::currentSubsiteID();
51
        }
52
53
        // The foreach is an ugly way of getting the first key :-)
54
        foreach ($query->getFrom() as $tableName => $info) {
55
            // The tableName should be SiteTree or SiteTree_Live...
56
            if (strpos($tableName, 'SiteTree') === false) {
57
                break;
58
            }
59
            $query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
60
            break;
61
        }
62
    }
63
    
64
    public function onBeforeWrite()
65
    {
66 View Code Duplication
        if (!$this->owner->ID && !$this->owner->SubsiteID) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
67
            $this->owner->SubsiteID = Subsite::currentSubsiteID();
68
        }
69
        
70
        parent::onBeforeWrite();
71
    }
72
73
    public function updateCMSFields(FieldList $fields)
74
    {
75
        $subsites = Subsite::accessible_sites("CMS_ACCESS_CMSMain");
76
        $subsitesMap = array();
77
        if ($subsites && $subsites->Count()) {
78
            $subsitesMap = $subsites->map('ID', 'Title');
79
            unset($subsitesMap[$this->owner->SubsiteID]);
80
        }
81
82
        // Master page edit field (only allowed from default subsite to avoid inconsistent relationships)
83
        $isDefaultSubsite = $this->owner->SubsiteID == 0 || $this->owner->Subsite()->DefaultSite;
84
        if ($isDefaultSubsite && $subsitesMap) {
85
            $fields->addFieldToTab(
86
                'Root.Main',
87
                new DropdownField(
88
                    "CopyToSubsiteID",
89
                    _t('SiteTreeSubsites.CopyToSubsite', "Copy page to subsite"),
90
                    $subsitesMap,
91
                    ''
92
                )
93
            );
94
            $fields->addFieldToTab(
95
                'Root.Main',
96
                $copyAction = new InlineFormAction(
97
                    "copytosubsite",
0 ignored issues
show
Documentation introduced by
'copytosubsite' is of type string, but the function expects a object<The>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
98
                    _t('SiteTreeSubsites.CopyAction', "Copy")
99
                )
100
            );
101
            $copyAction->includeDefaultJS(false);
102
        }
103
104
        // replace readonly link prefix
105
        $subsite = $this->owner->Subsite();
106
        $nested_urls_enabled = Config::inst()->get('SiteTree', 'nested_urls');
107
        if ($subsite && $subsite->exists()) {
108
            // Use baseurl from domain
109
            $baseLink = $subsite->absoluteBaseURL();
110
            
111
            // Add parent page if enabled
112
            if($nested_urls_enabled && $this->owner->ParentID) {
113
                $baseLink = Controller::join_links(
114
                    $baseLink,
115
                    $this->owner->Parent()->RelativeLink(true)
116
                );
117
            }
118
            
119
            $urlsegment = $fields->dataFieldByName('URLSegment');
120
            $urlsegment->setURLPrefix($baseLink);
121
        }
122
    }
123
    
124
    public function alternateSiteConfig()
125
    {
126
        if (!$this->owner->SubsiteID) {
127
            return false;
128
        }
129
        $sc = DataObject::get_one('SiteConfig', '"SubsiteID" = ' . $this->owner->SubsiteID);
130
        if (!$sc) {
131
            $sc = new SiteConfig();
132
            $sc->SubsiteID = $this->owner->SubsiteID;
133
            $sc->Title = _t('Subsite.SiteConfigTitle', 'Your Site Name');
134
            $sc->Tagline = _t('Subsite.SiteConfigSubtitle', 'Your tagline here');
135
            $sc->write();
136
        }
137
        return $sc;
138
    }
139
    
140
    /**
141
     * Only allow editing of a page if the member satisfies one of the following conditions:
142
     * - Is in a group which has access to the subsite this page belongs to
143
     * - Is in a group with edit permissions on the "main site"
144
     * 
145
     * @return boolean
146
     */
147
    public function canEdit($member = null)
148
    {
149
        if (!$member) {
150
            $member = Member::currentUser();
151
        }
152
        
153
        // Find the sites that this user has access to
154
        $goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true, 'all', $member)->column('ID');
155
156
        if (!is_null($this->owner->SubsiteID)) {
157
            $subsiteID = $this->owner->SubsiteID;
158
        } else {
159
            // The relationships might not be available during the record creation when using a GridField.
160
            // In this case the related objects will have empty fields, and SubsiteID will not be available.
161
            //
162
            // We do the second best: fetch the likely SubsiteID from the session. The drawback is this might
163
            // make it possible to force relations to point to other (forbidden) subsites.
164
            $subsiteID = Subsite::currentSubsiteID();
165
        }
166
167
        // Return true if they have access to this object's site
168
        if (!(in_array(0, $goodSites) || in_array($subsiteID, $goodSites))) {
169
            return false;
170
        }
171
    }
172
    
173
    /**
174
     * @return boolean
175
     */
176 View Code Duplication
    public function canDelete($member = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
177
    {
178
        if (!$member && $member !== false) {
179
            $member = Member::currentUser();
180
        }
181
        
182
        return $this->canEdit($member);
183
    }
184
    
185
    /**
186
     * @return boolean
187
     */
188 View Code Duplication
    public function canAddChildren($member = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
189
    {
190
        if (!$member && $member !== false) {
191
            $member = Member::currentUser();
192
        }
193
        
194
        return $this->canEdit($member);
195
    }
196
    
197
    /**
198
     * @return boolean
199
     */
200 View Code Duplication
    public function canPublish($member = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
    {
202
        if (!$member && $member !== false) {
203
            $member = Member::currentUser();
204
        }
205
206
        return $this->canEdit($member);
207
    }
208
209
    /**
210
     * Create a duplicate of this page and save it to another subsite
211
     * @param $subsiteID int|Subsite The Subsite to copy to, or its ID
212
     */
213
    public function duplicateToSubsite($subsiteID = null)
214
    {
215
        if (is_object($subsiteID)) {
216
            $subsite = $subsiteID;
217
            $subsiteID = $subsite->ID;
218
        } else {
219
            $subsite = DataObject::get_by_id('Subsite', $subsiteID);
0 ignored issues
show
Unused Code introduced by
$subsite is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
220
        }
221
        
222
        $oldSubsite=Subsite::currentSubsiteID();
223
        if ($subsiteID) {
224
            Subsite::changeSubsite($subsiteID);
225
        } else {
226
            $subsiteID=$oldSubsite;
227
        }
228
229
        $page = $this->owner->duplicate(false);
230
231
        $page->CheckedPublicationDifferences = $page->AddedToStage = true;
232
        $subsiteID = ($subsiteID ? $subsiteID : $oldSubsite);
233
        $page->SubsiteID = $subsiteID;
234
235
        // MasterPageID is here for legacy purposes, to satisfy the subsites_relatedpages module
236
        $page->MasterPageID = $this->owner->ID;
237
        $page->write();
238
239
        Subsite::changeSubsite($oldSubsite);
240
241
        return $page;
242
    }
243
244
    /**
245
     * Called by ContentController::init();
246
     */
247
    public static function contentcontrollerInit($controller)
0 ignored issues
show
Unused Code introduced by
The parameter $controller is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
248
    {
249
        $subsite = Subsite::currentSubsite();
250
251
        if ($subsite && $subsite->Theme) {
0 ignored issues
show
Documentation introduced by
The property Theme does not exist on object<Subsite>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
252
            Config::inst()->update('SSViewer', 'theme', Subsite::currentSubsite()->Theme);
0 ignored issues
show
Documentation introduced by
The property Theme does not exist on object<Subsite>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
253
        }
254
    }
255
256
    public function alternateAbsoluteLink()
257
    {
258
        // Generate the existing absolute URL and replace the domain with the subsite domain.
259
        // This helps deal with Link() returning an absolute URL.
260
        $url = Director::absoluteURL($this->owner->Link());
261
        if ($this->owner->SubsiteID) {
262
            $url = preg_replace('/\/\/[^\/]+\//', '//' .  $this->owner->Subsite()->domain() . '/', $url);
263
        }
264
        return $url;
265
    }
266
267
    /**
268
     * Use the CMS domain for iframed CMS previews to prevent single-origin violations
269
     * and SSL cert problems.
270
     */
271
    public function alternatePreviewLink($action = null)
0 ignored issues
show
Unused Code introduced by
The parameter $action is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
272
    {
273
        $url = Director::absoluteURL($this->owner->Link());
274
        if ($this->owner->SubsiteID) {
275
            $url = HTTP::setGetVar('SubsiteID', $this->owner->SubsiteID, $url);
0 ignored issues
show
Security Bug introduced by
It seems like $url can also be of type false; however, HTTP::setGetVar() does only seem to accept string|null, did you maybe forget to handle an error condition?
Loading history...
276
        }
277
        return $url;
278
    }
279
280
    /**
281
     * Inject the subsite ID into the content so it can be used by frontend scripts.
282
     */
283
    public function MetaTags(&$tags)
284
    {
285
        if ($this->owner->SubsiteID) {
286
            $tags .= "<meta name=\"x-subsite-id\" content=\"" . $this->owner->SubsiteID . "\" />\n";
287
        }
288
289
        return $tags;
290
    }
291
292
    public function augmentSyncLinkTracking()
293
    {
294
        // Set LinkTracking appropriately
295
        $links = HTTP::getLinksIn($this->owner->Content);
296
        $linkedPages = array();
297
        
298
        if ($links) {
299
            foreach ($links as $link) {
300
                if (substr($link, 0, strlen('http://')) == 'http://') {
301
                    $withoutHttp = substr($link, strlen('http://'));
302
                    if (strpos($withoutHttp, '/') && strpos($withoutHttp, '/') < strlen($withoutHttp)) {
303
                        $domain = substr($withoutHttp, 0, strpos($withoutHttp, '/'));
304
                        $rest = substr($withoutHttp, strpos($withoutHttp, '/') + 1);
305
                    
306
                        $subsiteID = Subsite::getSubsiteIDForDomain($domain);
307
                        if ($subsiteID == 0) {
308
                            continue;
309
                        } // We have no idea what the domain for the main site is, so cant track links to it
310
311
                    $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
312
                        Subsite::disable_subsite_filter(true);
313
                        $candidatePage = DataObject::get_one("SiteTree", "\"URLSegment\" = '" . Convert::raw2sql(urldecode($rest)) . "' AND \"SubsiteID\" = " . $subsiteID, false);
314
                        Subsite::disable_subsite_filter($origDisableSubsiteFilter);
315
                    
316
                        if ($candidatePage) {
317
                            $linkedPages[] = $candidatePage->ID;
318
                        } else {
319
                            $this->owner->HasBrokenLink = true;
320
                        }
321
                    }
322
                }
323
            }
324
        }
325
        
326
        $this->owner->CrossSubsiteLinkTracking()->setByIDList($linkedPages);
327
    }
328
    
329
    /**
330
     * Return a piece of text to keep DataObject cache keys appropriately specific
331
     */
332
    public function cacheKeyComponent()
333
    {
334
        return 'subsite-'.Subsite::currentSubsiteID();
335
    }
336
    
337
    /**
338
     * @param Member
339
     * @return boolean|null
340
     */
341
    public function canCreate($member = null)
342
    {
343
        // Typically called on a singleton, so we're not using the Subsite() relation
344
        $subsite = Subsite::currentSubsite();
345
        if ($subsite && $subsite->exists() && $subsite->PageTypeBlacklist) {
0 ignored issues
show
Documentation introduced by
The property PageTypeBlacklist does not exist on object<Subsite>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
346
            $blacklisted = explode(',', $subsite->PageTypeBlacklist);
0 ignored issues
show
Documentation introduced by
The property PageTypeBlacklist does not exist on object<Subsite>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
347
            // All subclasses need to be listed explicitly
348
            if (in_array($this->owner->class, $blacklisted)) {
349
                return false;
350
            }
351
        }
352
    }
353
}
354