Passed
Pull Request — master (#282)
by Will
01:57
created

BaseElement::Last()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
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\TextCheckboxGroupField;
7
use Exception;
8
use SilverStripe\CMS\Controllers\CMSPageEditController;
9
use SilverStripe\CMS\Model\SiteTree;
10
use SilverStripe\Control\Controller;
11
use SilverStripe\Control\Director;
12
use SilverStripe\Core\ClassInfo;
13
use SilverStripe\Core\Injector\Injector;
14
use SilverStripe\Forms\CheckboxField;
15
use SilverStripe\Forms\DropdownField;
16
use SilverStripe\Forms\FieldList;
17
use SilverStripe\Forms\HiddenField;
18
use SilverStripe\Forms\NumericField;
19
use SilverStripe\Forms\TextField;
20
use SilverStripe\ORM\DataObject;
21
use SilverStripe\ORM\FieldType\DBField;
22
use SilverStripe\ORM\FieldType\DBHTMLText;
23
use SilverStripe\Security\Member;
24
use SilverStripe\Security\Permission;
25
use SilverStripe\Versioned\Versioned;
26
use SilverStripe\VersionedAdmin\Forms\HistoryViewerField;
27
use SilverStripe\View\ArrayData;
28
use SilverStripe\View\Parsers\URLSegmentFilter;
29
use SilverStripe\View\Requirements;
30
31
/**
32
 * Class BaseElement
33
 * @package DNADesign\Elemental\Models
34
 *
35
 * @property string $Title
36
 * @property bool $ShowTitle
37
 * @property int $Sort
38
 * @property string $ExtraClass
39
 * @property string $Style
40
 *
41
 * @method ElementalArea Parent()
42
 */
43
class BaseElement extends DataObject
44
{
45
    /**
46
     * Override this on your custom elements to specify a CSS icon class
47
     *
48
     * @var string
49
     */
50
    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...
51
52
    /**
53
     * Describe the purpose of this element
54
     *
55
     * @config
56
     * @var string
57
     */
58
    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...
59
60
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
61
        'Title' => 'Varchar(255)',
62
        'ShowTitle' => 'Boolean',
63
        'Sort' => 'Int',
64
        'ExtraClass' => 'Varchar(255)',
65
        'Style' => 'Varchar(255)'
66
    ];
67
68
    private static $has_one = [
0 ignored issues
show
introduced by
The private property $has_one is not used, and could be removed.
Loading history...
69
        'Parent' => ElementalArea::class
70
    ];
71
72
    private static $extensions = [
0 ignored issues
show
introduced by
The private property $extensions is not used, and could be removed.
Loading history...
73
        Versioned::class
74
    ];
75
76
    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...
77
78
    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...
79
80
    /**
81
     * @var string
82
     */
83
    private static $controller_class = ElementController::class;
84
85
    /**
86
     * @var string
87
     */
88
    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...
89
90
    /**
91
     * @var ElementController
92
     */
93
    protected $controller;
94
95
    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...
96
97
    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...
98
99
    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...
100
101
    private static $summary_fields = [
0 ignored issues
show
introduced by
The private property $summary_fields is not used, and could be removed.
Loading history...
102
        'EditorPreview' => 'Summary'
103
    ];
104
105
    /**
106
     * @config
107
     * @var array
108
     */
109
    private static $styles = [];
0 ignored issues
show
introduced by
The private property $styles is not used, and could be removed.
Loading history...
110
111
    private static $searchable_fields = [
0 ignored issues
show
introduced by
The private property $searchable_fields is not used, and could be removed.
Loading history...
112
        'ID' => [
113
            'field' => NumericField::class,
114
        ],
115
        'Title',
116
        'LastEdited'
117
    ];
118
119
    /**
120
     * Enable for backwards compatibility
121
     *
122
     * @var boolean
123
     */
124
    private static $disable_pretty_anchor_name = false;
125
126
    /**
127
     * Store used anchor names, this is to avoid title clashes
128
     * when calling 'getAnchor'
129
     *
130
     * @var array
131
     */
132
    protected static $used_anchors = [];
133
134
    /**
135
     * For caching 'getAnchor'
136
     *
137
     * @var string
138
     */
139
    protected $anchor = null;
140
141
    /**
142
     * Basic permissions, defaults to page perms where possible.
143
     *
144
     * @param Member $member
145
     * @return boolean
146
     */
147
    public function canView($member = null)
148
    {
149
        $extended = $this->extendedCan(__FUNCTION__, $member);
150
        if ($extended !== null) {
151
            return $extended;
152
        }
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
    public function canEdit($member = null)
171
    {
172
        $extended = $this->extendedCan(__FUNCTION__, $member);
173
        if ($extended !== null) {
174
            return $extended;
175
        }
176
177
        if ($this->hasMethod('getPage')) {
178
            if ($page = $this->getPage()) {
179
                return $page->canEdit($member);
180
            }
181
        }
182
183
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
184
    }
185
186
    /**
187
     * Basic permissions, defaults to page perms where possible.
188
     *
189
     * Uses archive not delete so that current stage is respected i.e if a
190
     * element is not published, then it can be deleted by someone who doesn't
191
     * have publishing permissions.
192
     *
193
     * @param Member $member
194
     *
195
     * @return boolean
196
     */
197
    public function canDelete($member = null)
198
    {
199
        $extended = $this->extendedCan(__FUNCTION__, $member);
200
        if ($extended !== null) {
201
            return $extended;
202
        }
203
204
        if ($this->hasMethod('getPage')) {
205
            if ($page = $this->getPage()) {
206
                return $page->canArchive($member);
207
            }
208
        }
209
210
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
211
    }
212
213
    /**
214
     * Basic permissions, defaults to page perms where possible.
215
     *
216
     * @param Member $member
217
     * @param array $context
218
     *
219
     * @return boolean
220
     */
221
    public function canCreate($member = null, $context = array())
222
    {
223
        $extended = $this->extendedCan(__FUNCTION__, $member);
224
        if ($extended !== null) {
225
            return $extended;
226
        }
227
228
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
229
    }
230
231
    /**
232
     * Increment the sort order if one hasn't been already defined. This
233
     * ensures that new elements are created at the end of the list by default.
234
     *
235
     * {@inheritDoc}
236
     */
237
    public function onBeforeWrite()
238
    {
239
        parent::onBeforeWrite();
240
241
        if (!$this->Sort) {
242
            if ($this->hasExtension(Versioned::class)) {
243
                $records = Versioned::get_by_stage(BaseElement::class, Versioned::DRAFT);
244
            } else {
245
                $records = BaseElement::get()->max('Sort');
246
            }
247
248
            $records = $records->filter('ParentID', $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...
249
250
            $this->Sort = $records->max('Sort') + 1;
251
        }
252
    }
253
254
    public function getCMSFields()
255
    {
256
        $this->beforeUpdateCMSFields(function (FieldList $fields) {
257
            // Remove relationship fields
258
            $fields->removeByName('ParentID');
259
            $fields->removeByName('Sort');
260
261
            $fields->addFieldToTab(
262
                'Root.Settings',
263
                TextField::create('ExtraClass', _t(__CLASS__ . '.ExtraCssClassesLabel', 'Custom CSS classes'))
264
                    ->setAttribute(
265
                        'placeholder',
266
                        _t(__CLASS__ . '.ExtraCssClassesPlaceholder', 'my_class another_class')
267
                    )
268
            );
269
270
            // Add a combined field for "Title" and "Displayed" checkbox in a Bootstrap input group
271
            $fields->removeByName('ShowTitle');
272
            $fields->replaceField(
273
                'Title',
274
                TextCheckboxGroupField::create(
275
                    TextField::create('Title', _t(__CLASS__ . '.TitleLabel', 'Title (displayed if checked)')),
276
                    CheckboxField::create('ShowTitle', _t(__CLASS__ . '.ShowTitleLabel', 'Displayed'))
277
                )
278
                    ->setName('TitleAndDisplayed')
279
            );
280
281
            // Rename the "Main" tab
282
            $fields->fieldByName('Root.Main')
283
                ->setTitle(_t(__CLASS__ . '.MainTabLabel', 'Content'));
284
285
            $fields->addFieldsToTab('Root.Main', [
286
                HiddenField::create('AbsoluteLink', false, Director::absoluteURL($this->PreviewLink())),
287
                HiddenField::create('LiveLink', false, Director::absoluteURL($this->Link())),
288
                HiddenField::create('StageLink', false, Director::absoluteURL($this->PreviewLink())),
289
            ]);
290
291
            $styles = $this->config()->get('styles');
292
293
            if ($styles && count($styles) > 0) {
294
                $styleDropdown = DropdownField::create('Style', _t(__CLASS__.'.STYLE', 'Style variation'), $styles);
295
296
                $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

296
                $fields->insertBefore($styleDropdown, /** @scrutinizer ignore-type */ 'ExtraClass');
Loading history...
297
298
                $styleDropdown->setEmptyString(_t(__CLASS__.'.CUSTOM_STYLES', 'Select a style..'));
299
            } else {
300
                $fields->removeByName('Style');
301
            }
302
303
            // Support for new history viewer in SS 4.2+
304
            if (class_exists(HistoryViewerField::class)) {
305
                Requirements::javascript('dnadesign/silverstripe-elemental:client/dist/js/bundle.js');
306
307
                $historyViewer = HistoryViewerField::create('ElementHistory');
308
                $fields->addFieldToTab('Root.History', $historyViewer);
309
310
                $fields->fieldByName('Root.History')
311
                    ->addExtraClass('elemental-block__history-tab tab--history-viewer');
312
            }
313
        });
314
315
        return parent::getCMSFields();
316
    }
317
318
    /**
319
     * Get the type of the current block, for use in GridField summaries, block
320
     * type dropdowns etc. Examples are "Content", "File", "Media", etc.
321
     *
322
     * @return string
323
     */
324
    public function getType()
325
    {
326
        return _t(__CLASS__ . '.BlockType', 'Block');
327
    }
328
329
    /**
330
     * @param ElementController $controller
331
     *
332
     * @return $this
333
     */
334
    public function setController($controller)
335
    {
336
        $this->controller = $controller;
337
338
        return $this;
339
    }
340
341
    /**
342
     * @throws Exception If the specified controller class doesn't exist
343
     *
344
     * @return ElementController
345
     */
346
    public function getController()
347
    {
348
        if ($this->controller) {
349
            return $this->controller;
350
        }
351
352
        $controllerClass = self::config()->controller_class;
353
354
        if (!class_exists($controllerClass)) {
355
            throw new Exception(
356
                'Could not find controller class ' . $controllerClass . ' as defined in ' . static::class
357
            );
358
        }
359
360
        $this->controller = Injector::inst()->create($controllerClass, $this);
361
        $this->controller->doInit();
362
363
        return $this->controller;
364
    }
365
366
    /**
367
     * @return Controller
368
     */
369
    public function Top()
370
    {
371
        return (Controller::has_curr()) ? Controller::curr() : null;
372
    }
373
374
    /**
375
     * Default way to render element in templates. Note that all blocks should
376
     * be rendered through their {@link ElementController} class as this
377
     * contains the holder styles.
378
     *
379
     * @return string|null HTML
380
     */
381
    public function forTemplate($holder = true)
382
    {
383
        $templates = $this->getRenderTemplates();
384
385
        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...
386
            return $this->renderWith($templates);
387
        }
388
389
        return null;
390
    }
391
392
    /**
393
     * @param string $suffix
394
     *
395
     * @return array
396
     */
397
    public function getRenderTemplates($suffix = '')
398
    {
399
        $classes = ClassInfo::ancestry($this->ClassName);
400
        $classes[static::class] = static::class;
401
        $classes = array_reverse($classes);
402
        $templates = [];
403
404
        foreach ($classes as $key => $class) {
405
            if ($class == BaseElement::class) {
406
                continue;
407
            }
408
409
            if ($class == DataObject::class) {
410
                break;
411
            }
412
413
            if ($style = $this->Style) {
414
                $templates[$class][] = $class . $suffix . '_'. $this->getAreaRelationName() . '_' . $style;
415
                $templates[$class][] = $class . $suffix . '_' . $style;
416
            }
417
            $templates[$class][] = $class . $suffix . '_'. $this->getAreaRelationName();
418
            $templates[$class][] = $class . $suffix;
419
        }
420
421
        $this->extend('updateRenderTemplates', $templates, $suffix);
422
423
        $templateFlat = [];
424
        foreach ($templates as $class => $variations) {
425
            $templateFlat = array_merge($templateFlat, $variations);
426
        }
427
428
        return $templateFlat;
429
    }
430
431
    /**
432
     * Strip all namespaces from class namespace.
433
     *
434
     * @param string $classname e.g. "\Fully\Namespaced\Class"
435
     *
436
     * @return string following the param example, "Class"
437
     */
438
    protected function stripNamespacing($classname)
439
    {
440
        $classParts = explode('\\', $classname);
441
        return array_pop($classParts);
442
    }
443
444
    /**
445
     * @return string
446
     */
447
    public function getSimpleClassName()
448
    {
449
        return strtolower($this->sanitiseClassName($this->ClassName, '__'));
450
    }
451
452
    /**
453
     * @return null|DataObject
454
     * @throws \Psr\Container\NotFoundExceptionInterface
455
     * @throws \SilverStripe\ORM\ValidationException
456
     */
457
    public function getPage()
458
    {
459
        $area = $this->Parent();
460
461
        if ($area instanceof ElementalArea && $area->exists()) {
462
            return $area->getOwnerPage();
463
        }
464
465
        return null;
466
    }
467
468
    /**
469
     * Get a unique anchor name
470
     *
471
     * @return string
472
     */
473
    public function getAnchor()
474
    {
475
        if ($this->anchor !== null) {
476
            return $this->anchor;
477
        }
478
479
        $anchorTitle = '';
480
481
        if (!$this->config()->disable_pretty_anchor_name) {
482
            if ($this->hasMethod('getAnchorTitle')) {
483
                $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

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

583
        /** @scrutinizer ignore-call */ 
584
        $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...
584
585
        if (!$page) {
586
            return null;
587
        }
588
589
        $editLinkPrefix = '';
590
        if (!$page instanceof SiteTree && method_exists($page, 'CMSEditLink')) {
591
            $editLinkPrefix = Controller::join_links($page->CMSEditLink(), 'ItemEditForm');
592
        } else {
593
            $editLinkPrefix = Controller::join_links(
594
                singleton(CMSPageEditController::class)->Link('EditForm'),
595
                $page->ID
596
            );
597
        }
598
599
        $link = Controller::join_links(
600
            $editLinkPrefix,
601
            'field/' . $relationName . '/item/',
602
            $this->ID
603
        );
604
605
        $link = Controller::join_links(
606
            $link,
607
            'edit'
608
        );
609
610
        $this->extend('updateCMSEditLink', $link);
611
612
        return $link;
613
    }
614
615
    /**
616
     * Retrieve a elemental area relation for creating cms links
617
     *
618
     * @return int|string The name of a valid elemental area relation
619
     * @throws \Psr\Container\NotFoundExceptionInterface
620
     * @throws \SilverStripe\ORM\ValidationException
621
     */
622
    public function getAreaRelationName()
623
    {
624
        $page = $this->getPage();
625
626
        if ($page) {
627
            $has_one = $page->config()->get('has_one');
628
            $area = $this->Parent();
629
630
            foreach ($has_one as $relationName => $relationClass) {
631
                if ($page instanceof BaseElement && $relationName === 'Parent') {
632
                    continue;
633
                }
634
                if ($relationClass === $area->ClassName && $page->{$relationName}()->ID === $area->ID) {
635
                    return $relationName;
636
                }
637
            }
638
        }
639
640
        return 'ElementalArea';
641
    }
642
643
    /**
644
     * Sanitise a model class' name for inclusion in a link.
645
     *
646
     * @return string
647
     */
648
    public function sanitiseClassName($class, $delimiter = '-')
649
    {
650
        return str_replace('\\', $delimiter, $class);
651
    }
652
653
    public function unsanitiseClassName($class, $delimiter = '-')
654
    {
655
        return str_replace($delimiter, '\\', $class);
656
    }
657
658
    /**
659
     * @return null|string
660
     * @throws \Psr\Container\NotFoundExceptionInterface
661
     * @throws \SilverStripe\ORM\ValidationException
662
     */
663
    public function getEditLink()
664
    {
665
        return $this->CMSEditLink();
666
    }
667
668
    /**
669
     * @return DBField|null
670
     * @throws \Psr\Container\NotFoundExceptionInterface
671
     * @throws \SilverStripe\ORM\ValidationException
672
     */
673
    public function PageCMSEditLink()
674
    {
675
        if ($page = $this->getPage()) {
676
            return DBField::create_field('HTMLText', sprintf(
677
                '<a href="%s">%s</a>',
678
                $page->CMSEditLink(),
679
                $page->Title
680
            ));
681
        }
682
683
        return null;
684
    }
685
686
    /**
687
     * @return string
688
     */
689
    public function getMimeType()
690
    {
691
        return 'text/html';
692
    }
693
694
    /**
695
     * This can be overridden on child elements to create a summary for display
696
     * in GridFields.
697
     *
698
     * @return string
699
     */
700
    public function getSummary()
701
    {
702
        return '';
703
    }
704
705
706
    /**
707
     * Generate markup for element type icons suitable for use in GridFields.
708
     *
709
     * @return null|DBHTMLText
710
     */
711
    public function getIcon()
712
    {
713
        $data = ArrayData::create([]);
714
715
        $iconClass = $this->config()->get('icon');
716
        if ($iconClass) {
717
            $data->IconClass = $iconClass;
718
719
            // Add versioned states (rendered as a circle over the icon)
720
            if ($this->hasExtension(Versioned::class)) {
721
                $data->IsVersioned = true;
722
                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

722
                if ($this->/** @scrutinizer ignore-call */ isOnDraftOnly()) {
Loading history...
723
                    $data->VersionState = 'draft';
724
                    $data->VersionStateTitle = _t(
725
                        'SilverStripe\\Versioned\\VersionedGridFieldState\\VersionedGridFieldState.ADDEDTODRAFTHELP',
726
                        'Item has not been published yet'
727
                    );
728
                } 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

728
                } elseif ($this->/** @scrutinizer ignore-call */ isModifiedOnDraft()) {
Loading history...
729
                    $data->VersionState = 'modified';
730
                    $data->VersionStateTitle = $data->VersionStateTitle = _t(
731
                        'SilverStripe\\Versioned\\VersionedGridFieldState\\VersionedGridFieldState.MODIFIEDONDRAFTHELP',
732
                        'Item has unpublished changes'
733
                    );
734
                }
735
            }
736
737
            return $data->renderWith(__CLASS__ . '/PreviewIcon');
738
        }
739
740
        return null;
741
    }
742
743
    /**
744
     * Get a description for this content element, if available
745
     *
746
     * @return string
747
     */
748
    public function getDescription()
749
    {
750
        $description = $this->config()->uninherited('description');
751
        if ($description) {
752
            return _t(__CLASS__ . '.Description', $description);
753
        }
754
        return '';
755
    }
756
757
    /**
758
     * Generate markup for element type, with description suitable for use in
759
     * GridFields.
760
     *
761
     * @return DBField
762
     */
763
    public function getTypeNice()
764
    {
765
        $description = $this->getDescription();
766
        $desc = ($description) ? ' <span class="element__note"> &mdash; ' . $description . '</span>' : '';
767
768
        return DBField::create_field(
769
            'HTMLVarchar',
770
            $this->getType() . $desc
771
        );
772
    }
773
774
    /**
775
     * @return \SilverStripe\ORM\FieldType\DBHTMLText
776
     */
777
    public function getEditorPreview()
778
    {
779
        $templates = $this->getRenderTemplates('_EditorPreview');
780
        $templates[] = BaseElement::class . '_EditorPreview';
781
782
        return $this->renderWith($templates);
783
    }
784
785
    /**
786
     * @return Member
787
     */
788
    public function getAuthor()
789
    {
790
        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...
791
            return Member::get()->byId($this->AuthorID);
792
        }
793
794
        return null;
795
    }
796
797
    /**
798
     * Get a user defined style variant for this element, if available
799
     *
800
     * @return string
801
     */
802
    public function getStyleVariant()
803
    {
804
        $style = $this->Style;
805
        $styles = $this->config()->get('styles');
806
807
        if (isset($styles[$style])) {
808
            $style = strtolower($style);
809
        } else {
810
            $style = '';
811
        }
812
813
        $this->extend('updateStyleVariant', $style);
814
815
        return $style;
816
    }
817
818
    /**
819
     * @return mixed|null
820
     * @throws \Psr\Container\NotFoundExceptionInterface
821
     * @throws \SilverStripe\ORM\ValidationException
822
     */
823
    public function getPageTitle()
824
    {
825
        $page = $this->getPage();
826
827
        if ($page) {
828
            return $page->Title;
829
        }
830
831
        return null;
832
    }
833
834
    /**
835
     * @return boolean
836
     */
837
    public function First()
838
    {
839
        return ($this->Parent()->Elements()->first()->ID === $this->ID);
840
    }
841
842
    /**
843
     * @return boolean
844
     */
845
    public function Last()
846
    {
847
        return ($this->Parent()->Elements()->last()->ID === $this->ID);
848
    }
849
850
    /**
851
     * @return boolean
852
     */
853
    public function TotalItems()
854
    {
855
        return $this->Parent()->Elements()->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->Parent()->Elements()->count() returns the type integer which is incompatible with the documented return type boolean.
Loading history...
856
    }
857
858
    /**
859
     * Returns the position of the current element.
860
     *
861
     * @return boolean
862
     */
863
    public function Pos()
864
    {
865
        return ($this->Parent()->Elements()->filter('Sort:LessThan', $this->Sort)->count() + 1);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->Parent()->...his->Sort)->count() + 1 returns the type integer which is incompatible with the documented return type boolean.
Loading history...
866
    }
867
868
    /**
869
     * @return boolean
870
     */
871
    public function EvenOdd()
872
    {
873
        $odd = (bool) ($this->Pos() % 2);
874
875
        return  ($odd) ? 'odd' : 'even';
0 ignored issues
show
Bug Best Practice introduced by
The expression return $odd ? 'odd' : 'even' returns the type string which is incompatible with the documented return type boolean.
Loading history...
876
    }
877
}
878