Passed
Pull Request — master (#123)
by Robbie
01:54
created

BaseElement::getAllowedElementClasses()   B

Complexity

Conditions 6
Paths 18

Size

Total Lines 31
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 31
rs 8.439
c 0
b 0
f 0
cc 6
eloc 15
nc 18
nop 0
1
<?php
2
3
namespace DNADesign\Elemental\Models;
4
5
use Exception;
6
use DNADesign\Elemental\Forms\ElementalGridFieldHistoryButton;
7
use DNADesign\Elemental\Forms\HistoricalVersionedGridFieldItemRequest;
8
use DNADesign\Elemental\Controllers\ElementController;
9
use SilverStripe\CMS\Controllers\CMSPageEditController;
10
use SilverStripe\Control\Controller;
11
use SilverStripe\Control\Director;
12
use SilverStripe\Core\ClassInfo;
13
use SilverStripe\Core\Config\Config;
14
use SilverStripe\Core\Injector\Injector;
15
use SilverStripe\Core\Manifest\ModuleResourceLoader;
16
use SilverStripe\Forms\CheckboxField;
17
use SilverStripe\Forms\DropdownField;
18
use SilverStripe\Forms\FieldGroup;
19
use SilverStripe\Forms\FieldList;
20
use SilverStripe\Forms\HiddenField;
21
use SilverStripe\Forms\NumericField;
22
use SilverStripe\Forms\ReadonlyField;
23
use SilverStripe\Forms\TextField;
24
use SilverStripe\Forms\GridField\GridField;
25
use SilverStripe\Forms\GridField\GridFieldConfig_RecordViewer;
26
use SilverStripe\Forms\GridField\GridFieldDataColumns;
27
use SilverStripe\Forms\GridField\GridFieldDetailForm;
28
use SilverStripe\Forms\GridField\GridFieldPageCount;
29
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
30
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
31
use SilverStripe\Forms\GridField\GridFieldVersionedState;
32
use SilverStripe\Forms\GridField\GridFieldViewButton;
33
use SilverStripe\ORM\ArrayList;
34
use SilverStripe\ORM\CMSPreviewable;
35
use SilverStripe\ORM\DataObject;
36
use SilverStripe\ORM\DB;
37
use SilverStripe\ORM\FieldType\DBField;
38
use SilverStripe\ORM\Search\SearchContext;
39
use SilverStripe\Security\Permission;
40
use SilverStripe\Security\Member;
41
use SilverStripe\SiteConfig\SiteConfig;
42
use SilverStripe\Versioned\Versioned;
43
use SilverStripe\View\Parsers\URLSegmentFilter;
44
use SilverStripe\View\SSViewer;
45
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
46
47
class BaseElement extends DataObject implements CMSPreviewable
48
{
49
    /**
50
     * Override this on your custom elements to specify a cms icon
51
     *
52
     * @var string
53
     */
54
    private static $icon = 'dnadesign/silverstripe-elemental:images/base.svg';
0 ignored issues
show
Unused Code introduced by
The property $icon is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
55
56
    private static $db = [
0 ignored issues
show
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
57
        'Title' => 'Varchar(255)',
58
        'ShowTitle' => 'Boolean',
59
        'Sort' => 'Int',
60
        'ExtraClass' => 'Varchar(255)',
61
        'Style' => 'Varchar(255)'
62
    ];
63
64
    private static $has_one = [
0 ignored issues
show
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
65
        'Parent' => ElementalArea::class
66
    ];
67
68
    private static $extensions = [
0 ignored issues
show
Unused Code introduced by
The property $extensions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
69
        Versioned::class
70
    ];
71
72
    private static $versioned_gridfield_extensions = true;
0 ignored issues
show
Unused Code introduced by
The property $versioned_gridfield_extensions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
73
74
    private static $table_name = 'Element';
0 ignored issues
show
Unused Code introduced by
The property $table_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
75
76
    /**
77
     * @var string
78
     */
79
    private static $controller_class = ElementController::class;
0 ignored issues
show
Unused Code introduced by
The property $controller_class is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
80
81
    /**
82
     * @var string
83
     */
84
    private static $controller_template = 'ElementHolder';
0 ignored issues
show
Unused Code introduced by
The property $controller_template is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
85
86
    /**
87
     * @var ElementController
88
     */
89
    protected $controller;
90
91
    private static $default_sort = 'Sort';
0 ignored issues
show
Unused Code introduced by
The property $default_sort is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
92
93
    private static $singular_name = 'block';
0 ignored issues
show
Unused Code introduced by
The property $singular_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
94
95
    private static $plural_name = 'blocks';
0 ignored issues
show
Unused Code introduced by
The property $plural_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
96
97
    private static $summary_fields = [
0 ignored issues
show
Unused Code introduced by
The property $summary_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
98
        'EditorPreview' => 'Summary'
99
    ];
100
101
    /**
102
     * @var array
103
     */
104
    private static $styles = [];
0 ignored issues
show
Unused Code introduced by
The property $styles is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
105
106
    private static $searchable_fields = [
0 ignored issues
show
Unused Code introduced by
The property $searchable_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
107
        'ID' => [
108
            'field' => NumericField::class,
109
        ],
110
        'Title',
111
        'LastEdited'
112
    ];
113
114
    /**
115
     * Enable for backwards compatibility
116
     *
117
     * @var boolean
118
     */
119
    private static $disable_pretty_anchor_name = false;
0 ignored issues
show
Unused Code introduced by
The property $disable_pretty_anchor_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
120
121
    /**
122
     * Store used anchor names, this is to avoid title clashes
123
     * when calling 'getAnchor'
124
     *
125
     * @var array
126
     */
127
    protected static $_used_anchors = [];
128
129
    /**
130
     * For caching 'getAnchor'
131
     *
132
     * @var string
133
     */
134
    protected $_anchor = null;
135
136
    /**
137
     * Basic permissions, defaults to page perms where possible.
138
     *
139
     * @param Member $member
140
     *
141
     * @return boolean
142
     */
143 View Code Duplication
    public function canView($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...
144
    {
145
        if ($this->hasMethod('getPage')) {
146
            if ($page = $this->getPage()) {
147
                return $page->canView($member);
148
            }
149
        }
150
151
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
152
    }
153
154
    /**
155
     * Basic permissions, defaults to page perms where possible.
156
     *
157
     * @param Member $member
158
     *
159
     * @return boolean
160
     */
161 View Code Duplication
    public function canEdit($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...
162
    {
163
        if ($this->hasMethod('getPage')) {
164
            if ($page = $this->getPage()) {
165
                return $page->canEdit($member);
166
            }
167
        }
168
169
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
170
    }
171
172
    /**
173
     * Basic permissions, defaults to page perms where possible.
174
     *
175
     * Uses archive not delete so that current stage is respected i.e if a
176
     * element is not published, then it can be deleted by someone who doesn't
177
     * have publishing permissions.
178
     *
179
     * @param Member $member
180
     *
181
     * @return boolean
182
     */
183 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...
184
    {
185
        if ($this->hasMethod('getPage')) {
186
            if ($page = $this->getPage()) {
187
                return $page->canArchive($member);
188
            }
189
        }
190
191
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
192
    }
193
194
    /**
195
     * Basic permissions, defaults to page perms where possible.
196
     *
197
     * @param Member $member
198
     * @param array $context
199
     *
200
     * @return boolean
201
     */
202
    public function canCreate($member = null, $context = array())
203
    {
204
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
205
    }
206
207
    /**
208
     *
209
     */
210
    public function onBeforeWrite()
211
    {
212
        parent::onBeforeWrite();
213
214
        if ($areaID = $this->ParentID) {
0 ignored issues
show
Bug Best Practice introduced by
The property ParentID does not exist on DNADesign\Elemental\Models\BaseElement. Since you implemented __get, consider adding a @property annotation.
Loading history...
215
            if ($elementalArea = ElementalArea::get()->byID($areaID)) {
216
                $elementalArea->write();
217
            }
218
        }
219
220
        if (!$this->Sort) {
221
            $parentID = ($this->ParentID) ? $this->ParentID : 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $parentID is dead and can be removed.
Loading history...
222
223
            $this->Sort = static::get()->max('Sort') + 1;
0 ignored issues
show
Bug Best Practice introduced by
The property Sort does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
224
        }
225
    }
226
227
    public function getCMSFields()
228
    {
229
        $this->beforeUpdateCMSFields(function (FieldList $fields) {
230
            // Remove relationship fields
231
            $fields->removeByName('ParentID');
232
            $fields->removeByName('Sort');
233
234
            $fields->addFieldToTab(
235
                'Root.Settings',
236
                TextField::create('ExtraClass', _t(__CLASS__ . '.ExtraCssClassesLabel', 'Custom CSS classes'))
0 ignored issues
show
Bug introduced by
'ExtraClass' 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

236
                TextField::create(/** @scrutinizer ignore-type */ 'ExtraClass', _t(__CLASS__ . '.ExtraCssClassesLabel', 'Custom CSS classes'))
Loading history...
237
                    ->setAttribute(
238
                        'placeholder',
239
                        _t(__CLASS__ . '.ExtraCssClassesPlaceholder', 'my_class another_class')
240
                    )
241
            );
242
243
            // Add a combined field for "Title" and "Displayed" checkbox in a Bootstrap input group
244
            $fields->removeByName('ShowTitle');
245
            $fields->replaceField(
246
                'Title',
247
                FieldGroup::create(
248
                    TextField::create('Title', ''),
249
                    CheckboxField::create('ShowTitle', _t(__CLASS__ . '.ShowTitleLabel', 'Displayed'))
250
                )
251
                    ->setTemplate(__CLASS__ . '\\FieldGroup')
252
                    ->setTitle(_t(__CLASS__ . '.TitleLabel', 'Title (not displayed unless specified)'))
253
            );
254
255
            // Rename the "Main" tab
256
            $fields->fieldByName('Root.Main')
257
                ->setTitle(_t(__CLASS__ . '.MainTabLabel', 'Content'));
258
259
            // Remove divider lines on all block forms
260
            $fields->fieldByName('Root')->addExtraClass('form--no-dividers');
261
262
            $fields->addFieldsToTab('Root.Main', [
263
                HiddenField::create('AbsoluteLink', false, Director::absoluteURL($this->PreviewLink())),
0 ignored issues
show
Bug introduced by
false of type false 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

263
                HiddenField::create('AbsoluteLink', /** @scrutinizer ignore-type */ false, Director::absoluteURL($this->PreviewLink())),
Loading history...
264
                HiddenField::create('LiveLink', false, Director::absoluteURL($this->Link())),
265
                HiddenField::create('StageLink', false, Director::absoluteURL($this->PreviewLink())),
266
            ]);
267
268
            $styles = $this->config()->get('styles');
269
270
            if ($styles && count($styles) > 0) {
271
                $styleDropdown = DropdownField::create('Style', _t(__CLASS__.'.STYLE', 'Style variation'), $styles);
272
273
                $fields->insertBefore($styleDropdown, 'ExtraClass');
0 ignored issues
show
Bug introduced by
'ExtraClass' of type string is incompatible with the type SilverStripe\Forms\FormField expected by parameter $item of SilverStripe\Forms\FieldList::insertBefore(). ( Ignorable by Annotation )

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

273
                $fields->insertBefore($styleDropdown, /** @scrutinizer ignore-type */ 'ExtraClass');
Loading history...
274
275
                $styleDropdown->setEmptyString(_t(__CLASS__.'.CUSTOM_STYLES', 'Select a style..'));
276
            } else {
277
                $fields->removeByName('Style');
278
            }
279
280
            $history = $this->getHistoryFields();
281
282
            if ($history) {
283
                $fields->addFieldsToTab('Root.History', $history);
0 ignored issues
show
Bug introduced by
$history of type SilverStripe\Forms\FieldList is incompatible with the type array expected by parameter $fields of SilverStripe\Forms\FieldList::addFieldsToTab(). ( Ignorable by Annotation )

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

283
                $fields->addFieldsToTab('Root.History', /** @scrutinizer ignore-type */ $history);
Loading history...
284
            }
285
        });
286
287
        return parent::getCMSFields();
288
    }
289
290
    /**
291
     * Returns the history fields for this element.
292
     *
293
     * @return FieldList
294
     */
295
    public function getHistoryFields()
296
    {
297
        if (!$this->isLatestVersion()) {
0 ignored issues
show
Bug introduced by
The method isLatestVersion() does not exist on DNADesign\Elemental\Models\BaseElement. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

297
        if (!$this->/** @scrutinizer ignore-call */ isLatestVersion()) {
Loading history...
298
            // if viewing the history of the of page then don't show the history
299
            // fields as then we have recursion.
300
            return null;
301
        }
302
303
        $config = GridFieldConfig_RecordViewer::create();
304
        $config->removeComponentsByType(GridFieldPageCount::class);
305
        $config->removeComponentsByType(GridFieldToolbarHeader::class);
306
        // Replace the sortable ID column with a static header component
307
        $config->removeComponentsByType(GridFieldSortableHeader::class);
308
        $config->addComponent(new GridFieldTitleHeader);
309
310
        $config
311
            ->getComponentByType(GridFieldDetailForm::class)
312
            ->setItemRequestClass(HistoricalVersionedGridFieldItemRequest::class);
313
314
        $config->getComponentByType(GridFieldDataColumns::class)
315
            ->setDisplayFields([
316
                'Version' => '#',
317
                'RecordStatus' => _t(__CLASS__ . '.Record', 'Record'),
318
                'getAuthor.Name' => _t(__CLASS__ . '.Author', 'Author')
319
            ])
320
            ->setFieldFormatting([
321
                'RecordStatus' => '$VersionedStateNice <span class=\"element-history__date--small\">on $LastEditedNice</span>',
322
            ]);
323
324
        $config->removeComponentsByType(GridFieldViewButton::class);
325
        $config->addComponent(new ElementalGridFieldHistoryButton());
326
327
        $history = Versioned::get_all_versions(__CLASS__, $this->ID)
328
            ->sort('Version', 'DESC');
329
330
        return FieldList::create(
331
            GridField::create('History', '', $history, $config)
0 ignored issues
show
Bug introduced by
$history of type SilverStripe\ORM\DataList 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

331
            GridField::create('History', '', /** @scrutinizer ignore-type */ $history, $config)
Loading history...
Bug introduced by
'History' 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

331
            GridField::create(/** @scrutinizer ignore-type */ 'History', '', $history, $config)
Loading history...
332
        );
333
    }
334
335
    /**
336
     * Get the type of the current block, for use in GridField summaries, block
337
     * type dropdowns etc. Examples are "Content", "File", "Media", etc.
338
     *
339
     * @return string
340
     */
341
    public function getType()
342
    {
343
        return _t(__CLASS__ . '.BlockType', 'Block');
344
    }
345
346
    /**
347
     * @param ElementController
348
     *
349
     * @return $this
350
     */
351
    public function setController($controller)
352
    {
353
        $this->controller = $controller;
354
355
        return $this;
356
    }
357
358
    /**
359
     * @throws Exception
360
     *
361
     * @return ElementController
362
     */
363
    public function getController()
364
    {
365
        if ($this->controller) {
366
            return $this->controller;
367
        }
368
369
        $controllerClass = self::config()->controller_class;
370
371
        if (!class_exists($controllerClass)) {
372
            throw new Exception('Could not find controller class ' . $controllerClass . ' as defined in ' . static::class);
373
        }
374
375
        $this->controller = Injector::inst()->create($controllerClass, $this);
376
        $this->controller->doInit();
377
378
        return $this->controller;
379
    }
380
381
    /**
382
     * @return Controller
383
     */
384
    public function Top()
385
    {
386
        return (Controller::has_curr()) ? Controller::curr() : null;
387
    }
388
389
    /**
390
     * Default way to render element in templates. Note that all blocks should
391
     * be rendered through their {@link ElementController} class as this
392
     * contains the holder styles.
393
     *
394
     * @return string HTML
395
     */
396
    public function forTemplate($holder = true)
0 ignored issues
show
Unused Code introduced by
The parameter $holder 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

396
    public function forTemplate(/** @scrutinizer ignore-unused */ $holder = true)

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...
397
    {
398
        $templates = $this->getRenderTemplates();
399
400
        if ($templates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $templates 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...
401
            return $this->renderWith($templates);
402
        }
403
    }
404
405
    /**
406
     * @param string $suffix
407
     *
408
     * @return array
409
     */
410
    public function getRenderTemplates($suffix = '')
411
    {
412
        $classes = ClassInfo::ancestry($this->ClassName);
413
        $classes[static::class] = static::class;
414
        $classes = array_reverse($classes);
415
        $templates = array();
416
417
        foreach ($classes as $key => $value) {
418
            if ($value == BaseElement::class) {
419
                continue;
420
            }
421
422
            if ($value == DataObject::class) {
423
                break;
424
            }
425
426
            $templates[] = $value . $suffix;
427
        }
428
429
        return $templates;
430
    }
431
432
    /**
433
     * Strip all namespaces from class namespace.
434
     *
435
     * @param string $classname e.g. "\Fully\Namespaced\Class"
436
     *
437
     * @return string following the param example, "Class"
438
     */
439
    protected function stripNamespacing($classname)
440
    {
441
        $classParts = explode('\\', $classname);
442
        return array_pop($classParts);
443
    }
444
445
    /**
446
     * @return string
447
     */
448
    public function getSimpleClassName()
449
    {
450
        return strtolower($this->sanitiseClassName($this->ClassName, '__'));
451
    }
452
453
    /**
454
     * @return SiteTree
0 ignored issues
show
Bug introduced by
The type DNADesign\Elemental\Models\SiteTree 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...
455
     */
456
    public function getPage()
457
    {
458
        $area = $this->Parent();
0 ignored issues
show
Bug introduced by
The method Parent() does not exist on DNADesign\Elemental\Models\BaseElement. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

458
        /** @scrutinizer ignore-call */ 
459
        $area = $this->Parent();
Loading history...
459
460
        if ($area instanceof ElementalArea && $area->exists()) {
461
            return $area->getOwnerPage();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $area->getOwnerPage() could also return false which is incompatible with the documented return type DNADesign\Elemental\Models\SiteTree. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
462
        }
463
464
        return null;
465
    }
466
467
    /**
468
     * Get a unique anchor name
469
     *
470
     * @return string
471
     */
472
    public function getAnchor()
473
    {
474
        if ($this->_anchor !== null) {
475
            return $this->_anchor;
476
        }
477
478
        $anchorTitle = '';
479
480
        if (!$this->config()->disable_pretty_anchor_name) {
481
            if ($this->hasMethod('getAnchorTitle')) {
482
                $anchorTitle = $this->getAnchorTitle();
483
            } elseif ($this->config()->enable_title_in_template) {
484
                $anchorTitle = $this->getField('Title');
485
            }
486
        }
487
488
        if (!$anchorTitle) {
489
            $anchorTitle = 'e'.$this->ID;
490
        }
491
492
        $filter = URLSegmentFilter::create();
493
        $titleAsURL = $filter->filter($anchorTitle);
494
495
        // Ensure that this anchor name isn't already in use
496
        // ie. If two elemental blocks have the same title, it'll append '-2', '-3'
497
        $result = $titleAsURL;
498
        $count = 1;
499
        while (isset(self::$_used_anchors[$result]) && self::$_used_anchors[$result] !== $this->ID) {
500
            ++$count;
501
            $result = $titleAsURL.'-'.$count;
502
        }
503
        self::$_used_anchors[$result] = $this->ID;
504
        return $this->_anchor = $result;
505
    }
506
507
    /**
508
     * @param string $action
509
     *
510
     * @return string
511
     */
512
    public function AbsoluteLink($action = null)
513
    {
514
        if ($page = $this->getPage()) {
515
            $link = $page->AbsoluteLink($action) . '#' . $this->getAnchor();
516
517
            return $link;
518
        }
519
    }
520
521
    /**
522
     * @param string $action
523
     *
524
     * @return string
525
     */
526
    public function Link($action = null)
527
    {
528
        if ($page = $this->getPage()) {
529
            $link = $page->Link($action) . '#' . $this->getAnchor();
530
531
            $this->extend('updateLink', $link);
532
533
            return $link;
534
        }
535
    }
536
537
    /**
538
     * @param string $action
539
     *
540
     * @return string
541
     */
542
    public function PreviewLink($action = null)
543
    {
544
        $action = $action . '?ElementalPreview=' . mt_rand();
545
        $link = $this->Link($action);
546
        $this->extend('updatePreviewLink', $link);
547
548
        return $link;
549
    }
550
551
    /**
552
     * @return boolean
553
     */
554
    public function isCMSPreview()
555
    {
556
        if (Controller::has_curr()) {
557
            $c = Controller::curr();
558
559
            if ($c->getRequest()->requestVar('CMSPreview')) {
560
                return true;
561
            }
562
        }
563
564
        return false;
565
    }
566
567
    /**
568
     * @return string
569
     */
570
    public function CMSEditLink()
571
    {
572
        $relationName = $this->getAreaRelationName();
573
        $page = $this->getPage(true);
0 ignored issues
show
Unused Code introduced by
The call to DNADesign\Elemental\Models\BaseElement::getPage() has too many arguments starting with true. ( Ignorable by Annotation )

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

573
        /** @scrutinizer ignore-call */ 
574
        $page = $this->getPage(true);

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...
574
575
        if (!$page) {
576
            return null;
577
        }
578
579
        $link = Controller::join_links(
580
            singleton(CMSPageEditController::class)->Link('EditForm'),
581
            $page->ID,
582
            'field/' . $relationName . '/item/',
583
            $this->ID
584
        );
585
586
        return Controller::join_links(
587
            $link,
588
            'edit'
589
        );
590
    }
591
592
    /**
593
     * Retrieve a elemental area relation for creating cms links
594
     *
595
     * @return string - the name of a valid elemental area relation
596
     */
597
    public function getAreaRelationName()
598
    {
599
        $page = $this->getPage(true);
0 ignored issues
show
Unused Code introduced by
The call to DNADesign\Elemental\Models\BaseElement::getPage() has too many arguments starting with true. ( Ignorable by Annotation )

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

599
        /** @scrutinizer ignore-call */ 
600
        $page = $this->getPage(true);

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...
600
601
        if ($page) {
602
            $has_one = $page->config()->get('has_one');
603
            $area = $this->Parent();
604
605
            foreach ($has_one as $relationName => $relationClass) {
606
                if ($relationClass === $area->ClassName) {
607
                    return $relationName;
608
                }
609
            }
610
        }
611
612
        return 'ElementalArea';
613
    }
614
615
    /**
616
     * Sanitise a model class' name for inclusion in a link.
617
     *
618
     * @return string
619
     */
620
    public function sanitiseClassName($class, $delimiter = '-')
621
    {
622
        return str_replace('\\', $delimiter, $class);
623
    }
624
625
    public function unsanitiseClassName($class, $delimiter = '-')
626
    {
627
        return str_replace($delimiter, '\\', $class);
628
    }
629
630
    /**
631
     * @return string
632
     */
633
    public function getEditLink()
634
    {
635
        return $this->CMSEditLink();
636
    }
637
638
    /**
639
     * @return HTMLText
0 ignored issues
show
Bug introduced by
The type DNADesign\Elemental\Models\HTMLText 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...
640
     */
641
    public function PageCMSEditLink()
642
    {
643
        if ($page = $this->getPage()) {
644
            return DBField::create_field('HTMLText', sprintf(
0 ignored issues
show
Bug Best Practice introduced by
The expression return SilverStripe\ORM\...tLink(), $page->Title)) returns the type SilverStripe\ORM\FieldType\DBField which is incompatible with the documented return type DNADesign\Elemental\Models\HTMLText.
Loading history...
645
                '<a href="%s">%s</a>',
646
                $page->CMSEditLink(),
647
                $page->Title
648
            ));
649
        }
650
    }
651
652
    /**
653
     * @return string
654
     */
655
    public function getMimeType()
656
    {
657
        return 'text/html';
658
    }
659
660
    /**
661
     * This can be overridden on child elements to create a summary for display
662
     * in GridFields.
663
     *
664
     * @return string
665
     */
666
    public function getSummary()
667
    {
668
        return '';
669
    }
670
671
672
    /**
673
     * Generate markup for element type icons suitable for use in GridFields.
674
     *
675
     * @return DBField
676
     */
677
    public function getIcon()
678
    {
679
        $icon = $this->config()->get('icon');
680
681
        if ($icon) {
682
            $icon = ModuleResourceLoader::resourceURL($icon);
683
684
            return DBField::create_field('HTMLVarchar', '<img width="16px" src="' . Director::absoluteBaseURL() . $icon . '" alt="" />');
685
        }
686
    }
687
688
    /**
689
     * Generate markup for element type, with description suitable for use in
690
     * GridFields.
691
     *
692
     * @return DBField
693
     */
694
    public function getTypeNice()
695
    {
696
        $description = $this->config()->get('description');
697
        $desc = ($description) ? ' <span class="el-description"> &mdash; ' . $description . '</span>' : '';
698
699
        return DBField::create_field(
700
            'HTMLVarchar',
701
            $this->getType() . $desc
702
        );
703
    }
704
705
    /**
706
     * @return HTMLText
707
     */
708
    public function getEditorPreview()
709
    {
710
        $templates = $this->getRenderTemplates('_EditorPreview');
711
        $templates[] = BaseElement::class . '_EditorPreview';
712
713
        return $this->renderWith($templates);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->renderWith($templates) returns the type SilverStripe\ORM\FieldType\DBHTMLText which is incompatible with the documented return type DNADesign\Elemental\Models\HTMLText.
Loading history...
714
    }
715
716
    /**
717
     * @return Member
718
     */
719
    public function getAuthor()
720
    {
721
        if ($this->AuthorID) {
0 ignored issues
show
Bug Best Practice introduced by
The property AuthorID does not exist on DNADesign\Elemental\Models\BaseElement. Since you implemented __get, consider adding a @property annotation.
Loading history...
722
            return Member::get()->byId($this->AuthorID);
723
        }
724
    }
725
726
    /**
727
     * Get a user defined style variant for this element, if available
728
     *
729
     * @return string
730
     */
731
    public function getStyleVariant()
732
    {
733
        $style = $this->Style;
0 ignored issues
show
Bug Best Practice introduced by
The property Style does not exist on DNADesign\Elemental\Models\BaseElement. Since you implemented __get, consider adding a @property annotation.
Loading history...
734
        $styles = $this->config()->get('styles');
735
736
        if (isset($styles[$style])) {
737
            $style = strtolower($style);
738
        } else {
739
            $style = '';
740
        }
741
742
        $this->extend('updateStyleVariant', $style);
743
744
        return $style;
745
    }
746
747
    /**
748
     *
749
     */
750
    public function getPageTitle()
751
    {
752
        $page = $this->getPage();
753
754
        if ($page) {
755
            return $page->Title;
756
        }
757
758
        return null;
759
    }
760
761
    /**
762
     * Get a "nice" label for use in the block history GridField
763
     *
764
     * @return string
765
     */
766
    public function getVersionedStateNice()
767
    {
768
        if ($this->WasPublished) {
0 ignored issues
show
Bug Best Practice introduced by
The property WasPublished does not exist on DNADesign\Elemental\Models\BaseElement. Since you implemented __get, consider adding a @property annotation.
Loading history...
769
            return _t(__CLASS__ . '.Published', 'Published');
770
        }
771
772
        return _t(__CLASS__ . '.Modified', 'Modified');
773
    }
774
775
    /**
776
     * Return a formatted date for use in the block history GridField
777
     *
778
     * @return string
779
     */
780
    public function getLastEditedNice()
781
    {
782
        return $this->dbObject('LastEdited')->Nice();
783
    }
784
}
785