Completed
Push — master ( 4c93a4...288b18 )
by Jason
13s
created

ViewableDataObject::validURLSegment()   C

Complexity

Conditions 14
Paths 76

Size

Total Lines 40
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 19.5228

Importance

Changes 0
Metric Value
cc 14
eloc 21
c 0
b 0
f 0
nc 76
nop 0
dl 0
loc 40
rs 5.0864
ccs 16
cts 23
cp 0.6957
crap 19.5228

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
namespace Dynamic\ViewableDataObject\Extensions;
4
5
use SilverStripe\CMS\Controllers\RootURLController;
6
use SilverStripe\CMS\Model\SiteTree;
7
use SilverStripe\Control\ContentNegotiator;
8
use SilverStripe\Control\RequestHandler;
9
use SilverStripe\ORM\DataExtension;
10
use SilverStripe\Forms\FieldList;
11
use SilverStripe\Forms\TextField;
12
use SilverStripe\Forms\TextareaField;
13
use SilverStripe\Forms\ToggleCompositeField;
14
use SilverStripe\CMS\Forms\SiteTreeURLSegmentField;
15
use SilverStripe\Control\Controller;
16
use SilverStripe\Control\Director;
17
use SilverStripe\ORM\FieldType\DBHTMLText;
18
use SilverStripe\SiteConfig\SiteConfig;
19
use SilverStripe\CMS\Controllers\ModelAsController;
20
use SilverStripe\ORM\DataObject;
21
use SilverStripe\View\HTML;
22
use SilverStripe\View\Parsers\URLSegmentFilter;
23
use SilverStripe\Versioned\Versioned;
24
use SilverStripe\View\SSViewer;
25
use SilverStripe\ORM\ArrayList;
26
use SilverStripe\View\ArrayData;
27
use SilverStripe\Core\Convert;
28
use SilverStripe\Core\Config\Config;
29
30
class ViewableDataObject extends DataExtension
31
{
32
    /**
33
     * @var array
34
     */
35
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
36
        'Title' => 'Varchar(255)',
37
        'MenuTitle' => 'Varchar(255)',
38
        'URLSegment' => 'Varchar(255)',
39
        'MetaTitle' => 'Varchar(255)',
40
        'MetaDescription' => 'Varchar(255)',
41
    ];
42
43
    /**
44
     * @var array
45
     */
46
    private static $defaults = array(
0 ignored issues
show
introduced by
The private property $defaults is not used, and could be removed.
Loading history...
47
        'Title' => 'New Item',
48
        'URLSegment' => 'new-item',
49
    );
50
51
    /**
52
     * @var array
53
     */
54
    private static $indexes = [
0 ignored issues
show
introduced by
The private property $indexes is not used, and could be removed.
Loading history...
55
        'URLSegment' => true,
56
    ];
57
58
    /**
59
     * @var array
60
     */
61
    private static $casting = array(
0 ignored issues
show
introduced by
The private property $casting is not used, and could be removed.
Loading history...
62
        'Breadcrumbs' => 'HTMLFragment',
63
        'Link' => 'Text',
64
        'RelativeLink' => 'Text',
65
        'AbsoluteLink' => 'Text',
66
        'MetaTags' => 'HTMLFragment',
67
    );
68
69
    /**
70
     * @param FieldList $fields
71
     */
72 1
    public function updateCMSFields(FieldList $fields)
73
    {
74 1
        $fields->removeByName(array(
75 1
            'MenuTitle',
76
            'URLSegment',
77
            'MetaTitle',
78
            'MetaDescription',
79
        ));
80
81 1
        $fields->insertAfter(
82 1
            TextField::create('MenuTitle'),
0 ignored issues
show
Bug introduced by
'MenuTitle' of type string is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

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

82
            TextField::create(/** @scrutinizer ignore-type */ 'MenuTitle'),
Loading history...
83 1
            'Title'
0 ignored issues
show
Bug introduced by
'Title' of type string is incompatible with the type SilverStripe\Forms\FormField expected by parameter $item of SilverStripe\Forms\FieldList::insertAfter(). ( Ignorable by Annotation )

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

83
            /** @scrutinizer ignore-type */ 'Title'
Loading history...
84
        );
85
86 1
        if ($page = $this->hasParentPage()) {
87 1
            $fields->insertAfter(
88 1
                SiteTreeURLSegmentField::create('URLSegment')
89 1
                    ->setURLPrefix($page->Link().$this->hasViewAction().'/'),
90 1
                'MenuTitle'
91
            );
92
        }
93
94 1
        $fields->addFieldToTab(
95 1
            'Root.Main',
96 1
            ToggleCompositeField::create(
97 1
                'Metadata',
98 1
                'Metadata',
99
                array(
100 1
                    new TextField('MetaTitle', $this->owner->fieldLabel('MetaTitle')),
101 1
                    new TextareaField('MetaDescription', $this->owner->fieldLabel('MetaDescription')),
102
                )
103
            )
104
        );
105
    }
106
107
    /**
108
     * @return bool
109
     */
110 3
    public function hasParentPage()
111
    {
112 3
        if ($this->owner->hasMethod('getParentPage')) {
113 3
            return $this->owner->getParentPage();
114
        }
115
116
        return false;
117
    }
118
119
    /**
120
     * @return string
121
     */
122 4
    public function hasViewAction()
123
    {
124 4
        if ($this->owner->hasMethod('getViewAction')) {
125 4
            return $this->owner->getViewAction();
126
        }
127
128
        return 'view';
129
    }
130
131
    /**
132
     * @param null $action
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $action is correct as it would always require null to be passed?
Loading history...
133
     *
134
     * @return bool|string
135
     */
136 2
    public function Link($action = null)
137
    {
138 2
        if ($this->hasParentPage()) {
139 2
            return Controller::join_links(
140 2
                $this->hasParentPage()->Link(),
141 2
                $this->hasViewAction(),
142 2
                $this->owner->RelativeLink($action)
143
            );
144
        }
145
146
        return false;
147
    }
148
149
    /**
150
     * @param null $action
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $action is correct as it would always require null to be passed?
Loading history...
151
     *
152
     * @return string
153
     */
154 1
    public function AbsoluteLink($action = null)
155
    {
156 1
        if ($this->owner->hasMethod('alternateAbsoluteLink')) {
157
            return $this->owner->alternateAbsoluteLink($action);
158
        } else {
159 1
            return Director::absoluteURL($this->owner->Link($action));
160
        }
161
    }
162
163
    /**
164
     * @param null $action
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $action is correct as it would always require null to be passed?
Loading history...
165
     *
166
     * @return string
167
     */
168 2
    public function RelativeLink($action = null)
169
    {
170 2
        if ($this->owner->ParentID && SiteConfig::current_site_config()->nested_urls) {
171
            $parent = $this->owner->Parent();
172
            // If page is removed select parent from version history (for archive page view)
173
            if ((!$parent || !$parent->exists()) && !$this->owner->isOnDraft()) {
174
                $parent = Versioned::get_latest_version($this->owner->ClassName, $this->owner->ParentID);
175
            }
176
            $base = $parent->RelativeLink($this->owner->URLSegment);
177 2
        } elseif (!$action && $this->owner->URLSegment == RootURLController::get_homepage_link()) {
178
            // Unset base for root-level homepages.
179
            // Note: Homepages with action parameters (or $action === true)
180
            // need to retain their URLSegment.
181
            $base = null;
182
        } else {
183 2
            $base = $this->owner->URLSegment;
184
        }
185
186 2
        $this->owner->extend('updateRelativeLink', $base, $action);
187
188
        // Legacy support: If $action === true, retain URLSegment for homepages,
189
        // but don't append any action
190 2
        if ($action === true) {
191
            $action = null;
192
        }
193
194 2
        return Controller::join_links($base, '/', $action);
195
    }
196
197
    /**
198
     * @return bool|mixed
199
     */
200 7
    public function validURLSegment()
201
    {
202 7
        if (SiteConfig::current_site_config()->nested_urls && $parent = $this->owner->Parent()) {
203
            if ($controller = ModelAsController::controller_for($parent)) {
204
                if ($controller instanceof Controller && $controller->hasAction($this->owner->URLSegment)) {
205
                    return false;
206
                }
207
            }
208
        }
209
210 7
        if (!SiteConfig::current_site_config()->nested_urls || !$this->owner->ParentID) {
211 7
            if (class_exists($this->owner->URLSegment) &&
212 7
                is_subclass_of($this->owner->URLSegment, RequestHandler::class)) {
213
                return false;
214
            }
215
        }
216
217
        // Filters by url, id, and parent
218 7
        $table = DataObject::getSchema()->tableForField($this->owner->ClassName, 'URLSegment');
219 7
        $filter = array('"'.$table.'"."URLSegment"' => $this->owner->URLSegment);
220 7
        if ($this->owner->ID) {
221 7
            $filter['"'.$table.'"."ID" <> ?'] = $this->owner->ID;
222
        }
223 7
        if (SiteConfig::current_site_config()->nested_urls) {
224
            $filter['"'.$table.'"."ParentID"'] = $this->owner->ParentID ? $this->owner->ParentID : 0;
225
        }
226
227
        // If any of the extensions return `0` consider the segment invalid
228 7
        $extensionResponses = array_filter(
229 7
            (array) $this->owner->extend('augmentValidURLSegment'),
230 7
            function ($response) {
231
                return !is_null($response);
232 7
            }
233
        );
234 7
        if ($extensionResponses) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $extensionResponses of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
235
            return min($extensionResponses);
236
        }
237
238
        // Check existence
239 7
        return !DataObject::get($this->owner->ClassName, $filter)->exists();
240
    }
241
242
    /**
243
     * @param $title
244
     *
245
     * @return string
246
     */
247
    public function generateURLSegment($title)
248
    {
249
        $filter = URLSegmentFilter::create();
250
        $t = $filter->filter($title);
251
        // Fallback to generic page name if path is empty (= no valid, convertable characters)
252
        if (!$t || $t == '-' || $t == '-1') {
253
            $t = "page-$this->owner->ID";
254
        }
255
        // Hook for extensions
256
        $this->owner->extend('updateURLSegment', $t, $title);
257
258
        return $t;
259
    }
260
261
    /**
262
     * Generate custom meta tags to display on the DataObject view page.
263
     *
264
     * @param bool $includeTitle
265
     *
266
     * @return string
267
     */
268
    public function MetaTags($includeTitle = true)
269
    {
270
        $tags = '';
271
272
        if ($includeTitle === true || $includeTitle == 'true') {
273
            $tags .= '<title>'.Convert::raw2xml(($this->owner->MetaTitle)
0 ignored issues
show
Bug introduced by
Are you sure SilverStripe\Core\Conver... : $this->owner->Title) of type string|array can be used in concatenation? ( Ignorable by Annotation )

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

273
            $tags .= '<title>'./** @scrutinizer ignore-type */ Convert::raw2xml(($this->owner->MetaTitle)
Loading history...
274
                    ? $this->owner->MetaTitle
275
                    : $this->owner->Title)
276
                    ."</title>\n";
277
        }
278
        $tags .= "<meta name=\"generator\" content=\"SilverStripe - http://silverstripe.org\" />\n";
279
        $charset = Config::inst()->get('ContentNegotiator', 'encoding');
280
        $tags .= "<meta http-equiv=\"Content-type\" content=\"text/html; charset=$charset\" />\n";
281
        if ($this->owner->MetaDescription) {
282
            $tags .= '<meta name="description" content="'.Convert::raw2att($this->owner->MetaDescription)."\" />\n";
0 ignored issues
show
Bug introduced by
Are you sure SilverStripe\Core\Conver...owner->MetaDescription) of type string|array can be used in concatenation? ( Ignorable by Annotation )

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

282
            $tags .= '<meta name="description" content="'./** @scrutinizer ignore-type */ Convert::raw2att($this->owner->MetaDescription)."\" />\n";
Loading history...
283
        }
284
        $this->owner->extend('updateMetaTags', $tags);
285
286
        return $tags;
287
    }
288
289
    /**
290
     * Produce the correct breadcrumb trail for use on the DataObject Item Page.
291
     *
292
     * @param int  $maxDepth
293
     * @param bool $unlinked
294
     * @param bool $stopAtPageType
295
     * @param bool $showHidden
296
     *
297
     * @return DBHTMLText
298
     */
299 1
    public function Breadcrumbs($maxDepth = 20, $unlinked = false, $stopAtPageType = false, $showHidden = false)
0 ignored issues
show
Unused Code introduced by
The parameter $unlinked is not used and could be removed. ( Ignorable by Annotation )

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

299
    public function Breadcrumbs($maxDepth = 20, /** @scrutinizer ignore-unused */ $unlinked = false, $stopAtPageType = false, $showHidden = false)

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

Loading history...
300
    {
301 1
        $page = Controller::curr();
302 1
        $pages = array();
303 1
        $pages[] = $this->owner;
304 1
        while ($page
305 1
            && (!$maxDepth || count($pages) < $maxDepth)
306 1
            && (!$stopAtPageType || $page->ClassName != $stopAtPageType)
307
        ) {
308 1
            if ($showHidden || $page->ShowInMenus || ($page->ID == $this->owner->ID)) {
309
                $pages[] = $page;
310
            }
311 1
            $page = $page->Parent;
312
        }
313 1
        $template = new SSViewer('BreadcrumbsTemplate');
314
315 1
        return $template->process($this->owner->customise(new ArrayData(array(
316 1
            'Pages' => new ArrayList(array_reverse($pages)),
317
        ))));
318
    }
319
320
    /**
321
     *
322
     */
323 7
    public function onBeforeWrite()
324
    {
325 7
        if (!$this->owner->URLSegment) {
326
            $siteTree = singleton(SiteTree::class);
327
            $this->owner->URLSegment = $siteTree->generateURLSegment($this->owner->Title);
328
        }
329
330
        // Ensure that this object has a non-conflicting URLSegment value.
331 7
        $count = 2;
332 7
        while (!$this->owner->validURLSegment()) {
333 7
            $this->owner->URLSegment = preg_replace('/-[0-9]+$/', null, $this->owner->URLSegment).'-'.$count;
334 7
            ++$count;
335
        }
336
337 7
        parent::onBeforeWrite();
338
    }
339
}
340