Passed
Push — master ( afbf56...1155ad )
by Robbie
02:47
created

BaseElement::getIcon()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 18
nc 5
nop 0
dl 0
loc 30
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
namespace DNADesign\Elemental\Models;
4
5
use DNADesign\Elemental\Controllers\ElementController;
6
use DNADesign\Elemental\Forms\ElementalGridFieldHistoryButton;
7
use DNADesign\Elemental\Forms\HistoricalVersionedGridFieldItemRequest;
8
use DNADesign\Elemental\Forms\TextCheckboxGroupField;
9
use Exception;
10
use SilverStripe\CMS\Controllers\CMSPageEditController;
11
use SilverStripe\CMS\Model\SiteTree;
12
use SilverStripe\Control\Controller;
13
use SilverStripe\Control\Director;
14
use SilverStripe\Core\ClassInfo;
15
use SilverStripe\Core\Injector\Injector;
16
use SilverStripe\Forms\CheckboxField;
17
use SilverStripe\Forms\DropdownField;
18
use SilverStripe\Forms\FieldList;
19
use SilverStripe\Forms\GridField\GridField;
20
use SilverStripe\Forms\GridField\GridFieldConfig_RecordViewer;
21
use SilverStripe\Forms\GridField\GridFieldDataColumns;
22
use SilverStripe\Forms\GridField\GridFieldDetailForm;
23
use SilverStripe\Forms\GridField\GridFieldPageCount;
24
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
25
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
26
use SilverStripe\Forms\GridField\GridFieldViewButton;
27
use SilverStripe\Forms\HiddenField;
28
use SilverStripe\Forms\NumericField;
29
use SilverStripe\Forms\TextField;
30
use SilverStripe\ORM\CMSPreviewable;
31
use SilverStripe\ORM\DataObject;
32
use SilverStripe\ORM\FieldType\DBField;
33
use SilverStripe\ORM\FieldType\DBHTMLText;
34
use SilverStripe\Security\Member;
35
use SilverStripe\Security\Permission;
36
use SilverStripe\Versioned\Versioned;
37
use SilverStripe\View\ArrayData;
38
use SilverStripe\View\Parsers\URLSegmentFilter;
39
use SilverStripe\View\Requirements;
40
use Symbiote\GridFieldExtensions\GridFieldTitleHeader;
41
42
/**
43
 * Class BaseElement
44
 * @package DNADesign\Elemental\Models
45
 *
46
 * @property string $Title
47
 * @property bool $ShowTitle
48
 * @property int $Sort
49
 * @property string $ExtraClass
50
 * @property string $Style
51
 *
52
 * @method ElementalArea Parent()
53
 */
54
class BaseElement extends DataObject implements CMSPreviewable
55
{
56
    /**
57
     * Override this on your custom elements to specify a CSS icon class
58
     *
59
     * @var string
60
     */
61
    private static $icon = 'font-icon-block-layout';
0 ignored issues
show
introduced by
The private property $icon is not used, and could be removed.
Loading history...
62
63
    /**
64
     * Describe the purpose of this element
65
     *
66
     * @config
67
     * @var string
68
     */
69
    private static $description = 'Base element class';
0 ignored issues
show
introduced by
The private property $description is not used, and could be removed.
Loading history...
70
71
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
72
        'Title' => 'Varchar(255)',
73
        'ShowTitle' => 'Boolean',
74
        'Sort' => 'Int',
75
        'ExtraClass' => 'Varchar(255)',
76
        'Style' => 'Varchar(255)'
77
    ];
78
79
    private static $has_one = [
0 ignored issues
show
introduced by
The private property $has_one is not used, and could be removed.
Loading history...
80
        'Parent' => ElementalArea::class
81
    ];
82
83
    private static $extensions = [
0 ignored issues
show
introduced by
The private property $extensions is not used, and could be removed.
Loading history...
84
        Versioned::class
85
    ];
86
87
    private static $versioned_gridfield_extensions = true;
0 ignored issues
show
introduced by
The private property $versioned_gridfield_extensions is not used, and could be removed.
Loading history...
88
89
    private static $table_name = 'Element';
0 ignored issues
show
introduced by
The private property $table_name is not used, and could be removed.
Loading history...
90
91
    /**
92
     * @var string
93
     */
94
    private static $controller_class = ElementController::class;
95
96
    /**
97
     * @var string
98
     */
99
    private static $controller_template = 'ElementHolder';
0 ignored issues
show
introduced by
The private property $controller_template is not used, and could be removed.
Loading history...
100
101
    /**
102
     * @var ElementController
103
     */
104
    protected $controller;
105
106
    private static $default_sort = 'Sort';
0 ignored issues
show
introduced by
The private property $default_sort is not used, and could be removed.
Loading history...
107
108
    private static $singular_name = 'block';
0 ignored issues
show
introduced by
The private property $singular_name is not used, and could be removed.
Loading history...
109
110
    private static $plural_name = 'blocks';
0 ignored issues
show
introduced by
The private property $plural_name is not used, and could be removed.
Loading history...
111
112
    private static $summary_fields = [
0 ignored issues
show
introduced by
The private property $summary_fields is not used, and could be removed.
Loading history...
113
        'EditorPreview' => 'Summary'
114
    ];
115
116
    /**
117
     * @config
118
     * @var array
119
     */
120
    private static $styles = [];
0 ignored issues
show
introduced by
The private property $styles is not used, and could be removed.
Loading history...
121
122
    private static $searchable_fields = [
0 ignored issues
show
introduced by
The private property $searchable_fields is not used, and could be removed.
Loading history...
123
        'ID' => [
124
            'field' => NumericField::class,
125
        ],
126
        'Title',
127
        'LastEdited'
128
    ];
129
130
    /**
131
     * Enable for backwards compatibility
132
     *
133
     * @var boolean
134
     */
135
    private static $disable_pretty_anchor_name = false;
136
137
    /**
138
     * Store used anchor names, this is to avoid title clashes
139
     * when calling 'getAnchor'
140
     *
141
     * @var array
142
     */
143
    protected static $_used_anchors = [];
144
145
    /**
146
     * For caching 'getAnchor'
147
     *
148
     * @var string
149
     */
150
    protected $_anchor = null;
151
152
    /**
153
     * Basic permissions, defaults to page perms where possible.
154
     *
155
     * @param Member $member
156
     * @return boolean
157
     */
158
    public function canView($member = null)
159
    {
160
        $extended = $this->extendedCan(__FUNCTION__, $member);
161
        if ($extended !== null) {
162
            return $extended;
163
        }
164
165
        if ($this->hasMethod('getPage')) {
166
            if ($page = $this->getPage()) {
167
                return $page->canView($member);
168
            }
169
        }
170
171
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
172
    }
173
174
    /**
175
     * Basic permissions, defaults to page perms where possible.
176
     *
177
     * @param Member $member
178
     *
179
     * @return boolean
180
     */
181
    public function canEdit($member = null)
182
    {
183
        $extended = $this->extendedCan(__FUNCTION__, $member);
184
        if ($extended !== null) {
185
            return $extended;
186
        }
187
188
        if ($this->hasMethod('getPage')) {
189
            if ($page = $this->getPage()) {
190
                return $page->canEdit($member);
191
            }
192
        }
193
194
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
195
    }
196
197
    /**
198
     * Basic permissions, defaults to page perms where possible.
199
     *
200
     * Uses archive not delete so that current stage is respected i.e if a
201
     * element is not published, then it can be deleted by someone who doesn't
202
     * have publishing permissions.
203
     *
204
     * @param Member $member
205
     *
206
     * @return boolean
207
     */
208
    public function canDelete($member = null)
209
    {
210
        $extended = $this->extendedCan(__FUNCTION__, $member);
211
        if ($extended !== null) {
212
            return $extended;
213
        }
214
215
        if ($this->hasMethod('getPage')) {
216
            if ($page = $this->getPage()) {
217
                return $page->canArchive($member);
218
            }
219
        }
220
221
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
222
    }
223
224
    /**
225
     * Basic permissions, defaults to page perms where possible.
226
     *
227
     * @param Member $member
228
     * @param array $context
229
     *
230
     * @return boolean
231
     */
232
    public function canCreate($member = null, $context = array())
233
    {
234
        $extended = $this->extendedCan(__FUNCTION__, $member);
235
        if ($extended !== null) {
236
            return $extended;
237
        }
238
239
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
240
    }
241
242
    /**
243
     * @throws \SilverStripe\ORM\ValidationException
244
     */
245
    public function onBeforeWrite()
246
    {
247
        parent::onBeforeWrite();
248
249
        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...
250
            if ($elementalArea = ElementalArea::get()->byID($areaID)) {
251
                $elementalArea->write();
252
            }
253
        }
254
255
        if (!$this->Sort) {
256
            $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...
257
258
            $this->Sort = static::get()->max('Sort') + 1;
259
        }
260
    }
261
262
    public function getCMSFields()
263
    {
264
        $this->beforeUpdateCMSFields(function (FieldList $fields) {
265
            // Remove relationship fields
266
            $fields->removeByName('ParentID');
267
            $fields->removeByName('Sort');
268
269
            $fields->addFieldToTab(
270
                'Root.Settings',
271
                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

271
                TextField::create(/** @scrutinizer ignore-type */ 'ExtraClass', _t(__CLASS__ . '.ExtraCssClassesLabel', 'Custom CSS classes'))
Loading history...
272
                    ->setAttribute(
273
                        'placeholder',
274
                        _t(__CLASS__ . '.ExtraCssClassesPlaceholder', 'my_class another_class')
275
                    )
276
            );
277
278
            // Add a combined field for "Title" and "Displayed" checkbox in a Bootstrap input group
279
            $fields->removeByName('ShowTitle');
280
            $fields->replaceField(
281
                'Title',
282
                TextCheckboxGroupField::create(
283
                    TextField::create('Title', _t(__CLASS__ . '.TitleLabel', 'Title (displayed if checked)')),
284
                    CheckboxField::create('ShowTitle', _t(__CLASS__ . '.ShowTitleLabel', 'Displayed'))
285
                )
286
                    ->setName('TitleAndDisplayed')
287
            );
288
289
            // Rename the "Main" tab
290
            $fields->fieldByName('Root.Main')
291
                ->setTitle(_t(__CLASS__ . '.MainTabLabel', 'Content'));
292
293
            $fields->addFieldsToTab('Root.Main', [
294
                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

294
                HiddenField::create('AbsoluteLink', /** @scrutinizer ignore-type */ false, Director::absoluteURL($this->PreviewLink())),
Loading history...
295
                HiddenField::create('LiveLink', false, Director::absoluteURL($this->Link())),
296
                HiddenField::create('StageLink', false, Director::absoluteURL($this->PreviewLink())),
297
            ]);
298
299
            $styles = $this->config()->get('styles');
300
301
            if ($styles && count($styles) > 0) {
302
                $styleDropdown = DropdownField::create('Style', _t(__CLASS__.'.STYLE', 'Style variation'), $styles);
303
304
                $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

304
                $fields->insertBefore($styleDropdown, /** @scrutinizer ignore-type */ 'ExtraClass');
Loading history...
305
306
                $styleDropdown->setEmptyString(_t(__CLASS__.'.CUSTOM_STYLES', 'Select a style..'));
307
            } else {
308
                $fields->removeByName('Style');
309
            }
310
311
            $history = $this->getHistoryFields();
312
313
            if ($history) {
314
                $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

314
                $fields->addFieldsToTab('Root.History', /** @scrutinizer ignore-type */ $history);
Loading history...
315
            }
316
        });
317
318
        return parent::getCMSFields();
319
    }
320
321
    /**
322
     * Returns the history fields for this element.
323
     *
324
     * @param  bool $checkLatestVersion Whether to check if this is the latest version. Prevents recursion, but can be
325
     *                                  overridden to get the history GridField if required.
326
     * @return FieldList
327
     */
328
    public function getHistoryFields($checkLatestVersion = true)
329
    {
330
        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

330
        if ($checkLatestVersion && !$this->/** @scrutinizer ignore-call */ isLatestVersion()) {
Loading history...
331
            // if viewing the history of the of page then don't show the history
332
            // fields as then we have recursion.
333
            return null;
334
        }
335
336
        Requirements::javascript('dnadesign/silverstripe-elemental:client/dist/js/bundle.js');
337
338
        $config = GridFieldConfig_RecordViewer::create();
339
        $config->removeComponentsByType(GridFieldPageCount::class);
340
        $config->removeComponentsByType(GridFieldToolbarHeader::class);
341
        // Replace the sortable ID column with a static header component
342
        $config->removeComponentsByType(GridFieldSortableHeader::class);
343
        $config->addComponent(new GridFieldTitleHeader);
344
345
        $config
346
            ->getComponentByType(GridFieldDetailForm::class)
347
            ->setItemRequestClass(HistoricalVersionedGridFieldItemRequest::class);
348
349
        $config->getComponentByType(GridFieldDataColumns::class)
350
            ->setDisplayFields([
351
                'Version' => '#',
352
                'RecordStatus' => _t(__CLASS__ . '.Record', 'Record'),
353
                'getAuthor.Name' => _t(__CLASS__ . '.Author', 'Author')
354
            ])
355
            ->setFieldFormatting([
356
                'RecordStatus' => '$VersionedStateNice <span class=\"element-history__date--small\">on $LastEditedNice</span>',
357
            ]);
358
359
        $config->removeComponentsByType(GridFieldViewButton::class);
360
        $config->addComponent(new ElementalGridFieldHistoryButton());
361
362
        $history = Versioned::get_all_versions(__CLASS__, $this->ID)
363
            ->sort('Version', 'DESC');
364
365
        return FieldList::create(
366
            GridField::create('History', '', $history, $config)
0 ignored issues
show
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

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

366
            GridField::create('History', '', /** @scrutinizer ignore-type */ $history, $config)
Loading history...
367
                ->addExtraClass('elemental-block__history')
368
        );
369
    }
370
371
    /**
372
     * Get the type of the current block, for use in GridField summaries, block
373
     * type dropdowns etc. Examples are "Content", "File", "Media", etc.
374
     *
375
     * @return string
376
     */
377
    public function getType()
378
    {
379
        return _t(__CLASS__ . '.BlockType', 'Block');
380
    }
381
382
    /**
383
     * @param ElementController $controller
384
     *
385
     * @return $this
386
     */
387
    public function setController($controller)
388
    {
389
        $this->controller = $controller;
390
391
        return $this;
392
    }
393
394
    /**
395
     * @throws Exception If the specified controller class doesn't exist
396
     *
397
     * @return ElementController
398
     */
399
    public function getController()
400
    {
401
        if ($this->controller) {
402
            return $this->controller;
403
        }
404
405
        $controllerClass = self::config()->controller_class;
406
407
        if (!class_exists($controllerClass)) {
408
            throw new Exception('Could not find controller class ' . $controllerClass . ' as defined in ' . static::class);
409
        }
410
411
        $this->controller = Injector::inst()->create($controllerClass, $this);
412
        $this->controller->doInit();
413
414
        return $this->controller;
415
    }
416
417
    /**
418
     * @return Controller
419
     */
420
    public function Top()
421
    {
422
        return (Controller::has_curr()) ? Controller::curr() : null;
423
    }
424
425
    /**
426
     * Default way to render element in templates. Note that all blocks should
427
     * be rendered through their {@link ElementController} class as this
428
     * contains the holder styles.
429
     *
430
     * @return string|null HTML
431
     */
432
    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

432
    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...
433
    {
434
        $templates = $this->getRenderTemplates();
435
436
        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...
437
            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 null|string.
Loading history...
438
        }
439
440
        return null;
441
    }
442
443
    /**
444
     * @param string $suffix
445
     *
446
     * @return array
447
     */
448
    public function getRenderTemplates($suffix = '')
449
    {
450
        $classes = ClassInfo::ancestry($this->ClassName);
451
        $classes[static::class] = static::class;
452
        $classes = array_reverse($classes);
453
        $templates = array();
454
455
        foreach ($classes as $key => $value) {
456
            if ($value == BaseElement::class) {
457
                continue;
458
            }
459
460
            if ($value == DataObject::class) {
461
                break;
462
            }
463
464
            $templates[] = $value . $suffix;
465
        }
466
467
        return $templates;
468
    }
469
470
    /**
471
     * Strip all namespaces from class namespace.
472
     *
473
     * @param string $classname e.g. "\Fully\Namespaced\Class"
474
     *
475
     * @return string following the param example, "Class"
476
     */
477
    protected function stripNamespacing($classname)
478
    {
479
        $classParts = explode('\\', $classname);
480
        return array_pop($classParts);
481
    }
482
483
    /**
484
     * @return string
485
     */
486
    public function getSimpleClassName()
487
    {
488
        return strtolower($this->sanitiseClassName($this->ClassName, '__'));
489
    }
490
491
    /**
492
     * @return null|DataObject
493
     * @throws \Psr\Container\NotFoundExceptionInterface
494
     * @throws \SilverStripe\ORM\ValidationException
495
     */
496
    public function getPage()
497
    {
498
        $area = $this->Parent();
499
500
        if ($area instanceof ElementalArea && $area->exists()) {
501
            return $area->getOwnerPage();
502
        }
503
504
        return null;
505
    }
506
507
    /**
508
     * Get a unique anchor name
509
     *
510
     * @return string
511
     */
512
    public function getAnchor()
513
    {
514
        if ($this->_anchor !== null) {
515
            return $this->_anchor;
516
        }
517
518
        $anchorTitle = '';
519
520
        if (!$this->config()->disable_pretty_anchor_name) {
521
            if ($this->hasMethod('getAnchorTitle')) {
522
                $anchorTitle = $this->getAnchorTitle();
0 ignored issues
show
Bug introduced by
The method getAnchorTitle() 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

522
                /** @scrutinizer ignore-call */ 
523
                $anchorTitle = $this->getAnchorTitle();
Loading history...
523
            } elseif ($this->config()->enable_title_in_template) {
524
                $anchorTitle = $this->getField('Title');
525
            }
526
        }
527
528
        if (!$anchorTitle) {
529
            $anchorTitle = 'e'.$this->ID;
530
        }
531
532
        $filter = URLSegmentFilter::create();
533
        $titleAsURL = $filter->filter($anchorTitle);
534
535
        // Ensure that this anchor name isn't already in use
536
        // ie. If two elemental blocks have the same title, it'll append '-2', '-3'
537
        $result = $titleAsURL;
538
        $count = 1;
539
        while (isset(self::$_used_anchors[$result]) && self::$_used_anchors[$result] !== $this->ID) {
540
            ++$count;
541
            $result = $titleAsURL.'-'.$count;
542
        }
543
        self::$_used_anchors[$result] = $this->ID;
544
        return $this->_anchor = $result;
545
    }
546
547
    /**
548
     * @param string|null $action
549
     * @return string|null
550
     * @throws \Psr\Container\NotFoundExceptionInterface
551
     * @throws \SilverStripe\ORM\ValidationException
552
     */
553
    public function AbsoluteLink($action = null)
554
    {
555
        if ($page = $this->getPage()) {
556
            $link = $page->AbsoluteLink($action) . '#' . $this->getAnchor();
557
558
            return $link;
559
        }
560
561
        return null;
562
    }
563
564
    /**
565
     * @param string|null $action
566
     * @return string
567
     * @throws \Psr\Container\NotFoundExceptionInterface
568
     * @throws \SilverStripe\ORM\ValidationException
569
     */
570
    public function Link($action = null)
571
    {
572
        if ($page = $this->getPage()) {
573
            $link = $page->Link($action) . '#' . $this->getAnchor();
574
575
            $this->extend('updateLink', $link);
576
577
            return $link;
578
        }
579
580
        return null;
581
    }
582
583
    /**
584
     * @param string|null $action
585
     * @return string
586
     * @throws \Psr\Container\NotFoundExceptionInterface
587
     * @throws \SilverStripe\ORM\ValidationException
588
     */
589
    public function PreviewLink($action = null)
590
    {
591
        $action = $action . '?ElementalPreview=' . mt_rand();
592
        $link = $this->Link($action);
593
        $this->extend('updatePreviewLink', $link);
594
595
        return $link;
596
    }
597
598
    /**
599
     * @return boolean
600
     */
601
    public function isCMSPreview()
602
    {
603
        if (Controller::has_curr()) {
604
            $controller = Controller::curr();
605
606
            if ($controller->getRequest()->requestVar('CMSPreview')) {
607
                return true;
608
            }
609
        }
610
611
        return false;
612
    }
613
614
    /**
615
     * @return null|string
616
     * @throws \Psr\Container\NotFoundExceptionInterface
617
     * @throws \SilverStripe\ORM\ValidationException
618
     */
619
    public function CMSEditLink()
620
    {
621
        $relationName = $this->getAreaRelationName();
622
        $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

622
        /** @scrutinizer ignore-call */ 
623
        $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...
623
624
        if (!$page) {
625
            return null;
626
        }
627
628
        $editLinkPrefix = '';
629
        if (!$page instanceof SiteTree && method_exists($page, 'CMSEditLink')) {
630
            $editLinkPrefix = Controller::join_links($page->CMSEditLink(), 'ItemEditForm');
631
        } else {
632
            $editLinkPrefix = Controller::join_links(singleton(CMSPageEditController::class)->Link('EditForm'), $page->ID);
633
        }
634
635
        $link = Controller::join_links(
636
            $editLinkPrefix,
637
            'field/' . $relationName . '/item/',
638
            $this->ID
639
        );
640
641
        $link = Controller::join_links(
642
            $link,
643
            'edit'
644
        );
645
646
        $this->extend('updateCMSEditLink', $link);
647
648
        return $link;
649
    }
650
651
    /**
652
     * Retrieve a elemental area relation for creating cms links
653
     *
654
     * @return int|string The name of a valid elemental area relation
655
     * @throws \Psr\Container\NotFoundExceptionInterface
656
     * @throws \SilverStripe\ORM\ValidationException
657
     */
658
    public function getAreaRelationName()
659
    {
660
        $page = $this->getPage();
661
662
        if ($page) {
663
            $has_one = $page->config()->get('has_one');
664
            $area = $this->Parent();
665
666
            foreach ($has_one as $relationName => $relationClass) {
667
                if ($page instanceof BaseElement && $relationName === 'Parent') {
668
                    continue;
669
                }
670
                if ($relationClass === $area->ClassName) {
671
                    return $relationName;
672
                }
673
            }
674
        }
675
676
        return 'ElementalArea';
677
    }
678
679
    /**
680
     * Sanitise a model class' name for inclusion in a link.
681
     *
682
     * @return string
683
     */
684
    public function sanitiseClassName($class, $delimiter = '-')
685
    {
686
        return str_replace('\\', $delimiter, $class);
687
    }
688
689
    public function unsanitiseClassName($class, $delimiter = '-')
690
    {
691
        return str_replace($delimiter, '\\', $class);
692
    }
693
694
    /**
695
     * @return null|string
696
     * @throws \Psr\Container\NotFoundExceptionInterface
697
     * @throws \SilverStripe\ORM\ValidationException
698
     */
699
    public function getEditLink()
700
    {
701
        return $this->CMSEditLink();
702
    }
703
704
    /**
705
     * @return DBField|null
706
     * @throws \Psr\Container\NotFoundExceptionInterface
707
     * @throws \SilverStripe\ORM\ValidationException
708
     */
709
    public function PageCMSEditLink()
710
    {
711
        if ($page = $this->getPage()) {
712
            return DBField::create_field('HTMLText', sprintf(
713
                '<a href="%s">%s</a>',
714
                $page->CMSEditLink(),
715
                $page->Title
716
            ));
717
        }
718
719
        return null;
720
    }
721
722
    /**
723
     * @return string
724
     */
725
    public function getMimeType()
726
    {
727
        return 'text/html';
728
    }
729
730
    /**
731
     * This can be overridden on child elements to create a summary for display
732
     * in GridFields.
733
     *
734
     * @return string
735
     */
736
    public function getSummary()
737
    {
738
        return '';
739
    }
740
741
742
    /**
743
     * Generate markup for element type icons suitable for use in GridFields.
744
     *
745
     * @return null|DBHTMLText
746
     */
747
    public function getIcon()
748
    {
749
        $data = ArrayData::create([]);
750
751
        $iconClass = $this->config()->get('icon');
752
        if ($iconClass) {
753
            $data->IconClass = $iconClass;
754
755
            // Add versioned states (rendered as a circle over the icon)
756
            if ($this->hasExtension(Versioned::class)) {
757
                $data->IsVersioned = true;
758
                if ($this->isOnDraftOnly()) {
0 ignored issues
show
Bug introduced by
The method isOnDraftOnly() 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

758
                if ($this->/** @scrutinizer ignore-call */ isOnDraftOnly()) {
Loading history...
759
                    $data->VersionState = 'draft';
760
                    $data->VersionStateTitle = _t(
761
                        'SilverStripe\\Versioned\\VersionedGridFieldState\\VersionedGridFieldState.ADDEDTODRAFTHELP',
762
                        'Item has not been published yet'
763
                    );
764
                } elseif ($this->isModifiedOnDraft()) {
0 ignored issues
show
Bug introduced by
The method isModifiedOnDraft() 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

764
                } elseif ($this->/** @scrutinizer ignore-call */ isModifiedOnDraft()) {
Loading history...
765
                    $data->VersionState = 'modified';
766
                    $data->VersionStateTitle = $data->VersionStateTitle = _t(
767
                        'SilverStripe\\Versioned\\VersionedGridFieldState\\VersionedGridFieldState.MODIFIEDONDRAFTHELP',
768
                        'Item has unpublished changes'
769
                    );
770
                }
771
            }
772
773
            return $data->renderWith(__CLASS__ . '/PreviewIcon');
774
        }
775
776
        return null;
777
    }
778
779
    /**
780
     * Get a description for this content element, if available
781
     *
782
     * @return string
783
     */
784
    public function getDescription()
785
    {
786
        $description = $this->config()->uninherited('description');
787
        if ($description) {
788
            return _t(__CLASS__ . '.Description', $description);
789
        }
790
        return '';
791
    }
792
793
    /**
794
     * Generate markup for element type, with description suitable for use in
795
     * GridFields.
796
     *
797
     * @return DBField
798
     */
799
    public function getTypeNice()
800
    {
801
        $description = $this->getDescription();
802
        $desc = ($description) ? ' <span class="element__note"> &mdash; ' . $description . '</span>' : '';
803
804
        return DBField::create_field(
805
            'HTMLVarchar',
806
            $this->getType() . $desc
807
        );
808
    }
809
810
    /**
811
     * @return \SilverStripe\ORM\FieldType\DBHTMLText
812
     */
813
    public function getEditorPreview()
814
    {
815
        $templates = $this->getRenderTemplates('_EditorPreview');
816
        $templates[] = BaseElement::class . '_EditorPreview';
817
818
        return $this->renderWith($templates);
819
    }
820
821
    /**
822
     * @return Member
823
     */
824
    public function getAuthor()
825
    {
826
        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...
827
            return Member::get()->byId($this->AuthorID);
828
        }
829
830
        return null;
831
    }
832
833
    /**
834
     * Get a user defined style variant for this element, if available
835
     *
836
     * @return string
837
     */
838
    public function getStyleVariant()
839
    {
840
        $style = $this->Style;
841
        $styles = $this->config()->get('styles');
842
843
        if (isset($styles[$style])) {
844
            $style = strtolower($style);
845
        } else {
846
            $style = '';
847
        }
848
849
        $this->extend('updateStyleVariant', $style);
850
851
        return $style;
852
    }
853
854
    /**
855
     * @return mixed|null
856
     * @throws \Psr\Container\NotFoundExceptionInterface
857
     * @throws \SilverStripe\ORM\ValidationException
858
     */
859
    public function getPageTitle()
860
    {
861
        $page = $this->getPage();
862
863
        if ($page) {
864
            return $page->Title;
865
        }
866
867
        return null;
868
    }
869
870
    /**
871
     * Get a "nice" label for use in the block history GridField
872
     *
873
     * @return string
874
     */
875
    public function getVersionedStateNice()
876
    {
877
        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...
878
            return _t(__CLASS__ . '.Published', 'Published');
879
        }
880
881
        return _t(__CLASS__ . '.Modified', 'Modified');
882
    }
883
884
    /**
885
     * Return a formatted date for use in the block history GridField
886
     *
887
     * @return string
888
     */
889
    public function getLastEditedNice()
890
    {
891
        return $this->dbObject('LastEdited')->Nice();
892
    }
893
}
894