Test Setup Failed
Pull Request — master (#197)
by Gorrie
01:07
created

src/Models/BaseElement.php (2 issues)

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';
62
63
    /**
64
     * Describe the purpose of this element
65
     *
66
     * @config
67
     * @var string
68
     */
69
    private static $description = 'Base element class';
70
71
    private static $db = [
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 = [
80
        'Parent' => ElementalArea::class
81
    ];
82
83
    private static $extensions = [
84
        Versioned::class
85
    ];
86
87
    private static $versioned_gridfield_extensions = true;
88
89
    private static $table_name = 'Element';
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';
100
101
    /**
102
     * @var ElementController
103
     */
104
    protected $controller;
105
106
    private static $default_sort = 'Sort';
107
108
    private static $singular_name = 'block';
109
110
    private static $plural_name = 'blocks';
111
112
    private static $summary_fields = [
113
        'EditorPreview' => 'Summary'
114
    ];
115
116
    /**
117
     * @config
118
     * @var array
119
     */
120
    private static $styles = [];
121
122
    private static $searchable_fields = [
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) {
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
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'))
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())),
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');
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);
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()) {
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)
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
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) {
437
            return $this->renderWith($templates);
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 . '_'. $this->getAreaRelationName();
465
            $templates[] = $value . $suffix;
466
        }
467
468
        return $templates;
469
    }
470
471
    /**
472
     * Strip all namespaces from class namespace.
473
     *
474
     * @param string $classname e.g. "\Fully\Namespaced\Class"
475
     *
476
     * @return string following the param example, "Class"
477
     */
478
    protected function stripNamespacing($classname)
479
    {
480
        $classParts = explode('\\', $classname);
481
        return array_pop($classParts);
482
    }
483
484
    /**
485
     * @return string
486
     */
487
    public function getSimpleClassName()
488
    {
489
        return strtolower($this->sanitiseClassName($this->ClassName, '__'));
490
    }
491
492
    /**
493
     * @return null|DataObject
494
     * @throws \Psr\Container\NotFoundExceptionInterface
495
     * @throws \SilverStripe\ORM\ValidationException
496
     */
497
    public function getPage()
498
    {
499
        $area = $this->Parent();
500
501
        if ($area instanceof ElementalArea && $area->exists()) {
502
            return $area->getOwnerPage();
503
        }
504
505
        return null;
506
    }
507
508
    /**
509
     * Get a unique anchor name
510
     *
511
     * @return string
512
     */
513
    public function getAnchor()
514
    {
515
        if ($this->_anchor !== null) {
516
            return $this->_anchor;
517
        }
518
519
        $anchorTitle = '';
520
521
        if (!$this->config()->disable_pretty_anchor_name) {
522
            if ($this->hasMethod('getAnchorTitle')) {
523
                $anchorTitle = $this->getAnchorTitle();
524
            } elseif ($this->config()->enable_title_in_template) {
525
                $anchorTitle = $this->getField('Title');
526
            }
527
        }
528
529
        if (!$anchorTitle) {
530
            $anchorTitle = 'e'.$this->ID;
531
        }
532
533
        $filter = URLSegmentFilter::create();
534
        $titleAsURL = $filter->filter($anchorTitle);
535
536
        // Ensure that this anchor name isn't already in use
537
        // ie. If two elemental blocks have the same title, it'll append '-2', '-3'
538
        $result = $titleAsURL;
539
        $count = 1;
540
        while (isset(self::$_used_anchors[$result]) && self::$_used_anchors[$result] !== $this->ID) {
541
            ++$count;
542
            $result = $titleAsURL.'-'.$count;
543
        }
544
        self::$_used_anchors[$result] = $this->ID;
545
        return $this->_anchor = $result;
546
    }
547
548
    /**
549
     * @param string|null $action
550
     * @return string|null
551
     * @throws \Psr\Container\NotFoundExceptionInterface
552
     * @throws \SilverStripe\ORM\ValidationException
553
     */
554
    public function AbsoluteLink($action = null)
555
    {
556
        if ($page = $this->getPage()) {
557
            $link = $page->AbsoluteLink($action) . '#' . $this->getAnchor();
558
559
            return $link;
560
        }
561
562
        return null;
563
    }
564
565
    /**
566
     * @param string|null $action
567
     * @return string
568
     * @throws \Psr\Container\NotFoundExceptionInterface
569
     * @throws \SilverStripe\ORM\ValidationException
570
     */
571
    public function Link($action = null)
572
    {
573
        if ($page = $this->getPage()) {
574
            $link = $page->Link($action) . '#' . $this->getAnchor();
575
576
            $this->extend('updateLink', $link);
577
578
            return $link;
579
        }
580
581
        return null;
582
    }
583
584
    /**
585
     * @param string|null $action
586
     * @return string
587
     * @throws \Psr\Container\NotFoundExceptionInterface
588
     * @throws \SilverStripe\ORM\ValidationException
589
     */
590
    public function PreviewLink($action = null)
591
    {
592
        $action = $action . '?ElementalPreview=' . mt_rand();
593
        $link = $this->Link($action);
594
        $this->extend('updatePreviewLink', $link);
595
596
        return $link;
597
    }
598
599
    /**
600
     * @return boolean
601
     */
602
    public function isCMSPreview()
603
    {
604
        if (Controller::has_curr()) {
605
            $controller = Controller::curr();
606
607
            if ($controller->getRequest()->requestVar('CMSPreview')) {
608
                return true;
609
            }
610
        }
611
612
        return false;
613
    }
614
615
    /**
616
     * @return null|string
617
     * @throws \Psr\Container\NotFoundExceptionInterface
618
     * @throws \SilverStripe\ORM\ValidationException
619
     */
620
    public function CMSEditLink()
621
    {
622
        $relationName = $this->getAreaRelationName();
623
        $page = $this->getPage(true);
624
625
        if (!$page) {
626
            return null;
627
        }
628
629
        $editLinkPrefix = '';
630
        if (!$page instanceof SiteTree && method_exists($page, 'CMSEditLink')) {
631
            $editLinkPrefix = Controller::join_links($page->CMSEditLink(), 'ItemEditForm');
632
        } else {
633
            $editLinkPrefix = Controller::join_links(singleton(CMSPageEditController::class)->Link('EditForm'), $page->ID);
634
        }
635
636
        $link = Controller::join_links(
637
            $editLinkPrefix,
638
            'field/' . $relationName . '/item/',
639
            $this->ID
640
        );
641
642
        $link = Controller::join_links(
643
            $link,
644
            'edit'
645
        );
646
647
        $this->extend('updateCMSEditLink', $link);
648
649
        return $link;
650
    }
651
652
    /**
653
     * Retrieve a elemental area relation for creating cms links
654
     *
655
     * @return int|string The name of a valid elemental area relation
656
     * @throws \Psr\Container\NotFoundExceptionInterface
657
     * @throws \SilverStripe\ORM\ValidationException
658
     */
659
    public function getAreaRelationName()
660
    {
661
        $page = $this->getPage();
662
663
        if ($page) {
664
            $has_one = $page->config()->get('has_one');
665
            $area = $this->Parent();
666
667
            foreach ($has_one as $relationName => $relationClass) {
668
                if ($page instanceof BaseElement && $relationName === 'Parent') {
669
                    continue;
670
                }
671
                if ($relationClass === $area->ClassName) {
672
                    return $relationName;
673
                }
674
            }
675
        }
676
677
        return 'ElementalArea';
678
    }
679
680
    /**
681
     * Sanitise a model class' name for inclusion in a link.
682
     *
683
     * @return string
684
     */
685
    public function sanitiseClassName($class, $delimiter = '-')
686
    {
687
        return str_replace('\\', $delimiter, $class);
688
    }
689
690
    public function unsanitiseClassName($class, $delimiter = '-')
691
    {
692
        return str_replace($delimiter, '\\', $class);
693
    }
694
695
    /**
696
     * @return null|string
697
     * @throws \Psr\Container\NotFoundExceptionInterface
698
     * @throws \SilverStripe\ORM\ValidationException
699
     */
700
    public function getEditLink()
701
    {
702
        return $this->CMSEditLink();
703
    }
704
705
    /**
706
     * @return DBField|null
707
     * @throws \Psr\Container\NotFoundExceptionInterface
708
     * @throws \SilverStripe\ORM\ValidationException
709
     */
710
    public function PageCMSEditLink()
711
    {
712
        if ($page = $this->getPage()) {
713
            return DBField::create_field('HTMLText', sprintf(
714
                '<a href="%s">%s</a>',
715
                $page->CMSEditLink(),
716
                $page->Title
717
            ));
718
        }
719
720
        return null;
721
    }
722
723
    /**
724
     * @return string
725
     */
726
    public function getMimeType()
727
    {
728
        return 'text/html';
729
    }
730
731
    /**
732
     * This can be overridden on child elements to create a summary for display
733
     * in GridFields.
734
     *
735
     * @return string
736
     */
737
    public function getSummary()
738
    {
739
        return '';
740
    }
741
742
743
    /**
744
     * Generate markup for element type icons suitable for use in GridFields.
745
     *
746
     * @return null|DBHTMLText
747
     */
748
    public function getIcon()
749
    {
750
        $data = ArrayData::create([]);
751
752
        $iconClass = $this->config()->get('icon');
753
        if ($iconClass) {
754
            $data->IconClass = $iconClass;
755
756
            // Add versioned states (rendered as a circle over the icon)
757
            if ($this->hasExtension(Versioned::class)) {
758
                $data->IsVersioned = true;
759
                if ($this->isOnDraftOnly()) {
760
                    $data->VersionState = 'draft';
761
                    $data->VersionStateTitle = _t(
762
                        'SilverStripe\\Versioned\\VersionedGridFieldState\\VersionedGridFieldState.ADDEDTODRAFTHELP',
763
                        'Item has not been published yet'
764
                    );
765
                } elseif ($this->isModifiedOnDraft()) {
766
                    $data->VersionState = 'modified';
767
                    $data->VersionStateTitle = $data->VersionStateTitle = _t(
768
                        'SilverStripe\\Versioned\\VersionedGridFieldState\\VersionedGridFieldState.MODIFIEDONDRAFTHELP',
769
                        'Item has unpublished changes'
770
                    );
771
                }
772
            }
773
774
            return $data->renderWith(__CLASS__ . '/PreviewIcon');
775
        }
776
777
        return null;
778
    }
779
780
    /**
781
     * Get a description for this content element, if available
782
     *
783
     * @return string
784
     */
785
    public function getDescription()
786
    {
787
        $description = $this->config()->uninherited('description');
788
        if ($description) {
789
            return _t(__CLASS__ . '.Description', $description);
790
        }
791
        return '';
792
    }
793
794
    /**
795
     * Generate markup for element type, with description suitable for use in
796
     * GridFields.
797
     *
798
     * @return DBField
799
     */
800
    public function getTypeNice()
801
    {
802
        $description = $this->getDescription();
803
        $desc = ($description) ? ' <span class="element__note"> &mdash; ' . $description . '</span>' : '';
804
805
        return DBField::create_field(
806
            'HTMLVarchar',
807
            $this->getType() . $desc
808
        );
809
    }
810
811
    /**
812
     * @return \SilverStripe\ORM\FieldType\DBHTMLText
813
     */
814
    public function getEditorPreview()
815
    {
816
        $templates = $this->getRenderTemplates('_EditorPreview');
817
        $templates[] = BaseElement::class . '_EditorPreview';
818
819
        return $this->renderWith($templates);
820
    }
821
822
    /**
823
     * @return Member
824
     */
825
    public function getAuthor()
826
    {
827
        if ($this->AuthorID) {
828
            return Member::get()->byId($this->AuthorID);
829
        }
830
831
        return null;
832
    }
833
834
    /**
835
     * Get a user defined style variant for this element, if available
836
     *
837
     * @return string
838
     */
839
    public function getStyleVariant()
840
    {
841
        $style = $this->Style;
842
        $styles = $this->config()->get('styles');
843
844
        if (isset($styles[$style])) {
845
            $style = strtolower($style);
846
        } else {
847
            $style = '';
848
        }
849
850
        $this->extend('updateStyleVariant', $style);
851
852
        return $style;
853
    }
854
855
    /**
856
     * @return mixed|null
857
     * @throws \Psr\Container\NotFoundExceptionInterface
858
     * @throws \SilverStripe\ORM\ValidationException
859
     */
860
    public function getPageTitle()
861
    {
862
        $page = $this->getPage();
863
864
        if ($page) {
865
            return $page->Title;
866
        }
867
868
        return null;
869
    }
870
871
    /**
872
     * Get a "nice" label for use in the block history GridField
873
     *
874
     * @return string
875
     */
876
    public function getVersionedStateNice()
877
    {
878
        if ($this->WasPublished) {
879
            return _t(__CLASS__ . '.Published', 'Published');
880
        }
881
882
        return _t(__CLASS__ . '.Modified', 'Modified');
883
    }
884
885
    /**
886
     * Return a formatted date for use in the block history GridField
887
     *
888
     * @return string
889
     */
890
    public function getLastEditedNice()
891
    {
892
        return $this->dbObject('LastEdited')->Nice();
893
    }
894
}
895