Completed
Push — master ( c30dd6...2c9ce4 )
by Robbie
13s
created

BaseElement::getDescription()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 9.4285
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\Forms\TextCheckboxGroupField;
9
use DNADesign\Elemental\Controllers\ElementController;
10
use SilverStripe\CMS\Controllers\CMSPageEditController;
11
use SilverStripe\Control\Controller;
12
use SilverStripe\Control\Director;
13
use SilverStripe\Core\ClassInfo;
14
use SilverStripe\Core\Config\Config;
15
use SilverStripe\Core\Injector\Injector;
16
use SilverStripe\Core\Manifest\ModuleResourceLoader;
17
use SilverStripe\Forms\CheckboxField;
18
use SilverStripe\Forms\DropdownField;
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\Requirements;
45
use SilverStripe\View\SSViewer;
46
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
47
48
class BaseElement extends DataObject implements CMSPreviewable
49
{
50
    /**
51
     * Override this on your custom elements to specify a cms icon
52
     *
53
     * @var string
54
     */
55
    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...
56
57
    /**
58
     * Describe the purpose of this element
59
     *
60
     * @config
61
     * @var string
62
     */
63
    private static $description = 'Base element class';
0 ignored issues
show
Unused Code introduced by
The property $description 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...
64
65
    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...
66
        'Title' => 'Varchar(255)',
67
        'ShowTitle' => 'Boolean',
68
        'Sort' => 'Int',
69
        'ExtraClass' => 'Varchar(255)',
70
        'Style' => 'Varchar(255)'
71
    ];
72
73
    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...
74
        'Parent' => ElementalArea::class
75
    ];
76
77
    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...
78
        Versioned::class
79
    ];
80
81
    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...
82
83
    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...
84
85
    /**
86
     * @var string
87
     */
88
    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...
89
90
    /**
91
     * @var string
92
     */
93
    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...
94
95
    /**
96
     * @var ElementController
97
     */
98
    protected $controller;
99
100
    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...
101
102
    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...
103
104
    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...
105
106
    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...
107
        'EditorPreview' => 'Summary'
108
    ];
109
110
    /**
111
     * @var array
112
     */
113
    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...
114
115
    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...
116
        'ID' => [
117
            'field' => NumericField::class,
118
        ],
119
        'Title',
120
        'LastEdited'
121
    ];
122
123
    /**
124
     * Enable for backwards compatibility
125
     *
126
     * @var boolean
127
     */
128
    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...
129
130
    /**
131
     * Store used anchor names, this is to avoid title clashes
132
     * when calling 'getAnchor'
133
     *
134
     * @var array
135
     */
136
    protected static $_used_anchors = [];
137
138
    /**
139
     * For caching 'getAnchor'
140
     *
141
     * @var string
142
     */
143
    protected $_anchor = null;
144
145
    /**
146
     * Basic permissions, defaults to page perms where possible.
147
     *
148
     * @param Member $member
149
     *
150
     * @return boolean
151
     */
152 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...
153
    {
154
        if ($this->hasMethod('getPage')) {
155
            if ($page = $this->getPage()) {
156
                return $page->canView($member);
157
            }
158
        }
159
160
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
161
    }
162
163
    /**
164
     * Basic permissions, defaults to page perms where possible.
165
     *
166
     * @param Member $member
167
     *
168
     * @return boolean
169
     */
170 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...
171
    {
172
        if ($this->hasMethod('getPage')) {
173
            if ($page = $this->getPage()) {
174
                return $page->canEdit($member);
175
            }
176
        }
177
178
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
179
    }
180
181
    /**
182
     * Basic permissions, defaults to page perms where possible.
183
     *
184
     * Uses archive not delete so that current stage is respected i.e if a
185
     * element is not published, then it can be deleted by someone who doesn't
186
     * have publishing permissions.
187
     *
188
     * @param Member $member
189
     *
190
     * @return boolean
191
     */
192 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...
193
    {
194
        if ($this->hasMethod('getPage')) {
195
            if ($page = $this->getPage()) {
196
                return $page->canArchive($member);
197
            }
198
        }
199
200
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
201
    }
202
203
    /**
204
     * Basic permissions, defaults to page perms where possible.
205
     *
206
     * @param Member $member
207
     * @param array $context
208
     *
209
     * @return boolean
210
     */
211
    public function canCreate($member = null, $context = array())
212
    {
213
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
214
    }
215
216
    /**
217
     *
218
     */
219
    public function onBeforeWrite()
220
    {
221
        parent::onBeforeWrite();
222
223
        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...
224
            if ($elementalArea = ElementalArea::get()->byID($areaID)) {
225
                $elementalArea->write();
226
            }
227
        }
228
229
        if (!$this->Sort) {
230
            $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...
231
232
            $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...
233
        }
234
    }
235
236
    public function getCMSFields()
237
    {
238
        $this->beforeUpdateCMSFields(function (FieldList $fields) {
239
            // Remove relationship fields
240
            $fields->removeByName('ParentID');
241
            $fields->removeByName('Sort');
242
243
            $fields->addFieldToTab(
244
                'Root.Settings',
245
                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

245
                TextField::create(/** @scrutinizer ignore-type */ 'ExtraClass', _t(__CLASS__ . '.ExtraCssClassesLabel', 'Custom CSS classes'))
Loading history...
246
                    ->setAttribute(
247
                        'placeholder',
248
                        _t(__CLASS__ . '.ExtraCssClassesPlaceholder', 'my_class another_class')
249
                    )
250
            );
251
252
            // Add a combined field for "Title" and "Displayed" checkbox in a Bootstrap input group
253
            $fields->removeByName('ShowTitle');
254
            $fields->replaceField(
255
                'Title',
256
                TextCheckboxGroupField::create(
257
                    TextField::create('Title', _t(__CLASS__ . '.TitleLabel', 'Title (not displayed unless specified)')),
258
                    CheckboxField::create('ShowTitle', _t(__CLASS__ . '.ShowTitleLabel', 'Displayed'))
259
                )
260
                    ->setName('TitleAndDisplayed')
261
            );
262
263
            // Rename the "Main" tab
264
            $fields->fieldByName('Root.Main')
265
                ->setTitle(_t(__CLASS__ . '.MainTabLabel', 'Content'));
266
267
            // Remove divider lines on all block forms
268
            $fields->fieldByName('Root')->addExtraClass('form--no-dividers');
269
270
            $fields->addFieldsToTab('Root.Main', [
271
                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

271
                HiddenField::create('AbsoluteLink', /** @scrutinizer ignore-type */ false, Director::absoluteURL($this->PreviewLink())),
Loading history...
272
                HiddenField::create('LiveLink', false, Director::absoluteURL($this->Link())),
273
                HiddenField::create('StageLink', false, Director::absoluteURL($this->PreviewLink())),
274
            ]);
275
276
            $styles = $this->config()->get('styles');
277
278
            if ($styles && count($styles) > 0) {
279
                $styleDropdown = DropdownField::create('Style', _t(__CLASS__.'.STYLE', 'Style variation'), $styles);
280
281
                $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

281
                $fields->insertBefore($styleDropdown, /** @scrutinizer ignore-type */ 'ExtraClass');
Loading history...
282
283
                $styleDropdown->setEmptyString(_t(__CLASS__.'.CUSTOM_STYLES', 'Select a style..'));
284
            } else {
285
                $fields->removeByName('Style');
286
            }
287
288
            $history = $this->getHistoryFields();
289
290
            if ($history) {
291
                $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

291
                $fields->addFieldsToTab('Root.History', /** @scrutinizer ignore-type */ $history);
Loading history...
292
            }
293
        });
294
295
        return parent::getCMSFields();
296
    }
297
298
    /**
299
     * Returns the history fields for this element.
300
     *
301
     * @param  bool $checkLatestVersion Whether to check if this is the latest version. Prevents recursion, but can be
302
     *                                  overridden to get the history GridField if required.
303
     * @return FieldList
304
     */
305
    public function getHistoryFields($checkLatestVersion = true)
306
    {
307
        if ($checkLatestVersion && !$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

307
        if ($checkLatestVersion && !$this->/** @scrutinizer ignore-call */ isLatestVersion()) {
Loading history...
308
            // if viewing the history of the of page then don't show the history
309
            // fields as then we have recursion.
310
            return null;
311
        }
312
313
        Requirements::javascript('dnadesign/silverstripe-elemental:javascript/block-history.js');
314
315
        $config = GridFieldConfig_RecordViewer::create();
316
        $config->removeComponentsByType(GridFieldPageCount::class);
317
        $config->removeComponentsByType(GridFieldToolbarHeader::class);
318
        // Replace the sortable ID column with a static header component
319
        $config->removeComponentsByType(GridFieldSortableHeader::class);
320
        $config->addComponent(new GridFieldTitleHeader);
321
322
        $config
323
            ->getComponentByType(GridFieldDetailForm::class)
324
            ->setItemRequestClass(HistoricalVersionedGridFieldItemRequest::class);
325
326
        $config->getComponentByType(GridFieldDataColumns::class)
327
            ->setDisplayFields([
328
                'Version' => '#',
329
                'RecordStatus' => _t(__CLASS__ . '.Record', 'Record'),
330
                'getAuthor.Name' => _t(__CLASS__ . '.Author', 'Author')
331
            ])
332
            ->setFieldFormatting([
333
                'RecordStatus' => '$VersionedStateNice <span class=\"element-history__date--small\">on $LastEditedNice</span>',
334
            ]);
335
336
        $config->removeComponentsByType(GridFieldViewButton::class);
337
        $config->addComponent(new ElementalGridFieldHistoryButton());
338
339
        $history = Versioned::get_all_versions(__CLASS__, $this->ID)
340
            ->sort('Version', 'DESC');
341
342
        return FieldList::create(
343
            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

343
            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

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

409
    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...
410
    {
411
        $templates = $this->getRenderTemplates();
412
413
        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...
414
            return $this->renderWith($templates);
415
        }
416
    }
417
418
    /**
419
     * @param string $suffix
420
     *
421
     * @return array
422
     */
423
    public function getRenderTemplates($suffix = '')
424
    {
425
        $classes = ClassInfo::ancestry($this->ClassName);
426
        $classes[static::class] = static::class;
427
        $classes = array_reverse($classes);
428
        $templates = array();
429
430
        foreach ($classes as $key => $value) {
431
            if ($value == BaseElement::class) {
432
                continue;
433
            }
434
435
            if ($value == DataObject::class) {
436
                break;
437
            }
438
439
            $templates[] = $value . $suffix;
440
        }
441
442
        return $templates;
443
    }
444
445
    /**
446
     * Strip all namespaces from class namespace.
447
     *
448
     * @param string $classname e.g. "\Fully\Namespaced\Class"
449
     *
450
     * @return string following the param example, "Class"
451
     */
452
    protected function stripNamespacing($classname)
453
    {
454
        $classParts = explode('\\', $classname);
455
        return array_pop($classParts);
456
    }
457
458
    /**
459
     * @return string
460
     */
461
    public function getSimpleClassName()
462
    {
463
        return strtolower($this->sanitiseClassName($this->ClassName, '__'));
464
    }
465
466
    /**
467
     * @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...
468
     */
469
    public function getPage()
470
    {
471
        $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

471
        /** @scrutinizer ignore-call */ 
472
        $area = $this->Parent();
Loading history...
472
473
        if ($area instanceof ElementalArea && $area->exists()) {
474
            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...
475
        }
476
477
        return null;
478
    }
479
480
    /**
481
     * Get a unique anchor name
482
     *
483
     * @return string
484
     */
485
    public function getAnchor()
486
    {
487
        if ($this->_anchor !== null) {
488
            return $this->_anchor;
489
        }
490
491
        $anchorTitle = '';
492
493
        if (!$this->config()->disable_pretty_anchor_name) {
494
            if ($this->hasMethod('getAnchorTitle')) {
495
                $anchorTitle = $this->getAnchorTitle();
496
            } elseif ($this->config()->enable_title_in_template) {
497
                $anchorTitle = $this->getField('Title');
498
            }
499
        }
500
501
        if (!$anchorTitle) {
502
            $anchorTitle = 'e'.$this->ID;
503
        }
504
505
        $filter = URLSegmentFilter::create();
506
        $titleAsURL = $filter->filter($anchorTitle);
507
508
        // Ensure that this anchor name isn't already in use
509
        // ie. If two elemental blocks have the same title, it'll append '-2', '-3'
510
        $result = $titleAsURL;
511
        $count = 1;
512
        while (isset(self::$_used_anchors[$result]) && self::$_used_anchors[$result] !== $this->ID) {
513
            ++$count;
514
            $result = $titleAsURL.'-'.$count;
515
        }
516
        self::$_used_anchors[$result] = $this->ID;
517
        return $this->_anchor = $result;
518
    }
519
520
    /**
521
     * @param string $action
522
     *
523
     * @return string
524
     */
525
    public function AbsoluteLink($action = null)
526
    {
527
        if ($page = $this->getPage()) {
528
            $link = $page->AbsoluteLink($action) . '#' . $this->getAnchor();
529
530
            return $link;
531
        }
532
    }
533
534
    /**
535
     * @param string $action
536
     *
537
     * @return string
538
     */
539
    public function Link($action = null)
540
    {
541
        if ($page = $this->getPage()) {
542
            $link = $page->Link($action) . '#' . $this->getAnchor();
543
544
            $this->extend('updateLink', $link);
545
546
            return $link;
547
        }
548
    }
549
550
    /**
551
     * @param string $action
552
     *
553
     * @return string
554
     */
555
    public function PreviewLink($action = null)
556
    {
557
        $action = $action . '?ElementalPreview=' . mt_rand();
558
        $link = $this->Link($action);
559
        $this->extend('updatePreviewLink', $link);
560
561
        return $link;
562
    }
563
564
    /**
565
     * @return boolean
566
     */
567
    public function isCMSPreview()
568
    {
569
        if (Controller::has_curr()) {
570
            $c = Controller::curr();
571
572
            if ($c->getRequest()->requestVar('CMSPreview')) {
573
                return true;
574
            }
575
        }
576
577
        return false;
578
    }
579
580
    /**
581
     * @return string
582
     */
583
    public function CMSEditLink()
584
    {
585
        $relationName = $this->getAreaRelationName();
586
        $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

586
        /** @scrutinizer ignore-call */ 
587
        $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...
587
588
        if (!$page) {
589
            return null;
590
        }
591
592
        $link = Controller::join_links(
593
            singleton(CMSPageEditController::class)->Link('EditForm'),
594
            $page->ID,
595
            'field/' . $relationName . '/item/',
596
            $this->ID
597
        );
598
599
        return Controller::join_links(
600
            $link,
601
            'edit'
602
        );
603
    }
604
605
    /**
606
     * Retrieve a elemental area relation for creating cms links
607
     *
608
     * @return string - the name of a valid elemental area relation
609
     */
610
    public function getAreaRelationName()
611
    {
612
        $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

612
        /** @scrutinizer ignore-call */ 
613
        $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...
613
614
        if ($page) {
615
            $has_one = $page->config()->get('has_one');
616
            $area = $this->Parent();
617
618
            foreach ($has_one as $relationName => $relationClass) {
619
                if ($relationClass === $area->ClassName) {
620
                    return $relationName;
621
                }
622
            }
623
        }
624
625
        return 'ElementalArea';
626
    }
627
628
    /**
629
     * Sanitise a model class' name for inclusion in a link.
630
     *
631
     * @return string
632
     */
633
    public function sanitiseClassName($class, $delimiter = '-')
634
    {
635
        return str_replace('\\', $delimiter, $class);
636
    }
637
638
    public function unsanitiseClassName($class, $delimiter = '-')
639
    {
640
        return str_replace($delimiter, '\\', $class);
641
    }
642
643
    /**
644
     * @return string
645
     */
646
    public function getEditLink()
647
    {
648
        return $this->CMSEditLink();
649
    }
650
651
    /**
652
     * @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...
653
     */
654
    public function PageCMSEditLink()
655
    {
656
        if ($page = $this->getPage()) {
657
            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...
658
                '<a href="%s">%s</a>',
659
                $page->CMSEditLink(),
660
                $page->Title
661
            ));
662
        }
663
    }
664
665
    /**
666
     * @return string
667
     */
668
    public function getMimeType()
669
    {
670
        return 'text/html';
671
    }
672
673
    /**
674
     * This can be overridden on child elements to create a summary for display
675
     * in GridFields.
676
     *
677
     * @return string
678
     */
679
    public function getSummary()
680
    {
681
        return '';
682
    }
683
684
685
    /**
686
     * Generate markup for element type icons suitable for use in GridFields.
687
     *
688
     * @return DBField
689
     */
690
    public function getIcon()
691
    {
692
        $icon = $this->config()->get('icon');
693
694
        if ($icon) {
695
            $icon = ModuleResourceLoader::resourceURL($icon);
696
697
            return DBField::create_field('HTMLVarchar', '<img width="16px" src="' . Director::absoluteBaseURL() . $icon . '" alt="" />');
698
        }
699
    }
700
701
    /**
702
     * Get a description for this content element, if available
703
     *
704
     * @return string
705
     */
706
    public function getDescription()
707
    {
708
        $description = $this->config()->uninherited('description');
709
        if ($description) {
710
            return _t(__CLASS__ . '.Description', $description);
711
        }
712
        return '';
713
    }
714
715
    /**
716
     * Generate markup for element type, with description suitable for use in
717
     * GridFields.
718
     *
719
     * @return DBField
720
     */
721
    public function getTypeNice()
722
    {
723
        $description = $this->getDescription();
724
        $desc = ($description) ? ' <span class="element__note"> &mdash; ' . $description . '</span>' : '';
725
726
        return DBField::create_field(
727
            'HTMLVarchar',
728
            $this->getType() . $desc
729
        );
730
    }
731
732
    /**
733
     * @return HTMLText
734
     */
735
    public function getEditorPreview()
736
    {
737
        $templates = $this->getRenderTemplates('_EditorPreview');
738
        $templates[] = BaseElement::class . '_EditorPreview';
739
740
        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...
741
    }
742
743
    /**
744
     * @return Member
745
     */
746
    public function getAuthor()
747
    {
748
        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...
749
            return Member::get()->byId($this->AuthorID);
750
        }
751
    }
752
753
    /**
754
     * Get a user defined style variant for this element, if available
755
     *
756
     * @return string
757
     */
758
    public function getStyleVariant()
759
    {
760
        $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...
761
        $styles = $this->config()->get('styles');
762
763
        if (isset($styles[$style])) {
764
            $style = strtolower($style);
765
        } else {
766
            $style = '';
767
        }
768
769
        $this->extend('updateStyleVariant', $style);
770
771
        return $style;
772
    }
773
774
    /**
775
     *
776
     */
777
    public function getPageTitle()
778
    {
779
        $page = $this->getPage();
780
781
        if ($page) {
782
            return $page->Title;
783
        }
784
785
        return null;
786
    }
787
788
    /**
789
     * Get a "nice" label for use in the block history GridField
790
     *
791
     * @return string
792
     */
793
    public function getVersionedStateNice()
794
    {
795
        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...
796
            return _t(__CLASS__ . '.Published', 'Published');
797
        }
798
799
        return _t(__CLASS__ . '.Modified', 'Modified');
800
    }
801
802
    /**
803
     * Return a formatted date for use in the block history GridField
804
     *
805
     * @return string
806
     */
807
    public function getLastEditedNice()
808
    {
809
        return $this->dbObject('LastEdited')->Nice();
810
    }
811
}
812