Passed
Pull Request — master (#123)
by Robbie
02:03
created

BaseElement::getLastEditedNice()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
117
118
    /**
119
     * Store used anchor names, this is to avoid title clashes
120
     * when calling 'getAnchor'
121
     *
122
     * @var array
123
     */
124
    protected static $_used_anchors = [];
125
126
    /**
127
     * For caching 'getAnchor'
128
     *
129
     * @var string
130
     */
131
    protected $_anchor = null;
132
133
    /**
134
     * Basic permissions, defaults to page perms where possible.
135
     *
136
     * @param Member $member
137
     *
138
     * @return boolean
139
     */
140 View Code Duplication
    public function canView($member = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
141
    {
142
        if ($this->hasMethod('getPage')) {
143
            if ($page = $this->getPage()) {
144
                return $page->canView($member);
145
            }
146
        }
147
148
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
149
    }
150
151
    /**
152
     * Basic permissions, defaults to page perms where possible.
153
     *
154
     * @param Member $member
155
     *
156
     * @return boolean
157
     */
158 View Code Duplication
    public function canEdit($member = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
    {
160
        if ($this->hasMethod('getPage')) {
161
            if ($page = $this->getPage()) {
162
                return $page->canEdit($member);
163
            }
164
        }
165
166
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
167
    }
168
169
    /**
170
     * Basic permissions, defaults to page perms where possible.
171
     *
172
     * Uses archive not delete so that current stage is respected i.e if a
173
     * element is not published, then it can be deleted by someone who doesn't
174
     * have publishing permissions.
175
     *
176
     * @param Member $member
177
     *
178
     * @return boolean
179
     */
180 View Code Duplication
    public function canDelete($member = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181
    {
182
        if ($this->hasMethod('getPage')) {
183
            if ($page = $this->getPage()) {
184
                return $page->canArchive($member);
185
            }
186
        }
187
188
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
189
    }
190
191
    /**
192
     * Basic permissions, defaults to page perms where possible.
193
     *
194
     * @param Member $member
195
     * @param array $context
196
     *
197
     * @return boolean
198
     */
199
    public function canCreate($member = null, $context = array())
200
    {
201
        return (Permission::check('CMS_ACCESS', 'any', $member)) ? true : null;
202
    }
203
204
    /**
205
     *
206
     */
207
    public function onBeforeWrite()
208
    {
209
        parent::onBeforeWrite();
210
211
        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...
212
            if ($elementalArea = ElementalArea::get()->byID($areaID)) {
213
                $elementalArea->write();
214
            }
215
        }
216
217
        if (!$this->Sort) {
218
            $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...
219
220
            $this->Sort = static::get()->max('Sort') + 1;
0 ignored issues
show
Bug Best Practice introduced by
The property Sort does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
221
        }
222
    }
223
224
    public function getCMSFields()
225
    {
226
        $this->beforeUpdateCMSFields(function (FieldList $fields) {
227
            // Remove relationship fields
228
            $fields->removeByName('ParentID');
229
            $fields->removeByName('Sort');
230
231
            $fields->addFieldToTab(
232
                'Root.Settings',
233
                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

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

260
                HiddenField::create('AbsoluteLink', /** @scrutinizer ignore-type */ false, Director::absoluteURL($this->PreviewLink())),
Loading history...
261
                HiddenField::create('LiveLink', false, Director::absoluteURL($this->Link())),
262
                HiddenField::create('StageLink', false, Director::absoluteURL($this->PreviewLink())),
263
            ]);
264
265
            $styles = $this->config()->get('styles');
266
267
            if ($styles && count($styles) > 0) {
268
                $styleDropdown = DropdownField::create('Style', _t(__CLASS__.'.STYLE', 'Style variation'), $styles);
269
270
                $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

270
                $fields->insertBefore($styleDropdown, /** @scrutinizer ignore-type */ 'ExtraClass');
Loading history...
271
272
                $styleDropdown->setEmptyString(_t(__CLASS__.'.CUSTOM_STYLES', 'Select a style..'));
273
            } else {
274
                $fields->removeByName('Style');
275
            }
276
277
            $history = $this->getHistoryFields();
278
279
            if ($history) {
280
                $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

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

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

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

325
                /** @scrutinizer ignore-type */ 'History',
Loading history...
326
                '',
327
                $history,
0 ignored issues
show
Bug introduced by
$history of type SilverStripe\ORM\DataList is incompatible with the type array expected by parameter $args of SilverStripe\View\ViewableData::create(). ( Ignorable by Annotation )

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

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

394
    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...
395
    {
396
        $templates = $this->getRenderTemplates();
397
398
        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...
399
            return $this->renderWith($templates);
400
        }
401
    }
402
403
    /**
404
     * @param string $suffix
405
     *
406
     * @return array
407
     */
408
    public function getRenderTemplates($suffix = '')
409
    {
410
        $classes = ClassInfo::ancestry($this->ClassName);
411
        $classes[static::class] = static::class;
412
        $classes = array_reverse($classes);
413
        $templates = array();
414
415
        foreach ($classes as $key => $value) {
416
            if ($value == BaseElement::class) {
417
                continue;
418
            }
419
420
            if ($value == DataObject::class) {
421
                break;
422
            }
423
424
            $templates[] = $value . $suffix;
425
        }
426
427
        return $templates;
428
    }
429
430
    /**
431
     * Strip all namespaces from class namespace.
432
     *
433
     * @param string $classname e.g. "\Fully\Namespaced\Class"
434
     *
435
     * @return string following the param example, "Class"
436
     */
437
    protected function stripNamespacing($classname)
438
    {
439
        $classParts = explode('\\', $classname);
440
        return array_pop($classParts);
441
    }
442
443
    /**
444
     * @return string
445
     */
446
    public function getSimpleClassName()
447
    {
448
        return strtolower($this->sanitiseClassName($this->ClassName, '__'));
449
    }
450
451
    /**
452
     * @return SiteTree
0 ignored issues
show
Bug introduced by
The type DNADesign\Elemental\Models\SiteTree was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
453
     */
454
    public function getPage()
455
    {
456
        $area = $this->Parent();
0 ignored issues
show
Bug introduced by
The method Parent() does not exist on DNADesign\Elemental\Models\BaseElement. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

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

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

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

571
        /** @scrutinizer ignore-call */ 
572
        $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...
572
573
        if (!$page) {
574
            return null;
575
        }
576
577
        $link = Controller::join_links(
578
            singleton(CMSPageEditController::class)->Link('EditForm'),
579
            $page->ID,
580
            'field/' . $relationName . '/item/',
581
            $this->ID
582
        );
583
584
        return Controller::join_links(
585
            $link,
586
            'edit'
587
        );
588
    }
589
590
    /**
591
     * Retrieve a elemental area relation for creating cms links
592
     *
593
     * @return string - the name of a valid elemental area relation
594
     */
595
    public function getAreaRelationName()
596
    {
597
        $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

597
        /** @scrutinizer ignore-call */ 
598
        $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...
598
599
        if ($page) {
600
            $has_one = $page->config()->get('has_one');
601
            $area = $this->Parent();
602
603
            foreach ($has_one as $relationName => $relationClass) {
604
                if ($relationClass === $area->ClassName) {
605
                    return $relationName;
606
                }
607
            }
608
        }
609
610
        return 'ElementalArea';
611
    }
612
613
    /**
614
     * Sanitise a model class' name for inclusion in a link.
615
     *
616
     * @return string
617
     */
618
    public function sanitiseClassName($class, $delimiter = '-')
619
    {
620
        return str_replace('\\', $delimiter, $class);
621
    }
622
623
    public function unsanitiseClassName($class, $delimiter = '-')
624
    {
625
        return str_replace($delimiter, '\\', $class);
626
    }
627
628
    /**
629
     * @return string
630
     */
631
    public function getEditLink()
632
    {
633
        return $this->CMSEditLink();
634
    }
635
636
    /**
637
     * @return HTMLText
0 ignored issues
show
Bug introduced by
The type DNADesign\Elemental\Models\HTMLText was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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